wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
dnl #############################################
dnl see if we have nanosecond resolution for stat
AC_CACHE_CHECK([for tv_nsec nanosecond fields in struct stat],ac_cv_have_stat_tv_nsec,[
AC_TRY_COMPILE(
[
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
],
[struct stat st;
st.st_mtim.tv_nsec;
st.st_atim.tv_nsec;
st.st_ctim.tv_nsec;
],
ac_cv_decl_have_stat_tv_nsec=yes,
ac_cv_decl_have_stat_tv_nsec=no)
])
if test x"$ac_cv_decl_have_stat_tv_nsec" = x"yes"; then
AC_DEFINE(HAVE_STAT_TV_NSEC,1,[Whether stat has tv_nsec nanosecond fields])
fi
AC_CHECK_HEADERS(blkid/blkid.h)
AC_SEARCH_LIBS_EXT(blkid_get_cache, [blkid], BLKID_LIBS)
AC_CHECK_FUNC_EXT(blkid_get_cache, $BLKID_LIBS)
SMB_EXT_LIB(BLKID,[${BLKID_LIBS}],[${BLKID_CFLAGS}],[${BLKID_CPPFLAGS}],[${BLKID_LDFLAGS}])
if test x"$ac_cv_func_ext_blkid_get_cache" = x"yes"; then
AC_DEFINE(HAVE_LIBBLKID,1,[Whether we have blkid support (e2fsprogs)])
SMB_ENABLE(BLKID,YES)
fi
+61
View File
@@ -0,0 +1,61 @@
################################################
# Start MODULE pvfs_acl_xattr
[MODULE::pvfs_acl_xattr]
INIT_FUNCTION = pvfs_acl_xattr_init
SUBSYSTEM = ntvfs
OBJ_FILES = \
pvfs_acl_xattr.o
PRIVATE_DEPENDENCIES = NDR_XATTR ntvfs_posix
# End MODULE pvfs_acl_xattr
################################################
################################################
# Start MODULE pvfs_acl_nfs4
[MODULE::pvfs_acl_nfs4]
INIT_FUNCTION = pvfs_acl_nfs4_init
SUBSYSTEM = ntvfs
OBJ_FILES = \
pvfs_acl_nfs4.o
PRIVATE_DEPENDENCIES = NDR_NFS4ACL SAMDB ntvfs_posix
# End MODULE pvfs_acl_nfs4
################################################
################################################
# Start MODULE ntvfs_posix
[MODULE::ntvfs_posix]
SUBSYSTEM = ntvfs
OUTPUT_TYPE = INTEGRATED
INIT_FUNCTION = ntvfs_posix_init
PRIVATE_PROTO_HEADER = vfs_posix_proto.h
OBJ_FILES = \
vfs_posix.o \
pvfs_util.o \
pvfs_search.o \
pvfs_dirlist.o \
pvfs_fileinfo.o \
pvfs_unlink.o \
pvfs_mkdir.o \
pvfs_open.o \
pvfs_read.o \
pvfs_flush.o \
pvfs_write.o \
pvfs_fsinfo.o \
pvfs_qfileinfo.o \
pvfs_setfileinfo.o \
pvfs_rename.o \
pvfs_resolve.o \
pvfs_shortname.o \
pvfs_lock.o \
pvfs_wait.o \
pvfs_seek.o \
pvfs_ioctl.o \
pvfs_xattr.o \
pvfs_streams.o \
pvfs_acl.o \
pvfs_notify.o \
xattr_system.o \
xattr_tdb.o
#PRIVATE_DEPENDENCIES = pvfs_acl_xattr pvfs_acl_nfs4
PUBLIC_DEPENDENCIES = NDR_XATTR WRAP_XATTR BLKID ntvfs_common MESSAGING
# End MODULE ntvfs_posix
################################################
+739
View File
@@ -0,0 +1,739 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - ACL support
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/auth.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/xattr.h"
#include "libcli/security/security.h"
/* the list of currently registered ACL backends */
static struct pvfs_acl_backend {
const struct pvfs_acl_ops *ops;
} *backends = NULL;
static int num_backends;
/*
register a pvfs acl backend.
The 'name' can be later used by other backends to find the operations
structure for this backend.
*/
_PUBLIC_ NTSTATUS pvfs_acl_register(const struct pvfs_acl_ops *ops)
{
struct pvfs_acl_ops *new_ops;
if (pvfs_acl_backend_byname(ops->name) != NULL) {
DEBUG(0,("pvfs acl backend '%s' already registered\n", ops->name));
return NT_STATUS_OBJECT_NAME_COLLISION;
}
backends = talloc_realloc(talloc_autofree_context(), backends, struct pvfs_acl_backend, num_backends+1);
NT_STATUS_HAVE_NO_MEMORY(backends);
new_ops = talloc_memdup(backends, ops, sizeof(*ops));
new_ops->name = talloc_strdup(new_ops, ops->name);
backends[num_backends].ops = new_ops;
num_backends++;
DEBUG(3,("NTVFS backend '%s' registered\n", ops->name));
return NT_STATUS_OK;
}
/*
return the operations structure for a named backend
*/
_PUBLIC_ const struct pvfs_acl_ops *pvfs_acl_backend_byname(const char *name)
{
int i;
for (i=0;i<num_backends;i++) {
if (strcmp(backends[i].ops->name, name) == 0) {
return backends[i].ops;
}
}
return NULL;
}
/*
map a single access_mask from generic to specific bits for files/dirs
*/
static uint32_t pvfs_translate_mask(uint32_t access_mask)
{
if (access_mask & SEC_MASK_GENERIC) {
if (access_mask & SEC_GENERIC_READ) access_mask |= SEC_RIGHTS_FILE_READ;
if (access_mask & SEC_GENERIC_WRITE) access_mask |= SEC_RIGHTS_FILE_WRITE;
if (access_mask & SEC_GENERIC_EXECUTE) access_mask |= SEC_RIGHTS_FILE_EXECUTE;
if (access_mask & SEC_GENERIC_ALL) access_mask |= SEC_RIGHTS_FILE_ALL;
access_mask &= ~SEC_MASK_GENERIC;
}
return access_mask;
}
/*
map any generic access bits in the given acl
this relies on the fact that the mappings for files and directories
are the same
*/
static void pvfs_translate_generic_bits(struct security_acl *acl)
{
unsigned i;
if (!acl) return;
for (i=0;i<acl->num_aces;i++) {
struct security_ace *ace = &acl->aces[i];
ace->access_mask = pvfs_translate_mask(ace->access_mask);
}
}
/*
setup a default ACL for a file
*/
static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name, int fd,
struct security_descriptor **psd)
{
struct security_descriptor *sd;
NTSTATUS status;
struct security_ace ace;
mode_t mode;
*psd = security_descriptor_initialise(req);
if (*psd == NULL) {
return NT_STATUS_NO_MEMORY;
}
sd = *psd;
status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
sd->type |= SEC_DESC_DACL_PRESENT;
mode = name->st.st_mode;
/*
we provide up to 4 ACEs
- Owner
- Group
- Everyone
- Administrator
*/
/* setup owner ACE */
ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
ace.flags = 0;
ace.trustee = *sd->owner_sid;
ace.access_mask = 0;
if (mode & S_IRUSR) {
if (mode & S_IWUSR) {
ace.access_mask |= SEC_RIGHTS_FILE_ALL;
} else {
ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
}
}
if (mode & S_IWUSR) {
ace.access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
}
if (ace.access_mask) {
security_descriptor_dacl_add(sd, &ace);
}
/* setup group ACE */
ace.trustee = *sd->group_sid;
ace.access_mask = 0;
if (mode & S_IRGRP) {
ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
}
if (mode & S_IWGRP) {
/* note that delete is not granted - this matches posix behaviour */
ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
}
if (ace.access_mask) {
security_descriptor_dacl_add(sd, &ace);
}
/* setup other ACE */
ace.trustee = *dom_sid_parse_talloc(req, SID_WORLD);
ace.access_mask = 0;
if (mode & S_IROTH) {
ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
}
if (mode & S_IWOTH) {
ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
}
if (ace.access_mask) {
security_descriptor_dacl_add(sd, &ace);
}
/* setup system ACE */
ace.trustee = *dom_sid_parse_talloc(req, SID_NT_SYSTEM);
ace.access_mask = SEC_RIGHTS_FILE_ALL;
security_descriptor_dacl_add(sd, &ace);
return NT_STATUS_OK;
}
/*
omit any security_descriptor elements not specified in the given
secinfo flags
*/
static void normalise_sd_flags(struct security_descriptor *sd, uint32_t secinfo_flags)
{
if (!(secinfo_flags & SECINFO_OWNER)) {
sd->owner_sid = NULL;
}
if (!(secinfo_flags & SECINFO_GROUP)) {
sd->group_sid = NULL;
}
if (!(secinfo_flags & SECINFO_DACL)) {
sd->dacl = NULL;
}
if (!(secinfo_flags & SECINFO_SACL)) {
sd->sacl = NULL;
}
}
/*
answer a setfileinfo for an ACL
*/
NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name, int fd,
uint32_t access_mask,
union smb_setfileinfo *info)
{
uint32_t secinfo_flags = info->set_secdesc.in.secinfo_flags;
struct security_descriptor *new_sd, *sd, orig_sd;
NTSTATUS status = NT_STATUS_NOT_FOUND;
uid_t old_uid = -1;
gid_t old_gid = -1;
uid_t new_uid = -1;
gid_t new_gid = -1;
if (pvfs->acl_ops != NULL) {
status = pvfs->acl_ops->acl_load(pvfs, name, fd, req, &sd);
}
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
status = pvfs_default_acl(pvfs, req, name, fd, &sd);
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
new_sd = info->set_secdesc.in.sd;
orig_sd = *sd;
old_uid = name->st.st_uid;
old_gid = name->st.st_gid;
/* only set the elements that have been specified */
if (secinfo_flags & SECINFO_OWNER) {
if (!(access_mask & SEC_STD_WRITE_OWNER)) {
return NT_STATUS_ACCESS_DENIED;
}
if (!dom_sid_equal(sd->owner_sid, new_sd->owner_sid)) {
status = sidmap_sid_to_unixuid(pvfs->sidmap, new_sd->owner_sid, &new_uid);
NT_STATUS_NOT_OK_RETURN(status);
}
sd->owner_sid = new_sd->owner_sid;
}
if (secinfo_flags & SECINFO_GROUP) {
if (!(access_mask & SEC_STD_WRITE_OWNER)) {
return NT_STATUS_ACCESS_DENIED;
}
if (!dom_sid_equal(sd->group_sid, new_sd->group_sid)) {
status = sidmap_sid_to_unixgid(pvfs->sidmap, new_sd->group_sid, &new_gid);
NT_STATUS_NOT_OK_RETURN(status);
}
sd->group_sid = new_sd->group_sid;
}
if (secinfo_flags & SECINFO_DACL) {
if (!(access_mask & SEC_STD_WRITE_DAC)) {
return NT_STATUS_ACCESS_DENIED;
}
sd->dacl = new_sd->dacl;
pvfs_translate_generic_bits(sd->dacl);
}
if (secinfo_flags & SECINFO_SACL) {
if (!(access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
return NT_STATUS_ACCESS_DENIED;
}
sd->sacl = new_sd->sacl;
pvfs_translate_generic_bits(sd->sacl);
}
if (new_uid == old_uid) {
new_uid = -1;
}
if (new_gid == old_gid) {
new_gid = -1;
}
/* if there's something to change try it */
if (new_uid != -1 || new_gid != -1) {
int ret;
if (fd == -1) {
ret = chown(name->full_name, new_uid, new_gid);
} else {
ret = fchown(fd, new_uid, new_gid);
}
if (ret == -1) {
return pvfs_map_errno(pvfs, errno);
}
}
/* we avoid saving if the sd is the same. This means when clients
copy files and end up copying the default sd that we don't
needlessly use xattrs */
if (!security_descriptor_equal(sd, &orig_sd) && pvfs->acl_ops) {
status = pvfs->acl_ops->acl_save(pvfs, name, fd, sd);
}
return status;
}
/*
answer a fileinfo query for the ACL
*/
NTSTATUS pvfs_acl_query(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name, int fd,
union smb_fileinfo *info)
{
NTSTATUS status = NT_STATUS_NOT_FOUND;
struct security_descriptor *sd;
if (pvfs->acl_ops) {
status = pvfs->acl_ops->acl_load(pvfs, name, fd, req, &sd);
}
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
status = pvfs_default_acl(pvfs, req, name, fd, &sd);
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
normalise_sd_flags(sd, info->query_secdesc.in.secinfo_flags);
info->query_secdesc.out.sd = sd;
return NT_STATUS_OK;
}
/*
check the read only bit against any of the write access bits
*/
static BOOL pvfs_read_only(struct pvfs_state *pvfs, uint32_t access_mask)
{
if ((pvfs->flags & PVFS_FLAG_READONLY) &&
(access_mask & (SEC_FILE_WRITE_DATA |
SEC_FILE_APPEND_DATA |
SEC_FILE_WRITE_EA |
SEC_FILE_WRITE_ATTRIBUTE |
SEC_STD_DELETE |
SEC_STD_WRITE_DAC |
SEC_STD_WRITE_OWNER |
SEC_DIR_DELETE_CHILD))) {
return True;
}
return False;
}
/*
default access check function based on unix permissions
doing this saves on building a full security descriptor
for the common case of access check on files with no
specific NT ACL
*/
NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name,
uint32_t *access_mask)
{
uid_t uid = geteuid();
uint32_t max_bits = SEC_RIGHTS_FILE_READ | SEC_FILE_ALL;
if (pvfs_read_only(pvfs, *access_mask)) {
return NT_STATUS_ACCESS_DENIED;
}
/* owner and root get extra permissions */
if (uid == 0) {
max_bits |= SEC_STD_ALL | SEC_FLAG_SYSTEM_SECURITY;
} else if (uid == name->st.st_uid) {
max_bits |= SEC_STD_ALL;
}
if (*access_mask == SEC_FLAG_MAXIMUM_ALLOWED) {
*access_mask = max_bits;
return NT_STATUS_OK;
}
if (uid != 0 && (*access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
return NT_STATUS_PRIVILEGE_NOT_HELD;
}
if (*access_mask & ~max_bits) {
return NT_STATUS_ACCESS_DENIED;
}
*access_mask |= SEC_FILE_READ_ATTRIBUTE;
return NT_STATUS_OK;
}
/*
check the security descriptor on a file, if any
*access_mask is modified with the access actually granted
*/
NTSTATUS pvfs_access_check(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name,
uint32_t *access_mask)
{
struct security_token *token = req->session_info->security_token;
struct xattr_NTACL *acl;
NTSTATUS status;
struct security_descriptor *sd;
if (pvfs_read_only(pvfs, *access_mask)) {
return NT_STATUS_ACCESS_DENIED;
}
acl = talloc(req, struct xattr_NTACL);
if (acl == NULL) {
return NT_STATUS_NO_MEMORY;
}
/* expand the generic access bits to file specific bits */
*access_mask = pvfs_translate_mask(*access_mask);
*access_mask &= ~SEC_FILE_READ_ATTRIBUTE;
status = pvfs_acl_load(pvfs, name, -1, acl);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
talloc_free(acl);
return pvfs_access_check_unix(pvfs, req, name, access_mask);
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
switch (acl->version) {
case 1:
sd = acl->info.sd;
break;
default:
return NT_STATUS_INVALID_ACL;
}
/* check the acl against the required access mask */
status = sec_access_check(sd, token, *access_mask, access_mask);
/* this bit is always granted, even if not asked for */
*access_mask |= SEC_FILE_READ_ATTRIBUTE;
talloc_free(acl);
return status;
}
/*
a simplified interface to access check, designed for calls that
do not take or return an access check mask
*/
NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name,
uint32_t access_needed)
{
if (access_needed == 0) {
return NT_STATUS_OK;
}
return pvfs_access_check(pvfs, req, name, &access_needed);
}
/*
access check for creating a new file/directory
*/
NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name,
uint32_t *access_mask)
{
struct pvfs_filename *parent;
NTSTATUS status;
status = pvfs_resolve_parent(pvfs, req, name, &parent);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_access_check(pvfs, req, parent, access_mask);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (! ((*access_mask) & SEC_DIR_ADD_FILE)) {
return pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
}
return status;
}
/*
access check for creating a new file/directory - no access mask supplied
*/
NTSTATUS pvfs_access_check_parent(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name,
uint32_t access_mask)
{
struct pvfs_filename *parent;
NTSTATUS status;
status = pvfs_resolve_parent(pvfs, req, name, &parent);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return pvfs_access_check_simple(pvfs, req, parent, access_mask);
}
/*
determine if an ACE is inheritable
*/
static BOOL pvfs_inheritable_ace(struct pvfs_state *pvfs,
const struct security_ace *ace,
BOOL container)
{
if (!container) {
return (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) != 0;
}
if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
return True;
}
if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) &&
!(ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
return True;
}
return False;
}
/*
this is the core of ACL inheritance. It copies any inheritable
aces from the parent SD to the child SD. Note that the algorithm
depends on whether the child is a container or not
*/
static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs,
struct security_descriptor *parent_sd,
struct security_descriptor *sd,
BOOL container)
{
int i;
for (i=0;i<parent_sd->dacl->num_aces;i++) {
struct security_ace ace = parent_sd->dacl->aces[i];
NTSTATUS status;
const struct dom_sid *creator = NULL, *new_id = NULL;
uint32_t orig_flags;
if (!pvfs_inheritable_ace(pvfs, &ace, container)) {
continue;
}
orig_flags = ace.flags;
/* see the RAW-ACLS inheritance test for details on these rules */
if (!container) {
ace.flags = 0;
} else {
ace.flags &= ~SEC_ACE_FLAG_INHERIT_ONLY;
if (!(ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
ace.flags |= SEC_ACE_FLAG_INHERIT_ONLY;
}
if (ace.flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
ace.flags = 0;
}
}
/* the CREATOR sids are special when inherited */
if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_owner)) {
creator = pvfs->sid_cache.creator_owner;
new_id = sd->owner_sid;
} else if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_group)) {
creator = pvfs->sid_cache.creator_group;
new_id = sd->group_sid;
} else {
new_id = &ace.trustee;
}
if (creator && container &&
(ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
uint32_t flags = ace.flags;
ace.trustee = *new_id;
ace.flags = 0;
status = security_descriptor_dacl_add(sd, &ace);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
ace.trustee = *creator;
ace.flags = flags | SEC_ACE_FLAG_INHERIT_ONLY;
status = security_descriptor_dacl_add(sd, &ace);
} else if (container &&
!(orig_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
status = security_descriptor_dacl_add(sd, &ace);
} else {
ace.trustee = *new_id;
status = security_descriptor_dacl_add(sd, &ace);
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
}
return NT_STATUS_OK;
}
/*
setup an ACL on a new file/directory based on the inherited ACL from
the parent. If there is no inherited ACL then we don't set anything,
as the default ACL applies anyway
*/
NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name,
int fd)
{
struct xattr_NTACL *acl;
NTSTATUS status;
struct pvfs_filename *parent;
struct security_descriptor *parent_sd, *sd;
BOOL container;
/* form the parents path */
status = pvfs_resolve_parent(pvfs, req, name, &parent);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
acl = talloc(req, struct xattr_NTACL);
if (acl == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = pvfs_acl_load(pvfs, parent, -1, acl);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
return NT_STATUS_OK;
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
switch (acl->version) {
case 1:
parent_sd = acl->info.sd;
break;
default:
return NT_STATUS_INVALID_ACL;
}
if (parent_sd == NULL ||
parent_sd->dacl == NULL ||
parent_sd->dacl->num_aces == 0) {
/* go with the default ACL */
return NT_STATUS_OK;
}
/* create the new sd */
sd = security_descriptor_initialise(req);
if (sd == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
sd->type |= SEC_DESC_DACL_PRESENT;
container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? True:False;
/* fill in the aces from the parent */
status = pvfs_acl_inherit_aces(pvfs, parent_sd, sd, container);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* if there is nothing to inherit then we fallback to the
default acl */
if (sd->dacl == NULL || sd->dacl->num_aces == 0) {
return NT_STATUS_OK;
}
acl->info.sd = sd;
status = pvfs_acl_save(pvfs, name, fd, acl);
return status;
}
+163
View File
@@ -0,0 +1,163 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - NT ACLs mapped to NFS4 ACLs, as per
http://www.suse.de/~agruen/nfs4acl/
Copyright (C) Andrew Tridgell 2006
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "lib/util/unix_privs.h"
#include "librpc/gen_ndr/ndr_nfs4acl.h"
#include "libcli/security/security.h"
#define ACE4_IDENTIFIER_GROUP 0x40
/*
load the current ACL from system.nfs4acl
*/
static NTSTATUS pvfs_acl_load_nfs4(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
TALLOC_CTX *mem_ctx,
struct security_descriptor **psd)
{
NTSTATUS status;
struct nfs4acl *acl;
struct security_descriptor *sd;
int i;
acl = talloc_zero(mem_ctx, struct nfs4acl);
NT_STATUS_HAVE_NO_MEMORY(acl);
status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, fd,
NFS4ACL_XATTR_NAME,
acl, ndr_pull_nfs4acl);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(acl);
return status;
}
*psd = security_descriptor_initialise(mem_ctx);
NT_STATUS_HAVE_NO_MEMORY(*psd);
sd = *psd;
sd->type |= acl->a_flags;
status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
NT_STATUS_NOT_OK_RETURN(status);
status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
NT_STATUS_NOT_OK_RETURN(status);
for (i=0;i<acl->a_count;i++) {
struct nfs4ace *a = &acl->ace[i];
struct security_ace ace;
struct dom_sid *sid;
ace.type = a->e_type;
ace.flags = a->e_flags;
ace.access_mask = a->e_mask;
if (a->e_flags & ACE4_IDENTIFIER_GROUP) {
status = sidmap_gid_to_sid(pvfs->sidmap, sd, a->e_id, &sid);
} else {
status = sidmap_uid_to_sid(pvfs->sidmap, sd, a->e_id, &sid);
}
NT_STATUS_NOT_OK_RETURN(status);
ace.trustee = *sid;
security_descriptor_dacl_add(sd, &ace);
}
return NT_STATUS_OK;
}
/*
save the acl for a file into system.nfs4acl
*/
static NTSTATUS pvfs_acl_save_nfs4(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
struct security_descriptor *sd)
{
NTSTATUS status;
void *privs;
struct nfs4acl acl;
int i;
TALLOC_CTX *tmp_ctx;
tmp_ctx = talloc_new(pvfs);
NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
acl.a_version = 0;
acl.a_flags = sd->type;
acl.a_count = sd->dacl?sd->dacl->num_aces:0;
acl.a_owner_mask = 0;
acl.a_group_mask = 0;
acl.a_other_mask = 0;
acl.ace = talloc_array(tmp_ctx, struct nfs4ace, acl.a_count);
if (!acl.ace) {
talloc_free(tmp_ctx);
return NT_STATUS_NO_MEMORY;
}
for (i=0;i<acl.a_count;i++) {
struct nfs4ace *a = &acl.ace[i];
struct security_ace *ace = &sd->dacl->aces[i];
a->e_type = ace->type;
a->e_flags = ace->flags;
a->e_mask = ace->access_mask;
if (sidmap_sid_is_group(pvfs->sidmap, &ace->trustee)) {
gid_t gid;
a->e_flags |= ACE4_IDENTIFIER_GROUP;
status = sidmap_sid_to_unixgid(pvfs->sidmap, &ace->trustee, &gid);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(tmp_ctx);
return status;
}
a->e_id = gid;
} else {
uid_t uid;
status = sidmap_sid_to_unixuid(pvfs->sidmap, &ace->trustee, &uid);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(tmp_ctx);
return status;
}
a->e_id = uid;
}
a->e_who = "";
}
privs = root_privileges();
status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
NFS4ACL_XATTR_NAME,
&acl, ndr_push_nfs4acl);
talloc_free(privs);
talloc_free(tmp_ctx);
return status;
}
/*
initialise pvfs acl NFS4 backend
*/
NTSTATUS pvfs_acl_nfs4_init(void)
{
struct pvfs_acl_ops ops = {
.name = "nfs4acl",
.acl_load = pvfs_acl_load_nfs4,
.acl_save = pvfs_acl_save_nfs4
};
return pvfs_acl_register(&ops);
}
+105
View File
@@ -0,0 +1,105 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - NT ACLs in xattrs
Copyright (C) Andrew Tridgell 2006
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "lib/util/unix_privs.h"
#include "librpc/gen_ndr/ndr_xattr.h"
/*
load the current ACL from extended attributes
*/
static NTSTATUS pvfs_acl_load_xattr(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
TALLOC_CTX *mem_ctx,
struct security_descriptor **sd)
{
NTSTATUS status;
struct xattr_NTACL *acl;
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_NOT_FOUND;
}
acl = talloc_zero(mem_ctx, struct xattr_NTACL);
NT_STATUS_HAVE_NO_MEMORY(acl);
status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, fd,
XATTR_NTACL_NAME,
acl,
(ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(acl);
return status;
}
if (acl->version != 1) {
talloc_free(acl);
return NT_STATUS_INVALID_ACL;
}
*sd = talloc_steal(mem_ctx, acl->info.sd);
return NT_STATUS_OK;
}
/*
save the acl for a file into filesystem xattr
*/
static NTSTATUS pvfs_acl_save_xattr(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
struct security_descriptor *sd)
{
NTSTATUS status;
void *privs;
struct xattr_NTACL acl;
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_OK;
}
acl.version = 1;
acl.info.sd = sd;
/* this xattr is in the "system" namespace, so we need
admin privileges to set it */
privs = root_privileges();
status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
XATTR_NTACL_NAME,
&acl,
(ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
talloc_free(privs);
return status;
}
/*
initialise pvfs acl xattr backend
*/
NTSTATUS pvfs_acl_xattr_init(void)
{
struct pvfs_acl_ops ops = {
.name = "xattr",
.acl_load = pvfs_acl_load_xattr,
.acl_save = pvfs_acl_save_xattr
};
return pvfs_acl_register(&ops);
}
+404
View File
@@ -0,0 +1,404 @@
/*
Unix SMB/CIFS implementation.
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
directory listing functions for posix backend
*/
#include "includes.h"
#include "vfs_posix.h"
#include "system/dir.h"
#define NAME_CACHE_SIZE 100
struct name_cache_entry {
char *name;
off_t offset;
};
struct pvfs_dir {
struct pvfs_state *pvfs;
BOOL no_wildcard;
char *single_name;
const char *pattern;
off_t offset;
DIR *dir;
const char *unix_path;
BOOL end_of_search;
struct name_cache_entry *name_cache;
uint32_t name_cache_index;
};
/* these three numbers are chosen to minimise the chances of a bad
interaction with the OS value for 'end of directory'. On IRIX
telldir() returns 0xFFFFFFFF at the end of a directory, and that
caused an infinite loop with the original values of 0,1,2
On XFS on linux telldir returns 0x7FFFFFFF at the end of a
directory. Thus the change from 0x80000002, as otherwise
0x7FFFFFFF+0x80000002==1==DIR_OFFSET_DOTDOT
*/
#define DIR_OFFSET_DOT 0
#define DIR_OFFSET_DOTDOT 1
#define DIR_OFFSET_BASE 0x80000022
/*
a special directory listing case where the pattern has no wildcard. We can just do a single stat()
thus avoiding the more expensive directory scan
*/
static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name,
const char *pattern, struct pvfs_dir *dir)
{
if (!name->exists) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
dir->pvfs = pvfs;
dir->no_wildcard = True;
dir->end_of_search = False;
dir->unix_path = talloc_strdup(dir, name->full_name);
if (!dir->unix_path) {
return NT_STATUS_NO_MEMORY;
}
dir->single_name = talloc_strdup(dir, pattern);
if (!dir->single_name) {
return NT_STATUS_NO_MEMORY;
}
dir->dir = NULL;
dir->offset = 0;
dir->pattern = NULL;
return NT_STATUS_OK;
}
/*
destroy an open search
*/
static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
{
if (dir->dir) closedir(dir->dir);
return 0;
}
/*
start to read a directory
if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
*/
NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
{
char *pattern;
struct pvfs_dir *dir;
(*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
if (*dirp == NULL) {
return NT_STATUS_NO_MEMORY;
}
dir = *dirp;
/* split the unix path into a directory + pattern */
pattern = strrchr(name->full_name, '/');
if (!pattern) {
/* this should not happen, as pvfs_unix_path is supposed to
return an absolute path */
return NT_STATUS_UNSUCCESSFUL;
}
*pattern++ = 0;
if (!name->has_wildcard) {
return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
}
dir->unix_path = talloc_strdup(dir, name->full_name);
if (!dir->unix_path) {
return NT_STATUS_NO_MEMORY;
}
dir->pattern = talloc_strdup(dir, pattern);
if (dir->pattern == NULL) {
return NT_STATUS_NO_MEMORY;
}
dir->dir = opendir(name->full_name);
if (!dir->dir) {
return pvfs_map_errno(pvfs, errno);
}
dir->pvfs = pvfs;
dir->no_wildcard = False;
dir->end_of_search = False;
dir->offset = DIR_OFFSET_DOT;
dir->name_cache = talloc_zero_array(dir,
struct name_cache_entry,
NAME_CACHE_SIZE);
if (dir->name_cache == NULL) {
talloc_free(dir);
return NT_STATUS_NO_MEMORY;
}
talloc_set_destructor(dir, pvfs_dirlist_destructor);
return NT_STATUS_OK;
}
/*
add an entry to the local cache
*/
static void dcache_add(struct pvfs_dir *dir, const char *name)
{
struct name_cache_entry *e;
dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
e = &dir->name_cache[dir->name_cache_index];
if (e->name) talloc_free(e->name);
e->name = talloc_strdup(dir->name_cache, name);
e->offset = dir->offset;
}
/*
return the next entry
*/
const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
{
struct dirent *de;
enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
/* non-wildcard searches are easy */
if (dir->no_wildcard) {
dir->end_of_search = True;
if (*ofs != 0) return NULL;
(*ofs)++;
return dir->single_name;
}
/* . and .. are handled separately as some unix systems will
not return them first in a directory, but windows client
may assume that these entries always appear first */
if (*ofs == DIR_OFFSET_DOT) {
(*ofs) = DIR_OFFSET_DOTDOT;
dir->offset = *ofs;
if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
dcache_add(dir, ".");
return ".";
}
}
if (*ofs == DIR_OFFSET_DOTDOT) {
(*ofs) = DIR_OFFSET_BASE;
dir->offset = *ofs;
if (ms_fnmatch(dir->pattern, "..", protocol) == 0) {
dcache_add(dir, "..");
return "..";
}
}
if (*ofs == DIR_OFFSET_BASE) {
rewinddir(dir->dir);
} else if (*ofs != dir->offset) {
seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
}
dir->offset = *ofs;
while ((de = readdir(dir->dir))) {
const char *dname = de->d_name;
if (ISDOT(dname) || ISDOTDOT(dname)) {
continue;
}
if (ms_fnmatch(dir->pattern, dname, protocol) != 0) {
char *short_name = pvfs_short_name_component(dir->pvfs, dname);
if (short_name == NULL ||
ms_fnmatch(dir->pattern, short_name, protocol) != 0) {
talloc_free(short_name);
continue;
}
talloc_free(short_name);
}
dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
(*ofs) = dir->offset;
dcache_add(dir, dname);
return dname;
}
dir->end_of_search = True;
return NULL;
}
/*
return unix directory of an open search
*/
const char *pvfs_list_unix_path(struct pvfs_dir *dir)
{
return dir->unix_path;
}
/*
return True if end of search has been reached
*/
BOOL pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
{
return dir->end_of_search;
}
/*
seek to the given name
*/
NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
{
struct dirent *de;
int i;
dir->end_of_search = False;
if (ISDOT(name)) {
dir->offset = DIR_OFFSET_DOTDOT;
*ofs = dir->offset;
return NT_STATUS_OK;
}
if (ISDOTDOT(name)) {
dir->offset = DIR_OFFSET_BASE;
*ofs = dir->offset;
return NT_STATUS_OK;
}
for (i=dir->name_cache_index;i>=0;i--) {
struct name_cache_entry *e = &dir->name_cache[i];
if (e->name && strcasecmp_m(name, e->name) == 0) {
*ofs = e->offset;
return NT_STATUS_OK;
}
}
for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
struct name_cache_entry *e = &dir->name_cache[i];
if (e->name && strcasecmp_m(name, e->name) == 0) {
*ofs = e->offset;
return NT_STATUS_OK;
}
}
rewinddir(dir->dir);
while ((de = readdir(dir->dir))) {
if (strcasecmp_m(name, de->d_name) == 0) {
dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
*ofs = dir->offset;
return NT_STATUS_OK;
}
}
dir->end_of_search = True;
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
/*
seek to the given offset
*/
NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
{
struct dirent *de;
int i;
dir->end_of_search = False;
if (resume_key == DIR_OFFSET_DOT) {
*ofs = DIR_OFFSET_DOTDOT;
return NT_STATUS_OK;
}
if (resume_key == DIR_OFFSET_DOTDOT) {
*ofs = DIR_OFFSET_BASE;
return NT_STATUS_OK;
}
if (resume_key == DIR_OFFSET_BASE) {
rewinddir(dir->dir);
if ((de=readdir(dir->dir)) == NULL) {
dir->end_of_search = True;
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
*ofs = telldir(dir->dir) + DIR_OFFSET_BASE;
dir->offset = *ofs;
return NT_STATUS_OK;
}
for (i=dir->name_cache_index;i>=0;i--) {
struct name_cache_entry *e = &dir->name_cache[i];
if (resume_key == (uint32_t)e->offset) {
*ofs = e->offset;
return NT_STATUS_OK;
}
}
for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
struct name_cache_entry *e = &dir->name_cache[i];
if (resume_key == (uint32_t)e->offset) {
*ofs = e->offset;
return NT_STATUS_OK;
}
}
rewinddir(dir->dir);
while ((de = readdir(dir->dir))) {
dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
if (resume_key == (uint32_t)dir->offset) {
*ofs = dir->offset;
return NT_STATUS_OK;
}
}
dir->end_of_search = True;
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
/*
see if a directory is empty
*/
BOOL pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
{
struct dirent *de;
DIR *dir = opendir(name->full_name);
if (dir == NULL) {
return True;
}
while ((de = readdir(dir))) {
if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
closedir(dir);
return False;
}
}
closedir(dir);
return True;
}
+121
View File
@@ -0,0 +1,121 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend -
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
/****************************************************************************
Change a unix mode to a dos mode.
****************************************************************************/
static uint32_t dos_mode_from_stat(struct pvfs_state *pvfs, struct stat *st)
{
int result = 0;
if ((st->st_mode & S_IWUSR) == 0)
result |= FILE_ATTRIBUTE_READONLY;
if ((pvfs->flags & PVFS_FLAG_MAP_ARCHIVE) && ((st->st_mode & S_IXUSR) != 0))
result |= FILE_ATTRIBUTE_ARCHIVE;
if ((pvfs->flags & PVFS_FLAG_MAP_SYSTEM) && ((st->st_mode & S_IXGRP) != 0))
result |= FILE_ATTRIBUTE_SYSTEM;
if ((pvfs->flags & PVFS_FLAG_MAP_HIDDEN) && ((st->st_mode & S_IXOTH) != 0))
result |= FILE_ATTRIBUTE_HIDDEN;
if (S_ISDIR(st->st_mode))
result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
return result;
}
/*
fill in the dos file attributes for a file
*/
NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
{
/* make directories appear as size 0 with 1 link */
if (S_ISDIR(name->st.st_mode)) {
name->st.st_size = 0;
name->st.st_nlink = 1;
}
/* for now just use the simple samba mapping */
unix_to_nt_time(&name->dos.create_time, name->st.st_ctime);
unix_to_nt_time(&name->dos.access_time, name->st.st_atime);
unix_to_nt_time(&name->dos.write_time, name->st.st_mtime);
unix_to_nt_time(&name->dos.change_time, name->st.st_ctime);
#ifdef HAVE_STAT_TV_NSEC
name->dos.create_time += name->st.st_ctim.tv_nsec / 100;
name->dos.access_time += name->st.st_atim.tv_nsec / 100;
name->dos.write_time += name->st.st_mtim.tv_nsec / 100;
name->dos.change_time += name->st.st_ctim.tv_nsec / 100;
#endif
name->dos.attrib = dos_mode_from_stat(pvfs, &name->st);
name->dos.alloc_size = pvfs_round_alloc_size(pvfs, name->st.st_size);
name->dos.nlink = name->st.st_nlink;
name->dos.ea_size = 4;
name->dos.file_id = (((uint64_t)name->st.st_dev)<<32) | name->st.st_ino;
name->dos.flags = 0;
return pvfs_dosattrib_load(pvfs, name, fd);
}
/*
return a set of unix file permissions for a new file or directory
*/
mode_t pvfs_fileperms(struct pvfs_state *pvfs, uint32_t attrib)
{
mode_t mode = S_IRUSR;
if (attrib & FILE_ATTRIBUTE_DIRECTORY) {
mode |= S_IXUSR;
}
if (!(attrib & FILE_ATTRIBUTE_READONLY) ||
(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
mode |= S_IWUSR;
}
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
if ((attrib & FILE_ATTRIBUTE_ARCHIVE) &&
(pvfs->flags & PVFS_FLAG_MAP_ARCHIVE)) {
mode |= S_IXUSR;
}
if ((attrib & FILE_ATTRIBUTE_SYSTEM) &&
(pvfs->flags & PVFS_FLAG_MAP_SYSTEM)) {
mode |= S_IXGRP;
}
if ((attrib & FILE_ATTRIBUTE_HIDDEN) &&
(pvfs->flags & PVFS_FLAG_MAP_HIDDEN)) {
mode |= S_IXOTH;
}
}
return mode;
}
+79
View File
@@ -0,0 +1,79 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - flush
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
/*
flush a single open file
*/
static void pvfs_flush_file(struct pvfs_state *pvfs, struct pvfs_file *f)
{
if (f->handle->fd == -1) {
return;
}
if (pvfs->flags & PVFS_FLAG_STRICT_SYNC) {
fsync(f->handle->fd);
}
}
/*
flush a fnum
*/
NTSTATUS pvfs_flush(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_flush *io)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f;
switch (io->generic.level) {
case RAW_FLUSH_FLUSH:
case RAW_FLUSH_SMB2:
/* TODO: take care of io->smb2.in.unknown */
f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
pvfs_flush_file(pvfs, f);
return NT_STATUS_OK;
case RAW_FLUSH_ALL:
if (!(pvfs->flags & PVFS_FLAG_STRICT_SYNC)) {
return NT_STATUS_OK;
}
/*
* they are asking to flush all open files
* for the given SMBPID
*/
for (f=pvfs->files.list;f;f=f->next) {
if (f->ntvfs->smbpid != req->smbpid) continue;
pvfs_flush_file(pvfs, f);
}
return NT_STATUS_OK;
}
return NT_STATUS_INVALID_LEVEL;
}
+208
View File
@@ -0,0 +1,208 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - fsinfo
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/xattr.h"
#include "librpc/ndr/libndr.h"
/* We use libblkid out of e2fsprogs to identify UUID of a volume */
#ifdef HAVE_LIBBLKID
#include <blkid/blkid.h>
#endif
static NTSTATUS pvfs_blkid_fs_uuid(struct pvfs_state *pvfs, struct stat *st, struct GUID *uuid)
{
#ifdef HAVE_LIBBLKID
NTSTATUS status;
char *uuid_value = NULL;
char *devname = NULL;
devname = blkid_devno_to_devname(st->st_dev);
if (!devname) {
ZERO_STRUCTP(uuid);
return NT_STATUS_OK;
}
uuid_value = blkid_get_tag_value(NULL, "UUID", devname);
free(devname);
if (!uuid_value) {
ZERO_STRUCTP(uuid);
return NT_STATUS_OK;
}
status = GUID_from_string(uuid_value, uuid);
free(uuid_value);
if (!NT_STATUS_IS_OK(status)) {
ZERO_STRUCTP(uuid);
return NT_STATUS_OK;
}
return NT_STATUS_OK;
#else
ZERO_STRUCTP(uuid);
return NT_STATUS_OK;
#endif
}
static NTSTATUS pvfs_cache_base_fs_uuid(struct pvfs_state *pvfs, struct stat *st)
{
NTSTATUS status;
struct GUID uuid;
if (pvfs->base_fs_uuid) return NT_STATUS_OK;
status = pvfs_blkid_fs_uuid(pvfs, st, &uuid);
NT_STATUS_NOT_OK_RETURN(status);
pvfs->base_fs_uuid = talloc(pvfs, struct GUID);
NT_STATUS_HAVE_NO_MEMORY(pvfs->base_fs_uuid);
*pvfs->base_fs_uuid = uuid;
return NT_STATUS_OK;
}
/*
return filesystem space info
*/
NTSTATUS pvfs_fsinfo(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_fsinfo *fs)
{
NTSTATUS status;
struct pvfs_state *pvfs = ntvfs->private_data;
uint64_t blocks_free, blocks_total;
uint_t bpunit;
struct stat st;
const uint16_t block_size = 512;
/* only some levels need the expensive sys_fsusage() call */
switch (fs->generic.level) {
case RAW_QFS_DSKATTR:
case RAW_QFS_ALLOCATION:
case RAW_QFS_SIZE_INFO:
case RAW_QFS_SIZE_INFORMATION:
case RAW_QFS_FULL_SIZE_INFORMATION:
if (sys_fsusage(pvfs->base_directory, &blocks_free, &blocks_total) == -1) {
return pvfs_map_errno(pvfs, errno);
}
default:
break;
}
if (stat(pvfs->base_directory, &st) != 0) {
return NT_STATUS_DISK_CORRUPT_ERROR;
}
/* now fill in the out fields */
switch (fs->generic.level) {
case RAW_QFS_GENERIC:
return NT_STATUS_INVALID_LEVEL;
case RAW_QFS_DSKATTR:
/* we need to scale the sizes to fit */
for (bpunit=64; bpunit<0x10000; bpunit *= 2) {
if (blocks_total * (double)block_size < bpunit * 512 * 65535.0) {
break;
}
}
fs->dskattr.out.blocks_per_unit = bpunit;
fs->dskattr.out.block_size = block_size;
fs->dskattr.out.units_total = (blocks_total * (double)block_size) / (bpunit * 512);
fs->dskattr.out.units_free = (blocks_free * (double)block_size) / (bpunit * 512);
/* we must return a maximum of 2G to old DOS systems, or they get very confused */
if (bpunit > 64 && req->ctx->protocol <= PROTOCOL_LANMAN2) {
fs->dskattr.out.blocks_per_unit = 64;
fs->dskattr.out.units_total = 0xFFFF;
fs->dskattr.out.units_free = 0xFFFF;
}
return NT_STATUS_OK;
case RAW_QFS_ALLOCATION:
fs->allocation.out.fs_id = st.st_dev;
fs->allocation.out.total_alloc_units = blocks_total;
fs->allocation.out.avail_alloc_units = blocks_free;
fs->allocation.out.sectors_per_unit = 1;
fs->allocation.out.bytes_per_sector = block_size;
return NT_STATUS_OK;
case RAW_QFS_VOLUME:
fs->volume.out.serial_number = st.st_ino;
fs->volume.out.volume_name.s = pvfs->share_name;
return NT_STATUS_OK;
case RAW_QFS_VOLUME_INFO:
case RAW_QFS_VOLUME_INFORMATION:
unix_to_nt_time(&fs->volume_info.out.create_time, st.st_ctime);
fs->volume_info.out.serial_number = st.st_ino;
fs->volume_info.out.volume_name.s = pvfs->share_name;
return NT_STATUS_OK;
case RAW_QFS_SIZE_INFO:
case RAW_QFS_SIZE_INFORMATION:
fs->size_info.out.total_alloc_units = blocks_total;
fs->size_info.out.avail_alloc_units = blocks_free;
fs->size_info.out.sectors_per_unit = 1;
fs->size_info.out.bytes_per_sector = block_size;
return NT_STATUS_OK;
case RAW_QFS_DEVICE_INFO:
case RAW_QFS_DEVICE_INFORMATION:
fs->device_info.out.device_type = 0;
fs->device_info.out.characteristics = 0;
return NT_STATUS_OK;
case RAW_QFS_ATTRIBUTE_INFO:
case RAW_QFS_ATTRIBUTE_INFORMATION:
fs->attribute_info.out.fs_attr = pvfs->fs_attribs;
fs->attribute_info.out.max_file_component_length = 255;
fs->attribute_info.out.fs_type.s = ntvfs->ctx->fs_type;
return NT_STATUS_OK;
case RAW_QFS_QUOTA_INFORMATION:
ZERO_STRUCT(fs->quota_information.out.unknown);
fs->quota_information.out.quota_soft = 0;
fs->quota_information.out.quota_hard = 0;
fs->quota_information.out.quota_flags = 0;
return NT_STATUS_OK;
case RAW_QFS_FULL_SIZE_INFORMATION:
fs->full_size_information.out.total_alloc_units = blocks_total;
fs->full_size_information.out.call_avail_alloc_units = blocks_free;
fs->full_size_information.out.actual_avail_alloc_units = blocks_free;
fs->full_size_information.out.sectors_per_unit = 1;
fs->full_size_information.out.bytes_per_sector = block_size;
return NT_STATUS_OK;
case RAW_QFS_OBJECTID_INFORMATION:
ZERO_STRUCT(fs->objectid_information.out.guid);
ZERO_STRUCT(fs->objectid_information.out.unknown);
status = pvfs_cache_base_fs_uuid(pvfs, &st);
NT_STATUS_NOT_OK_RETURN(status);
fs->objectid_information.out.guid = *pvfs->base_fs_uuid;
return NT_STATUS_OK;
default:
break;
}
return NT_STATUS_INVALID_LEVEL;
}
+81
View File
@@ -0,0 +1,81 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - open and close
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "libcli/raw/ioctl.h"
/*
old ioctl interface
*/
static NTSTATUS pvfs_ioctl_old(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_ioctl *io)
{
return NT_STATUS_DOS(ERRSRV, ERRerror);
}
/*
nt ioctl interface
*/
static NTSTATUS pvfs_ntioctl(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_ioctl *io)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f;
f = pvfs_find_fd(pvfs, req, io->ntioctl.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
switch (io->ntioctl.in.function) {
case FSCTL_SET_SPARSE:
/* maybe some posix systems have a way of marking
a file non-sparse? */
io->ntioctl.out.blob = data_blob(NULL, 0);
return NT_STATUS_OK;
}
return NT_STATUS_NOT_SUPPORTED;
}
/*
ioctl interface
*/
NTSTATUS pvfs_ioctl(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_ioctl *io)
{
switch (io->generic.level) {
case RAW_IOCTL_IOCTL:
return pvfs_ioctl_old(ntvfs, req, io);
case RAW_IOCTL_NTIOCTL:
return pvfs_ntioctl(ntvfs, req, io);
case RAW_IOCTL_SMB2:
case RAW_IOCTL_SMB2_NO_HANDLE:
return NT_STATUS_FS_DRIVER_REQUIRED;
}
return NT_STATUS_INVALID_LEVEL;
}
+395
View File
@@ -0,0 +1,395 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - locking
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "system/time.h"
#include "lib/util/dlinklist.h"
#include "messaging/messaging.h"
/*
check if we can perform IO on a range that might be locked
*/
NTSTATUS pvfs_check_lock(struct pvfs_state *pvfs,
struct pvfs_file *f,
uint16_t smbpid,
uint64_t offset, uint64_t count,
enum brl_type rw)
{
if (!(pvfs->flags & PVFS_FLAG_STRICT_LOCKING)) {
return NT_STATUS_OK;
}
return brl_locktest(pvfs->brl_context,
f->brl_handle,
smbpid,
offset, count, rw);
}
/* this state structure holds information about a lock we are waiting on */
struct pvfs_pending_lock {
struct pvfs_pending_lock *next, *prev;
struct pvfs_state *pvfs;
union smb_lock *lck;
struct pvfs_file *f;
struct ntvfs_request *req;
int pending_lock;
void *wait_handle;
struct timeval end_time;
};
/*
a secondary attempt to setup a lock has failed - back out
the locks we did get and send an error
*/
static void pvfs_lock_async_failed(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_file *f,
struct smb_lock_entry *locks,
int i,
NTSTATUS status)
{
/* undo the locks we just did */
for (i=i-1;i>=0;i--) {
brl_unlock(pvfs->brl_context,
f->brl_handle,
locks[i].pid,
locks[i].offset,
locks[i].count);
f->lock_count--;
}
req->async_states->status = status;
req->async_states->send_fn(req);
}
/*
called when we receive a pending lock notification. It means that
either our lock timed out or somoene else has unlocked a overlapping
range, so we should try the lock again. Note that on timeout we
do retry the lock, giving it a last chance.
*/
static void pvfs_pending_lock_continue(void *private, enum pvfs_wait_notice reason)
{
struct pvfs_pending_lock *pending = private;
struct pvfs_state *pvfs = pending->pvfs;
struct pvfs_file *f = pending->f;
struct ntvfs_request *req = pending->req;
union smb_lock *lck = pending->lck;
struct smb_lock_entry *locks;
enum brl_type rw;
NTSTATUS status;
int i;
BOOL timed_out;
timed_out = (reason != PVFS_WAIT_EVENT);
locks = lck->lockx.in.locks + lck->lockx.in.ulock_cnt;
if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
rw = READ_LOCK;
} else {
rw = WRITE_LOCK;
}
DLIST_REMOVE(f->pending_list, pending);
/* we don't retry on a cancel */
if (reason == PVFS_WAIT_CANCEL) {
status = NT_STATUS_FILE_LOCK_CONFLICT;
} else {
/*
* here it's important to pass the pending pointer
* because with this we'll get the correct error code
* FILE_LOCK_CONFLICT in the error case
*/
status = brl_lock(pvfs->brl_context,
f->brl_handle,
req->smbpid,
locks[pending->pending_lock].offset,
locks[pending->pending_lock].count,
rw, pending);
}
if (NT_STATUS_IS_OK(status)) {
f->lock_count++;
timed_out = False;
}
/* if we have failed and timed out, or succeeded, then we
don't need the pending lock any more */
if (NT_STATUS_IS_OK(status) || timed_out) {
NTSTATUS status2;
status2 = brl_remove_pending(pvfs->brl_context,
f->brl_handle, pending);
if (!NT_STATUS_IS_OK(status2)) {
DEBUG(0,("pvfs_lock: failed to remove pending lock - %s\n", nt_errstr(status2)));
}
talloc_free(pending->wait_handle);
}
if (!NT_STATUS_IS_OK(status)) {
if (timed_out) {
/* no more chances */
pvfs_lock_async_failed(pvfs, req, f, locks, pending->pending_lock, status);
} else {
/* we can try again */
DLIST_ADD(f->pending_list, pending);
}
return;
}
/* if we haven't timed out yet, then we can do more pending locks */
if (rw == READ_LOCK) {
rw = PENDING_READ_LOCK;
} else {
rw = PENDING_WRITE_LOCK;
}
/* we've now got the pending lock. try and get the rest, which might
lead to more pending locks */
for (i=pending->pending_lock+1;i<lck->lockx.in.lock_cnt;i++) {
if (pending) {
pending->pending_lock = i;
}
status = brl_lock(pvfs->brl_context,
f->brl_handle,
req->smbpid,
locks[i].offset,
locks[i].count,
rw, pending);
if (!NT_STATUS_IS_OK(status)) {
if (pending) {
/* a timed lock failed - setup a wait message to handle
the pending lock notification or a timeout */
pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
pending->end_time,
pvfs_pending_lock_continue,
pending);
if (pending->wait_handle == NULL) {
pvfs_lock_async_failed(pvfs, req, f, locks, i, NT_STATUS_NO_MEMORY);
} else {
talloc_steal(pending, pending->wait_handle);
DLIST_ADD(f->pending_list, pending);
}
return;
}
pvfs_lock_async_failed(pvfs, req, f, locks, i, status);
return;
}
f->lock_count++;
}
/* we've managed to get all the locks. Tell the client */
req->async_states->status = NT_STATUS_OK;
req->async_states->send_fn(req);
}
/*
called when we close a file that might have locks
*/
void pvfs_lock_close(struct pvfs_state *pvfs, struct pvfs_file *f)
{
struct pvfs_pending_lock *p, *next;
if (f->lock_count || f->pending_list) {
DEBUG(5,("pvfs_lock: removing %.0f locks on close\n",
(double)f->lock_count));
brl_close(f->pvfs->brl_context, f->brl_handle);
f->lock_count = 0;
}
/* reply to all the pending lock requests, telling them the
lock failed */
for (p=f->pending_list;p;p=next) {
next = p->next;
DLIST_REMOVE(f->pending_list, p);
p->req->async_states->status = NT_STATUS_RANGE_NOT_LOCKED;
p->req->async_states->send_fn(p->req);
}
}
/*
cancel a set of locks
*/
static NTSTATUS pvfs_lock_cancel(struct pvfs_state *pvfs, struct ntvfs_request *req, union smb_lock *lck,
struct pvfs_file *f)
{
struct pvfs_pending_lock *p;
for (p=f->pending_list;p;p=p->next) {
/* check if the lock request matches exactly - you can only cancel with exact matches */
if (p->lck->lockx.in.ulock_cnt == lck->lockx.in.ulock_cnt &&
p->lck->lockx.in.lock_cnt == lck->lockx.in.lock_cnt &&
p->lck->lockx.in.file.ntvfs== lck->lockx.in.file.ntvfs &&
p->lck->lockx.in.mode == (lck->lockx.in.mode & ~LOCKING_ANDX_CANCEL_LOCK)) {
int i;
for (i=0;i<lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt;i++) {
if (p->lck->lockx.in.locks[i].pid != lck->lockx.in.locks[i].pid ||
p->lck->lockx.in.locks[i].offset != lck->lockx.in.locks[i].offset ||
p->lck->lockx.in.locks[i].count != lck->lockx.in.locks[i].count) {
break;
}
}
if (i < lck->lockx.in.ulock_cnt) continue;
/* an exact match! we can cancel it, which is equivalent
to triggering the timeout early */
pvfs_pending_lock_continue(p, PVFS_WAIT_TIMEOUT);
return NT_STATUS_OK;
}
}
return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
}
/*
lock or unlock a byte range
*/
NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_lock *lck)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f;
struct smb_lock_entry *locks;
int i;
enum brl_type rw;
struct pvfs_pending_lock *pending = NULL;
NTSTATUS status;
if (lck->generic.level != RAW_LOCK_GENERIC) {
return ntvfs_map_lock(ntvfs, req, lck);
}
f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
if (f->handle->fd == -1) {
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
if (lck->lockx.in.timeout != 0 &&
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
pending = talloc(f, struct pvfs_pending_lock);
if (pending == NULL) {
return NT_STATUS_NO_MEMORY;
}
pending->pvfs = pvfs;
pending->lck = lck;
pending->f = f;
pending->req = req;
pending->end_time =
timeval_current_ofs(lck->lockx.in.timeout/1000,
1000*(lck->lockx.in.timeout%1000));
}
if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
rw = pending? PENDING_READ_LOCK : READ_LOCK;
} else {
rw = pending? PENDING_WRITE_LOCK : WRITE_LOCK;
}
if (lck->lockx.in.mode & LOCKING_ANDX_CANCEL_LOCK) {
return pvfs_lock_cancel(pvfs, req, lck, f);
}
if (lck->lockx.in.mode & LOCKING_ANDX_CHANGE_LOCKTYPE) {
/* this seems to not be supported by any windows server,
or used by any clients */
return NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks);
}
if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
DEBUG(0,("received unexpected oplock break\n"));
return NT_STATUS_NOT_IMPLEMENTED;
}
/* the unlocks happen first */
locks = lck->lockx.in.locks;
for (i=0;i<lck->lockx.in.ulock_cnt;i++) {
status = brl_unlock(pvfs->brl_context,
f->brl_handle,
locks[i].pid,
locks[i].offset,
locks[i].count);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
f->lock_count--;
}
locks += i;
for (i=0;i<lck->lockx.in.lock_cnt;i++) {
if (pending) {
pending->pending_lock = i;
}
status = brl_lock(pvfs->brl_context,
f->brl_handle,
locks[i].pid,
locks[i].offset,
locks[i].count,
rw, pending);
if (!NT_STATUS_IS_OK(status)) {
if (pending) {
/* a timed lock failed - setup a wait message to handle
the pending lock notification or a timeout */
pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
pending->end_time,
pvfs_pending_lock_continue,
pending);
if (pending->wait_handle == NULL) {
return NT_STATUS_NO_MEMORY;
}
talloc_steal(pending, pending->wait_handle);
DLIST_ADD(f->pending_list, pending);
return NT_STATUS_OK;
}
/* undo the locks we just did */
for (i=i-1;i>=0;i--) {
brl_unlock(pvfs->brl_context,
f->brl_handle,
locks[i].pid,
locks[i].offset,
locks[i].count);
f->lock_count--;
}
return status;
}
f->lock_count++;
}
return NT_STATUS_OK;
}
+195
View File
@@ -0,0 +1,195 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - mkdir and rmdir
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/dir.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/security.h"
/*
create a directory with EAs
*/
static NTSTATUS pvfs_t2mkdir(struct pvfs_state *pvfs,
struct ntvfs_request *req, union smb_mkdir *md)
{
NTSTATUS status;
struct pvfs_filename *name;
mode_t mode;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (name->exists) {
return NT_STATUS_OBJECT_NAME_COLLISION;
}
status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY);
if (mkdir(name->full_name, mode) == -1) {
return pvfs_map_errno(pvfs, errno);
}
pvfs_xattr_unlink_hook(pvfs, name->full_name);
status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (!name->exists ||
!(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
return NT_STATUS_INTERNAL_ERROR;
}
/* setup an inherited acl from the parent */
status = pvfs_acl_inherit(pvfs, req, name, -1);
if (!NT_STATUS_IS_OK(status)) {
rmdir(name->full_name);
return status;
}
/* setup any EAs that were asked for */
status = pvfs_setfileinfo_ea_set(pvfs, name, -1,
md->t2mkdir.in.num_eas,
md->t2mkdir.in.eas);
if (!NT_STATUS_IS_OK(status)) {
rmdir(name->full_name);
return status;
}
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_ADDED,
FILE_NOTIFY_CHANGE_DIR_NAME,
name->full_name);
return NT_STATUS_OK;
}
/*
create a directory
*/
NTSTATUS pvfs_mkdir(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_mkdir *md)
{
struct pvfs_state *pvfs = ntvfs->private_data;
NTSTATUS status;
struct pvfs_filename *name;
mode_t mode;
if (md->generic.level == RAW_MKDIR_T2MKDIR) {
return pvfs_t2mkdir(pvfs, req, md);
}
if (md->generic.level != RAW_MKDIR_MKDIR) {
return NT_STATUS_INVALID_LEVEL;
}
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, md->mkdir.in.path, 0, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (name->exists) {
return NT_STATUS_OBJECT_NAME_COLLISION;
}
status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY);
if (mkdir(name->full_name, mode) == -1) {
return pvfs_map_errno(pvfs, errno);
}
pvfs_xattr_unlink_hook(pvfs, name->full_name);
/* setup an inherited acl from the parent */
status = pvfs_acl_inherit(pvfs, req, name, -1);
if (!NT_STATUS_IS_OK(status)) {
rmdir(name->full_name);
return status;
}
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_ADDED,
FILE_NOTIFY_CHANGE_DIR_NAME,
name->full_name);
return NT_STATUS_OK;
}
/*
remove a directory
*/
NTSTATUS pvfs_rmdir(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, struct smb_rmdir *rd)
{
struct pvfs_state *pvfs = ntvfs->private_data;
NTSTATUS status;
struct pvfs_filename *name;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, rd->in.path, 0, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (!name->exists) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_xattr_unlink_hook(pvfs, name->full_name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (rmdir(name->full_name) == -1) {
/* some olders systems don't return ENOTEMPTY to rmdir() */
if (errno == EEXIST) {
return NT_STATUS_DIRECTORY_NOT_EMPTY;
}
return pvfs_map_errno(pvfs, errno);
}
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_REMOVED,
FILE_NOTIFY_CHANGE_DIR_NAME,
name->full_name);
return NT_STATUS_OK;
}
+284
View File
@@ -0,0 +1,284 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - notify
Copyright (C) Andrew Tridgell 2006
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "lib/messaging/irpc.h"
#include "messaging/messaging.h"
#include "lib/util/dlinklist.h"
#include "lib/events/events.h"
/* pending notifies buffer, hung off struct pvfs_file for open directories
that have used change notify */
struct pvfs_notify_buffer {
struct pvfs_file *f;
uint32_t num_changes;
struct notify_changes *changes;
uint32_t max_buffer_size;
uint32_t current_buffer_size;
/* a list of requests waiting for events on this handle */
struct notify_pending {
struct notify_pending *next, *prev;
struct ntvfs_request *req;
union smb_notify *info;
} *pending;
};
/*
send a notify on the next event run.
*/
static void pvfs_notify_send_next(struct event_context *ev, struct timed_event *te,
struct timeval t, void *ptr)
{
struct ntvfs_request *req = talloc_get_type(ptr, struct ntvfs_request);
req->async_states->send_fn(req);
}
/*
send a reply to a pending notify request
*/
static void pvfs_notify_send(struct pvfs_notify_buffer *notify_buffer,
NTSTATUS status, BOOL immediate)
{
struct notify_pending *pending = notify_buffer->pending;
struct ntvfs_request *req;
union smb_notify *info;
if (notify_buffer->current_buffer_size > notify_buffer->max_buffer_size &&
notify_buffer->num_changes != 0) {
/* on buffer overflow return no changes and destroys the notify buffer */
notify_buffer->num_changes = 0;
while (notify_buffer->pending) {
pvfs_notify_send(notify_buffer, NT_STATUS_OK, immediate);
}
talloc_free(notify_buffer);
return;
}
/* see if there is anyone waiting */
if (notify_buffer->pending == NULL) {
return;
}
DLIST_REMOVE(notify_buffer->pending, pending);
req = pending->req;
info = pending->info;
info->nttrans.out.num_changes = notify_buffer->num_changes;
info->nttrans.out.changes = talloc_steal(req, notify_buffer->changes);
notify_buffer->num_changes = 0;
notify_buffer->changes = NULL;
notify_buffer->current_buffer_size = 0;
talloc_free(pending);
if (info->nttrans.out.num_changes != 0) {
status = NT_STATUS_OK;
}
req->async_states->status = status;
if (immediate) {
req->async_states->send_fn(req);
return;
}
/* we can't call pvfs_notify_send() directly here, as that
would free the request, and the ntvfs modules above us
could use it, so call it on the next event */
event_add_timed(req->ctx->event_ctx,
req, timeval_zero(), pvfs_notify_send_next, req);
}
/*
destroy a notify buffer. Called when the handle is closed
*/
static int pvfs_notify_destructor(struct pvfs_notify_buffer *n)
{
notify_remove(n->f->pvfs->notify_context, n);
n->f->notify_buffer = NULL;
pvfs_notify_send(n, NT_STATUS_OK, True);
return 0;
}
/*
called when a async notify event comes in
*/
static void pvfs_notify_callback(void *private, const struct notify_event *ev)
{
struct pvfs_notify_buffer *n = talloc_get_type(private, struct pvfs_notify_buffer);
size_t len;
struct notify_changes *n2;
char *new_path;
n2 = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
if (n2 == NULL) {
/* nothing much we can do for this */
return;
}
n->changes = n2;
new_path = talloc_strdup(n->changes, ev->path);
if (new_path == NULL) {
return;
}
string_replace(new_path, '/', '\\');
n->changes[n->num_changes].action = ev->action;
n->changes[n->num_changes].name.s = new_path;
n->num_changes++;
/*
work out how much room this will take in the buffer
*/
len = 12 + strlen_m(ev->path)*2;
if (len & 3) {
len += 4 - (len & 3);
}
n->current_buffer_size += len;
/* send what we have, unless its the first part of a rename */
if (ev->action != NOTIFY_ACTION_OLD_NAME) {
pvfs_notify_send(n, NT_STATUS_OK, True);
}
}
/*
setup a notify buffer on a directory handle
*/
static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f,
uint32_t buffer_size, uint32_t filter, BOOL recursive)
{
NTSTATUS status;
struct notify_entry e;
f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
f->notify_buffer->max_buffer_size = buffer_size;
f->notify_buffer->f = f;
e.filter = filter;
e.path = f->handle->name->full_name;
if (recursive) {
e.subdir_filter = filter;
} else {
e.subdir_filter = 0;
}
status = notify_add(pvfs->notify_context, &e,
pvfs_notify_callback, f->notify_buffer);
NT_STATUS_NOT_OK_RETURN(status);
talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
return NT_STATUS_OK;
}
/*
called from the pvfs_wait code when either an event has come in, or
the notify request has been cancelled
*/
static void pvfs_notify_end(void *private, enum pvfs_wait_notice reason)
{
struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private,
struct pvfs_notify_buffer);
if (reason == PVFS_WAIT_CANCEL) {
pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED, False);
} else {
pvfs_notify_send(notify_buffer, NT_STATUS_OK, True);
}
}
/* change notify request - always async. This request blocks until the
event buffer is non-empty */
NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_notify *info)
{
struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
struct pvfs_state);
struct pvfs_file *f;
NTSTATUS status;
struct notify_pending *pending;
if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
return ntvfs_map_notify(ntvfs, req, info);
}
f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
/* this request doesn't make sense unless its async */
if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
return NT_STATUS_INVALID_PARAMETER;
}
/* its only valid for directories */
if (f->handle->fd != -1) {
return NT_STATUS_INVALID_PARAMETER;
}
/* if the handle doesn't currently have a notify buffer then
create one */
if (f->notify_buffer == NULL) {
status = pvfs_notify_setup(pvfs, f,
info->nttrans.in.buffer_size,
info->nttrans.in.completion_filter,
info->nttrans.in.recursive);
NT_STATUS_NOT_OK_RETURN(status);
}
/* we update the max_buffer_size on each call, but we do not
update the recursive flag or filter */
f->notify_buffer->max_buffer_size = info->nttrans.in.buffer_size;
pending = talloc(f->notify_buffer, struct notify_pending);
NT_STATUS_HAVE_NO_MEMORY(pending);
pending->req = talloc_reference(pending, req);
NT_STATUS_HAVE_NO_MEMORY(pending->req);
pending->info = info;
DLIST_ADD_END(f->notify_buffer->pending, pending, struct notify_pending *);
/* if the buffer is empty then start waiting */
if (f->notify_buffer->num_changes == 0) {
void *wait_handle =
pvfs_wait_message(pvfs, req, -1, timeval_zero(),
pvfs_notify_end, f->notify_buffer);
NT_STATUS_HAVE_NO_MEMORY(wait_handle);
talloc_steal(req, wait_handle);
return NT_STATUS_OK;
}
req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, False);
return NT_STATUS_OK;
}
File diff suppressed because it is too large Load Diff
+425
View File
@@ -0,0 +1,425 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - read
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/xattr.h"
/*
determine what access bits are needed for a call
*/
static uint32_t pvfs_fileinfo_access(union smb_fileinfo *info)
{
uint32_t needed;
switch (info->generic.level) {
case RAW_FILEINFO_EA_LIST:
case RAW_FILEINFO_ALL_EAS:
needed = SEC_FILE_READ_EA;
break;
case RAW_FILEINFO_IS_NAME_VALID:
needed = 0;
break;
case RAW_FILEINFO_SEC_DESC:
needed = 0;
if (info->query_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
needed |= SEC_STD_READ_CONTROL;
}
if (info->query_secdesc.in.secinfo_flags & SECINFO_DACL) {
needed |= SEC_STD_READ_CONTROL;
}
if (info->query_secdesc.in.secinfo_flags & SECINFO_SACL) {
needed |= SEC_FLAG_SYSTEM_SECURITY;
}
break;
default:
needed = SEC_FILE_READ_ATTRIBUTE;
break;
}
return needed;
}
/*
reply to a RAW_FILEINFO_EA_LIST call
*/
NTSTATUS pvfs_query_ea_list(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
struct pvfs_filename *name, int fd,
uint_t num_names,
struct ea_name *names,
struct smb_ea_list *eas)
{
NTSTATUS status;
int i;
struct xattr_DosEAs *ealist = talloc(mem_ctx, struct xattr_DosEAs);
ZERO_STRUCTP(eas);
status = pvfs_doseas_load(pvfs, name, fd, ealist);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
eas->eas = talloc_array(mem_ctx, struct ea_struct, num_names);
if (eas->eas == NULL) {
return NT_STATUS_NO_MEMORY;
}
eas->num_eas = num_names;
for (i=0;i<num_names;i++) {
int j;
eas->eas[i].flags = 0;
eas->eas[i].name.s = names[i].name.s;
eas->eas[i].value = data_blob(NULL, 0);
for (j=0;j<ealist->num_eas;j++) {
if (strcasecmp_m(eas->eas[i].name.s,
ealist->eas[j].name) == 0) {
eas->eas[i].value = ealist->eas[j].value;
break;
}
}
}
return NT_STATUS_OK;
}
/*
reply to a RAW_FILEINFO_ALL_EAS call
*/
static NTSTATUS pvfs_query_all_eas(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
struct pvfs_filename *name, int fd,
struct smb_ea_list *eas)
{
NTSTATUS status;
int i;
struct xattr_DosEAs *ealist = talloc(mem_ctx, struct xattr_DosEAs);
ZERO_STRUCTP(eas);
status = pvfs_doseas_load(pvfs, name, fd, ealist);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
eas->eas = talloc_array(mem_ctx, struct ea_struct, ealist->num_eas);
if (eas->eas == NULL) {
return NT_STATUS_NO_MEMORY;
}
eas->num_eas = 0;
for (i=0;i<ealist->num_eas;i++) {
eas->eas[eas->num_eas].flags = 0;
eas->eas[eas->num_eas].name.s = ealist->eas[i].name;
eas->eas[eas->num_eas].value = ealist->eas[i].value;
eas->num_eas++;
}
return NT_STATUS_OK;
}
/*
approximately map a struct pvfs_filename to a generic fileinfo struct
*/
static NTSTATUS pvfs_map_fileinfo(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name, union smb_fileinfo *info,
int fd)
{
switch (info->generic.level) {
case RAW_FILEINFO_GENERIC:
return NT_STATUS_INVALID_LEVEL;
case RAW_FILEINFO_GETATTR:
info->getattr.out.attrib = name->dos.attrib;
info->getattr.out.size = name->st.st_size;
info->getattr.out.write_time = nt_time_to_unix(name->dos.write_time);
return NT_STATUS_OK;
case RAW_FILEINFO_GETATTRE:
case RAW_FILEINFO_STANDARD:
info->standard.out.create_time = nt_time_to_unix(name->dos.create_time);
info->standard.out.access_time = nt_time_to_unix(name->dos.access_time);
info->standard.out.write_time = nt_time_to_unix(name->dos.write_time);
info->standard.out.size = name->st.st_size;
info->standard.out.alloc_size = name->dos.alloc_size;
info->standard.out.attrib = name->dos.attrib;
return NT_STATUS_OK;
case RAW_FILEINFO_EA_SIZE:
info->ea_size.out.create_time = nt_time_to_unix(name->dos.create_time);
info->ea_size.out.access_time = nt_time_to_unix(name->dos.access_time);
info->ea_size.out.write_time = nt_time_to_unix(name->dos.write_time);
info->ea_size.out.size = name->st.st_size;
info->ea_size.out.alloc_size = name->dos.alloc_size;
info->ea_size.out.attrib = name->dos.attrib;
info->ea_size.out.ea_size = name->dos.ea_size;
return NT_STATUS_OK;
case RAW_FILEINFO_EA_LIST:
return pvfs_query_ea_list(pvfs, req, name, fd,
info->ea_list.in.num_names,
info->ea_list.in.ea_names,
&info->ea_list.out);
case RAW_FILEINFO_ALL_EAS:
return pvfs_query_all_eas(pvfs, req, name, fd, &info->all_eas.out);
case RAW_FILEINFO_IS_NAME_VALID:
return NT_STATUS_OK;
case RAW_FILEINFO_BASIC_INFO:
case RAW_FILEINFO_BASIC_INFORMATION:
info->basic_info.out.create_time = name->dos.create_time;
info->basic_info.out.access_time = name->dos.access_time;
info->basic_info.out.write_time = name->dos.write_time;
info->basic_info.out.change_time = name->dos.change_time;
info->basic_info.out.attrib = name->dos.attrib;
return NT_STATUS_OK;
case RAW_FILEINFO_STANDARD_INFO:
case RAW_FILEINFO_STANDARD_INFORMATION:
info->standard_info.out.alloc_size = name->dos.alloc_size;
info->standard_info.out.size = name->st.st_size;
info->standard_info.out.nlink = name->dos.nlink;
info->standard_info.out.delete_pending = 0; /* only for qfileinfo */
info->standard_info.out.directory =
(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
return NT_STATUS_OK;
case RAW_FILEINFO_EA_INFO:
case RAW_FILEINFO_EA_INFORMATION:
info->ea_info.out.ea_size = name->dos.ea_size;
return NT_STATUS_OK;
case RAW_FILEINFO_NAME_INFO:
case RAW_FILEINFO_NAME_INFORMATION:
info->name_info.out.fname.s = name->original_name;
return NT_STATUS_OK;
case RAW_FILEINFO_ALL_INFO:
case RAW_FILEINFO_ALL_INFORMATION:
info->all_info.out.create_time = name->dos.create_time;
info->all_info.out.access_time = name->dos.access_time;
info->all_info.out.write_time = name->dos.write_time;
info->all_info.out.change_time = name->dos.change_time;
info->all_info.out.attrib = name->dos.attrib;
info->all_info.out.alloc_size = name->dos.alloc_size;
info->all_info.out.size = name->st.st_size;
info->all_info.out.nlink = name->dos.nlink;
info->all_info.out.delete_pending = 0; /* only set by qfileinfo */
info->all_info.out.directory =
(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
info->all_info.out.ea_size = name->dos.ea_size;
info->all_info.out.fname.s = name->original_name;
return NT_STATUS_OK;
case RAW_FILEINFO_ALT_NAME_INFO:
case RAW_FILEINFO_ALT_NAME_INFORMATION:
info->name_info.out.fname.s = pvfs_short_name(pvfs, name, name);
return NT_STATUS_OK;
case RAW_FILEINFO_STREAM_INFO:
case RAW_FILEINFO_STREAM_INFORMATION:
return pvfs_stream_information(pvfs, req, name, fd, &info->stream_info.out);
case RAW_FILEINFO_COMPRESSION_INFO:
case RAW_FILEINFO_COMPRESSION_INFORMATION:
info->compression_info.out.compressed_size = name->st.st_size;
info->compression_info.out.format = 0;
info->compression_info.out.unit_shift = 0;
info->compression_info.out.chunk_shift = 0;
info->compression_info.out.cluster_shift = 0;
return NT_STATUS_OK;
case RAW_FILEINFO_INTERNAL_INFORMATION:
info->internal_information.out.file_id = name->dos.file_id;
return NT_STATUS_OK;
case RAW_FILEINFO_ACCESS_INFORMATION:
info->access_information.out.access_flags = 0; /* only set by qfileinfo */
return NT_STATUS_OK;
case RAW_FILEINFO_POSITION_INFORMATION:
info->position_information.out.position = 0; /* only set by qfileinfo */
return NT_STATUS_OK;
case RAW_FILEINFO_MODE_INFORMATION:
info->mode_information.out.mode = 0; /* only set by qfileinfo */
return NT_STATUS_OK;
case RAW_FILEINFO_ALIGNMENT_INFORMATION:
info->alignment_information.out.alignment_requirement = 0;
return NT_STATUS_OK;
case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
info->network_open_information.out.create_time = name->dos.create_time;
info->network_open_information.out.access_time = name->dos.access_time;
info->network_open_information.out.write_time = name->dos.write_time;
info->network_open_information.out.change_time = name->dos.change_time;
info->network_open_information.out.alloc_size = name->dos.alloc_size;
info->network_open_information.out.size = name->st.st_size;
info->network_open_information.out.attrib = name->dos.attrib;
return NT_STATUS_OK;
case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
info->attribute_tag_information.out.attrib = name->dos.attrib;
info->attribute_tag_information.out.reparse_tag = 0;
return NT_STATUS_OK;
case RAW_FILEINFO_SEC_DESC:
return pvfs_acl_query(pvfs, req, name, fd, info);
case RAW_FILEINFO_SMB2_ALL_INFORMATION:
info->all_info2.out.create_time = name->dos.create_time;
info->all_info2.out.access_time = name->dos.access_time;
info->all_info2.out.write_time = name->dos.write_time;
info->all_info2.out.change_time = name->dos.change_time;
info->all_info2.out.attrib = name->dos.attrib;
info->all_info2.out.unknown1 = 0;
info->all_info2.out.alloc_size = name->dos.alloc_size;
info->all_info2.out.size = name->st.st_size;
info->all_info2.out.nlink = name->dos.nlink;
info->all_info2.out.delete_pending = 0; /* only set by qfileinfo */
info->all_info2.out.directory =
(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
info->all_info2.out.file_id = name->dos.file_id;
info->all_info2.out.ea_size = name->dos.ea_size;
info->all_info2.out.access_mask = 0; /* only set by qfileinfo */
info->all_info2.out.position = 0; /* only set by qfileinfo */
info->all_info2.out.mode = 0; /* only set by qfileinfo */
info->all_info2.out.fname.s = name->original_name;
return NT_STATUS_OK;
}
return NT_STATUS_INVALID_LEVEL;
}
/*
return info on a pathname
*/
NTSTATUS pvfs_qpathinfo(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_fileinfo *info)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_filename *name;
NTSTATUS status;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, PVFS_RESOLVE_STREAMS, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (!name->stream_exists) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
status = pvfs_can_stat(pvfs, req, name);
if (!NT_STATUS_IS_OK(status)) {
return NT_STATUS_DELETE_PENDING;
}
status = pvfs_access_check_simple(pvfs, req, name,
pvfs_fileinfo_access(info));
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_map_fileinfo(pvfs, req, name, info, -1);
return status;
}
/*
query info on a open file
*/
NTSTATUS pvfs_qfileinfo(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_fileinfo *info)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f;
struct pvfs_file_handle *h;
NTSTATUS status;
uint32_t access_needed;
f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
h = f->handle;
access_needed = pvfs_fileinfo_access(info);
if ((f->access_mask & access_needed) != access_needed) {
return NT_STATUS_ACCESS_DENIED;
}
/* update the file information */
status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_map_fileinfo(pvfs, req, h->name, info, h->fd);
/* a qfileinfo can fill in a bit more info than a qpathinfo -
now modify the levels that need to be fixed up */
switch (info->generic.level) {
case RAW_FILEINFO_STANDARD_INFO:
case RAW_FILEINFO_STANDARD_INFORMATION:
if (pvfs_delete_on_close_set(pvfs, h, NULL, NULL)) {
info->standard_info.out.delete_pending = 1;
info->standard_info.out.nlink--;
}
break;
case RAW_FILEINFO_ALL_INFO:
case RAW_FILEINFO_ALL_INFORMATION:
if (pvfs_delete_on_close_set(pvfs, h, NULL, NULL)) {
info->all_info.out.delete_pending = 1;
info->all_info.out.nlink--;
}
break;
case RAW_FILEINFO_POSITION_INFORMATION:
info->position_information.out.position = h->position;
break;
case RAW_FILEINFO_ACCESS_INFORMATION:
info->access_information.out.access_flags = f->access_mask;
break;
case RAW_FILEINFO_MODE_INFORMATION:
info->mode_information.out.mode = h->mode;
break;
case RAW_FILEINFO_SMB2_ALL_INFORMATION:
if (pvfs_delete_on_close_set(pvfs, h, NULL, NULL)) {
info->all_info2.out.delete_pending = 1;
info->all_info2.out.nlink--;
}
info->all_info2.out.position = h->position;
info->all_info2.out.access_mask = f->access_mask;
info->all_info2.out.mode = h->mode;
break;
default:
break;
}
return status;
}
+94
View File
@@ -0,0 +1,94 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - read
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/security.h"
/*
read from a file
*/
NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_read *rd)
{
struct pvfs_state *pvfs = ntvfs->private_data;
ssize_t ret;
struct pvfs_file *f;
NTSTATUS status;
uint32_t maxcnt;
uint32_t mask;
if (rd->generic.level != RAW_READ_READX) {
return ntvfs_map_read(ntvfs, req, rd);
}
f = pvfs_find_fd(pvfs, req, rd->readx.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
if (f->handle->fd == -1) {
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
mask = SEC_FILE_READ_DATA;
if (rd->readx.in.read_for_execute) {
mask |= SEC_FILE_EXECUTE;
}
if (!(f->access_mask & mask)) {
return NT_STATUS_ACCESS_DENIED;
}
maxcnt = rd->readx.in.maxcnt;
if (maxcnt > UINT16_MAX && req->ctx->protocol < PROTOCOL_SMB2) {
maxcnt = 0;
}
status = pvfs_check_lock(pvfs, f, req->smbpid,
rd->readx.in.offset,
maxcnt,
READ_LOCK);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (f->handle->name->stream_name) {
ret = pvfs_stream_read(pvfs, f->handle,
rd->readx.out.data, maxcnt, rd->readx.in.offset);
} else {
ret = pread(f->handle->fd,
rd->readx.out.data,
maxcnt,
rd->readx.in.offset);
}
if (ret == -1) {
return pvfs_map_errno(pvfs, errno);
}
f->handle->position = f->handle->seek_offset = rd->readx.in.offset + ret;
rd->readx.out.nread = ret;
rd->readx.out.remaining = 0xFFFF;
rd->readx.out.compaction_mode = 0;
return NT_STATUS_OK;
}
+478
View File
@@ -0,0 +1,478 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - rename
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/security.h"
/*
do a file rename, and send any notify triggers
*/
NTSTATUS pvfs_do_rename(struct pvfs_state *pvfs, const struct pvfs_filename *name1,
const char *name2)
{
const char *r1, *r2;
uint32_t mask;
if (rename(name1->full_name, name2) == -1) {
return pvfs_map_errno(pvfs, errno);
}
if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
mask = FILE_NOTIFY_CHANGE_DIR_NAME;
} else {
mask = FILE_NOTIFY_CHANGE_FILE_NAME;
}
/*
renames to the same directory cause a OLD_NAME->NEW_NAME notify.
renames to a different directory are considered a remove/add
*/
r1 = strrchr_m(name1->full_name, '/');
r2 = strrchr_m(name2, '/');
if ((r1-name1->full_name) != (r2-name2) ||
strncmp(name1->full_name, name2, r1-name1->full_name) != 0) {
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_REMOVED,
mask,
name1->full_name);
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_ADDED,
mask,
name2);
} else {
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_OLD_NAME,
mask,
name1->full_name);
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_NEW_NAME,
mask,
name2);
}
/* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES
and CHANGE_CREATION on the new file when renming files, but not
directories */
if ((name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) == 0) {
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_MODIFIED,
FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION,
name2);
}
return NT_STATUS_OK;
}
/*
resolve a wildcard rename pattern. This works on one component of the name
*/
static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx,
const char *fname,
const char *pattern)
{
const char *p1, *p2;
char *dest, *d;
/* the length is bounded by the length of the two strings combined */
dest = talloc_size(mem_ctx, strlen(fname) + strlen(pattern) + 1);
if (dest == NULL) {
return NULL;
}
p1 = fname;
p2 = pattern;
d = dest;
while (*p2) {
codepoint_t c1, c2;
size_t c_size1, c_size2;
c1 = next_codepoint(p1, &c_size1);
c2 = next_codepoint(p2, &c_size2);
if (c2 == '?') {
d += push_codepoint(d, c1);
} else if (c2 == '*') {
memcpy(d, p1, strlen(p1));
d += strlen(p1);
break;
} else {
d += push_codepoint(d, c2);
}
p1 += c_size1;
p2 += c_size2;
}
*d = 0;
return dest;
}
/*
resolve a wildcard rename pattern.
*/
static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
const char *fname,
const char *pattern)
{
const char *base1, *base2;
const char *ext1, *ext2;
char *p;
/* break into base part plus extension */
p = strrchr_m(fname, '.');
if (p == NULL) {
ext1 = "";
base1 = fname;
} else {
ext1 = talloc_strdup(mem_ctx, p+1);
base1 = talloc_strndup(mem_ctx, fname, p-fname);
}
if (ext1 == NULL || base1 == NULL) {
return NULL;
}
p = strrchr_m(pattern, '.');
if (p == NULL) {
ext2 = "";
base2 = fname;
} else {
ext2 = talloc_strdup(mem_ctx, p+1);
base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
}
if (ext2 == NULL || base2 == NULL) {
return NULL;
}
base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2);
ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2);
if (base1 == NULL || ext1 == NULL) {
return NULL;
}
if (*ext1 == 0) {
return base1;
}
return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
}
/*
rename one file from a wildcard set
*/
static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs,
struct ntvfs_request *req,
const char *dir_path,
const char *fname1,
const char *fname2,
uint16_t attrib)
{
struct pvfs_filename *name1, *name2;
TALLOC_CTX *mem_ctx = talloc_new(req);
NTSTATUS status;
struct odb_lock *lck, *lck2;
/* resolve the wildcard pattern for this name */
fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2);
if (fname2 == NULL) {
return NT_STATUS_NO_MEMORY;
}
/* get a pvfs_filename source object */
status = pvfs_resolve_partial(pvfs, mem_ctx,
dir_path, fname1, &name1);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
/* make sure its matches the given attributes */
status = pvfs_match_attrib(pvfs, name1, attrib, 0);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
status = pvfs_can_rename(pvfs, req, name1, &lck);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
/* get a pvfs_filename dest object */
status = pvfs_resolve_partial(pvfs, mem_ctx,
dir_path, fname2, &name2);
if (NT_STATUS_IS_OK(status)) {
status = pvfs_can_delete(pvfs, req, name2, &lck2);
if (!NT_STATUS_IS_OK(status)) {
goto failed;
}
}
status = NT_STATUS_OK;
fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
if (fname2 == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = pvfs_do_rename(pvfs, name1, fname2);
if (NT_STATUS_IS_OK(status)) {
status = odb_rename(lck, fname2);
}
failed:
talloc_free(mem_ctx);
return status;
}
/*
rename a set of files with wildcards
*/
static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
struct ntvfs_request *req,
union smb_rename *ren,
struct pvfs_filename *name1,
struct pvfs_filename *name2)
{
struct pvfs_dir *dir;
NTSTATUS status;
off_t ofs = 0;
const char *fname, *fname2, *dir_path;
uint16_t attrib = ren->rename.in.attrib;
int total_renamed = 0;
/* get list of matching files */
status = pvfs_list_start(pvfs, name1, req, &dir);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = NT_STATUS_NO_SUCH_FILE;
dir_path = pvfs_list_unix_path(dir);
/* only allow wildcard renames within a directory */
if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
name2->full_name[strlen(dir_path)] != '/' ||
strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
return NT_STATUS_INVALID_PARAMETER;
}
fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
if (fname2 == NULL) {
return NT_STATUS_NO_MEMORY;
}
while ((fname = pvfs_list_next(dir, &ofs))) {
status = pvfs_rename_one(pvfs, req,
dir_path,
fname, fname2, attrib);
if (NT_STATUS_IS_OK(status)) {
total_renamed++;
}
}
if (total_renamed == 0) {
return status;
}
return NT_STATUS_OK;
}
/*
rename a set of files - SMBmv interface
*/
static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_rename *ren)
{
struct pvfs_state *pvfs = ntvfs->private_data;
NTSTATUS status;
struct pvfs_filename *name1, *name2;
struct odb_lock *lck;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1,
PVFS_RESOLVE_WILDCARD, &name1);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
PVFS_RESOLVE_WILDCARD, &name2);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (name1->has_wildcard || name2->has_wildcard) {
return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
}
if (!name1->exists) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
if (strcmp(name1->full_name, name2->full_name) == 0) {
return NT_STATUS_OK;
}
if (name2->exists) {
return NT_STATUS_OBJECT_NAME_COLLISION;
}
status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_can_rename(pvfs, req, name1, &lck);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_do_rename(pvfs, name1, name2->full_name);
if (NT_STATUS_IS_OK(status)) {
status = odb_rename(lck, name2->full_name);
}
return NT_STATUS_OK;
}
/*
rename a set of files - ntrename interface
*/
static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_rename *ren)
{
struct pvfs_state *pvfs = ntvfs->private_data;
NTSTATUS status;
struct pvfs_filename *name1, *name2;
struct odb_lock *lck;
switch (ren->ntrename.in.flags) {
case RENAME_FLAG_RENAME:
case RENAME_FLAG_HARD_LINK:
case RENAME_FLAG_COPY:
case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
break;
default:
return NT_STATUS_ACCESS_DENIED;
}
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
PVFS_RESOLVE_WILDCARD, &name1);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
PVFS_RESOLVE_WILDCARD, &name2);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (name1->has_wildcard || name2->has_wildcard) {
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
}
if (!name1->exists) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
if (strcmp(name1->full_name, name2->full_name) == 0) {
return NT_STATUS_OK;
}
if (name2->exists) {
return NT_STATUS_OBJECT_NAME_COLLISION;
}
status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_can_rename(pvfs, req, name1, &lck);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
switch (ren->ntrename.in.flags) {
case RENAME_FLAG_RENAME:
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
NT_STATUS_NOT_OK_RETURN(status);
status = pvfs_do_rename(pvfs, name1, name2->full_name);
NT_STATUS_NOT_OK_RETURN(status);
break;
case RENAME_FLAG_HARD_LINK:
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
NT_STATUS_NOT_OK_RETURN(status);
if (link(name1->full_name, name2->full_name) == -1) {
return pvfs_map_errno(pvfs, errno);
}
break;
case RENAME_FLAG_COPY:
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
NT_STATUS_NOT_OK_RETURN(status);
return pvfs_copy_file(pvfs, name1, name2);
case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
return NT_STATUS_INVALID_PARAMETER;
default:
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
/*
rename a set of files - ntrename interface
*/
NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_rename *ren)
{
switch (ren->generic.level) {
case RAW_RENAME_RENAME:
return pvfs_rename_mv(ntvfs, req, ren);
case RAW_RENAME_NTRENAME:
return pvfs_rename_nt(ntvfs, req, ren);
default:
break;
}
return NT_STATUS_INVALID_LEVEL;
}
+634
View File
@@ -0,0 +1,634 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - filename resolution
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
this is the core code for converting a filename from the format as
given by a client to a posix filename, including any case-matching
required, and checks for legal characters
*/
#include "includes.h"
#include "vfs_posix.h"
#include "system/dir.h"
/*
compare two filename components. This is where the name mangling hook will go
*/
static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
{
int ret;
ret = strcasecmp_m(comp, name);
if (ret != 0) {
char *shortname = pvfs_short_name_component(pvfs, name);
if (shortname) {
ret = strcasecmp_m(comp, shortname);
talloc_free(shortname);
}
}
return ret;
}
/*
search for a filename in a case insensitive fashion
TODO: add a cache for previously resolved case-insensitive names
TODO: add mangled name support
*/
static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name)
{
/* break into a series of components */
int num_components;
char **components;
char *p, *partial_name;
int i;
/* break up the full name info pathname components */
num_components=2;
p = name->full_name + strlen(pvfs->base_directory) + 1;
for (;*p;p++) {
if (*p == '/') {
num_components++;
}
}
components = talloc_array(name, char *, num_components);
p = name->full_name + strlen(pvfs->base_directory);
*p++ = 0;
components[0] = name->full_name;
for (i=1;i<num_components;i++) {
components[i] = p;
p = strchr(p, '/');
if (p) *p++ = 0;
if (pvfs_is_reserved_name(pvfs, components[i])) {
return NT_STATUS_ACCESS_DENIED;
}
}
partial_name = talloc_strdup(name, components[0]);
if (!partial_name) {
return NT_STATUS_NO_MEMORY;
}
/* for each component, check if it exists as-is, and if not then
do a directory scan */
for (i=1;i<num_components;i++) {
char *test_name;
DIR *dir;
struct dirent *de;
char *long_component;
/* possibly remap from the short name cache */
long_component = pvfs_mangled_lookup(pvfs, name, components[i]);
if (long_component) {
components[i] = long_component;
}
test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
if (!test_name) {
return NT_STATUS_NO_MEMORY;
}
/* check if this component exists as-is */
if (stat(test_name, &name->st) == 0) {
if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
talloc_free(partial_name);
partial_name = test_name;
if (i == num_components - 1) {
name->exists = True;
}
continue;
}
/* the filesystem might be case insensitive, in which
case a search is pointless unless the name is
mangled */
if ((pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) &&
!pvfs_is_mangled_component(pvfs, components[i])) {
if (i < num_components-1) {
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
partial_name = test_name;
continue;
}
dir = opendir(partial_name);
if (!dir) {
return pvfs_map_errno(pvfs, errno);
}
while ((de = readdir(dir))) {
if (component_compare(pvfs, components[i], de->d_name) == 0) {
break;
}
}
if (!de) {
if (i < num_components-1) {
closedir(dir);
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
} else {
components[i] = talloc_strdup(name, de->d_name);
}
test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
talloc_free(partial_name);
partial_name = test_name;
closedir(dir);
}
if (!name->exists) {
if (stat(partial_name, &name->st) == 0) {
name->exists = True;
}
}
talloc_free(name->full_name);
name->full_name = partial_name;
if (name->exists) {
return pvfs_fill_dos_info(pvfs, name, -1);
}
return NT_STATUS_OK;
}
/*
parse a alternate data stream name
*/
static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
{
char *p;
name->stream_name = talloc_strdup(name, s+1);
if (name->stream_name == NULL) {
return NT_STATUS_NO_MEMORY;
}
p = strchr_m(name->stream_name, ':');
if (p == NULL) {
name->stream_id = pvfs_name_hash(name->stream_name,
strlen(name->stream_name));
return NT_STATUS_OK;
}
if (strcasecmp_m(p, ":$DATA") != 0) {
return NT_STATUS_OBJECT_NAME_INVALID;
}
*p = 0;
if (strcmp(name->stream_name, "") == 0) {
name->stream_name = NULL;
name->stream_id = 0;
} else {
name->stream_id = pvfs_name_hash(name->stream_name,
strlen(name->stream_name));
}
return NT_STATUS_OK;
}
/*
convert a CIFS pathname to a unix pathname. Note that this does NOT
take into account case insensitivity, and in fact does not access
the filesystem at all. It is merely a reformatting and charset
checking routine.
errors are returned if the filename is illegal given the flags
*/
static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
uint_t flags, struct pvfs_filename *name)
{
char *ret, *p, *p_start;
NTSTATUS status;
name->original_name = talloc_strdup(name, cifs_name);
name->stream_name = NULL;
name->stream_id = 0;
name->has_wildcard = False;
while (*cifs_name == '\\') {
cifs_name++;
}
if (*cifs_name == 0) {
name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
if (name->full_name == NULL) {
return NT_STATUS_NO_MEMORY;
}
return NT_STATUS_OK;
}
ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
if (ret == NULL) {
return NT_STATUS_NO_MEMORY;
}
p = ret + strlen(pvfs->base_directory) + 1;
/* now do an in-place conversion of '\' to '/', checking
for legal characters */
p_start = p;
while (*p) {
size_t c_size;
codepoint_t c = next_codepoint(p, &c_size);
switch (c) {
case '\\':
if (name->has_wildcard) {
/* wildcards are only allowed in the last part
of a name */
return NT_STATUS_ILLEGAL_CHARACTER;
}
if (p > p_start && p[1] == 0) {
*p = 0;
} else {
*p = '/';
}
break;
case ':':
if (!(flags & PVFS_RESOLVE_STREAMS)) {
return NT_STATUS_ILLEGAL_CHARACTER;
}
if (name->has_wildcard) {
return NT_STATUS_ILLEGAL_CHARACTER;
}
status = parse_stream_name(name, p);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
*p-- = 0;
break;
case '*':
case '>':
case '<':
case '?':
case '"':
if (!(flags & PVFS_RESOLVE_WILDCARD)) {
return NT_STATUS_OBJECT_NAME_INVALID;
}
name->has_wildcard = True;
break;
case '/':
case '|':
return NT_STATUS_ILLEGAL_CHARACTER;
case '.':
/* see if it is definately a .. or
. component. If it is then fail here, and
let the next layer up try again after
pvfs_reduce_name() if it wants to. This is
much more efficient on average than always
scanning for these separately */
if (p[1] == '.' &&
(p[2] == 0 || p[2] == '\\') &&
(p == p_start || p[-1] == '/')) {
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
}
if ((p[1] == 0 || p[1] == '\\') &&
(p == p_start || p[-1] == '/')) {
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
}
break;
}
p += c_size;
}
name->full_name = ret;
return NT_STATUS_OK;
}
/*
reduce a name that contains .. components or repeated \ separators
return NULL if it can't be reduced
*/
static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char **fname, uint_t flags)
{
codepoint_t c;
size_t c_size, len;
int i, num_components, err_count;
char **components;
char *p, *s, *ret;
s = talloc_strdup(mem_ctx, *fname);
if (s == NULL) return NT_STATUS_NO_MEMORY;
for (num_components=1, p=s; *p; p += c_size) {
c = next_codepoint(p, &c_size);
if (c == '\\') num_components++;
}
components = talloc_array(s, char *, num_components+1);
if (components == NULL) {
talloc_free(s);
return NT_STATUS_NO_MEMORY;
}
components[0] = s;
for (i=0, p=s; *p; p += c_size) {
c = next_codepoint(p, &c_size);
if (c == '\\') {
*p = 0;
components[++i] = p+1;
}
}
components[i+1] = NULL;
/*
rather bizarre!
'.' components are not allowed, but the rules for what error
code to give don't seem to make sense. This is a close
approximation.
*/
for (err_count=i=0;components[i];i++) {
if (strcmp(components[i], "") == 0) {
continue;
}
if (ISDOT(components[i]) || err_count) {
err_count++;
}
}
if (err_count) {
if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
if (err_count==1) {
return NT_STATUS_OBJECT_NAME_INVALID;
} else {
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
}
/* remove any null components */
for (i=0;components[i];i++) {
if (strcmp(components[i], "") == 0) {
memmove(&components[i], &components[i+1],
sizeof(char *)*(num_components-i));
i--;
continue;
}
if (ISDOTDOT(components[i])) {
if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
memmove(&components[i-1], &components[i+1],
sizeof(char *)*(num_components-(i+1)));
i -= 2;
continue;
}
}
if (components[0] == NULL) {
talloc_free(s);
*fname = talloc_strdup(mem_ctx, "\\");
return NT_STATUS_OK;
}
for (len=i=0;components[i];i++) {
len += strlen(components[i]) + 1;
}
/* rebuild the name */
ret = talloc_size(mem_ctx, len+1);
if (ret == NULL) {
talloc_free(s);
return NT_STATUS_NO_MEMORY;
}
for (len=0,i=0;components[i];i++) {
size_t len1 = strlen(components[i]);
ret[len] = '\\';
memcpy(ret+len+1, components[i], len1);
len += len1 + 1;
}
ret[len] = 0;
talloc_free(s);
*fname = ret;
return NT_STATUS_OK;
}
/*
resolve a name from relative client format to a struct pvfs_filename
the memory for the filename is made as a talloc child of 'name'
flags include:
PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
PVFS_RESOLVE_STREAMS = stream names are allowed
TODO: ../ collapsing, and outside share checking
*/
NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
const char *cifs_name,
uint_t flags, struct pvfs_filename **name)
{
NTSTATUS status;
*name = talloc(mem_ctx, struct pvfs_filename);
if (*name == NULL) {
return NT_STATUS_NO_MEMORY;
}
(*name)->exists = False;
(*name)->stream_exists = False;
if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
flags &= ~PVFS_RESOLVE_STREAMS;
}
/* do the basic conversion to a unix formatted path,
also checking for allowable characters */
status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
/* it might contain .. components which need to be reduced */
status = pvfs_reduce_name(*name, &cifs_name, flags);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* if it has a wildcard then no point doing a stat() */
if ((*name)->has_wildcard) {
return NT_STATUS_OK;
}
/* if we can stat() the full name now then we are done */
if (stat((*name)->full_name, &(*name)->st) == 0) {
(*name)->exists = True;
return pvfs_fill_dos_info(pvfs, *name, -1);
}
/* search for a matching filename */
status = pvfs_case_search(pvfs, *name);
return status;
}
/*
do a partial resolve, returning a pvfs_filename structure given a
base path and a relative component. It is an error if the file does
not exist. No case-insensitive matching is done.
this is used in places like directory searching where we need a pvfs_filename
to pass to a function, but already know the unix base directory and component
*/
NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
const char *unix_dir, const char *fname,
struct pvfs_filename **name)
{
NTSTATUS status;
*name = talloc(mem_ctx, struct pvfs_filename);
if (*name == NULL) {
return NT_STATUS_NO_MEMORY;
}
(*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
if ((*name)->full_name == NULL) {
return NT_STATUS_NO_MEMORY;
}
if (stat((*name)->full_name, &(*name)->st) == -1) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
(*name)->exists = True;
(*name)->stream_exists = True;
(*name)->has_wildcard = False;
(*name)->original_name = talloc_strdup(*name, fname);
(*name)->stream_name = NULL;
(*name)->stream_id = 0;
status = pvfs_fill_dos_info(pvfs, *name, -1);
return status;
}
/*
fill in the pvfs_filename info for an open file, given the current
info for a (possibly) non-open file. This is used by places that need
to update the pvfs_filename stat information, and by pvfs_open()
*/
NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
struct pvfs_filename *name)
{
dev_t device = (dev_t)0;
ino_t inode = 0;
if (name->exists) {
device = name->st.st_dev;
inode = name->st.st_ino;
}
if (fd == -1) {
if (stat(name->full_name, &name->st) == -1) {
return NT_STATUS_INVALID_HANDLE;
}
} else {
if (fstat(fd, &name->st) == -1) {
return NT_STATUS_INVALID_HANDLE;
}
}
if (name->exists &&
(device != name->st.st_dev || inode != name->st.st_ino)) {
/* the file we are looking at has changed! this could
be someone trying to exploit a race
condition. Certainly we don't want to continue
operating on this file */
DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
name->full_name));
return NT_STATUS_UNEXPECTED_IO_ERROR;
}
name->exists = True;
return pvfs_fill_dos_info(pvfs, name, fd);
}
/*
resolve the parent of a given name
*/
NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
const struct pvfs_filename *child,
struct pvfs_filename **name)
{
NTSTATUS status;
char *p;
*name = talloc(mem_ctx, struct pvfs_filename);
if (*name == NULL) {
return NT_STATUS_NO_MEMORY;
}
(*name)->full_name = talloc_strdup(*name, child->full_name);
if ((*name)->full_name == NULL) {
return NT_STATUS_NO_MEMORY;
}
p = strrchr_m((*name)->full_name, '/');
if (p == NULL) {
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
}
/* this handles the root directory */
if (p == (*name)->full_name) {
p[1] = 0;
} else {
p[0] = 0;
}
if (stat((*name)->full_name, &(*name)->st) == -1) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
(*name)->exists = True;
(*name)->stream_exists = True;
(*name)->has_wildcard = False;
/* we can't get the correct 'original_name', but for the purposes
of this call this is close enough */
(*name)->original_name = talloc_reference(*name, child->original_name);
(*name)->stream_name = NULL;
(*name)->stream_id = 0;
status = pvfs_fill_dos_info(pvfs, *name, -1);
return status;
}
+843
View File
@@ -0,0 +1,843 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - directory search functions
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "system/time.h"
#include "librpc/gen_ndr/security.h"
#include "smbd/service_stream.h"
#include "lib/events/events.h"
#include "lib/util/dlinklist.h"
/* place a reasonable limit on old-style searches as clients tend to
not send search close requests */
#define MAX_OLD_SEARCHES 2000
#define MAX_SEARCH_HANDLES (UINT16_MAX - 1)
#define INVALID_SEARCH_HANDLE UINT16_MAX
/*
destroy an open search
*/
static int pvfs_search_destructor(struct pvfs_search_state *search)
{
DLIST_REMOVE(search->pvfs->search.list, search);
idr_remove(search->pvfs->search.idtree, search->handle);
return 0;
}
/*
called when a search timer goes off
*/
static void pvfs_search_timer(struct event_context *ev, struct timed_event *te,
struct timeval t, void *ptr)
{
struct pvfs_search_state *search = talloc_get_type(ptr, struct pvfs_search_state);
talloc_free(search);
}
/*
setup a timer to destroy a open search after a inactivity period
*/
static void pvfs_search_setup_timer(struct pvfs_search_state *search)
{
struct event_context *ev = search->pvfs->ntvfs->ctx->event_ctx;
if (search->handle == INVALID_SEARCH_HANDLE) return;
talloc_free(search->te);
search->te = event_add_timed(ev, search,
timeval_current_ofs(search->pvfs->search.inactivity_time, 0),
pvfs_search_timer, search);
}
/*
fill in a single search result for a given info level
*/
static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
enum smb_search_data_level level,
const char *unix_path,
const char *fname,
struct pvfs_search_state *search,
off_t dir_offset,
union smb_search_data *file)
{
struct pvfs_filename *name;
NTSTATUS status;
const char *shortname;
uint32_t dir_index = (uint32_t)dir_offset; /* truncated - see the code
in pvfs_list_seek_ofs() for
how we cope with this */
status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
switch (level) {
case RAW_SEARCH_DATA_SEARCH:
shortname = pvfs_short_name(pvfs, name, name);
file->search.attrib = name->dos.attrib;
file->search.write_time = nt_time_to_unix(name->dos.write_time);
file->search.size = name->st.st_size;
file->search.name = shortname;
file->search.id.reserved = search->handle >> 8;
memset(file->search.id.name, ' ', sizeof(file->search.id.name));
memcpy(file->search.id.name, shortname,
MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
file->search.id.handle = search->handle & 0xFF;
file->search.id.server_cookie = dir_index;
file->search.id.client_cookie = 0;
return NT_STATUS_OK;
case RAW_SEARCH_DATA_STANDARD:
file->standard.resume_key = dir_index;
file->standard.create_time = nt_time_to_unix(name->dos.create_time);
file->standard.access_time = nt_time_to_unix(name->dos.access_time);
file->standard.write_time = nt_time_to_unix(name->dos.write_time);
file->standard.size = name->st.st_size;
file->standard.alloc_size = name->dos.alloc_size;
file->standard.attrib = name->dos.attrib;
file->standard.name.s = fname;
return NT_STATUS_OK;
case RAW_SEARCH_DATA_EA_SIZE:
file->ea_size.resume_key = dir_index;
file->ea_size.create_time = nt_time_to_unix(name->dos.create_time);
file->ea_size.access_time = nt_time_to_unix(name->dos.access_time);
file->ea_size.write_time = nt_time_to_unix(name->dos.write_time);
file->ea_size.size = name->st.st_size;
file->ea_size.alloc_size = name->dos.alloc_size;
file->ea_size.attrib = name->dos.attrib;
file->ea_size.ea_size = name->dos.ea_size;
file->ea_size.name.s = fname;
return NT_STATUS_OK;
case RAW_SEARCH_DATA_EA_LIST:
file->ea_list.resume_key = dir_index;
file->ea_list.create_time = nt_time_to_unix(name->dos.create_time);
file->ea_list.access_time = nt_time_to_unix(name->dos.access_time);
file->ea_list.write_time = nt_time_to_unix(name->dos.write_time);
file->ea_list.size = name->st.st_size;
file->ea_list.alloc_size = name->dos.alloc_size;
file->ea_list.attrib = name->dos.attrib;
file->ea_list.name.s = fname;
return pvfs_query_ea_list(pvfs, file, name, -1,
search->num_ea_names,
search->ea_names,
&file->ea_list.eas);
case RAW_SEARCH_DATA_DIRECTORY_INFO:
file->directory_info.file_index = dir_index;
file->directory_info.create_time = name->dos.create_time;
file->directory_info.access_time = name->dos.access_time;
file->directory_info.write_time = name->dos.write_time;
file->directory_info.change_time = name->dos.change_time;
file->directory_info.size = name->st.st_size;
file->directory_info.alloc_size = name->dos.alloc_size;
file->directory_info.attrib = name->dos.attrib;
file->directory_info.name.s = fname;
return NT_STATUS_OK;
case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
file->full_directory_info.file_index = dir_index;
file->full_directory_info.create_time = name->dos.create_time;
file->full_directory_info.access_time = name->dos.access_time;
file->full_directory_info.write_time = name->dos.write_time;
file->full_directory_info.change_time = name->dos.change_time;
file->full_directory_info.size = name->st.st_size;
file->full_directory_info.alloc_size = name->dos.alloc_size;
file->full_directory_info.attrib = name->dos.attrib;
file->full_directory_info.ea_size = name->dos.ea_size;
file->full_directory_info.name.s = fname;
return NT_STATUS_OK;
case RAW_SEARCH_DATA_NAME_INFO:
file->name_info.file_index = dir_index;
file->name_info.name.s = fname;
return NT_STATUS_OK;
case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
file->both_directory_info.file_index = dir_index;
file->both_directory_info.create_time = name->dos.create_time;
file->both_directory_info.access_time = name->dos.access_time;
file->both_directory_info.write_time = name->dos.write_time;
file->both_directory_info.change_time = name->dos.change_time;
file->both_directory_info.size = name->st.st_size;
file->both_directory_info.alloc_size = name->dos.alloc_size;
file->both_directory_info.attrib = name->dos.attrib;
file->both_directory_info.ea_size = name->dos.ea_size;
file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
file->both_directory_info.name.s = fname;
return NT_STATUS_OK;
case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
file->id_full_directory_info.file_index = dir_index;
file->id_full_directory_info.create_time = name->dos.create_time;
file->id_full_directory_info.access_time = name->dos.access_time;
file->id_full_directory_info.write_time = name->dos.write_time;
file->id_full_directory_info.change_time = name->dos.change_time;
file->id_full_directory_info.size = name->st.st_size;
file->id_full_directory_info.alloc_size = name->dos.alloc_size;
file->id_full_directory_info.attrib = name->dos.attrib;
file->id_full_directory_info.ea_size = name->dos.ea_size;
file->id_full_directory_info.file_id = name->dos.file_id;
file->id_full_directory_info.name.s = fname;
return NT_STATUS_OK;
case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
file->id_both_directory_info.file_index = dir_index;
file->id_both_directory_info.create_time = name->dos.create_time;
file->id_both_directory_info.access_time = name->dos.access_time;
file->id_both_directory_info.write_time = name->dos.write_time;
file->id_both_directory_info.change_time = name->dos.change_time;
file->id_both_directory_info.size = name->st.st_size;
file->id_both_directory_info.alloc_size = name->dos.alloc_size;
file->id_both_directory_info.attrib = name->dos.attrib;
file->id_both_directory_info.ea_size = name->dos.ea_size;
file->id_both_directory_info.file_id = name->dos.file_id;
file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
file->id_both_directory_info.name.s = fname;
return NT_STATUS_OK;
case RAW_SEARCH_DATA_GENERIC:
break;
}
return NT_STATUS_INVALID_LEVEL;
}
/*
the search fill loop
*/
static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
uint_t max_count,
struct pvfs_search_state *search,
enum smb_search_data_level level,
uint_t *reply_count,
void *search_private,
BOOL (*callback)(void *, union smb_search_data *))
{
struct pvfs_dir *dir = search->dir;
NTSTATUS status;
*reply_count = 0;
if (max_count == 0) {
max_count = 1;
}
while ((*reply_count) < max_count) {
union smb_search_data *file;
const char *name;
off_t ofs = search->current_index;
name = pvfs_list_next(dir, &search->current_index);
if (name == NULL) break;
file = talloc(mem_ctx, union smb_search_data);
if (!file) {
return NT_STATUS_NO_MEMORY;
}
status = fill_search_info(pvfs, level,
pvfs_list_unix_path(dir), name,
search, search->current_index, file);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(file);
continue;
}
if (!callback(search_private, file)) {
talloc_free(file);
search->current_index = ofs;
break;
}
(*reply_count)++;
talloc_free(file);
}
pvfs_search_setup_timer(search);
return NT_STATUS_OK;
}
/*
we've run out of search handles - cleanup those that the client forgot
to close
*/
static void pvfs_search_cleanup(struct pvfs_state *pvfs)
{
int i;
time_t t = time(NULL);
for (i=0;i<MAX_OLD_SEARCHES;i++) {
struct pvfs_search_state *search = idr_find(pvfs->search.idtree, i);
if (search == NULL) return;
if (pvfs_list_eos(search->dir, search->current_index) &&
search->last_used != 0 &&
t > search->last_used + 30) {
/* its almost certainly been forgotten
about */
talloc_free(search);
}
}
}
/*
list files in a directory matching a wildcard pattern - old SMBsearch interface
*/
static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_first *io,
void *search_private,
BOOL (*callback)(void *, union smb_search_data *))
{
struct pvfs_dir *dir;
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_search_state *search;
uint_t reply_count;
uint16_t search_attrib;
const char *pattern;
NTSTATUS status;
struct pvfs_filename *name;
int id;
search_attrib = io->search_first.in.search_attrib;
pattern = io->search_first.in.pattern;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (!name->has_wildcard && !name->exists) {
return STATUS_NO_MORE_FILES;
}
status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* we initially make search a child of the request, then if we
need to keep it long term we steal it for the private
structure */
search = talloc(req, struct pvfs_search_state);
if (!search) {
return NT_STATUS_NO_MEMORY;
}
/* do the actual directory listing */
status = pvfs_list_start(pvfs, name, search, &dir);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* we need to give a handle back to the client so it
can continue a search */
id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
if (id == -1) {
pvfs_search_cleanup(pvfs);
id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
}
if (id == -1) {
return NT_STATUS_INSUFFICIENT_RESOURCES;
}
search->pvfs = pvfs;
search->handle = id;
search->dir = dir;
search->current_index = 0;
search->search_attrib = search_attrib & 0xFF;
search->must_attrib = (search_attrib>>8) & 0xFF;
search->last_used = time(NULL);
search->te = NULL;
DLIST_ADD(pvfs->search.list, search);
talloc_set_destructor(search, pvfs_search_destructor);
status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
&reply_count, search_private, callback);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
io->search_first.out.count = reply_count;
/* not matching any entries is an error */
if (reply_count == 0) {
return STATUS_NO_MORE_FILES;
}
talloc_steal(pvfs, search);
return NT_STATUS_OK;
}
/* continue a old style search */
static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_next *io,
void *search_private,
BOOL (*callback)(void *, union smb_search_data *))
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_search_state *search;
struct pvfs_dir *dir;
uint_t reply_count, max_count;
uint16_t handle;
NTSTATUS status;
handle = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
max_count = io->search_next.in.max_count;
search = idr_find(pvfs->search.idtree, handle);
if (search == NULL) {
/* we didn't find the search handle */
return NT_STATUS_INVALID_HANDLE;
}
dir = search->dir;
status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie,
&search->current_index);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
search->last_used = time(NULL);
status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
&reply_count, search_private, callback);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
io->search_next.out.count = reply_count;
/* not matching any entries means end of search */
if (reply_count == 0) {
talloc_free(search);
}
return NT_STATUS_OK;
}
/*
list files in a directory matching a wildcard pattern
*/
static NTSTATUS pvfs_search_first_trans2(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_first *io,
void *search_private,
BOOL (*callback)(void *, union smb_search_data *))
{
struct pvfs_dir *dir;
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_search_state *search;
uint_t reply_count;
uint16_t search_attrib, max_count;
const char *pattern;
NTSTATUS status;
struct pvfs_filename *name;
int id;
search_attrib = io->t2ffirst.in.search_attrib;
pattern = io->t2ffirst.in.pattern;
max_count = io->t2ffirst.in.max_count;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (!name->has_wildcard && !name->exists) {
return NT_STATUS_NO_SUCH_FILE;
}
status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* we initially make search a child of the request, then if we
need to keep it long term we steal it for the private
structure */
search = talloc(req, struct pvfs_search_state);
if (!search) {
return NT_STATUS_NO_MEMORY;
}
/* do the actual directory listing */
status = pvfs_list_start(pvfs, name, search, &dir);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
if (id == -1) {
return NT_STATUS_INSUFFICIENT_RESOURCES;
}
search->pvfs = pvfs;
search->handle = id;
search->dir = dir;
search->current_index = 0;
search->search_attrib = search_attrib;
search->must_attrib = 0;
search->last_used = 0;
search->num_ea_names = io->t2ffirst.in.num_names;
search->ea_names = io->t2ffirst.in.ea_names;
search->te = NULL;
DLIST_ADD(pvfs->search.list, search);
talloc_set_destructor(search, pvfs_search_destructor);
status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
&reply_count, search_private, callback);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* not matching any entries is an error */
if (reply_count == 0) {
return NT_STATUS_NO_SUCH_FILE;
}
io->t2ffirst.out.count = reply_count;
io->t2ffirst.out.handle = search->handle;
io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
/* work out if we are going to keep the search state
and allow for a search continue */
if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
io->t2ffirst.out.end_of_search)) {
talloc_free(search);
} else {
talloc_steal(pvfs, search);
}
return NT_STATUS_OK;
}
/* continue a search */
static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_next *io,
void *search_private,
BOOL (*callback)(void *, union smb_search_data *))
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_search_state *search;
struct pvfs_dir *dir;
uint_t reply_count;
uint16_t handle;
NTSTATUS status;
handle = io->t2fnext.in.handle;
search = idr_find(pvfs->search.idtree, handle);
if (search == NULL) {
/* we didn't find the search handle */
return NT_STATUS_INVALID_HANDLE;
}
dir = search->dir;
status = NT_STATUS_OK;
/* work out what type of continuation is being used */
if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
&search->current_index);
}
} else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
&search->current_index);
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
search->num_ea_names = io->t2fnext.in.num_names;
search->ea_names = io->t2fnext.in.ea_names;
status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
&reply_count, search_private, callback);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
io->t2fnext.out.count = reply_count;
io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
/* work out if we are going to keep the search state */
if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
io->t2fnext.out.end_of_search)) {
talloc_free(search);
}
return NT_STATUS_OK;
}
static NTSTATUS pvfs_search_first_smb2(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, const struct smb2_find *io,
void *search_private,
BOOL (*callback)(void *, union smb_search_data *))
{
struct pvfs_dir *dir;
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_search_state *search;
uint_t reply_count;
uint16_t max_count;
const char *pattern;
NTSTATUS status;
struct pvfs_filename *name;
struct pvfs_file *f;
f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
if (!f) {
return NT_STATUS_FILE_CLOSED;
}
/* its only valid for directories */
if (f->handle->fd != -1) {
return NT_STATUS_INVALID_PARAMETER;
}
if (!(f->access_mask & SEC_DIR_LIST)) {
return NT_STATUS_ACCESS_DENIED;
}
if (f->search) {
talloc_free(f->search);
f->search = NULL;
}
if (strequal(io->in.pattern, "")) {
return NT_STATUS_OBJECT_NAME_INVALID;
}
if (strchr_m(io->in.pattern, '\\')) {
return NT_STATUS_OBJECT_NAME_INVALID;
}
if (strchr_m(io->in.pattern, '/')) {
return NT_STATUS_OBJECT_NAME_INVALID;
}
if (strequal("", f->handle->name->original_name)) {
pattern = talloc_asprintf(req, "\\%s", io->in.pattern);
NT_STATUS_HAVE_NO_MEMORY(pattern);
} else {
pattern = talloc_asprintf(req, "\\%s\\%s",
f->handle->name->original_name,
io->in.pattern);
NT_STATUS_HAVE_NO_MEMORY(pattern);
}
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
NT_STATUS_NOT_OK_RETURN(status);
if (!name->has_wildcard && !name->exists) {
return NT_STATUS_NO_SUCH_FILE;
}
/* we initially make search a child of the request, then if we
need to keep it long term we steal it for the private
structure */
search = talloc(req, struct pvfs_search_state);
NT_STATUS_HAVE_NO_MEMORY(search);
/* do the actual directory listing */
status = pvfs_list_start(pvfs, name, search, &dir);
NT_STATUS_NOT_OK_RETURN(status);
search->pvfs = pvfs;
search->handle = INVALID_SEARCH_HANDLE;
search->dir = dir;
search->current_index = 0;
search->search_attrib = 0;
search->must_attrib = 0;
search->last_used = 0;
search->num_ea_names = 0;
search->ea_names = NULL;
search->te = NULL;
if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
max_count = 1;
} else {
max_count = UINT16_MAX;
}
status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
&reply_count, search_private, callback);
NT_STATUS_NOT_OK_RETURN(status);
/* not matching any entries is an error */
if (reply_count == 0) {
return NT_STATUS_NO_SUCH_FILE;
}
f->search = talloc_steal(f, search);
return NT_STATUS_OK;
}
static NTSTATUS pvfs_search_next_smb2(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, const struct smb2_find *io,
void *search_private,
BOOL (*callback)(void *, union smb_search_data *))
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_search_state *search;
uint_t reply_count;
uint16_t max_count;
NTSTATUS status;
struct pvfs_file *f;
f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
if (!f) {
return NT_STATUS_FILE_CLOSED;
}
/* its only valid for directories */
if (f->handle->fd != -1) {
return NT_STATUS_INVALID_PARAMETER;
}
/* if there's no search started on the dir handle, it's like a search_first */
search = f->search;
if (!search) {
return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
}
if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
search->current_index = 0;
}
if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
max_count = 1;
} else {
max_count = UINT16_MAX;
}
status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
&reply_count, search_private, callback);
NT_STATUS_NOT_OK_RETURN(status);
/* not matching any entries is an error */
if (reply_count == 0) {
return STATUS_NO_MORE_FILES;
}
return NT_STATUS_OK;
}
/*
list files in a directory matching a wildcard pattern
*/
NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_first *io,
void *search_private,
BOOL (*callback)(void *, union smb_search_data *))
{
switch (io->generic.level) {
case RAW_SEARCH_SEARCH:
case RAW_SEARCH_FFIRST:
case RAW_SEARCH_FUNIQUE:
return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
case RAW_SEARCH_TRANS2:
return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
case RAW_SEARCH_SMB2:
return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
}
return NT_STATUS_INVALID_LEVEL;
}
/* continue a search */
NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_next *io,
void *search_private,
BOOL (*callback)(void *, union smb_search_data *))
{
switch (io->generic.level) {
case RAW_SEARCH_SEARCH:
case RAW_SEARCH_FFIRST:
return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
case RAW_SEARCH_FUNIQUE:
return NT_STATUS_INVALID_LEVEL;
case RAW_SEARCH_TRANS2:
return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
case RAW_SEARCH_SMB2:
return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
}
return NT_STATUS_INVALID_LEVEL;
}
/* close a search */
NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_close *io)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_search_state *search;
uint16_t handle = INVALID_SEARCH_HANDLE;
switch (io->generic.level) {
case RAW_FINDCLOSE_GENERIC:
return NT_STATUS_INVALID_LEVEL;
case RAW_FINDCLOSE_FCLOSE:
handle = io->fclose.in.id.handle;
break;
case RAW_FINDCLOSE_FINDCLOSE:
handle = io->findclose.in.handle;
break;
}
search = idr_find(pvfs->search.idtree, handle);
if (search == NULL) {
/* we didn't find the search handle */
return NT_STATUS_INVALID_HANDLE;
}
talloc_free(search);
return NT_STATUS_OK;
}
+65
View File
@@ -0,0 +1,65 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - seek
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
/*
seek in a file
*/
NTSTATUS pvfs_seek(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_seek *io)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f;
struct pvfs_file_handle *h;
NTSTATUS status;
f = pvfs_find_fd(pvfs, req, io->lseek.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
h = f->handle;
status = NT_STATUS_OK;
switch (io->lseek.in.mode) {
case SEEK_MODE_START:
h->seek_offset = io->lseek.in.offset;
break;
case SEEK_MODE_CURRENT:
h->seek_offset += io->lseek.in.offset;
break;
case SEEK_MODE_END:
status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
h->seek_offset = h->name->st.st_size + io->lseek.in.offset;
break;
}
io->lseek.out.offset = h->seek_offset;
return status;
}
+635
View File
@@ -0,0 +1,635 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - setfileinfo
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "system/time.h"
#include "librpc/gen_ndr/xattr.h"
/*
determine what access bits are needed for a call
*/
static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info)
{
uint32_t needed;
switch (info->generic.level) {
case RAW_SFILEINFO_EA_SET:
needed = SEC_FILE_WRITE_EA;
break;
case RAW_SFILEINFO_DISPOSITION_INFO:
case RAW_SFILEINFO_DISPOSITION_INFORMATION:
needed = SEC_STD_DELETE;
break;
case RAW_SFILEINFO_END_OF_FILE_INFO:
needed = SEC_FILE_WRITE_DATA;
break;
case RAW_SFILEINFO_POSITION_INFORMATION:
needed = 0;
break;
case RAW_SFILEINFO_SEC_DESC:
needed = 0;
if (info->set_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
needed |= SEC_STD_WRITE_OWNER;
}
if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) {
needed |= SEC_STD_WRITE_DAC;
}
if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) {
needed |= SEC_FLAG_SYSTEM_SECURITY;
}
break;
default:
needed = SEC_FILE_WRITE_ATTRIBUTE;
break;
}
return needed;
}
/*
rename_information level
*/
static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name,
union smb_setfileinfo *info)
{
NTSTATUS status;
struct pvfs_filename *name2;
char *new_name, *p;
/* renames are only allowed within a directory */
if (strchr_m(info->rename_information.in.new_name, '\\')) {
return NT_STATUS_NOT_SUPPORTED;
}
if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
/* don't allow this for now */
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
/* don't allow stream renames for now */
if (name->stream_name) {
return NT_STATUS_INVALID_PARAMETER;
}
/* w2k3 does not appear to allow relative rename */
if (info->rename_information.in.root_fid != 0) {
return NT_STATUS_INVALID_PARAMETER;
}
/* construct the fully qualified windows name for the new file name */
new_name = talloc_strdup(req, name->original_name);
if (new_name == NULL) {
return NT_STATUS_NO_MEMORY;
}
p = strrchr_m(new_name, '\\');
if (p == NULL) {
return NT_STATUS_OBJECT_NAME_INVALID;
}
*p = 0;
new_name = talloc_asprintf(req, "%s\\%s", new_name,
info->rename_information.in.new_name);
if (new_name == NULL) {
return NT_STATUS_NO_MEMORY;
}
/* resolve the new name */
status = pvfs_resolve_name(pvfs, name, new_name, 0, &name2);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* if the destination exists, then check the rename is allowed */
if (name2->exists) {
struct odb_lock *lck;
if (strcmp(name2->full_name, name->full_name) == 0) {
/* rename to same name is null-op */
return NT_STATUS_OK;
}
if (!info->rename_information.in.overwrite) {
return NT_STATUS_OBJECT_NAME_COLLISION;
}
status = pvfs_can_delete(pvfs, req, name2, &lck);
if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
return NT_STATUS_ACCESS_DENIED;
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
}
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_do_rename(pvfs, name, name2->full_name);
if (NT_STATUS_IS_OK(status)) {
name->full_name = talloc_steal(name, name2->full_name);
name->original_name = talloc_steal(name, name2->original_name);
}
return NT_STATUS_OK;
}
/*
add a single DOS EA
*/
NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
struct pvfs_filename *name,
int fd, uint16_t num_eas,
struct ea_struct *eas)
{
struct xattr_DosEAs *ealist;
int i, j;
NTSTATUS status;
if (num_eas == 0) {
return NT_STATUS_OK;
}
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_NOT_SUPPORTED;
}
ealist = talloc(name, struct xattr_DosEAs);
/* load the current list */
status = pvfs_doseas_load(pvfs, name, fd, ealist);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
for (j=0;j<num_eas;j++) {
struct ea_struct *ea = &eas[j];
/* see if its already there */
for (i=0;i<ealist->num_eas;i++) {
if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
ealist->eas[i].value = ea->value;
break;
}
}
if (i==ealist->num_eas) {
/* add it */
ealist->eas = talloc_realloc(ealist, ealist->eas,
struct xattr_EA,
ealist->num_eas+1);
if (ealist->eas == NULL) {
return NT_STATUS_NO_MEMORY;
}
ealist->eas[i].name = ea->name.s;
ealist->eas[i].value = ea->value;
ealist->num_eas++;
}
}
/* pull out any null EAs */
for (i=0;i<ealist->num_eas;i++) {
if (ealist->eas[i].value.length == 0) {
memmove(&ealist->eas[i],
&ealist->eas[i+1],
(ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
ealist->num_eas--;
i--;
}
}
status = pvfs_doseas_save(pvfs, name, fd, ealist);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_MODIFIED,
FILE_NOTIFY_CHANGE_EA,
name->full_name);
name->dos.ea_size = 4;
for (i=0;i<ealist->num_eas;i++) {
name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 +
ealist->eas[i].value.length;
}
/* update the ea_size attrib */
return pvfs_dosattrib_save(pvfs, name, fd);
}
/*
set info on a open file
*/
NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_setfileinfo *info)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct utimbuf unix_times;
struct pvfs_file *f;
struct pvfs_file_handle *h;
struct pvfs_filename newstats;
NTSTATUS status;
uint32_t access_needed;
uint32_t change_mask = 0;
f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
h = f->handle;
access_needed = pvfs_setfileinfo_access(info);
if ((f->access_mask & access_needed) != access_needed) {
return NT_STATUS_ACCESS_DENIED;
}
/* update the file information */
status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* we take a copy of the current file stats, then update
newstats in each of the elements below. At the end we
compare, and make any changes needed */
newstats = *h->name;
switch (info->generic.level) {
case RAW_SFILEINFO_SETATTR:
if (!null_time(info->setattr.in.write_time)) {
unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
}
if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
newstats.dos.attrib = info->setattr.in.attrib;
}
break;
case RAW_SFILEINFO_SETATTRE:
case RAW_SFILEINFO_STANDARD:
if (!null_time(info->setattre.in.create_time)) {
unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
}
if (!null_time(info->setattre.in.access_time)) {
unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
}
if (!null_time(info->setattre.in.write_time)) {
unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
}
break;
case RAW_SFILEINFO_EA_SET:
return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
info->ea_set.in.num_eas,
info->ea_set.in.eas);
case RAW_SFILEINFO_BASIC_INFO:
case RAW_SFILEINFO_BASIC_INFORMATION:
if (!null_nttime(info->basic_info.in.create_time)) {
newstats.dos.create_time = info->basic_info.in.create_time;
}
if (!null_nttime(info->basic_info.in.access_time)) {
newstats.dos.access_time = info->basic_info.in.access_time;
}
if (!null_nttime(info->basic_info.in.write_time)) {
newstats.dos.write_time = info->basic_info.in.write_time;
newstats.dos.flags |= XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
h->sticky_write_time = True;
}
if (!null_nttime(info->basic_info.in.change_time)) {
newstats.dos.change_time = info->basic_info.in.change_time;
}
if (info->basic_info.in.attrib != 0) {
newstats.dos.attrib = info->basic_info.in.attrib;
}
break;
case RAW_SFILEINFO_DISPOSITION_INFO:
case RAW_SFILEINFO_DISPOSITION_INFORMATION:
return pvfs_set_delete_on_close(pvfs, req, f,
info->disposition_info.in.delete_on_close);
case RAW_SFILEINFO_ALLOCATION_INFO:
case RAW_SFILEINFO_ALLOCATION_INFORMATION:
newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
if (newstats.dos.alloc_size < newstats.st.st_size) {
newstats.st.st_size = newstats.dos.alloc_size;
}
newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
newstats.dos.alloc_size);
break;
case RAW_SFILEINFO_END_OF_FILE_INFO:
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
newstats.st.st_size = info->end_of_file_info.in.size;
break;
case RAW_SFILEINFO_POSITION_INFORMATION:
h->position = info->position_information.in.position;
break;
case RAW_SFILEINFO_MODE_INFORMATION:
/* this one is a puzzle */
if (info->mode_information.in.mode != 0 &&
info->mode_information.in.mode != 2 &&
info->mode_information.in.mode != 4 &&
info->mode_information.in.mode != 6) {
return NT_STATUS_INVALID_PARAMETER;
}
h->mode = info->mode_information.in.mode;
break;
case RAW_SFILEINFO_RENAME_INFORMATION:
return pvfs_setfileinfo_rename(pvfs, req, h->name,
info);
case RAW_SFILEINFO_SEC_DESC:
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_MODIFIED,
FILE_NOTIFY_CHANGE_SECURITY,
h->name->full_name);
return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
default:
return NT_STATUS_INVALID_LEVEL;
}
/* possibly change the file size */
if (newstats.st.st_size != h->name->st.st_size) {
if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
if (h->name->stream_name) {
status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
} else {
int ret;
if (f->access_mask &
(SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
ret = ftruncate(h->fd, newstats.st.st_size);
} else {
ret = truncate(h->name->full_name, newstats.st.st_size);
}
if (ret == -1) {
return pvfs_map_errno(pvfs, errno);
}
change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
}
}
/* possibly change the file timestamps */
ZERO_STRUCT(unix_times);
if (newstats.dos.create_time != h->name->dos.create_time) {
change_mask |= FILE_NOTIFY_CHANGE_CREATION;
}
if (newstats.dos.access_time != h->name->dos.access_time) {
unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
}
if (newstats.dos.write_time != h->name->dos.write_time) {
unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
}
if (unix_times.actime != 0 || unix_times.modtime != 0) {
if (utime(h->name->full_name, &unix_times) == -1) {
return pvfs_map_errno(pvfs, errno);
}
}
/* possibly change the attribute */
if (newstats.dos.attrib != h->name->dos.attrib) {
mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
if (fchmod(h->fd, mode) == -1) {
return pvfs_map_errno(pvfs, errno);
}
}
change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
}
*h->name = newstats;
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_MODIFIED,
change_mask,
h->name->full_name);
return pvfs_dosattrib_save(pvfs, h->name, h->fd);
}
/*
set info on a pathname
*/
NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_setfileinfo *info)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_filename *name;
struct pvfs_filename newstats;
NTSTATUS status;
struct utimbuf unix_times;
uint32_t access_needed;
uint32_t change_mask = 0;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
PVFS_RESOLVE_STREAMS, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (!name->exists) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
access_needed = pvfs_setfileinfo_access(info);
status = pvfs_access_check_simple(pvfs, req, name, access_needed);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* we take a copy of the current file stats, then update
newstats in each of the elements below. At the end we
compare, and make any changes needed */
newstats = *name;
switch (info->generic.level) {
case RAW_SFILEINFO_SETATTR:
if (!null_time(info->setattr.in.write_time)) {
unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
}
if (info->setattr.in.attrib == 0) {
newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
} else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
newstats.dos.attrib = info->setattr.in.attrib;
}
break;
case RAW_SFILEINFO_SETATTRE:
case RAW_SFILEINFO_STANDARD:
if (!null_time(info->setattre.in.create_time)) {
unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
}
if (!null_time(info->setattre.in.access_time)) {
unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
}
if (!null_time(info->setattre.in.write_time)) {
unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
}
break;
case RAW_SFILEINFO_EA_SET:
return pvfs_setfileinfo_ea_set(pvfs, name, -1,
info->ea_set.in.num_eas,
info->ea_set.in.eas);
case RAW_SFILEINFO_BASIC_INFO:
case RAW_SFILEINFO_BASIC_INFORMATION:
if (!null_nttime(info->basic_info.in.create_time)) {
newstats.dos.create_time = info->basic_info.in.create_time;
}
if (!null_nttime(info->basic_info.in.access_time)) {
newstats.dos.access_time = info->basic_info.in.access_time;
}
if (!null_nttime(info->basic_info.in.write_time)) {
newstats.dos.write_time = info->basic_info.in.write_time;
}
if (!null_nttime(info->basic_info.in.change_time)) {
newstats.dos.change_time = info->basic_info.in.change_time;
}
if (info->basic_info.in.attrib != 0) {
newstats.dos.attrib = info->basic_info.in.attrib;
}
break;
case RAW_SFILEINFO_ALLOCATION_INFO:
case RAW_SFILEINFO_ALLOCATION_INFORMATION:
if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
/* strange. Increasing the allocation size via setpathinfo
should be silently ignored */
break;
}
newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
if (newstats.dos.alloc_size < newstats.st.st_size) {
newstats.st.st_size = newstats.dos.alloc_size;
}
newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
newstats.dos.alloc_size);
break;
case RAW_SFILEINFO_END_OF_FILE_INFO:
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
newstats.st.st_size = info->end_of_file_info.in.size;
break;
case RAW_SFILEINFO_MODE_INFORMATION:
if (info->mode_information.in.mode != 0 &&
info->mode_information.in.mode != 2 &&
info->mode_information.in.mode != 4 &&
info->mode_information.in.mode != 6) {
return NT_STATUS_INVALID_PARAMETER;
}
return NT_STATUS_OK;
case RAW_SFILEINFO_RENAME_INFORMATION:
return pvfs_setfileinfo_rename(pvfs, req, name,
info);
case RAW_SFILEINFO_DISPOSITION_INFO:
case RAW_SFILEINFO_DISPOSITION_INFORMATION:
case RAW_SFILEINFO_POSITION_INFORMATION:
return NT_STATUS_OK;
default:
return NT_STATUS_INVALID_LEVEL;
}
/* possibly change the file size */
if (newstats.st.st_size != name->st.st_size) {
if (name->stream_name) {
status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
} else if (truncate(name->full_name, newstats.st.st_size) == -1) {
return pvfs_map_errno(pvfs, errno);
}
change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
}
/* possibly change the file timestamps */
ZERO_STRUCT(unix_times);
if (newstats.dos.create_time != name->dos.create_time) {
change_mask |= FILE_NOTIFY_CHANGE_CREATION;
}
if (newstats.dos.access_time != name->dos.access_time) {
unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
}
if (newstats.dos.write_time != name->dos.write_time) {
unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
}
if (unix_times.actime != 0 || unix_times.modtime != 0) {
if (utime(name->full_name, &unix_times) == -1) {
return pvfs_map_errno(pvfs, errno);
}
}
/* possibly change the attribute */
newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
if (newstats.dos.attrib != name->dos.attrib) {
mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
if (chmod(name->full_name, mode) == -1) {
return pvfs_map_errno(pvfs, errno);
}
change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
}
*name = newstats;
if (change_mask != 0) {
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_MODIFIED,
change_mask,
name->full_name);
}
return pvfs_dosattrib_save(pvfs, name, -1);
}
+695
View File
@@ -0,0 +1,695 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - 8.3 name routines
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/locale.h"
#include "vfs_posix.h"
/*
this mangling scheme uses the following format
Annnn~n.AAA
where nnnnn is a base 36 hash, and A represents characters from the original string
The hash is taken of the leading part of the long filename, in uppercase
for simplicity, we only allow ascii characters in 8.3 names
*/
/*
===============================================================================
NOTE NOTE NOTE!!!
This file deliberately uses non-multibyte string functions in many places. This
is *not* a mistake. This code is multi-byte safe, but it gets this property
through some very subtle knowledge of the way multi-byte strings are encoded
and the fact that this mangling algorithm only supports ascii characters in
8.3 names.
please don't convert this file to use the *_m() functions!!
===============================================================================
*/
#if 1
#define M_DEBUG(level, x) DEBUG(level, x)
#else
#define M_DEBUG(level, x)
#endif
/* these flags are used to mark characters in as having particular
properties */
#define FLAG_BASECHAR 1
#define FLAG_ASCII 2
#define FLAG_ILLEGAL 4
#define FLAG_WILDCARD 8
/* the "possible" flags are used as a fast way to find possible DOS
reserved filenames */
#define FLAG_POSSIBLE1 16
#define FLAG_POSSIBLE2 32
#define FLAG_POSSIBLE3 64
#define FLAG_POSSIBLE4 128
#define DEFAULT_MANGLE_PREFIX 4
#define MANGLE_BASECHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define FLAG_CHECK(c, flag) (ctx->char_flags[(uint8_t)(c)] & (flag))
static const char *reserved_names[] =
{ "AUX", "CON", "COM1", "COM2", "COM3", "COM4",
"LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL };
struct pvfs_mangle_context {
uint8_t char_flags[256];
/*
this determines how many characters are used from the original
filename in the 8.3 mangled name. A larger value leads to a weaker
hash and more collisions. The largest possible value is 6.
*/
int mangle_prefix;
uint32_t mangle_modulus;
/* we will use a very simple direct mapped prefix cache. The big
advantage of this cache structure is speed and low memory usage
The cache is indexed by the low-order bits of the hash, and confirmed by
hashing the resulting cache entry to match the known hash
*/
uint32_t cache_size;
char **prefix_cache;
uint32_t *prefix_cache_hashes;
/* this is used to reverse the base 36 mapping */
unsigned char base_reverse[256];
};
/*
hash a string of the specified length. The string does not need to be
null terminated
this hash needs to be fast with a low collision rate (what hash doesn't?)
*/
static uint32_t mangle_hash(struct pvfs_mangle_context *ctx,
const char *key, size_t length)
{
return pvfs_name_hash(key, length) % ctx->mangle_modulus;
}
/*
insert an entry into the prefix cache. The string might not be null
terminated */
static void cache_insert(struct pvfs_mangle_context *ctx,
const char *prefix, int length, uint32_t hash)
{
int i = hash % ctx->cache_size;
if (ctx->prefix_cache[i]) {
talloc_free(ctx->prefix_cache[i]);
}
ctx->prefix_cache[i] = talloc_strndup(ctx->prefix_cache, prefix, length);
ctx->prefix_cache_hashes[i] = hash;
}
/*
lookup an entry in the prefix cache. Return NULL if not found.
*/
static const char *cache_lookup(struct pvfs_mangle_context *ctx, uint32_t hash)
{
int i = hash % ctx->cache_size;
if (!ctx->prefix_cache[i] || hash != ctx->prefix_cache_hashes[i]) {
return NULL;
}
/* yep, it matched */
return ctx->prefix_cache[i];
}
/*
determine if a string is possibly in a mangled format, ignoring
case
In this algorithm, mangled names use only pure ascii characters (no
multi-byte) so we can avoid doing a UCS2 conversion
*/
static BOOL is_mangled_component(struct pvfs_mangle_context *ctx,
const char *name, size_t len)
{
unsigned int i;
M_DEBUG(10,("is_mangled_component %s (len %u) ?\n", name, (unsigned int)len));
/* check the length */
if (len > 12 || len < 8)
return False;
/* the best distinguishing characteristic is the ~ */
if (name[6] != '~')
return False;
/* check extension */
if (len > 8) {
if (name[8] != '.')
return False;
for (i=9; name[i] && i < len; i++) {
if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
return False;
}
}
}
/* check lead characters */
for (i=0;i<ctx->mangle_prefix;i++) {
if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
return False;
}
}
/* check rest of hash */
if (! FLAG_CHECK(name[7], FLAG_BASECHAR)) {
return False;
}
for (i=ctx->mangle_prefix;i<6;i++) {
if (! FLAG_CHECK(name[i], FLAG_BASECHAR)) {
return False;
}
}
M_DEBUG(10,("is_mangled_component %s (len %u) -> yes\n", name, (unsigned int)len));
return True;
}
/*
determine if a string is possibly in a mangled format, ignoring
case
In this algorithm, mangled names use only pure ascii characters (no
multi-byte) so we can avoid doing a UCS2 conversion
NOTE! This interface must be able to handle a path with unix
directory separators. It should return true if any component is
mangled
*/
static BOOL is_mangled(struct pvfs_mangle_context *ctx, const char *name)
{
const char *p;
const char *s;
M_DEBUG(10,("is_mangled %s ?\n", name));
for (s=name; (p=strchr(s, '/')); s=p+1) {
if (is_mangled_component(ctx, s, PTR_DIFF(p, s))) {
return True;
}
}
/* and the last part ... */
return is_mangled_component(ctx, s, strlen(s));
}
/*
see if a filename is an allowable 8.3 name.
we are only going to allow ascii characters in 8.3 names, as this
simplifies things greatly (it means that we know the string won't
get larger when converted from UNIX to DOS formats)
*/
static BOOL is_8_3(struct pvfs_mangle_context *ctx,
const char *name, BOOL check_case, BOOL allow_wildcards)
{
int len, i;
char *dot_p;
/* as a special case, the names '.' and '..' are allowable 8.3 names */
if (name[0] == '.') {
if (!name[1] || (name[1] == '.' && !name[2])) {
return True;
}
}
/* the simplest test is on the overall length of the
filename. Note that we deliberately use the ascii string
length (not the multi-byte one) as it is faster, and gives us
the result we need in this case. Using strlen_m would not
only be slower, it would be incorrect */
len = strlen(name);
if (len > 12)
return False;
/* find the '.'. Note that once again we use the non-multibyte
function */
dot_p = strchr(name, '.');
if (!dot_p) {
/* if the name doesn't contain a '.' then its length
must be less than 8 */
if (len > 8) {
return False;
}
} else {
int prefix_len, suffix_len;
/* if it does contain a dot then the prefix must be <=
8 and the suffix <= 3 in length */
prefix_len = PTR_DIFF(dot_p, name);
suffix_len = len - (prefix_len+1);
if (prefix_len > 8 || suffix_len > 3 || suffix_len == 0) {
return False;
}
/* a 8.3 name cannot contain more than 1 '.' */
if (strchr(dot_p+1, '.')) {
return False;
}
}
/* the length are all OK. Now check to see if the characters themselves are OK */
for (i=0; name[i]; i++) {
/* note that we may allow wildcard petterns! */
if (!FLAG_CHECK(name[i], FLAG_ASCII|(allow_wildcards ? FLAG_WILDCARD : 0)) &&
name[i] != '.') {
return False;
}
}
/* it is a good 8.3 name */
return True;
}
/*
try to find a 8.3 name in the cache, and if found then
return the original long name.
*/
static char *check_cache(struct pvfs_mangle_context *ctx,
TALLOC_CTX *mem_ctx, const char *name)
{
uint32_t hash, multiplier;
unsigned int i;
const char *prefix;
char extension[4];
/* make sure that this is a mangled name from this cache */
if (!is_mangled(ctx, name)) {
M_DEBUG(10,("check_cache: %s -> not mangled\n", name));
return NULL;
}
/* we need to extract the hash from the 8.3 name */
hash = ctx->base_reverse[(unsigned char)name[7]];
for (multiplier=36, i=5;i>=ctx->mangle_prefix;i--) {
uint32_t v = ctx->base_reverse[(unsigned char)name[i]];
hash += multiplier * v;
multiplier *= 36;
}
/* now look in the prefix cache for that hash */
prefix = cache_lookup(ctx, hash);
if (!prefix) {
M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash));
return NULL;
}
/* we found it - construct the full name */
if (name[8] == '.') {
strncpy(extension, name+9, 3);
extension[3] = 0;
} else {
extension[0] = 0;
}
if (extension[0]) {
return talloc_asprintf(mem_ctx, "%s.%s", prefix, extension);
}
return talloc_strdup(mem_ctx, prefix);
}
/*
look for a DOS reserved name
*/
static BOOL is_reserved_name(struct pvfs_mangle_context *ctx, const char *name)
{
if (FLAG_CHECK(name[0], FLAG_POSSIBLE1) &&
FLAG_CHECK(name[1], FLAG_POSSIBLE2) &&
FLAG_CHECK(name[2], FLAG_POSSIBLE3) &&
FLAG_CHECK(name[3], FLAG_POSSIBLE4)) {
/* a likely match, scan the lot */
int i;
for (i=0; reserved_names[i]; i++) {
if (strcasecmp(name, reserved_names[i]) == 0) {
return True;
}
}
}
return False;
}
/*
See if a filename is a legal long filename.
A filename ending in a '.' is not legal unless it's "." or "..". JRA.
*/
static BOOL is_legal_name(struct pvfs_mangle_context *ctx, const char *name)
{
while (*name) {
size_t c_size;
codepoint_t c = next_codepoint(name, &c_size);
if (c == INVALID_CODEPOINT) {
return False;
}
/* all high chars are OK */
if (c >= 128) {
name += c_size;
continue;
}
if (FLAG_CHECK(c, FLAG_ILLEGAL)) {
return False;
}
name += c_size;
}
return True;
}
/*
the main forward mapping function, which converts a long filename to
a 8.3 name
if need83 is not set then we only do the mangling if the name is illegal
as a long name
if cache83 is not set then we don't cache the result
return NULL if we don't need to do any conversion
*/
static char *name_map(struct pvfs_mangle_context *ctx,
const char *name, BOOL need83, BOOL cache83)
{
char *dot_p;
char lead_chars[7];
char extension[4];
unsigned int extension_length, i;
unsigned int prefix_len;
uint32_t hash, v;
char *new_name;
const char *basechars = MANGLE_BASECHARS;
/* reserved names are handled specially */
if (!is_reserved_name(ctx, name)) {
/* if the name is already a valid 8.3 name then we don't need to
do anything */
if (is_8_3(ctx, name, False, False)) {
return NULL;
}
/* if the caller doesn't strictly need 8.3 then just check for illegal
filenames */
if (!need83 && is_legal_name(ctx, name)) {
return NULL;
}
}
/* find the '.' if any */
dot_p = strrchr(name, '.');
if (dot_p) {
/* if the extension contains any illegal characters or
is too long or zero length then we treat it as part
of the prefix */
for (i=0; i<4 && dot_p[i+1]; i++) {
if (! FLAG_CHECK(dot_p[i+1], FLAG_ASCII)) {
dot_p = NULL;
break;
}
}
if (i == 0 || i == 4) dot_p = NULL;
}
/* the leading characters in the mangled name is taken from
the first characters of the name, if they are ascii otherwise
'_' is used
*/
for (i=0;i<ctx->mangle_prefix && name[i];i++) {
lead_chars[i] = name[i];
if (! FLAG_CHECK(lead_chars[i], FLAG_ASCII)) {
lead_chars[i] = '_';
}
lead_chars[i] = toupper((unsigned char)lead_chars[i]);
}
for (;i<ctx->mangle_prefix;i++) {
lead_chars[i] = '_';
}
/* the prefix is anything up to the first dot */
if (dot_p) {
prefix_len = PTR_DIFF(dot_p, name);
} else {
prefix_len = strlen(name);
}
/* the extension of the mangled name is taken from the first 3
ascii chars after the dot */
extension_length = 0;
if (dot_p) {
for (i=1; extension_length < 3 && dot_p[i]; i++) {
unsigned char c = dot_p[i];
if (FLAG_CHECK(c, FLAG_ASCII)) {
extension[extension_length++] = toupper(c);
}
}
}
/* find the hash for this prefix */
v = hash = mangle_hash(ctx, name, prefix_len);
new_name = talloc_array(ctx, char, 13);
if (new_name == NULL) {
return NULL;
}
/* now form the mangled name. */
for (i=0;i<ctx->mangle_prefix;i++) {
new_name[i] = lead_chars[i];
}
new_name[7] = basechars[v % 36];
new_name[6] = '~';
for (i=5; i>=ctx->mangle_prefix; i--) {
v = v / 36;
new_name[i] = basechars[v % 36];
}
/* add the extension */
if (extension_length) {
new_name[8] = '.';
memcpy(&new_name[9], extension, extension_length);
new_name[9+extension_length] = 0;
} else {
new_name[8] = 0;
}
if (cache83) {
/* put it in the cache */
cache_insert(ctx, name, prefix_len, hash);
}
M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n",
name, hash, new_name, cache83));
return new_name;
}
/* initialise the flags table
we allow only a very restricted set of characters as 'ascii' in this
mangling backend. This isn't a significant problem as modern clients
use the 'long' filenames anyway, and those don't have these
restrictions.
*/
static void init_tables(struct pvfs_mangle_context *ctx)
{
const char *basechars = MANGLE_BASECHARS;
int i;
/* the list of reserved dos names - all of these are illegal */
ZERO_STRUCT(ctx->char_flags);
for (i=1;i<128;i++) {
if ((i >= '0' && i <= '9') ||
(i >= 'a' && i <= 'z') ||
(i >= 'A' && i <= 'Z')) {
ctx->char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR);
}
if (strchr("_-$~", i)) {
ctx->char_flags[i] |= FLAG_ASCII;
}
if (strchr("*\\/?<>|\":", i)) {
ctx->char_flags[i] |= FLAG_ILLEGAL;
}
if (strchr("*?\"<>", i)) {
ctx->char_flags[i] |= FLAG_WILDCARD;
}
}
ZERO_STRUCT(ctx->base_reverse);
for (i=0;i<36;i++) {
ctx->base_reverse[(uint8_t)basechars[i]] = i;
}
/* fill in the reserved names flags. These are used as a very
fast filter for finding possible DOS reserved filenames */
for (i=0; reserved_names[i]; i++) {
unsigned char c1, c2, c3, c4;
c1 = (unsigned char)reserved_names[i][0];
c2 = (unsigned char)reserved_names[i][1];
c3 = (unsigned char)reserved_names[i][2];
c4 = (unsigned char)reserved_names[i][3];
ctx->char_flags[c1] |= FLAG_POSSIBLE1;
ctx->char_flags[c2] |= FLAG_POSSIBLE2;
ctx->char_flags[c3] |= FLAG_POSSIBLE3;
ctx->char_flags[c4] |= FLAG_POSSIBLE4;
ctx->char_flags[tolower(c1)] |= FLAG_POSSIBLE1;
ctx->char_flags[tolower(c2)] |= FLAG_POSSIBLE2;
ctx->char_flags[tolower(c3)] |= FLAG_POSSIBLE3;
ctx->char_flags[tolower(c4)] |= FLAG_POSSIBLE4;
ctx->char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4;
}
ctx->mangle_modulus = 1;
for (i=0;i<(7-ctx->mangle_prefix);i++) {
ctx->mangle_modulus *= 36;
}
}
/*
initialise the mangling code
*/
NTSTATUS pvfs_mangle_init(struct pvfs_state *pvfs)
{
struct pvfs_mangle_context *ctx;
ctx = talloc(pvfs, struct pvfs_mangle_context);
if (ctx == NULL) {
return NT_STATUS_NO_MEMORY;
}
/* by default have a max of 512 entries in the cache. */
ctx->cache_size = lp_parm_int(-1, "mangle", "cachesize", 512);
ctx->prefix_cache = talloc_array(ctx, char *, ctx->cache_size);
if (ctx->prefix_cache == NULL) {
return NT_STATUS_NO_MEMORY;
}
ctx->prefix_cache_hashes = talloc_array(ctx, uint32_t, ctx->cache_size);
if (ctx->prefix_cache_hashes == NULL) {
return NT_STATUS_NO_MEMORY;
}
memset(ctx->prefix_cache, 0, sizeof(char *) * ctx->cache_size);
memset(ctx->prefix_cache_hashes, 0, sizeof(uint32_t) * ctx->cache_size);
ctx->mangle_prefix = lp_parm_int(-1, "mangle", "prefix", -1);
if (ctx->mangle_prefix < 0 || ctx->mangle_prefix > 6) {
ctx->mangle_prefix = DEFAULT_MANGLE_PREFIX;
}
init_tables(ctx);
pvfs->mangle_ctx = ctx;
return NT_STATUS_OK;
}
/*
return the short name for a component of a full name
*/
char *pvfs_short_name_component(struct pvfs_state *pvfs, const char *name)
{
return name_map(pvfs->mangle_ctx, name, True, True);
}
/*
return the short name for a given entry in a directory
*/
const char *pvfs_short_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
struct pvfs_filename *name)
{
char *p = strrchr(name->full_name, '/');
char *ret = pvfs_short_name_component(pvfs, p+1);
if (ret == NULL) {
return p+1;
}
talloc_steal(mem_ctx, ret);
return ret;
}
/*
lookup a mangled name, returning the original long name if present
in the cache
*/
char *pvfs_mangled_lookup(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
const char *name)
{
return check_cache(pvfs->mangle_ctx, mem_ctx, name);
}
/*
look for a DOS reserved name
*/
BOOL pvfs_is_reserved_name(struct pvfs_state *pvfs, const char *name)
{
return is_reserved_name(pvfs->mangle_ctx, name);
}
/*
see if a component of a filename could be a mangled name from our
mangling code
*/
BOOL pvfs_is_mangled_component(struct pvfs_state *pvfs, const char *name)
{
return is_mangled_component(pvfs->mangle_ctx, name, strlen(name));
}
+358
View File
@@ -0,0 +1,358 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - alternate data streams
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/xattr.h"
/*
return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION
*/
NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs,
TALLOC_CTX *mem_ctx,
struct pvfs_filename *name, int fd,
struct stream_information *info)
{
struct xattr_DosStreams *streams;
int i;
NTSTATUS status;
streams = talloc(mem_ctx, struct xattr_DosStreams);
if (streams == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = pvfs_streams_load(pvfs, name, fd, streams);
if (!NT_STATUS_IS_OK(status)) {
ZERO_STRUCTP(streams);
}
info->num_streams = streams->num_streams+1;
info->streams = talloc_array(mem_ctx, struct stream_struct, info->num_streams);
if (!info->streams) {
return NT_STATUS_NO_MEMORY;
}
info->streams[0].size = name->st.st_size;
info->streams[0].alloc_size = name->dos.alloc_size;
info->streams[0].stream_name.s = talloc_strdup(info->streams, "::$DATA");
for (i=0;i<streams->num_streams;i++) {
info->streams[i+1].size = streams->streams[i].size;
info->streams[i+1].alloc_size = streams->streams[i].alloc_size;
info->streams[i+1].stream_name.s = talloc_asprintf(streams->streams,
":%s:$DATA",
streams->streams[i].name);
}
return NT_STATUS_OK;
}
/*
fill in the stream information for a name
*/
NTSTATUS pvfs_stream_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
{
struct xattr_DosStreams *streams;
int i;
NTSTATUS status;
/* the NULL stream always exists */
if (name->stream_name == NULL) {
name->stream_exists = True;
return NT_STATUS_OK;
}
streams = talloc(name, struct xattr_DosStreams);
if (streams == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = pvfs_streams_load(pvfs, name, fd, streams);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(streams);
return status;
}
for (i=0;i<streams->num_streams;i++) {
struct xattr_DosStream *s = &streams->streams[i];
if (strcasecmp_m(s->name, name->stream_name) == 0) {
name->dos.alloc_size = pvfs_round_alloc_size(pvfs, s->alloc_size);
name->st.st_size = s->size;
name->stream_exists = True;
talloc_free(streams);
return NT_STATUS_OK;
}
}
talloc_free(streams);
name->dos.alloc_size = 0;
name->st.st_size = 0;
name->stream_exists = False;
return NT_STATUS_OK;
}
/*
update size information for a stream
*/
static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
off_t size)
{
struct xattr_DosStreams *streams;
int i;
NTSTATUS status;
streams = talloc(name, struct xattr_DosStreams);
if (streams == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = pvfs_streams_load(pvfs, name, fd, streams);
if (!NT_STATUS_IS_OK(status)) {
ZERO_STRUCTP(streams);
}
for (i=0;i<streams->num_streams;i++) {
struct xattr_DosStream *s = &streams->streams[i];
if (strcasecmp_m(s->name, name->stream_name) == 0) {
s->size = size;
s->alloc_size = pvfs_round_alloc_size(pvfs, size);
break;
}
}
if (i == streams->num_streams) {
struct xattr_DosStream *s;
streams->streams = talloc_realloc(streams, streams->streams,
struct xattr_DosStream,
streams->num_streams+1);
if (streams->streams == NULL) {
talloc_free(streams);
return NT_STATUS_NO_MEMORY;
}
streams->num_streams++;
s = &streams->streams[i];
s->flags = XATTR_STREAM_FLAG_INTERNAL;
s->size = size;
s->alloc_size = pvfs_round_alloc_size(pvfs, size);
s->name = name->stream_name;
}
status = pvfs_streams_save(pvfs, name, fd, streams);
talloc_free(streams);
return status;
}
/*
create the xattr for a alternate data stream
*/
NTSTATUS pvfs_stream_create(struct pvfs_state *pvfs,
struct pvfs_filename *name,
int fd)
{
NTSTATUS status;
status = pvfs_xattr_create(pvfs, name->full_name, fd,
XATTR_DOSSTREAM_PREFIX, name->stream_name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return pvfs_stream_update_size(pvfs, name, fd, 0);
}
/*
delete the xattr for a alternate data stream
*/
NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs,
struct pvfs_filename *name,
int fd)
{
NTSTATUS status;
struct xattr_DosStreams *streams;
int i;
status = pvfs_xattr_delete(pvfs, name->full_name, fd,
XATTR_DOSSTREAM_PREFIX, name->stream_name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
streams = talloc(name, struct xattr_DosStreams);
if (streams == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = pvfs_streams_load(pvfs, name, fd, streams);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(streams);
return status;
}
for (i=0;i<streams->num_streams;i++) {
struct xattr_DosStream *s = &streams->streams[i];
if (strcasecmp_m(s->name, name->stream_name) == 0) {
memmove(s, s+1, (streams->num_streams - (i+1)) * sizeof(*s));
streams->num_streams--;
break;
}
}
status = pvfs_streams_save(pvfs, name, fd, streams);
talloc_free(streams);
return status;
}
/*
the equvalent of pread() on a stream
*/
ssize_t pvfs_stream_read(struct pvfs_state *pvfs,
struct pvfs_file_handle *h, void *data, size_t count, off_t offset)
{
NTSTATUS status;
DATA_BLOB blob;
if (count == 0) {
return 0;
}
status = pvfs_xattr_load(pvfs, h, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
h->name->stream_name, offset+count, &blob);
if (!NT_STATUS_IS_OK(status)) {
errno = EIO;
return -1;
}
if (offset >= blob.length) {
data_blob_free(&blob);
return 0;
}
if (count > blob.length - offset) {
count = blob.length - offset;
}
memcpy(data, blob.data + offset, count);
data_blob_free(&blob);
return count;
}
/*
the equvalent of pwrite() on a stream
*/
ssize_t pvfs_stream_write(struct pvfs_state *pvfs,
struct pvfs_file_handle *h, const void *data, size_t count, off_t offset)
{
NTSTATUS status;
DATA_BLOB blob;
if (count == 0) {
return 0;
}
if (offset > XATTR_MAX_STREAM_SIZE) {
errno = ENOSPC;
return -1;
}
/* we have to load the existing stream, then modify, then save */
status = pvfs_xattr_load(pvfs, h, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
h->name->stream_name, offset+count, &blob);
if (!NT_STATUS_IS_OK(status)) {
blob = data_blob(NULL, 0);
}
if (count+offset > blob.length) {
blob.data = talloc_realloc(blob.data, blob.data, uint8_t, count+offset);
if (blob.data == NULL) {
errno = ENOMEM;
return -1;
}
if (offset > blob.length) {
memset(blob.data+blob.length, 0, offset - blob.length);
}
blob.length = count+offset;
}
memcpy(blob.data + offset, data, count);
status = pvfs_xattr_save(pvfs, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
h->name->stream_name, &blob);
if (!NT_STATUS_IS_OK(status)) {
data_blob_free(&blob);
/* getting this error mapping right is probably
not worth it */
errno = ENOSPC;
return -1;
}
status = pvfs_stream_update_size(pvfs, h->name, h->fd, blob.length);
data_blob_free(&blob);
if (!NT_STATUS_IS_OK(status)) {
errno = EIO;
return -1;
}
return count;
}
/*
the equvalent of truncate() on a stream
*/
NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs,
struct pvfs_filename *name, int fd, off_t length)
{
NTSTATUS status;
DATA_BLOB blob;
if (length > XATTR_MAX_STREAM_SIZE) {
return NT_STATUS_DISK_FULL;
}
/* we have to load the existing stream, then modify, then save */
status = pvfs_xattr_load(pvfs, name, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
name->stream_name, length, &blob);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (length <= blob.length) {
blob.length = length;
} else if (length > blob.length) {
blob.data = talloc_realloc(blob.data, blob.data, uint8_t, length);
if (blob.data == NULL) {
return NT_STATUS_NO_MEMORY;
}
memset(blob.data+blob.length, 0, length - blob.length);
blob.length = length;
}
status = pvfs_xattr_save(pvfs, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
name->stream_name, &blob);
data_blob_free(&blob);
if (NT_STATUS_IS_OK(status)) {
status = pvfs_stream_update_size(pvfs, name, fd, blob.length);
}
return status;
}
+185
View File
@@ -0,0 +1,185 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - unlink
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "system/dir.h"
/*
unlink a stream
*/
static NTSTATUS pvfs_unlink_stream(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name,
uint16_t attrib)
{
NTSTATUS status;
struct odb_lock *lck;
if (!name->stream_exists) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
/* make sure its matches the given attributes */
status = pvfs_match_attrib(pvfs, name, attrib, 0);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = pvfs_can_delete(pvfs, req, name, &lck);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return pvfs_stream_delete(pvfs, name, -1);
}
/*
unlink one file
*/
static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
struct ntvfs_request *req,
const char *unix_path,
const char *fname, uint32_t attrib)
{
struct pvfs_filename *name;
NTSTATUS status;
struct odb_lock *lck;
/* get a pvfs_filename object */
status = pvfs_resolve_partial(pvfs, req,
unix_path, fname, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* make sure its matches the given attributes */
status = pvfs_match_attrib(pvfs, name, attrib, 0);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(name);
return status;
}
status = pvfs_can_delete(pvfs, req, name, &lck);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(name);
return status;
}
if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
talloc_free(name);
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
if (name->st.st_nlink == 1) {
status = pvfs_xattr_unlink_hook(pvfs, name->full_name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
}
/* finally try the actual unlink */
if (unlink(name->full_name) == -1) {
status = pvfs_map_errno(pvfs, errno);
}
if (NT_STATUS_IS_OK(status)) {
notify_trigger(pvfs->notify_context,
NOTIFY_ACTION_REMOVED,
FILE_NOTIFY_CHANGE_FILE_NAME,
name->full_name);
}
talloc_free(name);
return status;
}
/*
delete a file - the dirtype specifies the file types to include in the search.
The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
*/
NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_unlink *unl)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_dir *dir;
NTSTATUS status;
uint32_t total_deleted=0;
struct pvfs_filename *name;
const char *fname;
off_t ofs;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, unl->unlink.in.pattern,
PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (!name->exists && !name->has_wildcard) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
if (name->exists &&
(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
if (name->stream_name) {
return pvfs_unlink_stream(pvfs, req, name, unl->unlink.in.attrib);
}
/* get list of matching files */
status = pvfs_list_start(pvfs, name, req, &dir);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
status = NT_STATUS_NO_SUCH_FILE;
ofs = 0;
while ((fname = pvfs_list_next(dir, &ofs))) {
/* this seems to be a special case */
if ((unl->unlink.in.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
(ISDOT(fname) || ISDOTDOT(fname))) {
return NT_STATUS_OBJECT_NAME_INVALID;
}
status = pvfs_unlink_one(pvfs, req, pvfs_list_unix_path(dir), fname, unl->unlink.in.attrib);
if (NT_STATUS_IS_OK(status)) {
total_deleted++;
}
}
if (total_deleted > 0) {
status = NT_STATUS_OK;
}
return status;
}
+203
View File
@@ -0,0 +1,203 @@
/*
Unix SMB/CIFS implementation.
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
utility functions for posix backend
*/
#include "includes.h"
#include "vfs_posix.h"
/*
return True if a string contains one of the CIFS wildcard characters
*/
BOOL pvfs_has_wildcard(const char *str)
{
if (strpbrk(str, "*?<>\"")) {
return True;
}
return False;
}
/*
map a unix errno to a NTSTATUS
*/
NTSTATUS pvfs_map_errno(struct pvfs_state *pvfs, int unix_errno)
{
return map_nt_error_from_unix(unix_errno);
}
/*
check if a filename has an attribute matching the given attribute search value
this is used by calls like unlink and search which take an attribute
and only include special files if they match the given attribute
*/
NTSTATUS pvfs_match_attrib(struct pvfs_state *pvfs, struct pvfs_filename *name,
uint32_t attrib, uint32_t must_attrib)
{
if ((name->dos.attrib & ~attrib) & FILE_ATTRIBUTE_DIRECTORY) {
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
if ((name->dos.attrib & ~attrib) & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) {
return NT_STATUS_NO_SUCH_FILE;
}
if (must_attrib & ~name->dos.attrib) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
return NT_STATUS_OK;
}
/*
normalise a file attribute
*/
uint32_t pvfs_attrib_normalise(uint32_t attrib, mode_t mode)
{
if (attrib != FILE_ATTRIBUTE_NORMAL) {
attrib &= ~FILE_ATTRIBUTE_NORMAL;
}
if (S_ISDIR(mode)) {
attrib |= FILE_ATTRIBUTE_DIRECTORY;
} else {
attrib &= ~FILE_ATTRIBUTE_DIRECTORY;
}
return attrib;
}
/*
copy a file. Caller is supposed to have already ensured that the
operation is allowed. The destination file must not exist.
*/
NTSTATUS pvfs_copy_file(struct pvfs_state *pvfs,
struct pvfs_filename *name1,
struct pvfs_filename *name2)
{
int fd1, fd2;
mode_t mode;
NTSTATUS status;
size_t buf_size = 0x10000;
char *buf = talloc_size(name2, buf_size);
if (buf == NULL) {
return NT_STATUS_NO_MEMORY;
}
fd1 = open(name1->full_name, O_RDONLY);
if (fd1 == -1) {
talloc_free(buf);
return pvfs_map_errno(pvfs, errno);
}
fd2 = open(name2->full_name, O_CREAT|O_EXCL|O_WRONLY, 0);
if (fd2 == -1) {
close(fd1);
talloc_free(buf);
return pvfs_map_errno(pvfs, errno);
}
while (1) {
ssize_t ret2, ret = read(fd1, buf, buf_size);
if (ret == -1 &&
(errno == EINTR || errno == EAGAIN)) {
continue;
}
if (ret <= 0) break;
ret2 = write(fd2, buf, ret);
if (ret2 == -1 &&
(errno == EINTR || errno == EAGAIN)) {
continue;
}
if (ret2 != ret) {
close(fd1);
close(fd2);
talloc_free(buf);
unlink(name2->full_name);
if (ret2 == -1) {
return pvfs_map_errno(pvfs, errno);
}
return NT_STATUS_DISK_FULL;
}
}
talloc_free(buf);
close(fd1);
mode = pvfs_fileperms(pvfs, name1->dos.attrib);
if (fchmod(fd2, mode) == -1) {
status = pvfs_map_errno(pvfs, errno);
close(fd2);
unlink(name2->full_name);
return status;
}
name2->st.st_mode = mode;
name2->dos = name1->dos;
status = pvfs_dosattrib_save(pvfs, name2, fd2);
if (!NT_STATUS_IS_OK(status)) {
close(fd2);
unlink(name2->full_name);
return status;
}
close(fd2);
return NT_STATUS_OK;
}
/*
hash a string of the specified length. The string does not need to be
null terminated
hash alghorithm changed to FNV1 by idra@samba.org (Simo Sorce).
see http://www.isthe.com/chongo/tech/comp/fnv/index.html for a
discussion on Fowler / Noll / Vo (FNV) Hash by one of it's authors
*/
uint32_t pvfs_name_hash(const char *key, size_t length)
{
const uint32_t fnv1_prime = 0x01000193;
const uint32_t fnv1_init = 0xa6b93095;
uint32_t value = fnv1_init;
while (*key && length--) {
size_t c_size;
codepoint_t c = next_codepoint(key, &c_size);
c = toupper_w(c);
value *= fnv1_prime;
value ^= (uint32_t)c;
key += c_size;
}
return value;
}
/*
file allocation size rounding. This is required to pass ifstest
*/
uint64_t pvfs_round_alloc_size(struct pvfs_state *pvfs, uint64_t size)
{
const uint32_t round_value = pvfs->alloc_size_rounding;
return round_value * ((size + round_value - 1)/round_value);
}
+191
View File
@@ -0,0 +1,191 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - async request wait routines
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "lib/events/events.h"
#include "lib/util/dlinklist.h"
#include "vfs_posix.h"
#include "smbd/service_stream.h"
#include "lib/messaging/irpc.h"
/* the context for a single wait instance */
struct pvfs_wait {
struct pvfs_wait *next, *prev;
struct pvfs_state *pvfs;
void (*handler)(void *, enum pvfs_wait_notice);
void *private;
int msg_type;
struct messaging_context *msg_ctx;
struct event_context *ev;
struct ntvfs_request *req;
enum pvfs_wait_notice reason;
};
/*
called from the ntvfs layer when we have requested setup of an async
call. this ensures that async calls runs with the right state of
previous ntvfs handlers in the chain (such as security context)
*/
NTSTATUS pvfs_async_setup(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, void *private)
{
struct pvfs_wait *pwait = private;
pwait->handler(pwait->private, pwait->reason);
return NT_STATUS_OK;
}
/*
receive a completion message for a wait
*/
static void pvfs_wait_dispatch(struct messaging_context *msg, void *private, uint32_t msg_type,
uint32_t src, DATA_BLOB *data)
{
struct pvfs_wait *pwait = private;
struct ntvfs_request *req;
/* we need to check that this one is for us. See
messaging_send_ptr() for the other side of this.
*/
if (data->length != sizeof(void *) ||
*(void **)data->data != pwait->private) {
return;
}
pwait->reason = PVFS_WAIT_EVENT;
req = pwait->req;
/* the extra reference here is to ensure that the req
structure is not destroyed when the async request reply is
sent, which would cause problems with the other ntvfs
modules above us */
talloc_increase_ref_count(req);
ntvfs_async_setup(pwait->req, pwait);
talloc_free(req);
}
/*
receive a timeout on a message wait
*/
static void pvfs_wait_timeout(struct event_context *ev,
struct timed_event *te, struct timeval t, void *private)
{
struct pvfs_wait *pwait = talloc_get_type(private, struct pvfs_wait);
struct ntvfs_request *req = pwait->req;
pwait->reason = PVFS_WAIT_TIMEOUT;
talloc_increase_ref_count(req);
ntvfs_async_setup(pwait->req, pwait);
talloc_free(req);
}
/*
destroy a pending wait
*/
static int pvfs_wait_destructor(struct pvfs_wait *pwait)
{
if (pwait->msg_type != -1) {
messaging_deregister(pwait->msg_ctx, pwait->msg_type, pwait);
}
DLIST_REMOVE(pwait->pvfs->wait_list, pwait);
return 0;
}
/*
setup a request to wait on a message of type msg_type, with a
timeout (given as an expiry time)
the return value is a handle. To stop waiting talloc_free this
handle.
if msg_type == -1 then no message is registered, and it is assumed
that the caller handles any messaging setup needed
*/
void *pvfs_wait_message(struct pvfs_state *pvfs,
struct ntvfs_request *req,
int msg_type,
struct timeval end_time,
void (*fn)(void *, enum pvfs_wait_notice),
void *private)
{
struct pvfs_wait *pwait;
pwait = talloc(pvfs, struct pvfs_wait);
if (pwait == NULL) {
return NULL;
}
pwait->private = private;
pwait->handler = fn;
pwait->msg_ctx = pvfs->ntvfs->ctx->msg_ctx;
pwait->ev = pvfs->ntvfs->ctx->event_ctx;
pwait->msg_type = msg_type;
pwait->req = talloc_reference(pwait, req);
pwait->pvfs = pvfs;
if (!timeval_is_zero(&end_time)) {
/* setup a timer */
event_add_timed(pwait->ev, pwait, end_time, pvfs_wait_timeout, pwait);
}
/* register with the messaging subsystem for this message
type */
if (msg_type != -1) {
messaging_register(pwait->msg_ctx,
pwait,
msg_type,
pvfs_wait_dispatch);
}
/* tell the main smb server layer that we will be replying
asynchronously */
req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
DLIST_ADD(pvfs->wait_list, pwait);
/* make sure we cleanup the timer and message handler */
talloc_set_destructor(pwait, pvfs_wait_destructor);
return pwait;
}
/*
cancel an outstanding async request
*/
NTSTATUS pvfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_wait *pwait;
for (pwait=pvfs->wait_list;pwait;pwait=pwait->next) {
if (pwait->req == req) {
/* trigger a cancel on the request */
pwait->reason = PVFS_WAIT_CANCEL;
ntvfs_async_setup(pwait->req, pwait);
return NT_STATUS_OK;
}
}
return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
}
+87
View File
@@ -0,0 +1,87 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - write
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/security.h"
/*
write to a file
*/
NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_write *wr)
{
struct pvfs_state *pvfs = ntvfs->private_data;
ssize_t ret;
struct pvfs_file *f;
NTSTATUS status;
if (wr->generic.level != RAW_WRITE_WRITEX) {
return ntvfs_map_write(ntvfs, req, wr);
}
f = pvfs_find_fd(pvfs, req, wr->writex.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
if (f->handle->fd == -1) {
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
if (!(f->access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA))) {
return NT_STATUS_ACCESS_DENIED;
}
status = pvfs_check_lock(pvfs, f, req->smbpid,
wr->writex.in.offset,
wr->writex.in.count,
WRITE_LOCK);
NT_STATUS_NOT_OK_RETURN(status);
if (f->handle->name->stream_name) {
ret = pvfs_stream_write(pvfs,
f->handle,
wr->writex.in.data,
wr->writex.in.count,
wr->writex.in.offset);
} else {
ret = pwrite(f->handle->fd,
wr->writex.in.data,
wr->writex.in.count,
wr->writex.in.offset);
}
if (ret == -1) {
if (errno == EFBIG) {
return NT_STATUS_INVALID_PARAMETER;
}
return pvfs_map_errno(pvfs, errno);
}
f->handle->seek_offset = wr->writex.in.offset + ret;
wr->writex.out.nwritten = ret;
wr->writex.out.remaining = 0; /* should fill this in? */
return NT_STATUS_OK;
}
+477
View File
@@ -0,0 +1,477 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - xattr support
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "lib/util/unix_privs.h"
#include "librpc/gen_ndr/ndr_xattr.h"
/*
pull a xattr as a blob
*/
static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs,
TALLOC_CTX *mem_ctx,
const char *attr_name,
const char *fname,
int fd,
size_t estimated_size,
DATA_BLOB *blob)
{
NTSTATUS status;
if (pvfs->ea_db) {
return pull_xattr_blob_tdb(pvfs, mem_ctx, attr_name, fname,
fd, estimated_size, blob);
}
status = pull_xattr_blob_system(pvfs, mem_ctx, attr_name, fname,
fd, estimated_size, blob);
/* if the filesystem doesn't support them, then tell pvfs not to try again */
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)||
NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)||
NT_STATUS_EQUAL(status, NT_STATUS_INVALID_SYSTEM_SERVICE)) {
DEBUG(5,("pvfs_xattr: xattr not supported in filesystem: %s\n", nt_errstr(status)));
pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
status = NT_STATUS_NOT_FOUND;
}
return status;
}
/*
push a xattr as a blob
*/
static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs,
const char *attr_name,
const char *fname,
int fd,
const DATA_BLOB *blob)
{
if (pvfs->ea_db) {
return push_xattr_blob_tdb(pvfs, attr_name, fname, fd, blob);
}
return push_xattr_blob_system(pvfs, attr_name, fname, fd, blob);
}
/*
delete a xattr
*/
static NTSTATUS delete_xattr(struct pvfs_state *pvfs, const char *attr_name,
const char *fname, int fd)
{
if (pvfs->ea_db) {
return delete_xattr_tdb(pvfs, attr_name, fname, fd);
}
return delete_xattr_system(pvfs, attr_name, fname, fd);
}
/*
a hook called on unlink - allows the tdb xattr backend to cleanup
*/
NTSTATUS pvfs_xattr_unlink_hook(struct pvfs_state *pvfs, const char *fname)
{
if (pvfs->ea_db) {
return unlink_xattr_tdb(pvfs, fname);
}
return unlink_xattr_system(pvfs, fname);
}
/*
load a NDR structure from a xattr
*/
_PUBLIC_ NTSTATUS pvfs_xattr_ndr_load(struct pvfs_state *pvfs,
TALLOC_CTX *mem_ctx,
const char *fname, int fd, const char *attr_name,
void *p, void *pull_fn)
{
NTSTATUS status;
DATA_BLOB blob;
status = pull_xattr_blob(pvfs, mem_ctx, attr_name, fname,
fd, XATTR_DOSATTRIB_ESTIMATED_SIZE, &blob);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* pull the blob */
status = ndr_pull_struct_blob(&blob, mem_ctx, p, (ndr_pull_flags_fn_t)pull_fn);
data_blob_free(&blob);
return status;
}
/*
save a NDR structure into a xattr
*/
_PUBLIC_ NTSTATUS pvfs_xattr_ndr_save(struct pvfs_state *pvfs,
const char *fname, int fd, const char *attr_name,
void *p, void *push_fn)
{
TALLOC_CTX *mem_ctx = talloc_new(NULL);
DATA_BLOB blob;
NTSTATUS status;
status = ndr_push_struct_blob(&blob, mem_ctx, p, (ndr_push_flags_fn_t)push_fn);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(mem_ctx);
return status;
}
status = push_xattr_blob(pvfs, attr_name, fname, fd, &blob);
talloc_free(mem_ctx);
return status;
}
/*
fill in file attributes from extended attributes
*/
NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
{
NTSTATUS status;
struct xattr_DosAttrib attrib;
TALLOC_CTX *mem_ctx = talloc_new(name);
struct xattr_DosInfo1 *info1;
struct xattr_DosInfo2 *info2;
if (name->stream_name != NULL) {
name->stream_exists = False;
} else {
name->stream_exists = True;
}
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_OK;
}
status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name,
fd, XATTR_DOSATTRIB_NAME,
&attrib,
(ndr_pull_flags_fn_t)ndr_pull_xattr_DosAttrib);
/* not having a DosAttrib is not an error */
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
talloc_free(mem_ctx);
return pvfs_stream_info(pvfs, name, fd);
}
if (!NT_STATUS_IS_OK(status)) {
talloc_free(mem_ctx);
return status;
}
switch (attrib.version) {
case 1:
info1 = &attrib.info.info1;
name->dos.attrib = pvfs_attrib_normalise(info1->attrib,
name->st.st_mode);
name->dos.ea_size = info1->ea_size;
if (name->st.st_size == info1->size) {
name->dos.alloc_size =
pvfs_round_alloc_size(pvfs, info1->alloc_size);
}
if (!null_nttime(info1->create_time)) {
name->dos.create_time = info1->create_time;
}
if (!null_nttime(info1->change_time)) {
name->dos.change_time = info1->change_time;
}
name->dos.flags = 0;
break;
case 2:
info2 = &attrib.info.info2;
name->dos.attrib = pvfs_attrib_normalise(info2->attrib,
name->st.st_mode);
name->dos.ea_size = info2->ea_size;
if (name->st.st_size == info2->size) {
name->dos.alloc_size =
pvfs_round_alloc_size(pvfs, info2->alloc_size);
}
if (!null_nttime(info2->create_time)) {
name->dos.create_time = info2->create_time;
}
if (!null_nttime(info2->change_time)) {
name->dos.change_time = info2->change_time;
}
name->dos.flags = info2->flags;
if (name->dos.flags & XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME) {
name->dos.write_time = info2->write_time;
}
break;
default:
DEBUG(0,("ERROR: Unsupported xattr DosAttrib version %d on '%s'\n",
attrib.version, name->full_name));
talloc_free(mem_ctx);
return NT_STATUS_INVALID_LEVEL;
}
talloc_free(mem_ctx);
status = pvfs_stream_info(pvfs, name, fd);
return status;
}
/*
save the file attribute into the xattr
*/
NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
{
struct xattr_DosAttrib attrib;
struct xattr_DosInfo2 *info2;
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_OK;
}
attrib.version = 2;
info2 = &attrib.info.info2;
name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib, name->st.st_mode);
info2->attrib = name->dos.attrib;
info2->ea_size = name->dos.ea_size;
info2->size = name->st.st_size;
info2->alloc_size = name->dos.alloc_size;
info2->create_time = name->dos.create_time;
info2->change_time = name->dos.change_time;
info2->write_time = name->dos.write_time;
info2->flags = name->dos.flags;
info2->name = "";
return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
XATTR_DOSATTRIB_NAME, &attrib,
(ndr_push_flags_fn_t)ndr_push_xattr_DosAttrib);
}
/*
load the set of DOS EAs
*/
NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
struct xattr_DosEAs *eas)
{
NTSTATUS status;
ZERO_STRUCTP(eas);
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_OK;
}
status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME,
eas, (ndr_pull_flags_fn_t)ndr_pull_xattr_DosEAs);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
return NT_STATUS_OK;
}
return status;
}
/*
save the set of DOS EAs
*/
NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
struct xattr_DosEAs *eas)
{
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_OK;
}
return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas,
(ndr_push_flags_fn_t)ndr_push_xattr_DosEAs);
}
/*
load the set of streams from extended attributes
*/
NTSTATUS pvfs_streams_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
struct xattr_DosStreams *streams)
{
NTSTATUS status;
ZERO_STRUCTP(streams);
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_OK;
}
status = pvfs_xattr_ndr_load(pvfs, streams, name->full_name, fd,
XATTR_DOSSTREAMS_NAME,
streams,
(ndr_pull_flags_fn_t)ndr_pull_xattr_DosStreams);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
return NT_STATUS_OK;
}
return status;
}
/*
save the set of streams into filesystem xattr
*/
NTSTATUS pvfs_streams_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
struct xattr_DosStreams *streams)
{
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_OK;
}
return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
XATTR_DOSSTREAMS_NAME,
streams,
(ndr_push_flags_fn_t)ndr_push_xattr_DosStreams);
}
/*
load the current ACL from extended attributes
*/
NTSTATUS pvfs_acl_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
struct xattr_NTACL *acl)
{
NTSTATUS status;
ZERO_STRUCTP(acl);
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_NOT_FOUND;
}
status = pvfs_xattr_ndr_load(pvfs, acl, name->full_name, fd,
XATTR_NTACL_NAME,
acl,
(ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
return status;
}
/*
save the acl for a file into filesystem xattr
*/
NTSTATUS pvfs_acl_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
struct xattr_NTACL *acl)
{
NTSTATUS status;
void *privs;
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
return NT_STATUS_OK;
}
/* this xattr is in the "system" namespace, so we need
admin privileges to set it */
privs = root_privileges();
status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
XATTR_NTACL_NAME,
acl,
(ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
talloc_free(privs);
return status;
}
/*
create a zero length xattr with the given name
*/
NTSTATUS pvfs_xattr_create(struct pvfs_state *pvfs,
const char *fname, int fd,
const char *attr_prefix,
const char *attr_name)
{
NTSTATUS status;
DATA_BLOB blob = data_blob(NULL, 0);
char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
if (aname == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = push_xattr_blob(pvfs, aname, fname, fd, &blob);
talloc_free(aname);
return status;
}
/*
delete a xattr with the given name
*/
NTSTATUS pvfs_xattr_delete(struct pvfs_state *pvfs,
const char *fname, int fd,
const char *attr_prefix,
const char *attr_name)
{
NTSTATUS status;
char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
if (aname == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = delete_xattr(pvfs, aname, fname, fd);
talloc_free(aname);
return status;
}
/*
load a xattr with the given name
*/
NTSTATUS pvfs_xattr_load(struct pvfs_state *pvfs,
TALLOC_CTX *mem_ctx,
const char *fname, int fd,
const char *attr_prefix,
const char *attr_name,
size_t estimated_size,
DATA_BLOB *blob)
{
NTSTATUS status;
char *aname = talloc_asprintf(mem_ctx, "%s%s", attr_prefix, attr_name);
if (aname == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = pull_xattr_blob(pvfs, mem_ctx, aname, fname, fd, estimated_size, blob);
talloc_free(aname);
return status;
}
/*
save a xattr with the given name
*/
NTSTATUS pvfs_xattr_save(struct pvfs_state *pvfs,
const char *fname, int fd,
const char *attr_prefix,
const char *attr_name,
const DATA_BLOB *blob)
{
NTSTATUS status;
char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
if (aname == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = push_xattr_blob(pvfs, aname, fname, fd, blob);
talloc_free(aname);
return status;
}
/*
probe for system support for xattrs
*/
void pvfs_xattr_probe(struct pvfs_state *pvfs)
{
TALLOC_CTX *tmp_ctx = talloc_new(pvfs);
DATA_BLOB blob;
pull_xattr_blob(pvfs, tmp_ctx, "user.XattrProbe", pvfs->base_directory,
-1, 1, &blob);
pull_xattr_blob(pvfs, tmp_ctx, "security.XattrProbe", pvfs->base_directory,
-1, 1, &blob);
talloc_free(tmp_ctx);
}
+358
View File
@@ -0,0 +1,358 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
this implements most of the POSIX NTVFS backend
This is the default backend
*/
#include "includes.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/security.h"
#include "lib/tdb/include/tdb.h"
#include "db_wrap.h"
#include "libcli/security/security.h"
#include "lib/events/events.h"
/*
setup config options for a posix share
*/
static void pvfs_setup_options(struct pvfs_state *pvfs)
{
struct share_config *scfg = pvfs->ntvfs->ctx->config;
const char *eadb;
if (share_bool_option(scfg, SHARE_MAP_HIDDEN, SHARE_MAP_HIDDEN_DEFAULT))
pvfs->flags |= PVFS_FLAG_MAP_HIDDEN;
if (share_bool_option(scfg, SHARE_MAP_ARCHIVE, SHARE_MAP_ARCHIVE_DEFAULT))
pvfs->flags |= PVFS_FLAG_MAP_ARCHIVE;
if (share_bool_option(scfg, SHARE_MAP_SYSTEM, SHARE_MAP_SYSTEM_DEFAULT))
pvfs->flags |= PVFS_FLAG_MAP_SYSTEM;
if (share_bool_option(scfg, SHARE_READONLY, SHARE_READONLY_DEFAULT))
pvfs->flags |= PVFS_FLAG_READONLY;
if (share_bool_option(scfg, SHARE_STRICT_SYNC, SHARE_STRICT_SYNC_DEFAULT))
pvfs->flags |= PVFS_FLAG_STRICT_SYNC;
if (share_bool_option(scfg, SHARE_STRICT_LOCKING, SHARE_STRICT_LOCKING_DEFAULT))
pvfs->flags |= PVFS_FLAG_STRICT_LOCKING;
if (share_bool_option(scfg, SHARE_CI_FILESYSTEM, SHARE_CI_FILESYSTEM_DEFAULT))
pvfs->flags |= PVFS_FLAG_CI_FILESYSTEM;
if (share_bool_option(scfg, PVFS_FAKE_OPLOCKS, PVFS_FAKE_OPLOCKS_DEFAULT)) {
pvfs->flags |= PVFS_FLAG_FAKE_OPLOCKS;
}
/* this must be a power of 2 */
pvfs->alloc_size_rounding = share_int_option(scfg,
PVFS_ALLOCATION_ROUNDING,
PVFS_ALLOCATION_ROUNDING_DEFAULT);
pvfs->search.inactivity_time = share_int_option(scfg,
PVFS_SEARCH_INACTIVITY,
PVFS_SEARCH_INACTIVITY_DEFAULT);
#if HAVE_XATTR_SUPPORT
if (share_bool_option(scfg, PVFS_XATTR, PVFS_XATTR_DEFAULT))
pvfs->flags |= PVFS_FLAG_XATTR_ENABLE;
#endif
pvfs->sharing_violation_delay = share_int_option(scfg,
PVFS_SHARE_DELAY,
PVFS_SHARE_DELAY_DEFAULT);
pvfs->share_name = talloc_strdup(pvfs, scfg->name);
pvfs->fs_attribs =
FS_ATTR_CASE_SENSITIVE_SEARCH |
FS_ATTR_CASE_PRESERVED_NAMES |
FS_ATTR_UNICODE_ON_DISK |
FS_ATTR_SPARSE_FILES;
/* allow xattrs to be stored in a external tdb */
eadb = share_string_option(scfg, PVFS_EADB, NULL);
if (eadb != NULL) {
pvfs->ea_db = tdb_wrap_open(pvfs, eadb, 50000,
TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
if (pvfs->ea_db != NULL) {
pvfs->flags |= PVFS_FLAG_XATTR_ENABLE;
} else {
DEBUG(0,("Failed to open eadb '%s' - %s\n",
eadb, strerror(errno)));
pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
}
}
if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
pvfs->fs_attribs |= FS_ATTR_NAMED_STREAMS;
}
if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
pvfs->fs_attribs |= FS_ATTR_PERSISTANT_ACLS;
}
pvfs->sid_cache.creator_owner = dom_sid_parse_talloc(pvfs, SID_CREATOR_OWNER);
pvfs->sid_cache.creator_group = dom_sid_parse_talloc(pvfs, SID_CREATOR_GROUP);
/* check if the system really supports xattrs */
if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
pvfs_xattr_probe(pvfs);
}
/* enable an ACL backend */
pvfs->acl_ops = pvfs_acl_backend_byname(share_string_option(scfg, PVFS_ACL, "xattr"));
}
static int pvfs_state_destructor(struct pvfs_state *pvfs)
{
struct pvfs_file *f, *fn;
struct pvfs_search_state *s, *sn;
/*
* make sure we cleanup files and searches before anything else
* because there destructors need to acess the pvfs_state struct
*/
for (f=pvfs->files.list; f; f=fn) {
fn = f->next;
talloc_free(f);
}
for (s=pvfs->search.list; s; s=sn) {
sn = s->next;
talloc_free(s);
}
return 0;
}
/*
connect to a share - used when a tree_connect operation comes
in. For a disk based backend we needs to ensure that the base
directory exists (tho it doesn't need to be accessible by the user,
that comes later)
*/
static NTSTATUS pvfs_connect(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, const char *sharename)
{
struct pvfs_state *pvfs;
struct stat st;
char *base_directory;
NTSTATUS status;
pvfs = talloc_zero(ntvfs, struct pvfs_state);
NT_STATUS_HAVE_NO_MEMORY(pvfs);
/* for simplicity of path construction, remove any trailing slash now */
base_directory = talloc_strdup(pvfs, share_string_option(ntvfs->ctx->config, SHARE_PATH, ""));
NT_STATUS_HAVE_NO_MEMORY(base_directory);
if (strcmp(base_directory, "/") != 0) {
trim_string(base_directory, NULL, "/");
}
pvfs->ntvfs = ntvfs;
pvfs->base_directory = base_directory;
/* the directory must exist. Note that we deliberately don't
check that it is readable */
if (stat(pvfs->base_directory, &st) != 0 || !S_ISDIR(st.st_mode)) {
DEBUG(0,("pvfs_connect: '%s' is not a directory, when connecting to [%s]\n",
pvfs->base_directory, sharename));
return NT_STATUS_BAD_NETWORK_NAME;
}
ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
ntvfs->private_data = pvfs;
pvfs->brl_context = brl_init(pvfs,
pvfs->ntvfs->ctx->server_id,
pvfs->ntvfs->ctx->msg_ctx);
if (pvfs->brl_context == NULL) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
pvfs->odb_context = odb_init(pvfs, pvfs->ntvfs->ctx);
if (pvfs->odb_context == NULL) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
/* allow this to be NULL - we just disable change notify */
pvfs->notify_context = notify_init(pvfs,
pvfs->ntvfs->ctx->server_id,
pvfs->ntvfs->ctx->msg_ctx,
event_context_find(pvfs),
pvfs->ntvfs->ctx->config);
pvfs->sidmap = sidmap_open(pvfs);
if (pvfs->sidmap == NULL) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
/* allocate the search handle -> ptr tree */
pvfs->search.idtree = idr_init(pvfs);
NT_STATUS_HAVE_NO_MEMORY(pvfs->search.idtree);
status = pvfs_mangle_init(pvfs);
NT_STATUS_NOT_OK_RETURN(status);
pvfs_setup_options(pvfs);
talloc_set_destructor(pvfs, pvfs_state_destructor);
#ifdef SIGXFSZ
/* who had the stupid idea to generate a signal on a large
file write instead of just failing it!? */
BlockSignals(True, SIGXFSZ);
#endif
return NT_STATUS_OK;
}
/*
disconnect from a share
*/
static NTSTATUS pvfs_disconnect(struct ntvfs_module_context *ntvfs)
{
return NT_STATUS_OK;
}
/*
check if a directory exists
*/
static NTSTATUS pvfs_chkpath(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_chkpath *cp)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_filename *name;
NTSTATUS status;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, cp->chkpath.in.path, 0, &name);
NT_STATUS_NOT_OK_RETURN(status);
if (!name->exists) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
if (!S_ISDIR(name->st.st_mode)) {
return NT_STATUS_NOT_A_DIRECTORY;
}
return NT_STATUS_OK;
}
/*
copy a set of files
*/
static NTSTATUS pvfs_copy(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, struct smb_copy *cp)
{
DEBUG(0,("pvfs_copy not implemented\n"));
return NT_STATUS_NOT_SUPPORTED;
}
/*
return print queue info
*/
static NTSTATUS pvfs_lpq(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_lpq *lpq)
{
return NT_STATUS_NOT_SUPPORTED;
}
/* SMBtrans - not used on file shares */
static NTSTATUS pvfs_trans(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, struct smb_trans2 *trans2)
{
return NT_STATUS_ACCESS_DENIED;
}
/*
initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
*/
NTSTATUS ntvfs_posix_init(void)
{
NTSTATUS ret;
struct ntvfs_ops ops;
NTVFS_CURRENT_CRITICAL_SIZES(vers);
ZERO_STRUCT(ops);
ops.type = NTVFS_DISK;
/* fill in all the operations */
ops.connect = pvfs_connect;
ops.disconnect = pvfs_disconnect;
ops.unlink = pvfs_unlink;
ops.chkpath = pvfs_chkpath;
ops.qpathinfo = pvfs_qpathinfo;
ops.setpathinfo = pvfs_setpathinfo;
ops.open = pvfs_open;
ops.mkdir = pvfs_mkdir;
ops.rmdir = pvfs_rmdir;
ops.rename = pvfs_rename;
ops.copy = pvfs_copy;
ops.ioctl = pvfs_ioctl;
ops.read = pvfs_read;
ops.write = pvfs_write;
ops.seek = pvfs_seek;
ops.flush = pvfs_flush;
ops.close = pvfs_close;
ops.exit = pvfs_exit;
ops.lock = pvfs_lock;
ops.setfileinfo = pvfs_setfileinfo;
ops.qfileinfo = pvfs_qfileinfo;
ops.fsinfo = pvfs_fsinfo;
ops.lpq = pvfs_lpq;
ops.search_first = pvfs_search_first;
ops.search_next = pvfs_search_next;
ops.search_close = pvfs_search_close;
ops.trans = pvfs_trans;
ops.logoff = pvfs_logoff;
ops.async_setup = pvfs_async_setup;
ops.cancel = pvfs_cancel;
ops.notify = pvfs_notify;
/* register ourselves with the NTVFS subsystem. We register
under the name 'default' as we wish to be the default
backend, and also register as 'posix' */
ops.name = "default";
ret = ntvfs_register(&ops, &vers);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register POSIX backend as '%s'!\n", ops.name));
}
ops.name = "posix";
ret = ntvfs_register(&ops, &vers);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register POSIX backend as '%s'!\n", ops.name));
}
if (NT_STATUS_IS_OK(ret)) {
ret = ntvfs_common_init();
}
return ret;
}
+243
View File
@@ -0,0 +1,243 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - structure definitions
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _VFS_POSIX_H_
#define _VFS_POSIX_H_
#include "librpc/gen_ndr/xattr.h"
#include "system/filesys.h"
#include "ntvfs/ntvfs.h"
#include "ntvfs/common/proto.h"
#include "dsdb/samdb/samdb.h"
/* this is the private structure for the posix vfs backend. It is used
to hold per-connection (per tree connect) state information */
struct pvfs_state {
struct ntvfs_module_context *ntvfs;
const char *base_directory;
struct GUID *base_fs_uuid;
const char *share_name;
uint_t flags;
struct pvfs_mangle_context *mangle_ctx;
struct brl_context *brl_context;
struct odb_context *odb_context;
struct notify_context *notify_context;
struct sidmap_context *sidmap;
/* a list of pending async requests. Needed to support
ntcancel */
struct pvfs_wait *wait_list;
/* the sharing violation timeout */
uint_t sharing_violation_delay;
/* filesystem attributes (see FS_ATTR_*) */
uint32_t fs_attribs;
/* if posix:eadb is set, then this gets setup */
struct tdb_wrap *ea_db;
/* the allocation size rounding */
uint32_t alloc_size_rounding;
struct {
/* the open files as DLINKLIST */
struct pvfs_file *list;
} files;
struct {
/* an id tree mapping open search ID to a pvfs_search_state structure */
struct idr_context *idtree;
/* the open searches as DLINKLIST */
struct pvfs_search_state *list;
/* how long to keep inactive searches around for */
uint_t inactivity_time;
} search;
/* used to accelerate acl mapping */
struct {
const struct dom_sid *creator_owner;
const struct dom_sid *creator_group;
} sid_cache;
/* the acl backend */
const struct pvfs_acl_ops *acl_ops;
};
/* this is the basic information needed about a file from the filesystem */
struct pvfs_dos_fileinfo {
NTTIME create_time;
NTTIME access_time;
NTTIME write_time;
NTTIME change_time;
uint32_t attrib;
uint64_t alloc_size;
uint32_t nlink;
uint32_t ea_size;
uint64_t file_id;
uint32_t flags;
};
/*
this is the structure returned by pvfs_resolve_name(). It holds the posix details of
a filename passed by the client to any function
*/
struct pvfs_filename {
const char *original_name;
char *full_name;
const char *stream_name; /* does not include :$DATA suffix */
uint32_t stream_id; /* this uses a hash, so is probabilistic */
BOOL has_wildcard;
BOOL exists; /* true if the base filename exists */
BOOL stream_exists; /* true if the stream exists */
struct stat st;
struct pvfs_dos_fileinfo dos;
};
/* open file handle state - encapsulates the posix fd
Note that this is separated from the pvfs_file structure in order
to cope with the openx DENY_DOS semantics where a 2nd DENY_DOS open
on the same connection gets the same low level filesystem handle,
rather than a new handle
*/
struct pvfs_file_handle {
int fd;
struct pvfs_filename *name;
/* a unique file key to be used for open file locking */
DATA_BLOB odb_locking_key;
uint32_t create_options;
/* this is set by the mode_information level. What does it do? */
uint32_t mode;
/* yes, we need 2 independent positions ... */
uint64_t seek_offset;
uint64_t position;
BOOL have_opendb_entry;
/* we need this hook back to our parent for lock destruction */
struct pvfs_state *pvfs;
/* have we set a sticky write time that we should remove on close */
BOOL sticky_write_time;
};
/* open file state */
struct pvfs_file {
struct pvfs_file *next, *prev;
struct pvfs_file_handle *handle;
struct ntvfs_handle *ntvfs;
struct pvfs_state *pvfs;
uint32_t impersonation;
uint32_t share_access;
uint32_t access_mask;
/* a list of pending locks - used for locking cancel operations */
struct pvfs_pending_lock *pending_list;
/* a file handle to be used for byte range locking */
struct brl_handle *brl_handle;
/* a count of active locks - used to avoid calling brl_close on
file close */
uint64_t lock_count;
/* for directories, a buffer of pending notify events */
struct pvfs_notify_buffer *notify_buffer;
/* for directories, the state of an incomplete SMB2 Find */
struct pvfs_search_state *search;
};
/* the state of a search started with pvfs_search_first() */
struct pvfs_search_state {
struct pvfs_search_state *prev, *next;
struct pvfs_state *pvfs;
uint16_t handle;
off_t current_index;
uint16_t search_attrib;
uint16_t must_attrib;
struct pvfs_dir *dir;
time_t last_used;
uint_t num_ea_names;
struct ea_name *ea_names;
struct timed_event *te;
};
/* flags to pvfs_resolve_name() */
#define PVFS_RESOLVE_WILDCARD (1<<0)
#define PVFS_RESOLVE_STREAMS (1<<1)
/* flags in pvfs->flags */
#define PVFS_FLAG_CI_FILESYSTEM (1<<0) /* the filesystem is case insensitive */
#define PVFS_FLAG_MAP_ARCHIVE (1<<1)
#define PVFS_FLAG_MAP_SYSTEM (1<<2)
#define PVFS_FLAG_MAP_HIDDEN (1<<3)
#define PVFS_FLAG_READONLY (1<<4)
#define PVFS_FLAG_STRICT_SYNC (1<<5)
#define PVFS_FLAG_STRICT_LOCKING (1<<6)
#define PVFS_FLAG_XATTR_ENABLE (1<<7)
#define PVFS_FLAG_FAKE_OPLOCKS (1<<8)
/* forward declare some anonymous structures */
struct pvfs_dir;
/* types of notification for pvfs wait events */
enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL};
#define PVFS_EADB "posix:eadb"
#define PVFS_XATTR "posix:xattr"
#define PVFS_FAKE_OPLOCKS "posix:fakeoplocks"
#define PVFS_SHARE_DELAY "posix:sharedelay"
#define PVFS_ALLOCATION_ROUNDING "posix:allocationrounding"
#define PVFS_SEARCH_INACTIVITY "posix:searchinactivity"
#define PVFS_ACL "posix:acl"
#define PVFS_XATTR_DEFAULT True
#define PVFS_FAKE_OPLOCKS_DEFAULT False
#define PVFS_SHARE_DELAY_DEFAULT 1000000
#define PVFS_ALLOCATION_ROUNDING_DEFAULT 512
#define PVFS_SEARCH_INACTIVITY_DEFAULT 300
struct pvfs_acl_ops {
const char *name;
NTSTATUS (*acl_load)(struct pvfs_state *, struct pvfs_filename *, int , TALLOC_CTX *,
struct security_descriptor **);
NTSTATUS (*acl_save)(struct pvfs_state *, struct pvfs_filename *, int , struct security_descriptor *);
};
#include "ntvfs/posix/vfs_posix_proto.h"
#endif /* _VFS_POSIX_H_ */
+147
View File
@@ -0,0 +1,147 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - xattr support using filesystem xattrs
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "lib/util/wrap_xattr.h"
/*
pull a xattr as a blob, from either a file or a file descriptor
*/
NTSTATUS pull_xattr_blob_system(struct pvfs_state *pvfs,
TALLOC_CTX *mem_ctx,
const char *attr_name,
const char *fname,
int fd,
size_t estimated_size,
DATA_BLOB *blob)
{
int ret;
*blob = data_blob_talloc(mem_ctx, NULL, estimated_size+16);
if (blob->data == NULL) {
return NT_STATUS_NO_MEMORY;
}
again:
if (fd != -1) {
ret = wrap_fgetxattr(fd, attr_name, blob->data, estimated_size);
} else {
ret = wrap_getxattr(fname, attr_name, blob->data, estimated_size);
}
if (ret == -1 && errno == ERANGE) {
estimated_size *= 2;
blob->data = talloc_realloc(mem_ctx, blob->data,
uint8_t, estimated_size);
if (blob->data == NULL) {
return NT_STATUS_NO_MEMORY;
}
blob->length = estimated_size;
goto again;
}
if (ret == -1 && errno == EPERM) {
struct stat statbuf;
if (fd != -1) {
ret = fstat(fd, &statbuf);
} else {
ret = stat(fname, &statbuf);
}
if (ret == 0) {
/* check if this is a directory and the sticky bit is set */
if (S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & S_ISVTX)) {
/* pretend we could not find the xattr */
data_blob_free(blob);
return NT_STATUS_NOT_FOUND;
} else {
/* if not this was probably a legittimate error
* reset ret and errno to the correct values */
errno = EPERM;
ret = -1;
}
}
}
if (ret == -1) {
data_blob_free(blob);
return pvfs_map_errno(pvfs, errno);
}
blob->length = ret;
return NT_STATUS_OK;
}
/*
push a xattr as a blob, from either a file or a file descriptor
*/
NTSTATUS push_xattr_blob_system(struct pvfs_state *pvfs,
const char *attr_name,
const char *fname,
int fd,
const DATA_BLOB *blob)
{
int ret;
if (fd != -1) {
ret = wrap_fsetxattr(fd, attr_name, blob->data, blob->length, 0);
} else {
ret = wrap_setxattr(fname, attr_name, blob->data, blob->length, 0);
}
if (ret == -1) {
return pvfs_map_errno(pvfs, errno);
}
return NT_STATUS_OK;
}
/*
delete a xattr
*/
NTSTATUS delete_xattr_system(struct pvfs_state *pvfs, const char *attr_name,
const char *fname, int fd)
{
int ret;
if (fd != -1) {
ret = wrap_fremovexattr(fd, attr_name);
} else {
ret = wrap_removexattr(fname, attr_name);
}
if (ret == -1) {
return pvfs_map_errno(pvfs, errno);
}
return NT_STATUS_OK;
}
/*
unlink a file - cleanup any xattrs
*/
NTSTATUS unlink_xattr_system(struct pvfs_state *pvfs, const char *fname)
{
/* nothing needs to be done for filesystem based xattrs */
return NT_STATUS_OK;
}
+233
View File
@@ -0,0 +1,233 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - xattr support using a tdb
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "vfs_posix.h"
#include "lib/tdb/include/tdb.h"
#include "db_wrap.h"
#define XATTR_LIST_ATTR ".xattr_list"
/*
we need to maintain a list of attributes on each file, so that unlink
can automatically clean them up
*/
static NTSTATUS xattr_tdb_add_list(struct pvfs_state *pvfs, const char *attr_name,
const char *fname, int fd)
{
DATA_BLOB blob;
TALLOC_CTX *mem_ctx;
const char *s;
NTSTATUS status;
size_t len;
if (strcmp(attr_name, XATTR_LIST_ATTR) == 0) {
return NT_STATUS_OK;
}
mem_ctx = talloc_new(pvfs);
status = pull_xattr_blob_tdb(pvfs, mem_ctx, XATTR_LIST_ATTR,
fname, fd, 100, &blob);
if (!NT_STATUS_IS_OK(status)) {
blob = data_blob(NULL, 0);
}
for (s=(const char *)blob.data; s < (const char *)(blob.data+blob.length); s += strlen(s) + 1) {
if (strcmp(attr_name, s) == 0) {
talloc_free(mem_ctx);
return NT_STATUS_OK;
}
}
len = strlen(attr_name) + 1;
blob.data = talloc_realloc(mem_ctx, blob.data, uint8_t, blob.length + len);
if (blob.data == NULL) {
talloc_free(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
memcpy(blob.data + blob.length, attr_name, len);
blob.length += len;
status = push_xattr_blob_tdb(pvfs, XATTR_LIST_ATTR, fname, fd, &blob);
talloc_free(mem_ctx);
return status;
}
/*
form a key for using in the ea_db
*/
static NTSTATUS get_ea_db_key(TALLOC_CTX *mem_ctx,
const char *attr_name,
const char *fname, int fd,
TDB_DATA *key)
{
struct stat st;
size_t len = strlen(attr_name);
if (fd == -1) {
if (stat(fname, &st) == -1) {
return NT_STATUS_NOT_FOUND;
}
} else {
if (fstat(fd, &st) == -1) {
return NT_STATUS_NOT_FOUND;
}
}
key->dptr = talloc_array(mem_ctx, uint8_t, 16 + len);
if (key->dptr == NULL) {
return NT_STATUS_NO_MEMORY;
}
key->dsize = 16 + len;
SBVAL(key->dptr, 0, st.st_dev);
SBVAL(key->dptr, 8, st.st_ino);
memcpy(key->dptr+16, attr_name, len);
return NT_STATUS_OK;
}
/*
pull a xattr as a blob, using the ea_db tdb
*/
NTSTATUS pull_xattr_blob_tdb(struct pvfs_state *pvfs,
TALLOC_CTX *mem_ctx,
const char *attr_name,
const char *fname,
int fd,
size_t estimated_size,
DATA_BLOB *blob)
{
TDB_DATA tkey, tdata;
NTSTATUS status;
status = get_ea_db_key(mem_ctx, attr_name, fname, fd, &tkey);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
tdata = tdb_fetch(pvfs->ea_db->tdb, tkey);
if (tdata.dptr == NULL) {
return NT_STATUS_NOT_FOUND;
}
*blob = data_blob_talloc(mem_ctx, tdata.dptr, tdata.dsize);
free(tdata.dptr);
if (blob->data == NULL) {
return NT_STATUS_NO_MEMORY;
}
return NT_STATUS_OK;
}
/*
push a xattr as a blob, using ea_db
*/
NTSTATUS push_xattr_blob_tdb(struct pvfs_state *pvfs,
const char *attr_name,
const char *fname,
int fd,
const DATA_BLOB *blob)
{
TDB_DATA tkey, tdata;
NTSTATUS status;
status = get_ea_db_key(pvfs, attr_name, fname, fd, &tkey);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
tdata.dptr = blob->data;
tdata.dsize = blob->length;
if (tdb_chainlock(pvfs->ea_db->tdb, tkey) != 0) {
talloc_free(tkey.dptr);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
status = xattr_tdb_add_list(pvfs, attr_name, fname, fd);
if (!NT_STATUS_IS_OK(status)) {
goto done;
}
if (tdb_store(pvfs->ea_db->tdb, tkey, tdata, TDB_REPLACE) == -1) {
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
}
done:
tdb_chainunlock(pvfs->ea_db->tdb, tkey);
talloc_free(tkey.dptr);
return status;
}
/*
delete a xattr
*/
NTSTATUS delete_xattr_tdb(struct pvfs_state *pvfs, const char *attr_name,
const char *fname, int fd)
{
TDB_DATA tkey;
NTSTATUS status;
status = get_ea_db_key(NULL, attr_name, fname, fd, &tkey);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (tdb_delete(pvfs->ea_db->tdb, tkey) == -1) {
talloc_free(tkey.dptr);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
talloc_free(tkey.dptr);
return NT_STATUS_OK;
}
/*
delete all xattrs for a file
*/
NTSTATUS unlink_xattr_tdb(struct pvfs_state *pvfs, const char *fname)
{
TALLOC_CTX *mem_ctx = talloc_new(pvfs);
DATA_BLOB blob;
const char *s;
NTSTATUS status;
status = pull_xattr_blob_tdb(pvfs, mem_ctx, XATTR_LIST_ATTR,
fname, -1, 100, &blob);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(mem_ctx);
return NT_STATUS_OK;
}
for (s=(const char *)blob.data; s < (const char *)(blob.data+blob.length); s += strlen(s) + 1) {
delete_xattr_tdb(pvfs, s, fname, -1);
}
return delete_xattr_tdb(pvfs, XATTR_LIST_ATTR, fname, -1);
}