summaryrefslogtreecommitdiff
path: root/source3/modules
diff options
context:
space:
mode:
Diffstat (limited to 'source3/modules')
-rw-r--r--source3/modules/gpfs.c20
-rw-r--r--source3/modules/nfs4_acls.c187
-rw-r--r--source3/modules/vfs_aio_fork.c728
-rw-r--r--source3/modules/vfs_default.c111
-rw-r--r--source3/modules/vfs_gpfs.c281
-rw-r--r--source3/modules/vfs_gpfs.h32
-rw-r--r--source3/modules/vfs_hpuxacl.c4
-rw-r--r--source3/modules/vfs_prealloc.c11
-rw-r--r--source3/modules/vfs_recycle.c4
-rw-r--r--source3/modules/vfs_shadow_copy2.c637
-rw-r--r--source3/modules/vfs_streams_depot.c641
-rw-r--r--source3/modules/vfs_streams_xattr.c685
-rw-r--r--source3/modules/vfs_tsmsm.c365
-rw-r--r--source3/modules/vfs_xattr_tdb.c36
14 files changed, 3632 insertions, 110 deletions
diff --git a/source3/modules/gpfs.c b/source3/modules/gpfs.c
index 300e90fa69..590dbac26f 100644
--- a/source3/modules/gpfs.c
+++ b/source3/modules/gpfs.c
@@ -22,9 +22,11 @@
#ifdef HAVE_GPFS
#include "gpfs_gpl.h"
+#include "vfs_gpfs.h"
static void *libgpfs_handle = NULL;
static bool gpfs_share_modes;
+static bool gpfs_leases;
static int (*gpfs_set_share_fn)(int fd, unsigned int allow, unsigned int deny);
static int (*gpfs_set_lease_fn)(int fd, unsigned int leaseType);
@@ -42,7 +44,7 @@ bool set_gpfs_sharemode(files_struct *fsp, uint32 access_mask,
if (!gpfs_share_modes) {
return True;
}
-
+
if (gpfs_set_share_fn == NULL) {
return False;
}
@@ -88,7 +90,7 @@ int set_gpfs_lease(int fd, int leasetype)
{
int gpfs_type = GPFS_LEASE_NONE;
- if (!gpfs_share_modes) {
+ if (!gpfs_leases) {
return True;
}
@@ -103,6 +105,13 @@ int set_gpfs_lease(int fd, int leasetype)
if (leasetype == F_WRLCK) {
gpfs_type = GPFS_LEASE_WRITE;
}
+
+ /* we unconditionally set CAP_LEASE, rather than looking for
+ -1/EACCES as there is a bug in some versions of
+ libgpfs_gpl.so which results in a leaked fd on /dev/ss0
+ each time we try this with the wrong capabilities set
+ */
+ linux_set_lease_capability();
return gpfs_set_lease_fn(fd, gpfs_type);
}
@@ -172,11 +181,8 @@ void init_gpfs(void)
goto failed;
}
- if (lp_parm_bool(-1, "gpfs", "sharemodes", True)) {
- gpfs_share_modes = True;
- } else {
- gpfs_share_modes = False;
- }
+ gpfs_share_modes = lp_parm_bool(-1, "gpfs", "sharemodes", True);
+ gpfs_leases = lp_parm_bool(-1, "gpfs", "leases", True);
return;
diff --git a/source3/modules/nfs4_acls.c b/source3/modules/nfs4_acls.c
index 52d3983fff..0c3d010dcd 100644
--- a/source3/modules/nfs4_acls.c
+++ b/source3/modules/nfs4_acls.c
@@ -20,6 +20,9 @@
#include "includes.h"
#include "nfs4_acls.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_ACLS
+
#define SMBACL4_PARAM_TYPE_NAME "nfs4"
#define SMB_ACE4_INT_MAGIC 0x76F8A967
@@ -352,6 +355,7 @@ typedef struct _smbacl4_vfs_params {
enum smbacl4_mode_enum mode;
bool do_chown;
enum smbacl4_acedup_enum acedup;
+ struct db_context *sid_mapping_table;
} smbacl4_vfs_params;
/*
@@ -451,8 +455,65 @@ static SMB_ACE4PROP_T *smbacl4_find_equal_special(
return NULL;
}
-static int smbacl4_fill_ace4(
+static bool nfs4_map_sid(smbacl4_vfs_params *params, const DOM_SID *src,
+ DOM_SID *dst)
+{
+ static struct db_context *mapping_db = NULL;
+ TDB_DATA data;
+
+ if (mapping_db == NULL) {
+ const char *dbname = lp_parm_const_string(
+ -1, SMBACL4_PARAM_TYPE_NAME, "sidmap", NULL);
+
+ if (dbname == NULL) {
+ DEBUG(10, ("%s:sidmap not defined\n",
+ SMBACL4_PARAM_TYPE_NAME));
+ return False;
+ }
+
+ become_root();
+ mapping_db = db_open(NULL, dbname, 0, TDB_DEFAULT,
+ O_RDONLY, 0600);
+ unbecome_root();
+
+ if (mapping_db == NULL) {
+ DEBUG(1, ("could not open sidmap: %s\n",
+ strerror(errno)));
+ return False;
+ }
+ }
+
+ if (mapping_db->fetch(mapping_db, NULL,
+ string_term_tdb_data(sid_string_tos(src)),
+ &data) == -1) {
+ DEBUG(10, ("could not find mapping for SID %s\n",
+ sid_string_dbg(src)));
+ return False;
+ }
+
+ if ((data.dptr == NULL) || (data.dsize <= 0)
+ || (data.dptr[data.dsize-1] != '\0')) {
+ DEBUG(5, ("invalid mapping for SID %s\n",
+ sid_string_dbg(src)));
+ TALLOC_FREE(data.dptr);
+ return False;
+ }
+
+ if (!string_to_sid(dst, (char *)data.dptr)) {
+ DEBUG(1, ("invalid mapping %s for SID %s\n",
+ (char *)data.dptr, sid_string_dbg(src)));
+ TALLOC_FREE(data.dptr);
+ return False;
+ }
+
+ TALLOC_FREE(data.dptr);
+
+ return True;
+}
+
+static bool smbacl4_fill_ace4(
TALLOC_CTX *mem_ctx,
+ const char *filename,
smbacl4_vfs_params *params,
uid_t ownerUID,
gid_t ownerGID,
@@ -460,11 +521,6 @@ static int smbacl4_fill_ace4(
SMB_ACE4PROP_T *ace_v4 /* output */
)
{
- const char *dom, *name;
- enum lsa_SidType type;
- uid_t uid;
- gid_t gid;
-
DEBUG(10, ("got ace for %s\n", sid_string_dbg(&ace_nt->trustee)));
memset(ace_v4, 0, sizeof(SMB_ACE4PROP_T));
@@ -485,18 +541,46 @@ static int smbacl4_fill_ace4(
ace_v4->who.special_id = SMB_ACE4_WHO_EVERYONE;
ace_v4->flags |= SMB_ACE4_ID_SPECIAL;
} else {
- if (!lookup_sid(mem_ctx, &ace_nt->trustee, &dom, &name, &type)) {
- DEBUG(8, ("Could not find %s' type\n",
- sid_string_dbg(&ace_nt->trustee)));
- errno = EINVAL;
- return -1;
+ const char *dom, *name;
+ enum lsa_SidType type;
+ uid_t uid;
+ gid_t gid;
+ DOM_SID sid;
+
+ sid_copy(&sid, &ace_nt->trustee);
+
+ if (!lookup_sid(mem_ctx, &sid, &dom, &name, &type)) {
+
+ DOM_SID mapped;
+
+ if (!nfs4_map_sid(params, &sid, &mapped)) {
+ DEBUG(1, ("nfs4_acls.c: file [%s]: SID %s "
+ "unknown\n", filename, sid_string_dbg(&sid)));
+ errno = EINVAL;
+ return False;
+ }
+
+ DEBUG(2, ("nfs4_acls.c: file [%s]: mapped SID %s "
+ "to %s\n", filename, sid_string_dbg(&sid), sid_string_dbg(&mapped)));
+
+ if (!lookup_sid(mem_ctx, &mapped, &dom,
+ &name, &type)) {
+ DEBUG(1, ("nfs4_acls.c: file [%s]: SID %s "
+ "mapped from %s is unknown\n",
+ filename, sid_string_dbg(&mapped), sid_string_dbg(&sid)));
+ errno = EINVAL;
+ return False;
+ }
+
+ sid_copy(&sid, &mapped);
}
-
+
if (type == SID_NAME_USER) {
- if (!sid_to_uid(&ace_nt->trustee, &uid)) {
- DEBUG(2, ("Could not convert %s to uid\n",
- sid_string_dbg(&ace_nt->trustee)));
- return -1;
+ if (!sid_to_uid(&sid, &uid)) {
+ DEBUG(1, ("nfs4_acls.c: file [%s]: could not "
+ "convert %s to uid\n", filename,
+ sid_string_dbg(&sid)));
+ return False;
}
if (params->mode==e_special && uid==ownerUID) {
@@ -506,11 +590,13 @@ static int smbacl4_fill_ace4(
ace_v4->who.uid = uid;
}
} else { /* else group? - TODO check it... */
- if (!sid_to_gid(&ace_nt->trustee, &gid)) {
- DEBUG(2, ("Could not convert %s to gid\n",
- sid_string_dbg(&ace_nt->trustee)));
- return -1;
+ if (!sid_to_gid(&sid, &gid)) {
+ DEBUG(1, ("nfs4_acls.c: file [%s]: could not "
+ "convert %s to gid\n", filename,
+ sid_string_dbg(&sid)));
+ return False;
}
+
ace_v4->aceFlags |= SMB_ACE4_IDENTIFIER_GROUP;
if (params->mode==e_special && gid==ownerGID) {
@@ -522,7 +608,7 @@ static int smbacl4_fill_ace4(
}
}
- return 0; /* OK */
+ return True; /* OK */
}
static int smbacl4_MergeIgnoreReject(
@@ -560,6 +646,7 @@ static int smbacl4_MergeIgnoreReject(
}
static SMB4ACL_T *smbacl4_win2nfs4(
+ const char *filename,
SEC_ACL *dacl,
smbacl4_vfs_params *pparams,
uid_t ownerUID,
@@ -580,9 +667,14 @@ static SMB4ACL_T *smbacl4_win2nfs4(
SMB_ACE4PROP_T ace_v4;
bool addNewACE = True;
- if (smbacl4_fill_ace4(mem_ctx, pparams, ownerUID, ownerGID,
- dacl->aces + i, &ace_v4))
- return NULL;
+ if (!smbacl4_fill_ace4(mem_ctx, filename, pparams,
+ ownerUID, ownerGID,
+ dacl->aces + i, &ace_v4)) {
+ DEBUG(3, ("Could not fill ace for file %s, SID %s\n",
+ filename,
+ sid_string_dbg(&((dacl->aces+i)->trustee))));
+ continue;
+ }
if (pparams->acedup!=e_dontcare) {
if (smbacl4_MergeIgnoreReject(pparams->acedup, acl,
@@ -607,6 +699,7 @@ NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp,
bool result;
SMB_STRUCT_STAT sbuf;
+ bool need_chown = False;
uid_t newUID = (uid_t)-1;
gid_t newGID = (gid_t)-1;
@@ -635,25 +728,33 @@ NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp,
return status;
}
if (((newUID != (uid_t)-1) && (sbuf.st_uid != newUID)) ||
- ((newGID != (gid_t)-1) && (sbuf.st_gid != newGID))) {
- if(try_chown(fsp->conn, fsp->fsp_name, newUID, newGID)) {
- DEBUG(3,("chown %s, %u, %u failed. Error = %s.\n",
- fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID, strerror(errno) ));
- if (errno == EPERM) {
- return NT_STATUS_INVALID_OWNER;
+ ((newGID != (gid_t)-1) && (sbuf.st_gid != newGID))) {
+ need_chown = True;
+ }
+ if (need_chown) {
+ if ((newUID == (uid_t)-1 || newUID == current_user.ut.uid)) {
+ if(try_chown(fsp->conn, fsp->fsp_name, newUID, newGID)) {
+ DEBUG(3,("chown %s, %u, %u failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID,
+ strerror(errno)));
+ return map_nt_error_from_unix(errno);
}
- return map_nt_error_from_unix(errno);
+
+ DEBUG(10,("chown %s, %u, %u succeeded.\n",
+ fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID));
+ if (smbacl4_GetFileOwner(fsp->conn, fsp->fsp_name, &sbuf))
+ return map_nt_error_from_unix(errno);
+ need_chown = False;
+ } else { /* chown is needed, but _after_ changing acl */
+ sbuf.st_uid = newUID; /* OWNER@ in case of e_special */
+ sbuf.st_gid = newGID; /* GROUP@ in case of e_special */
}
- DEBUG(10,("chown %s, %u, %u succeeded.\n",
- fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID));
- if (smbacl4_fGetFileOwner(fsp, &sbuf))
- return map_nt_error_from_unix(errno);
}
}
if ((security_info_sent & DACL_SECURITY_INFORMATION)!=0 && psd->dacl!=NULL)
{
- acl = smbacl4_win2nfs4(psd->dacl, &params, sbuf.st_uid, sbuf.st_gid);
+ acl = smbacl4_win2nfs4(fsp->fsp_name, psd->dacl, &params, sbuf.st_uid, sbuf.st_gid);
if (!acl)
return map_nt_error_from_unix(errno);
@@ -668,6 +769,20 @@ NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp,
} else
DEBUG(10, ("no dacl found; security_info_sent = 0x%x\n", security_info_sent));
+ /* Any chown pending? */
+ if (need_chown) {
+ DEBUG(3,("chown#2 %s. uid = %u, gid = %u.\n",
+ fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID));
+ if (try_chown(fsp->conn, fsp->fsp_name, newUID, newGID)) {
+ DEBUG(2,("chown#2 %s, %u, %u failed. Error = %s.\n",
+ fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID,
+ strerror(errno)));
+ return map_nt_error_from_unix(errno);
+ }
+ DEBUG(10,("chown#2 %s, %u, %u succeeded.\n",
+ fsp->fsp_name, (unsigned int)newUID, (unsigned int)newGID));
+ }
+
DEBUG(10, ("smb_set_nt_acl_nfs4 succeeded\n"));
return NT_STATUS_OK;
}
diff --git a/source3/modules/vfs_aio_fork.c b/source3/modules/vfs_aio_fork.c
new file mode 100644
index 0000000000..21f63d0b87
--- /dev/null
+++ b/source3/modules/vfs_aio_fork.c
@@ -0,0 +1,728 @@
+/*
+ * Simulate the Posix AIO using mmap/fork
+ *
+ * Copyright (C) Volker Lendecke 2008
+ *
+ * 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"
+
+struct mmap_area {
+ size_t size;
+ volatile void *ptr;
+};
+
+static int mmap_area_destructor(struct mmap_area *area)
+{
+ munmap((void *)area->ptr, area->size);
+ return 0;
+}
+
+static struct mmap_area *mmap_area_init(TALLOC_CTX *mem_ctx, size_t size)
+{
+ struct mmap_area *result;
+ int fd;
+
+ result = talloc(mem_ctx, struct mmap_area);
+ if (result == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ goto fail;
+ }
+
+ fd = open("/dev/zero", O_RDWR);
+ if (fd == -1) {
+ DEBUG(3, ("open(\"/dev/zero\") failed: %s\n",
+ strerror(errno)));
+ goto fail;
+ }
+
+ result->ptr = mmap(NULL, size, PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_FILE, fd, 0);
+ if (result->ptr == MAP_FAILED) {
+ DEBUG(1, ("mmap failed: %s\n", strerror(errno)));
+ goto fail;
+ }
+
+ result->size = size;
+ talloc_set_destructor(result, mmap_area_destructor);
+
+ return result;
+
+fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+struct rw_cmd {
+ size_t n;
+ SMB_OFF_T offset;
+ bool read_cmd;
+};
+
+struct rw_ret {
+ ssize_t size;
+ int ret_errno;
+};
+
+struct aio_child_list;
+
+struct aio_child {
+ struct aio_child *prev, *next;
+ struct aio_child_list *list;
+ SMB_STRUCT_AIOCB *aiocb;
+ pid_t pid;
+ int sockfd;
+ struct fd_event *sock_event;
+ struct rw_ret retval;
+ struct mmap_area *map; /* ==NULL means write request */
+ bool dont_delete; /* Marked as in use since last cleanup */
+ bool cancelled;
+ bool read_cmd;
+};
+
+struct aio_child_list {
+ struct aio_child *children;
+ struct timed_event *cleanup_event;
+};
+
+static void free_aio_children(void **p)
+{
+ TALLOC_FREE(*p);
+}
+
+static ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+ ssize_t n;
+#ifndef HAVE_MSGHDR_MSG_CONTROL
+ int newfd;
+#endif
+
+#ifdef HAVE_MSGHDR_MSG_CONTROL
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(int))];
+ } control_un;
+ struct cmsghdr *cmptr;
+
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+#else
+#if HAVE_MSGHDR_MSG_ACCTRIGHTS
+ msg.msg_accrights = (caddr_t) &newfd;
+ msg.msg_accrightslen = sizeof(int);
+#else
+#error Can not pass file descriptors
+#endif
+#endif
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ iov[0].iov_base = ptr;
+ iov[0].iov_len = nbytes;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ if ( (n = recvmsg(fd, &msg, 0)) <= 0) {
+ return(n);
+ }
+
+#ifdef HAVE_MSGHDR_MSG_CONTROL
+ if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL
+ && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
+ if (cmptr->cmsg_level != SOL_SOCKET) {
+ DEBUG(10, ("control level != SOL_SOCKET"));
+ errno = EINVAL;
+ return -1;
+ }
+ if (cmptr->cmsg_type != SCM_RIGHTS) {
+ DEBUG(10, ("control type != SCM_RIGHTS"));
+ errno = EINVAL;
+ return -1;
+ }
+ *recvfd = *((int *) CMSG_DATA(cmptr));
+ } else {
+ *recvfd = -1; /* descriptor was not passed */
+ }
+#else
+ if (msg.msg_accrightslen == sizeof(int)) {
+ *recvfd = newfd;
+ }
+ else {
+ *recvfd = -1; /* descriptor was not passed */
+ }
+#endif
+
+ return(n);
+}
+
+static ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+
+#ifdef HAVE_MSGHDR_MSG_CONTROL
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(int))];
+ } control_un;
+ struct cmsghdr *cmptr;
+
+ ZERO_STRUCT(msg);
+ ZERO_STRUCT(control_un);
+
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+
+ cmptr = CMSG_FIRSTHDR(&msg);
+ cmptr->cmsg_len = CMSG_LEN(sizeof(int));
+ cmptr->cmsg_level = SOL_SOCKET;
+ cmptr->cmsg_type = SCM_RIGHTS;
+ *((int *) CMSG_DATA(cmptr)) = sendfd;
+#else
+ ZERO_STRUCT(msg);
+ msg.msg_accrights = (caddr_t) &sendfd;
+ msg.msg_accrightslen = sizeof(int);
+#endif
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ ZERO_STRUCT(iov);
+ iov[0].iov_base = ptr;
+ iov[0].iov_len = nbytes;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ return (sendmsg(fd, &msg, 0));
+}
+
+static void aio_child_cleanup(struct event_context *event_ctx,
+ struct timed_event *te,
+ const struct timeval *now,
+ void *private_data)
+{
+ struct aio_child_list *list = talloc_get_type_abort(
+ private_data, struct aio_child_list);
+ struct aio_child *child, *next;
+
+ TALLOC_FREE(list->cleanup_event);
+
+ for (child = list->children; child != NULL; child = next) {
+ next = child->next;
+
+ if (child->aiocb != NULL) {
+ DEBUG(10, ("child %d currently active\n",
+ (int)child->pid));
+ continue;
+ }
+
+ if (child->dont_delete) {
+ DEBUG(10, ("Child %d was active since last cleanup\n",
+ (int)child->pid));
+ child->dont_delete = false;
+ continue;
+ }
+
+ DEBUG(10, ("Child %d idle for more than 30 seconds, "
+ "deleting\n", (int)child->pid));
+
+ TALLOC_FREE(child);
+ }
+
+ if (list->children != NULL) {
+ /*
+ * Re-schedule the next cleanup round
+ */
+ list->cleanup_event = event_add_timed(smbd_event_context(), list,
+ timeval_add(now, 30, 0),
+ "aio_child_cleanup",
+ aio_child_cleanup, list);
+
+ }
+}
+
+static struct aio_child_list *init_aio_children(struct vfs_handle_struct *handle)
+{
+ struct aio_child_list *data = NULL;
+
+ if (SMB_VFS_HANDLE_TEST_DATA(handle)) {
+ SMB_VFS_HANDLE_GET_DATA(handle, data, struct aio_child_list,
+ return NULL);
+ }
+
+ if (data == NULL) {
+ data = TALLOC_ZERO_P(NULL, struct aio_child_list);
+ if (data == NULL) {
+ return NULL;
+ }
+ }
+
+ /*
+ * Regardless of whether the child_list had been around or not, make
+ * sure that we have a cleanup timed event. This timed event will
+ * delete itself when it finds that no children are around anymore.
+ */
+
+ if (data->cleanup_event == NULL) {
+ data->cleanup_event = event_add_timed(smbd_event_context(), data,
+ timeval_current_ofs(30, 0),
+ "aio_child_cleanup",
+ aio_child_cleanup, data);
+ if (data->cleanup_event == NULL) {
+ TALLOC_FREE(data);
+ return NULL;
+ }
+ }
+
+ if (!SMB_VFS_HANDLE_TEST_DATA(handle)) {
+ SMB_VFS_HANDLE_SET_DATA(handle, data, free_aio_children,
+ struct aio_child_list, return False);
+ }
+
+ return data;
+}
+
+static void aio_child_loop(int sockfd, struct mmap_area *map)
+{
+ while (true) {
+ int fd = -1;
+ ssize_t ret;
+ struct rw_cmd cmd_struct;
+ struct rw_ret ret_struct;
+
+ ret = read_fd(sockfd, &cmd_struct, sizeof(cmd_struct), &fd);
+ if (ret != sizeof(cmd_struct)) {
+ DEBUG(10, ("read_fd returned %d: %s\n", (int)ret,
+ strerror(errno)));
+ exit(1);
+ }
+
+ DEBUG(10, ("aio_child_loop: %s %d bytes at %d from fd %d\n",
+ cmd_struct.read_cmd ? "read" : "write",
+ (int)cmd_struct.n, (int)cmd_struct.offset, fd));
+
+#ifdef ENABLE_BUILD_FARM_HACKS
+ {
+ /*
+ * In the build farm, we want erratic behaviour for
+ * async I/O times
+ */
+ uint8_t randval;
+ unsigned msecs;
+ /*
+ * use generate_random_buffer, we just forked from a
+ * common parent state
+ */
+ generate_random_buffer(&randval, sizeof(randval));
+ msecs = randval + 20;
+ DEBUG(10, ("delaying for %u msecs\n", msecs));
+ smb_msleep(msecs);
+ }
+#endif
+
+
+ ZERO_STRUCT(ret_struct);
+
+ if (cmd_struct.read_cmd) {
+ ret_struct.size = sys_pread(
+ fd, (void *)map->ptr, cmd_struct.n,
+ cmd_struct.offset);
+ }
+ else {
+ ret_struct.size = sys_pwrite(
+ fd, (void *)map->ptr, cmd_struct.n,
+ cmd_struct.offset);
+ }
+
+ DEBUG(10, ("aio_child_loop: syscall returned %d\n",
+ (int)ret_struct.size));
+
+ if (ret_struct.size == -1) {
+ ret_struct.ret_errno = errno;
+ }
+
+ ret = write_data(sockfd, (char *)&ret_struct,
+ sizeof(ret_struct));
+ if (ret != sizeof(ret_struct)) {
+ DEBUG(10, ("could not write ret_struct: %s\n",
+ strerror(errno)));
+ exit(2);
+ }
+
+ close(fd);
+ }
+}
+
+static void handle_aio_completion(struct event_context *event_ctx,
+ struct fd_event *event, uint16 flags,
+ void *p)
+{
+ struct aio_child *child = (struct aio_child *)p;
+ uint16 mid;
+
+ DEBUG(10, ("handle_aio_completion called with flags=%d\n", flags));
+
+ if ((flags & EVENT_FD_READ) == 0) {
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(read_data(child->sockfd,
+ (char *)&child->retval,
+ sizeof(child->retval)))) {
+ DEBUG(0, ("aio child %d died\n", (int)child->pid));
+ child->retval.size = -1;
+ child->retval.ret_errno = EIO;
+ }
+
+ if (child->cancelled) {
+ child->aiocb = NULL;
+ child->cancelled = false;
+ return;
+ }
+
+ if (child->read_cmd && (child->retval.size > 0)) {
+ SMB_ASSERT(child->retval.size <= child->aiocb->aio_nbytes);
+ memcpy((void *)child->aiocb->aio_buf, (void *)child->map->ptr,
+ child->retval.size);
+ }
+
+ mid = child->aiocb->aio_sigevent.sigev_value.sival_int;
+
+ DEBUG(10, ("mid %d finished\n", (int)mid));
+
+ aio_request_done(mid);
+ process_aio_queue();
+}
+
+static int aio_child_destructor(struct aio_child *child)
+{
+ SMB_ASSERT((child->aiocb == NULL) || child->cancelled);
+ close(child->sockfd);
+ DLIST_REMOVE(child->list->children, child);
+ return 0;
+}
+
+static NTSTATUS create_aio_child(struct aio_child_list *children,
+ size_t map_size,
+ struct aio_child **presult)
+{
+ struct aio_child *result;
+ int fdpair[2];
+ NTSTATUS status;
+
+ fdpair[0] = fdpair[1] = -1;
+
+ result = TALLOC_ZERO_P(children, struct aio_child);
+ NT_STATUS_HAVE_NO_MEMORY(result);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) == -1) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(10, ("socketpair() failed: %s\n", strerror(errno)));
+ TALLOC_FREE(result);
+ goto fail;
+ }
+
+ DEBUG(10, ("fdpair = %d/%d\n", fdpair[0], fdpair[1]));
+
+ result->map = mmap_area_init(result, map_size);
+ if (result->map == NULL) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(0, ("Could not create mmap area\n"));
+ goto fail;
+ }
+
+ result->pid = sys_fork();
+ if (result->pid == -1) {
+ status = map_nt_error_from_unix(errno);
+ DEBUG(0, ("fork failed: %s\n", strerror(errno)));
+ goto fail;
+ }
+
+ if (result->pid == 0) {
+ close(fdpair[0]);
+ result->sockfd = fdpair[1];
+ aio_child_loop(result->sockfd, result->map);
+ }
+
+ DEBUG(10, ("Child %d created\n", result->pid));
+
+ result->sockfd = fdpair[0];
+ close(fdpair[1]);
+
+ result->sock_event = event_add_fd(smbd_event_context(), result,
+ result->sockfd, EVENT_FD_READ,
+ handle_aio_completion,
+ result);
+ if (result->sock_event == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("event_add_fd failed\n"));
+ goto fail;
+ }
+
+ result->list = children;
+ DLIST_ADD(children->children, result);
+
+ talloc_set_destructor(result, aio_child_destructor);
+
+ *presult = result;
+
+ return NT_STATUS_OK;
+
+ fail:
+ if (fdpair[0] != -1) close(fdpair[0]);
+ if (fdpair[1] != -1) close(fdpair[1]);
+ TALLOC_FREE(result);
+
+ return status;
+}
+
+static NTSTATUS get_idle_child(struct vfs_handle_struct *handle,
+ struct aio_child **pchild)
+{
+ struct aio_child_list *children;
+ struct aio_child *child;
+ NTSTATUS status;
+
+ children = init_aio_children(handle);
+ if (children == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (child = children->children; child != NULL; child = child->next) {
+ if (child->aiocb == NULL) {
+ /* idle */
+ break;
+ }
+ }
+
+ if (child == NULL) {
+ DEBUG(10, ("no idle child found, creating new one\n"));
+
+ status = create_aio_child(children, 128*1024, &child);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("create_aio_child failed: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ child->dont_delete = true;
+
+ *pchild = child;
+ return NT_STATUS_OK;
+}
+
+static int aio_fork_read(struct vfs_handle_struct *handle,
+ struct files_struct *fsp, SMB_STRUCT_AIOCB *aiocb)
+{
+ struct aio_child *child;
+ struct rw_cmd cmd;
+ ssize_t ret;
+ NTSTATUS status;
+
+ if (aiocb->aio_nbytes > 128*1024) {
+ /* TODO: support variable buffers */
+ errno = EINVAL;
+ return -1;
+ }
+
+ status = get_idle_child(handle, &child);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Could not get an idle child\n"));
+ return -1;
+ }
+
+ child->read_cmd = true;
+ child->aiocb = aiocb;
+ child->retval.ret_errno = EINPROGRESS;
+
+ ZERO_STRUCT(cmd);
+ cmd.n = aiocb->aio_nbytes;
+ cmd.offset = aiocb->aio_offset;
+ cmd.read_cmd = child->read_cmd;
+
+ DEBUG(10, ("sending fd %d to child %d\n", fsp->fh->fd,
+ (int)child->pid));
+
+ ret = write_fd(child->sockfd, &cmd, sizeof(cmd), fsp->fh->fd);
+ if (ret == -1) {
+ DEBUG(10, ("write_fd failed: %s\n", strerror(errno)));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int aio_fork_write(struct vfs_handle_struct *handle,
+ struct files_struct *fsp, SMB_STRUCT_AIOCB *aiocb)
+{
+ struct aio_child *child;
+ struct rw_cmd cmd;
+ ssize_t ret;
+ NTSTATUS status;
+
+ if (aiocb->aio_nbytes > 128*1024) {
+ /* TODO: support variable buffers */
+ errno = EINVAL;
+ return -1;
+ }
+
+ status = get_idle_child(handle, &child);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Could not get an idle child\n"));
+ return -1;
+ }
+
+ child->read_cmd = false;
+ child->aiocb = aiocb;
+ child->retval.ret_errno = EINPROGRESS;
+
+ memcpy((void *)child->map->ptr, (void *)aiocb->aio_buf,
+ aiocb->aio_nbytes);
+
+ ZERO_STRUCT(cmd);
+ cmd.n = aiocb->aio_nbytes;
+ cmd.offset = aiocb->aio_offset;
+ cmd.read_cmd = child->read_cmd;
+
+ DEBUG(10, ("sending fd %d to child %d\n", fsp->fh->fd,
+ (int)child->pid));
+
+ ret = write_fd(child->sockfd, &cmd, sizeof(cmd), fsp->fh->fd);
+ if (ret == -1) {
+ DEBUG(10, ("write_fd failed: %s\n", strerror(errno)));
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct aio_child *aio_fork_find_child(struct vfs_handle_struct *handle,
+ SMB_STRUCT_AIOCB *aiocb)
+{
+ struct aio_child_list *children;
+ struct aio_child *child;
+
+ children = init_aio_children(handle);
+ if (children == NULL) {
+ return NULL;
+ }
+
+ for (child = children->children; child != NULL; child = child->next) {
+ if (child->aiocb == aiocb) {
+ return child;
+ }
+ }
+
+ return NULL;
+}
+
+static ssize_t aio_fork_return_fn(struct vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ SMB_STRUCT_AIOCB *aiocb)
+{
+ struct aio_child *child = aio_fork_find_child(handle, aiocb);
+
+ if (child == NULL) {
+ errno = EINVAL;
+ DEBUG(0, ("returning EINVAL\n"));
+ return -1;
+ }
+
+ child->aiocb = NULL;
+
+ if (child->retval.size == -1) {
+ errno = child->retval.ret_errno;
+ }
+
+ return child->retval.size;
+}
+
+static int aio_fork_cancel(struct vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ SMB_STRUCT_AIOCB *aiocb)
+{
+ struct aio_child_list *children;
+ struct aio_child *child;
+
+ children = init_aio_children(handle);
+ if (children == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (child = children->children; child != NULL; child = child->next) {
+ if (child->aiocb == NULL) {
+ continue;
+ }
+ if (child->aiocb->aio_fildes != fsp->fh->fd) {
+ continue;
+ }
+ if ((aiocb != NULL) && (child->aiocb != aiocb)) {
+ continue;
+ }
+
+ /*
+ * We let the child do its job, but we discard the result when
+ * it's finished.
+ */
+
+ child->cancelled = true;
+ }
+
+ return AIO_CANCELED;
+}
+
+static int aio_fork_error_fn(struct vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ SMB_STRUCT_AIOCB *aiocb)
+{
+ struct aio_child *child = aio_fork_find_child(handle, aiocb);
+
+ if (child == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return child->retval.ret_errno;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple aio_fork_ops[] = {
+ {SMB_VFS_OP(aio_fork_read), SMB_VFS_OP_AIO_READ,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(aio_fork_write), SMB_VFS_OP_AIO_WRITE,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(aio_fork_return_fn), SMB_VFS_OP_AIO_RETURN,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(aio_fork_cancel), SMB_VFS_OP_AIO_CANCEL,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(aio_fork_error_fn), SMB_VFS_OP_AIO_ERROR,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP,
+ SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_aio_fork_init(void);
+NTSTATUS vfs_aio_fork_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
+ "aio_fork", aio_fork_ops);
+}
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index e21136ccee..31ebb6352a 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -90,6 +90,17 @@ static int vfswrap_statvfs(struct vfs_handle_struct *handle, const char *path,
return sys_statvfs(path, statbuf);
}
+static uint32_t vfswrap_fs_capabilities(struct vfs_handle_struct *handle)
+{
+#if defined(DARWINOS)
+ struct vfs_statvfs_struct statbuf;
+ ZERO_STRUCT(statbuf);
+ sys_statvfs(handle->conn->connectpath, &statbuf);
+ return statbuf.FsCapabilities;
+#endif
+ return FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES;
+}
+
/* Directory operations */
static SMB_STRUCT_DIR *vfswrap_opendir(vfs_handle_struct *handle, const char *fname, const char *mask, uint32 attr)
@@ -457,7 +468,7 @@ static int vfswrap_rename(vfs_handle_struct *handle, const char *oldname, const
START_PROFILE(syscall_rename);
result = rename(oldname, newname);
- if (errno == EXDEV) {
+ if ((result == -1) && (errno == EXDEV)) {
/* Rename across filesystems needed. */
result = copy_reg(oldname, newname);
}
@@ -942,6 +953,62 @@ static struct file_id vfswrap_file_id_create(struct vfs_handle_struct *handle, S
return file_id_create_dev(dev, inode);
}
+static NTSTATUS vfswrap_streaminfo(vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ const char *fname,
+ TALLOC_CTX *mem_ctx,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams)
+{
+ SMB_STRUCT_STAT sbuf;
+ unsigned int num_streams = 0;
+ struct stream_struct *streams = NULL;
+ int ret;
+
+ if ((fsp != NULL) && (fsp->is_directory)) {
+ /*
+ * No default streams on directories
+ */
+ goto done;
+ }
+
+ if ((fsp != NULL) && (fsp->fh->fd != -1)) {
+ ret = SMB_VFS_FSTAT(fsp, &sbuf);
+ }
+ else {
+ ret = SMB_VFS_STAT(handle->conn, fname, &sbuf);
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (S_ISDIR(sbuf.st_mode)) {
+ goto done;
+ }
+
+ streams = talloc(mem_ctx, struct stream_struct);
+
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ streams->size = sbuf.st_size;
+ streams->alloc_size = get_allocation_size(handle->conn, fsp, &sbuf);
+
+ streams->name = talloc_strdup(streams, "::$DATA");
+ if (streams->name == NULL) {
+ TALLOC_FREE(streams);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ num_streams = 1;
+ done:
+ *pnum_streams = num_streams;
+ *pstreams = streams;
+ return NT_STATUS_OK;
+}
+
static NTSTATUS vfswrap_fget_nt_acl(vfs_handle_struct *handle,
files_struct *fsp,
uint32 security_info, SEC_DESC **ppdesc)
@@ -1225,6 +1292,36 @@ static int vfswrap_aio_suspend(struct vfs_handle_struct *handle, struct files_st
return sys_aio_suspend(aiocb, n, timeout);
}
+static bool vfswrap_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
+{
+ return false;
+}
+
+static bool vfswrap_is_offline(struct vfs_handle_struct *handle, const char *path, SMB_STRUCT_STAT *sbuf)
+{
+ if (ISDOT(path) || ISDOTDOT(path)) {
+ return false;
+ }
+
+ if (!lp_dmapi_support(SNUM(handle->conn)) || !dmapi_have_session()) {
+#if defined(ENOTSUP)
+ errno = ENOTSUP;
+#endif
+ return false;
+ }
+
+ return (dmapi_file_flags(path) & FILE_ATTRIBUTE_OFFLINE) != 0;
+}
+
+static int vfswrap_set_offline(struct vfs_handle_struct *handle, const char *path)
+{
+ /* We don't know how to set offline bit by default, needs to be overriden in the vfs modules */
+#if defined(ENOTSUP)
+ errno = ENOTSUP;
+#endif
+ return -1;
+}
+
static vfs_op_tuple vfs_default_ops[] = {
/* Disk operations */
@@ -1243,6 +1340,8 @@ static vfs_op_tuple vfs_default_ops[] = {
SMB_VFS_LAYER_OPAQUE},
{SMB_VFS_OP(vfswrap_statvfs), SMB_VFS_OP_STATVFS,
SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(vfswrap_fs_capabilities), SMB_VFS_OP_FS_CAPABILITIES,
+ SMB_VFS_LAYER_OPAQUE},
/* Directory operations */
@@ -1337,6 +1436,8 @@ static vfs_op_tuple vfs_default_ops[] = {
SMB_VFS_LAYER_OPAQUE},
{SMB_VFS_OP(vfswrap_file_id_create), SMB_VFS_OP_FILE_ID_CREATE,
SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(vfswrap_streaminfo), SMB_VFS_OP_STREAMINFO,
+ SMB_VFS_LAYER_OPAQUE},
/* NT ACL operations. */
@@ -1442,6 +1543,14 @@ static vfs_op_tuple vfs_default_ops[] = {
{SMB_VFS_OP(vfswrap_aio_suspend),SMB_VFS_OP_AIO_SUSPEND,
SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(vfswrap_aio_force), SMB_VFS_OP_AIO_FORCE,
+ SMB_VFS_LAYER_OPAQUE},
+
+ {SMB_VFS_OP(vfswrap_is_offline),SMB_VFS_OP_IS_OFFLINE,
+ SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(vfswrap_set_offline),SMB_VFS_OP_SET_OFFLINE,
+ SMB_VFS_LAYER_OPAQUE},
+
/* Finish VFS operations definition */
{SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP,
diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c
index bcf61f3bc7..d10906dfb1 100644
--- a/source3/modules/vfs_gpfs.c
+++ b/source3/modules/vfs_gpfs.c
@@ -30,7 +30,7 @@
#include <gpfs_gpl.h>
#include "nfs4_acls.h"
-
+#include "vfs_gpfs.h"
static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
uint32 share_mode)
@@ -153,7 +153,7 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
DEBUG(10, ("gpfs_get_nfs4_acl invoked for %s\n", fname));
/* First get the real acl length */
- gacl = gpfs_getacl_alloc(fname, GPFS_ACL_TYPE_NFS4);
+ gacl = gpfs_getacl_alloc(fname, 0);
if (gacl == NULL) {
DEBUG(9, ("gpfs_getacl failed for %s with %s\n",
fname, strerror(errno)));
@@ -208,10 +208,10 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
if (i > 0 && gace->aceType == SMB_ACE4_ACCESS_DENIED_ACE_TYPE) {
struct gpfs_ace_v4 *prev = &gacl->ace_v4[i-1];
if (prev->aceType == SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE &&
- prev->aceFlags == gace->aceFlags &&
- prev->aceIFlags == gace->aceIFlags &&
- (gace->aceMask & prev->aceMask) == 0 &&
- gace->aceWho == prev->aceWho) {
+ prev->aceFlags == gace->aceFlags &&
+ prev->aceIFlags == gace->aceIFlags &&
+ (gace->aceMask & prev->aceMask) == 0 &&
+ gace->aceWho == prev->aceWho) {
/* its redundent - skip it */
continue;
}
@@ -256,7 +256,7 @@ static NTSTATUS gpfsacl_get_nt_acl(vfs_handle_struct *handle,
int result;
*ppdesc = NULL;
- result = gpfs_get_nfs4_acl(fsp->fsp_name, &pacl);
+ result = gpfs_get_nfs4_acl(name, &pacl);
if (result == 0)
return smb_get_nt_acl_nfs4(handle->conn, name, security_info, ppdesc, pacl);
@@ -301,8 +301,31 @@ static bool gpfsacl_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
gace->aceType = aceprop->aceType;
gace->aceFlags = aceprop->aceFlags;
gace->aceMask = aceprop->aceMask;
+
+ /*
+ * GPFS can't distinguish between WRITE and APPEND on
+ * files, so one being set without the other is an
+ * error. Sorry for the many ()'s :-)
+ */
+
+ if (!fsp->is_directory
+ &&
+ ((((gace->aceMask & ACE4_MASK_WRITE) == 0)
+ && ((gace->aceMask & ACE4_MASK_APPEND) != 0))
+ ||
+ (((gace->aceMask & ACE4_MASK_WRITE) != 0)
+ && ((gace->aceMask & ACE4_MASK_APPEND) == 0)))
+ &&
+ lp_parm_bool(fsp->conn->params->service, "gpfs",
+ "merge_writeappend", True)) {
+ DEBUG(2, ("vfs_gpfs.c: file [%s]: ACE contains "
+ "WRITE^APPEND, setting WRITE|APPEND\n",
+ fsp->fsp_name));
+ gace->aceMask |= ACE4_MASK_WRITE|ACE4_MASK_APPEND;
+ }
+
gace->aceIFlags = (aceprop->flags&SMB_ACE4_ID_SPECIAL) ? ACE4_IFLAG_SPECIAL_ID : 0;
-
+
if (aceprop->flags&SMB_ACE4_ID_SPECIAL)
{
switch(aceprop->who.special_id)
@@ -347,7 +370,7 @@ static NTSTATUS gpfsacl_set_nt_acl_internal(files_struct *fsp, uint32 security_i
struct gpfs_acl *acl;
NTSTATUS result = NT_STATUS_ACCESS_DENIED;
- acl = gpfs_getacl_alloc(fsp->fsp_name, GPFS_ACL_TYPE_ACCESS);
+ acl = gpfs_getacl_alloc(fsp->fsp_name, 0);
if (acl == NULL)
return result;
@@ -628,75 +651,225 @@ int gpfsacl_sys_acl_delete_def_file(vfs_handle_struct *handle,
return -1;
}
+/*
+ * Assumed: mode bits are shiftable and standard
+ * Output: the new aceMask field for an smb nfs4 ace
+ */
+static uint32 gpfsacl_mask_filter(uint32 aceType, uint32 aceMask, uint32 rwx)
+{
+ const uint32 posix_nfs4map[3] = {
+ SMB_ACE4_EXECUTE, /* execute */
+ SMB_ACE4_WRITE_DATA | SMB_ACE4_APPEND_DATA, /* write; GPFS specific */
+ SMB_ACE4_READ_DATA /* read */
+ };
+ int i;
+ uint32_t posix_mask = 0x01;
+ uint32_t posix_bit;
+ uint32_t nfs4_bits;
+
+ for(i=0; i<3; i++) {
+ nfs4_bits = posix_nfs4map[i];
+ posix_bit = rwx & posix_mask;
+
+ if (aceType==SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) {
+ if (posix_bit)
+ aceMask |= nfs4_bits;
+ else
+ aceMask &= ~nfs4_bits;
+ } else {
+ /* add deny bits when suitable */
+ if (!posix_bit)
+ aceMask |= nfs4_bits;
+ else
+ aceMask &= ~nfs4_bits;
+ } /* other ace types are unexpected */
+
+ posix_mask <<= 1;
+ }
+
+ return aceMask;
+}
+
+static int gpfsacl_emu_chmod(const char *path, mode_t mode)
+{
+ SMB4ACL_T *pacl = NULL;
+ int result;
+ bool haveAllowEntry[SMB_ACE4_WHO_EVERYONE + 1] = {False, False, False, False};
+ int i;
+ files_struct fake_fsp; /* TODO: rationalize parametrization */
+ SMB4ACE_T *smbace;
+
+ DEBUG(10, ("gpfsacl_emu_chmod invoked for %s mode %o\n", path, mode));
+
+ result = gpfs_get_nfs4_acl(path, &pacl);
+ if (result)
+ return result;
+
+ if (mode & ~(S_IRWXU | S_IRWXG | S_IRWXO)) {
+ DEBUG(2, ("WARNING: cutting extra mode bits %o on %s\n", mode, path));
+ }
+
+ for (smbace=smb_first_ace4(pacl); smbace!=NULL; smbace = smb_next_ace4(smbace)) {
+ SMB_ACE4PROP_T *ace = smb_get_ace4(smbace);
+ uint32_t specid = ace->who.special_id;
+
+ if (ace->flags&SMB_ACE4_ID_SPECIAL &&
+ ace->aceType<=SMB_ACE4_ACCESS_DENIED_ACE_TYPE &&
+ specid <= SMB_ACE4_WHO_EVERYONE) {
+
+ uint32_t newMask;
+
+ if (ace->aceType==SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE)
+ haveAllowEntry[specid] = True;
+
+ /* mode >> 6 for @owner, mode >> 3 for @group,
+ * mode >> 0 for @everyone */
+ newMask = gpfsacl_mask_filter(ace->aceType, ace->aceMask,
+ mode >> ((SMB_ACE4_WHO_EVERYONE - specid) * 3));
+ if (ace->aceMask!=newMask) {
+ DEBUG(10, ("ace changed for %s (%o -> %o) id=%d\n",
+ path, ace->aceMask, newMask, specid));
+ }
+ ace->aceMask = newMask;
+ }
+ }
+
+ /* make sure we have at least ALLOW entries
+ * for all the 3 special ids (@EVERYONE, @OWNER, @GROUP)
+ * - if necessary
+ */
+ for(i = SMB_ACE4_WHO_OWNER; i<=SMB_ACE4_WHO_EVERYONE; i++) {
+ SMB_ACE4PROP_T ace;
+
+ if (haveAllowEntry[i]==True)
+ continue;
+
+ memset(&ace, 0, sizeof(SMB_ACE4PROP_T));
+ ace.aceType = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE;
+ ace.flags |= SMB_ACE4_ID_SPECIAL;
+ ace.who.special_id = i;
+
+ if (i==SMB_ACE4_WHO_GROUP) /* not sure it's necessary... */
+ ace.aceFlags |= SMB_ACE4_IDENTIFIER_GROUP;
+
+ ace.aceMask = gpfsacl_mask_filter(ace.aceType, ace.aceMask,
+ mode >> ((SMB_ACE4_WHO_EVERYONE - i) * 3));
+
+ /* don't add unnecessary aces */
+ if (!ace.aceMask)
+ continue;
+
+ /* we add it to the END - as windows expects allow aces */
+ smb_add_ace4(pacl, &ace);
+ DEBUG(10, ("Added ALLOW ace for %s, mode=%o, id=%d, aceMask=%x\n",
+ path, mode, i, ace.aceMask));
+ }
+
+ /* don't add complementary DENY ACEs here */
+ memset(&fake_fsp, 0, sizeof(struct files_struct));
+ fake_fsp.fsp_name = (char *)path; /* no file_new is needed here */
+
+ /* put the acl */
+ if (gpfsacl_process_smbacl(&fake_fsp, pacl) == False)
+ return -1;
+ return 0; /* ok for [f]chmod */
+}
+
static int vfs_gpfs_chmod(vfs_handle_struct *handle, const char *path, mode_t mode)
{
SMB_STRUCT_STAT st;
+ int rc;
+
if (SMB_VFS_NEXT_STAT(handle, path, &st) != 0) {
- return -1;
+ return -1;
}
+
/* avoid chmod() if possible, to preserve acls */
if ((st.st_mode & ~S_IFMT) == mode) {
- return 0;
+ return 0;
}
- return SMB_VFS_NEXT_CHMOD(handle, path, mode);
+
+ rc = gpfsacl_emu_chmod(path, mode);
+ if (rc == 1)
+ return SMB_VFS_NEXT_CHMOD(handle, path, mode);
+ return rc;
}
static int vfs_gpfs_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
{
SMB_STRUCT_STAT st;
+ int rc;
+
if (SMB_VFS_NEXT_FSTAT(handle, fsp, &st) != 0) {
- return -1;
+ return -1;
}
+
/* avoid chmod() if possible, to preserve acls */
if ((st.st_mode & ~S_IFMT) == mode) {
- return 0;
+ return 0;
}
- return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
+
+ rc = gpfsacl_emu_chmod(fsp->fsp_name, mode);
+ if (rc == 1)
+ return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
+ return rc;
}
/* VFS operations structure */
static vfs_op_tuple gpfs_op_tuples[] = {
-
- { SMB_VFS_OP(vfs_gpfs_kernel_flock), SMB_VFS_OP_KERNEL_FLOCK,
- SMB_VFS_LAYER_OPAQUE },
-
- { SMB_VFS_OP(vfs_gpfs_setlease), SMB_VFS_OP_LINUX_SETLEASE,
- SMB_VFS_LAYER_OPAQUE },
-
- { SMB_VFS_OP(gpfsacl_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL,
- SMB_VFS_LAYER_TRANSPARENT },
-
- { SMB_VFS_OP(gpfsacl_get_nt_acl), SMB_VFS_OP_GET_NT_ACL,
- SMB_VFS_LAYER_TRANSPARENT },
-
- { SMB_VFS_OP(gpfsacl_fset_nt_acl), SMB_VFS_OP_FSET_NT_ACL,
- SMB_VFS_LAYER_TRANSPARENT },
-
- { SMB_VFS_OP(gpfsacl_set_nt_acl), SMB_VFS_OP_SET_NT_ACL,
- SMB_VFS_LAYER_TRANSPARENT },
-
- { SMB_VFS_OP(gpfsacl_sys_acl_get_file), SMB_VFS_OP_SYS_ACL_GET_FILE,
- SMB_VFS_LAYER_TRANSPARENT },
-
- { SMB_VFS_OP(gpfsacl_sys_acl_get_fd), SMB_VFS_OP_SYS_ACL_GET_FD,
- SMB_VFS_LAYER_TRANSPARENT },
-
- { SMB_VFS_OP(gpfsacl_sys_acl_set_file), SMB_VFS_OP_SYS_ACL_SET_FILE,
- SMB_VFS_LAYER_TRANSPARENT },
-
- { SMB_VFS_OP(gpfsacl_sys_acl_set_fd), SMB_VFS_OP_SYS_ACL_SET_FD,
- SMB_VFS_LAYER_TRANSPARENT },
-
+
+ { SMB_VFS_OP(vfs_gpfs_kernel_flock),
+ SMB_VFS_OP_KERNEL_FLOCK,
+ SMB_VFS_LAYER_OPAQUE },
+
+ { SMB_VFS_OP(vfs_gpfs_setlease),
+ SMB_VFS_OP_LINUX_SETLEASE,
+ SMB_VFS_LAYER_OPAQUE },
+
+ { SMB_VFS_OP(gpfsacl_fget_nt_acl),
+ SMB_VFS_OP_FGET_NT_ACL,
+ SMB_VFS_LAYER_TRANSPARENT },
+
+ { SMB_VFS_OP(gpfsacl_get_nt_acl),
+ SMB_VFS_OP_GET_NT_ACL,
+ SMB_VFS_LAYER_TRANSPARENT },
+
+ { SMB_VFS_OP(gpfsacl_fset_nt_acl),
+ SMB_VFS_OP_FSET_NT_ACL,
+ SMB_VFS_LAYER_TRANSPARENT },
+
+ { SMB_VFS_OP(gpfsacl_set_nt_acl),
+ SMB_VFS_OP_SET_NT_ACL,
+ SMB_VFS_LAYER_TRANSPARENT },
+
+ { SMB_VFS_OP(gpfsacl_sys_acl_get_file),
+ SMB_VFS_OP_SYS_ACL_GET_FILE,
+ SMB_VFS_LAYER_TRANSPARENT },
+
+ { SMB_VFS_OP(gpfsacl_sys_acl_get_fd),
+ SMB_VFS_OP_SYS_ACL_GET_FD,
+ SMB_VFS_LAYER_TRANSPARENT },
+
+ { SMB_VFS_OP(gpfsacl_sys_acl_set_file),
+ SMB_VFS_OP_SYS_ACL_SET_FILE,
+ SMB_VFS_LAYER_TRANSPARENT },
+
+ { SMB_VFS_OP(gpfsacl_sys_acl_set_fd),
+ SMB_VFS_OP_SYS_ACL_SET_FD,
+ SMB_VFS_LAYER_TRANSPARENT },
+
{ SMB_VFS_OP(gpfsacl_sys_acl_delete_def_file),
- SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,
- SMB_VFS_LAYER_TRANSPARENT },
-
- { SMB_VFS_OP(vfs_gpfs_chmod), SMB_VFS_OP_CHMOD,
- SMB_VFS_LAYER_TRANSPARENT },
-
- { SMB_VFS_OP(vfs_gpfs_fchmod), SMB_VFS_OP_FCHMOD,
- SMB_VFS_LAYER_TRANSPARENT },
+ SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,
+ SMB_VFS_LAYER_TRANSPARENT },
+
+ { SMB_VFS_OP(vfs_gpfs_chmod),
+ SMB_VFS_OP_CHMOD,
+ SMB_VFS_LAYER_TRANSPARENT },
+
+ { SMB_VFS_OP(vfs_gpfs_fchmod),
+ SMB_VFS_OP_FCHMOD,
+ SMB_VFS_LAYER_TRANSPARENT },
{ SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP }
diff --git a/source3/modules/vfs_gpfs.h b/source3/modules/vfs_gpfs.h
new file mode 100644
index 0000000000..3c499b0850
--- /dev/null
+++ b/source3/modules/vfs_gpfs.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+ Wrap gpfs calls in vfs functions.
+
+ Copyright (C) Christian Ambach <cambach1@de.ibm.com> 2006
+
+ Major code contributions by Chetan Shringarpure <chetan.sh@in.ibm.com>
+ and Gomati Mohanan <gomati.mohanan@in.ibm.com>
+
+ 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.
+
+
+*/
+
+bool set_gpfs_sharemode(files_struct *fsp, uint32 access_mask,
+ uint32 share_access);
+int set_gpfs_lease(int fd, int leasetype);
+int smbd_gpfs_getacl(char *pathname, int flags, void *acl);
+int smbd_gpfs_putacl(char *pathname, int flags, void *acl);
+void init_gpfs(void);
diff --git a/source3/modules/vfs_hpuxacl.c b/source3/modules/vfs_hpuxacl.c
index e101886450..f9293405fb 100644
--- a/source3/modules/vfs_hpuxacl.c
+++ b/source3/modules/vfs_hpuxacl.c
@@ -140,7 +140,7 @@ SMB_ACL_T hpuxacl_sys_acl_get_file(vfs_handle_struct *handle,
{
SMB_ACL_T result = NULL;
int count;
- HPUX_ACL_T hpux_acl;
+ HPUX_ACL_T hpux_acl = NULL;
DEBUG(10, ("hpuxacl_sys_acl_get_file called for file '%s'.\n",
path_p));
@@ -213,7 +213,7 @@ int hpuxacl_sys_acl_set_file(vfs_handle_struct *handle,
{
int ret = -1;
SMB_STRUCT_STAT s;
- HPUX_ACL_T hpux_acl;
+ HPUX_ACL_T hpux_acl = NULL;
int count;
DEBUG(10, ("hpuxacl_sys_acl_set_file called for file '%s'\n",
diff --git a/source3/modules/vfs_prealloc.c b/source3/modules/vfs_prealloc.c
index 2d64bc0184..2a06e3d81b 100644
--- a/source3/modules/vfs_prealloc.c
+++ b/source3/modules/vfs_prealloc.c
@@ -47,11 +47,16 @@
#define lock_type struct flock64
#endif
+#ifdef HAVE_GPFS
+#include "gpfs_gpl.h"
+#endif
+
#define MODULE "prealloc"
static int module_debug;
static int preallocate_space(int fd, SMB_OFF_T size)
{
+#ifndef HAVE_GPFS
lock_type fl = {0};
int err;
@@ -78,6 +83,9 @@ static int preallocate_space(int fd, SMB_OFF_T size)
err = -1;
errno = ENOSYS;
#endif
+#else /* GPFS uses completely different interface */
+ err = gpfs_prealloc(fd, (gpfs_off64_t)0, (gpfs_off64_t)size);
+#endif
if (err) {
DEBUG(module_debug,
@@ -191,7 +199,7 @@ static int prealloc_ftruncate(vfs_handle_struct * handle,
/* Maintain the allocated space even in the face of truncates. */
if ((psize = VFS_FETCH_FSP_EXTENSION(handle, fsp))) {
- preallocate_space(fd, *psize);
+ preallocate_space(fsp->fh->fd, *psize);
}
return ret;
@@ -210,4 +218,3 @@ NTSTATUS vfs_prealloc_init(void)
return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
MODULE, prealloc_op_tuples);
}
-
diff --git a/source3/modules/vfs_recycle.c b/source3/modules/vfs_recycle.c
index fef65efa77..da1716719a 100644
--- a/source3/modules/vfs_recycle.c
+++ b/source3/modules/vfs_recycle.c
@@ -269,6 +269,7 @@ static bool recycle_create_dir(vfs_handle_struct *handle, const char *dname)
char *token;
char *tok_str;
bool ret = False;
+ char *saveptr;
mode = recycle_directory_mode(handle);
@@ -286,7 +287,8 @@ static bool recycle_create_dir(vfs_handle_struct *handle, const char *dname)
}
/* Create directory tree if neccessary */
- for(token = strtok(tok_str, "/"); token; token = strtok(NULL, "/")) {
+ for(token = strtok_r(tok_str, "/", &saveptr); token;
+ token = strtok_r(NULL, "/", &saveptr)) {
safe_strcat(new_dir, token, len);
if (recycle_directory_exist(handle, new_dir))
DEBUG(10, ("recycle: dir %s already exists\n", new_dir));
diff --git a/source3/modules/vfs_shadow_copy2.c b/source3/modules/vfs_shadow_copy2.c
new file mode 100644
index 0000000000..ddbc5aab18
--- /dev/null
+++ b/source3/modules/vfs_shadow_copy2.c
@@ -0,0 +1,637 @@
+/*
+ * implementation of an Shadow Copy module - version 2
+ *
+ * Copyright (C) Andrew Tridgell 2007
+ *
+ * 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"
+
+/*
+
+ This is a 2nd implemetation of a shadow copy module for exposing
+ snapshots to windows clients as shadow copies. This version has the
+ following features:
+
+ 1) you don't need to populate your shares with symlinks to the
+ snapshots. This can be very important when you have thousands of
+ shares, or use [homes]
+
+ 2) the inode number of the files is altered so it is different
+ from the original. This allows the 'restore' button to work
+ without a sharing violation
+
+ Module options:
+
+ shadow:snapdir = <directory where snapshots are kept>
+
+ This is the directory containing the @GMT-* snapshot directories. If it is an absolute
+ path it is used as-is. If it is a relative path, then it is taken relative to the mount
+ point of the filesystem that the root of this share is on
+
+ shadow:basedir = <base directory that snapshots are from>
+
+ This is an optional parameter that specifies the directory that
+ the snapshots are relative to. It defaults to the filesystem
+ mount point
+
+ shadow:fixinodes = yes/no
+
+ If you enable shadow:fixinodes then this module will modify the
+ apparent inode number of files in the snapshot directories using
+ a hash of the files path. This is needed for snapshot systems
+ where the snapshots have the same device:inode number as the
+ original files (such as happens with GPFS snapshots). If you
+ don't set this option then the 'restore' button in the shadow
+ copy UI will fail with a sharing violation.
+
+ Note that the directory names in the snapshot directory must take the form
+ @GMT-YYYY.MM.DD-HH.MM.SS
+
+ The following command would generate a correctly formatted directory name:
+ date -u +@GMT-%Y.%m.%d-%H.%M.%S
+
+ */
+
+static int vfs_shadow_copy2_debug_level = DBGC_VFS;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS vfs_shadow_copy2_debug_level
+
+#define GMT_NAME_LEN 24 /* length of a @GMT- name */
+
+/*
+ make very sure it is one of our special names
+ */
+static inline bool shadow_copy2_match_name(const char *name)
+{
+ unsigned year, month, day, hr, min, sec;
+ if (name[0] != '@') return False;
+ if (strncmp(name, "@GMT-", 5) != 0) return False;
+ if (sscanf(name, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
+ &day, &hr, &min, &sec) != 6) {
+ return False;
+ }
+ if (name[24] != 0 && name[24] != '/') {
+ return False;
+ }
+ return True;
+}
+
+/*
+ convert a name to the shadow directory
+ */
+
+#define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
+ const char *name = fname; \
+ if (shadow_copy2_match_name(fname)) { \
+ char *name2; \
+ rtype ret; \
+ name2 = convert_shadow2_name(handle, fname); \
+ if (name2 == NULL) { \
+ errno = EINVAL; \
+ return eret; \
+ } \
+ name = name2; \
+ ret = SMB_VFS_NEXT_ ## op args; \
+ talloc_free(name2); \
+ if (ret != eret) extra; \
+ return ret; \
+ } else { \
+ return SMB_VFS_NEXT_ ## op args; \
+ } \
+} while (0)
+
+/*
+ convert a name to the shadow directory: NTSTATUS-specific handling
+ */
+
+#define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
+ const char *name = fname; \
+ if (shadow_copy2_match_name(fname)) { \
+ char *name2; \
+ NTSTATUS ret; \
+ name2 = convert_shadow2_name(handle, fname); \
+ if (name2 == NULL) { \
+ errno = EINVAL; \
+ return eret; \
+ } \
+ name = name2; \
+ ret = SMB_VFS_NEXT_ ## op args; \
+ talloc_free(name2); \
+ if (!NT_STATUS_EQUAL(ret, eret)) extra; \
+ return ret; \
+ } else { \
+ return SMB_VFS_NEXT_ ## op args; \
+ } \
+} while (0)
+
+#define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
+
+#define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
+
+#define SHADOW2_NEXT2(op, args) do { \
+ if (shadow_copy2_match_name(oldname) || shadow_copy2_match_name(newname)) { \
+ errno = EROFS; \
+ return -1; \
+ } else { \
+ return SMB_VFS_NEXT_ ## op args; \
+ } \
+} while (0)
+
+
+/*
+ find the mount point of a filesystem
+ */
+static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
+{
+ char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
+ dev_t dev;
+ struct stat st;
+ char *p;
+
+ if (stat(path, &st) != 0) {
+ talloc_free(path);
+ return NULL;
+ }
+
+ dev = st.st_dev;
+
+ while ((p = strrchr(path, '/')) && p > path) {
+ *p = 0;
+ if (stat(path, &st) != 0) {
+ talloc_free(path);
+ return NULL;
+ }
+ if (st.st_dev != dev) {
+ *p = '/';
+ break;
+ }
+ }
+
+ return path;
+}
+
+/*
+ work out the location of the snapshot for this share
+ */
+static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
+{
+ const char *snapdir;
+ char *mount_point;
+ const char *ret;
+
+ snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
+ if (snapdir == NULL) {
+ return NULL;
+ }
+ /* if its an absolute path, we're done */
+ if (*snapdir == '/') {
+ return snapdir;
+ }
+
+ /* other its relative to the filesystem mount point */
+ mount_point = find_mount_point(mem_ctx, handle);
+ if (mount_point == NULL) {
+ return NULL;
+ }
+
+ ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
+ talloc_free(mount_point);
+ return ret;
+}
+
+/*
+ work out the location of the base directory for snapshots of this share
+ */
+static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
+{
+ const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
+
+ /* other its the filesystem mount point */
+ if (basedir == NULL) {
+ basedir = find_mount_point(mem_ctx, handle);
+ }
+
+ return basedir;
+}
+
+/*
+ convert a filename from a share relative path, to a path in the
+ snapshot directory
+ */
+static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
+ const char *snapdir, *relpath, *baseoffset, *basedir;
+ size_t baselen;
+ char *ret;
+
+ snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
+ if (snapdir == NULL) {
+ DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
+ if (basedir == NULL) {
+ DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ relpath = fname + GMT_NAME_LEN;
+ baselen = strlen(basedir);
+ baseoffset = handle->conn->connectpath + baselen;
+
+ /* some sanity checks */
+ if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
+ (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
+ DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
+ basedir, handle->conn->connectpath));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ if (*relpath == '/') relpath++;
+ if (*baseoffset == '/') baseoffset++;
+
+ ret = talloc_asprintf(handle->data, "%s/%.*s/%s/%s",
+ snapdir,
+ GMT_NAME_LEN, fname,
+ baseoffset,
+ relpath);
+ DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+/*
+ simple string hash
+ */
+static uint32 string_hash(const char *s)
+{
+ uint32 n = 0;
+ while (*s) {
+ n = ((n << 5) + n) ^ (uint32)(*s++);
+ }
+ return n;
+}
+
+/*
+ modify a sbuf return to ensure that inodes in the shadow directory
+ are different from those in the main directory
+ */
+static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
+ /* some snapshot systems, like GPFS, return the name
+ device:inode for the snapshot files as the current
+ files. That breaks the 'restore' button in the shadow copy
+ GUI, as the client gets a sharing violation.
+
+ This is a crude way of allowing both files to be
+ open at once. It has a slight chance of inode
+ number collision, but I can't see a better approach
+ without significant VFS changes
+ */
+ uint32_t shash = string_hash(fname) & 0xFF000000;
+ if (shash == 0) {
+ shash = 1;
+ }
+ sbuf->st_ino ^= shash;
+ }
+}
+
+static int shadow_copy2_rename(vfs_handle_struct *handle,
+ const char *oldname, const char *newname)
+{
+ SHADOW2_NEXT2(RENAME, (handle, oldname, newname));
+}
+
+static int shadow_copy2_symlink(vfs_handle_struct *handle,
+ const char *oldname, const char *newname)
+{
+ SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
+}
+
+static int shadow_copy2_link(vfs_handle_struct *handle,
+ const char *oldname, const char *newname)
+{
+ SHADOW2_NEXT2(LINK, (handle, oldname, newname));
+}
+
+static int shadow_copy2_open(vfs_handle_struct *handle,
+ const char *fname, files_struct *fsp, int flags, mode_t mode)
+{
+ SHADOW2_NEXT(OPEN, (handle, name, fsp, flags, mode), int, -1);
+}
+
+static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
+ const char *fname, const char *mask, uint32 attr)
+{
+ SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
+}
+
+static int shadow_copy2_stat(vfs_handle_struct *handle,
+ const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ _SHADOW2_NEXT(STAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf));
+}
+
+static int shadow_copy2_lstat(vfs_handle_struct *handle,
+ const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ _SHADOW2_NEXT(LSTAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf));
+}
+
+static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
+{
+ int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+ if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name)) {
+ convert_sbuf(handle, fsp->fsp_name, sbuf);
+ }
+ return ret;
+}
+
+static int shadow_copy2_unlink(vfs_handle_struct *handle, const char *fname)
+{
+ SHADOW2_NEXT(UNLINK, (handle, name), int, -1);
+}
+
+static int shadow_copy2_chmod(vfs_handle_struct *handle,
+ const char *fname, mode_t mode)
+{
+ SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
+}
+
+static int shadow_copy2_chown(vfs_handle_struct *handle,
+ const char *fname, uid_t uid, gid_t gid)
+{
+ SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
+}
+
+static int shadow_copy2_chdir(vfs_handle_struct *handle,
+ const char *fname)
+{
+ SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
+}
+
+static int shadow_copy2_ntimes(vfs_handle_struct *handle,
+ const char *fname, const struct timespec ts[2])
+{
+ SHADOW2_NEXT(NTIMES, (handle, name, ts), int, -1);
+}
+
+static int shadow_copy2_readlink(vfs_handle_struct *handle,
+ const char *fname, char *buf, size_t bufsiz)
+{
+ SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
+}
+
+static int shadow_copy2_mknod(vfs_handle_struct *handle,
+ const char *fname, mode_t mode, SMB_DEV_T dev)
+{
+ SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
+}
+
+static char *shadow_copy2_realpath(vfs_handle_struct *handle,
+ const char *fname, char *resolved_path)
+{
+ SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *, NULL);
+}
+
+static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
+ const char *fname, uint32 security_info,
+ struct security_descriptor **ppdesc)
+{
+ SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
+}
+
+static NTSTATUS shadow_copy2_set_nt_acl(vfs_handle_struct *handle, files_struct *fsp,
+ const char *fname, uint32 security_info_sent,
+ struct security_descriptor *psd)
+{
+ SHADOW2_NTSTATUS_NEXT(SET_NT_ACL, (handle, fsp, name, security_info_sent, psd), NT_STATUS_ACCESS_DENIED);
+}
+
+static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
+{
+ SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
+}
+
+static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
+{
+ SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
+}
+
+static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, int flags)
+{
+ SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
+}
+
+static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
+ const char *fname, const char *aname, void *value, size_t size)
+{
+ SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
+}
+
+static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
+ const char *fname, const char *aname, void *value, size_t size)
+{
+ SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
+}
+
+static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname,
+ char *list, size_t size)
+{
+ SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
+}
+
+static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname,
+ const char *aname)
+{
+ SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
+}
+
+static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname,
+ const char *aname)
+{
+ SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
+}
+
+static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname,
+ const char *aname, const void *value, size_t size, int flags)
+{
+ SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
+}
+
+static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname,
+ const char *aname, const void *value, size_t size, int flags)
+{
+ SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
+}
+
+static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
+ const char *fname, mode_t mode)
+{
+ /* If the underlying VFS doesn't have ACL support... */
+ if (!handle->vfs_next.ops.chmod_acl) {
+ errno = ENOSYS;
+ return -1;
+ }
+ SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
+}
+
+static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle,
+ files_struct *fsp,
+ SHADOW_COPY_DATA *shadow_copy2_data,
+ bool labels)
+{
+ SMB_STRUCT_DIR *p;
+ const char *snapdir;
+ SMB_STRUCT_DIRENT *d;
+ TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
+
+ snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
+ if (snapdir == NULL) {
+ DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
+ handle->conn->connectpath));
+ errno = EINVAL;
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
+
+ if (!p) {
+ DEBUG(0,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s' - %s\n",
+ snapdir, strerror(errno)));
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ talloc_free(tmp_ctx);
+
+ shadow_copy2_data->num_volumes = 0;
+ shadow_copy2_data->labels = NULL;
+
+ while ((d = SMB_VFS_NEXT_READDIR(handle, p))) {
+ SHADOW_COPY_LABEL *tlabels;
+
+ /* ignore names not of the right form in the snapshot directory */
+ if (!shadow_copy2_match_name(d->d_name)) {
+ continue;
+ }
+
+ if (!labels) {
+ /* the caller doesn't want the labels */
+ shadow_copy2_data->num_volumes++;
+ continue;
+ }
+
+ tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
+ shadow_copy2_data->labels,
+ SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
+ if (tlabels == NULL) {
+ DEBUG(0,("shadow_copy2: out of memory\n"));
+ SMB_VFS_NEXT_CLOSEDIR(handle, p);
+ return -1;
+ }
+
+ strlcpy(tlabels[shadow_copy2_data->num_volumes], d->d_name, sizeof(*tlabels));
+ shadow_copy2_data->num_volumes++;
+ shadow_copy2_data->labels = tlabels;
+ }
+
+ SMB_VFS_NEXT_CLOSEDIR(handle,p);
+ return 0;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple shadow_copy2_ops[] = {
+ {SMB_VFS_OP(shadow_copy2_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* directory operations */
+ {SMB_VFS_OP(shadow_copy2_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* xattr and flags operations */
+ {SMB_VFS_OP(shadow_copy2_chflags), SMB_VFS_OP_CHFLAGS, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_getxattr), SMB_VFS_OP_GETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_lgetxattr), SMB_VFS_OP_LGETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_listxattr), SMB_VFS_OP_LISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_lremovexattr),SMB_VFS_OP_LREMOVEXATTR,SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_setxattr), SMB_VFS_OP_SETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_lsetxattr), SMB_VFS_OP_LSETXATTR, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* File operations */
+ {SMB_VFS_OP(shadow_copy2_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_lstat), SMB_VFS_OP_LSTAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_fstat), SMB_VFS_OP_FSTAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_chown), SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_chdir), SMB_VFS_OP_CHDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_ntimes), SMB_VFS_OP_NTIMES, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_readlink), SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_link), SMB_VFS_OP_LINK, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_mknod), SMB_VFS_OP_MKNOD, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_realpath), SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* NT File ACL operations */
+ {SMB_VFS_OP(shadow_copy2_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy2_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* POSIX ACL operations */
+ {SMB_VFS_OP(shadow_copy2_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
+
+ /* special shadown copy op */
+ {SMB_VFS_OP(shadow_copy2_get_shadow_copy2_data),
+ SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE},
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_shadow_copy2_init(void);
+NTSTATUS vfs_shadow_copy2_init(void)
+{
+ NTSTATUS ret;
+
+ ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2", shadow_copy2_ops);
+
+ if (!NT_STATUS_IS_OK(ret))
+ return ret;
+
+ vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
+ if (vfs_shadow_copy2_debug_level == -1) {
+ vfs_shadow_copy2_debug_level = DBGC_VFS;
+ DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
+ "vfs_shadow_copy2_init"));
+ } else {
+ DEBUG(10, ("%s: Debug class number of '%s': %d\n",
+ "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
+ }
+
+ return ret;
+}
diff --git a/source3/modules/vfs_streams_depot.c b/source3/modules/vfs_streams_depot.c
new file mode 100644
index 0000000000..fa85ea4a57
--- /dev/null
+++ b/source3/modules/vfs_streams_depot.c
@@ -0,0 +1,641 @@
+/*
+ * Store streams in a separate subdirectory
+ *
+ * Copyright (C) Volker Lendecke, 2007
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+/*
+ * Excerpt from a mail from tridge:
+ *
+ * Volker, what I'm thinking of is this:
+ * /mount-point/.streams/XX/YY/aaaa.bbbb/namedstream1
+ * /mount-point/.streams/XX/YY/aaaa.bbbb/namedstream2
+ *
+ * where XX/YY is a 2 level hash based on the fsid/inode. "aaaa.bbbb"
+ * is the fsid/inode. "namedstreamX" is a file named after the stream
+ * name.
+ */
+
+static uint32_t hash_fn(DATA_BLOB key)
+{
+ uint32_t value; /* Used to compute the hash value. */
+ uint32_t i; /* Used to cycle through random values. */
+
+ /* Set the initial value from the key size. */
+ for (value = 0x238F13AF * key.length, i=0; i < key.length; i++)
+ value = (value + (key.data[i] << (i*5 % 24)));
+
+ return (1103515243 * value + 12345);
+}
+
+/*
+ * With the hashing scheme based on the inode we need to protect against
+ * streams showing up on files with re-used inodes. This can happen if we
+ * create a stream directory from within Samba, and a local process or NFS
+ * client deletes the file without deleting the streams directory. When the
+ * inode is re-used and the stream directory is still around, the streams in
+ * there would be show up as belonging to the new file.
+ *
+ * There are several workarounds for this, probably the easiest one is on
+ * systems which have a true birthtime stat element: When the file has a later
+ * birthtime than the streams directory, then we have to recreate the
+ * directory.
+ *
+ * The other workaround is to somehow mark the file as generated by Samba with
+ * something that a NFS client would not do. The closest one is a special
+ * xattr value being set. On systems which do not support xattrs, it might be
+ * an option to put in a special ACL entry for a non-existing group.
+ */
+
+#define SAMBA_XATTR_MARKER "user.SAMBA_STREAMS"
+
+static bool file_is_valid(vfs_handle_struct *handle, const char *path)
+{
+ char buf;
+
+ DEBUG(10, ("file_is_valid (%s) called\n", path));
+
+ if (SMB_VFS_NEXT_GETXATTR(handle, path, SAMBA_XATTR_MARKER,
+ &buf, sizeof(buf)) != sizeof(buf)) {
+ DEBUG(10, ("GETXATTR failed: %s\n", strerror(errno)));
+ return false;
+ }
+
+ if (buf != '1') {
+ DEBUG(10, ("got wrong buffer content: '%c'\n", buf));
+ return false;
+ }
+
+ return true;
+}
+
+static bool mark_file_valid(vfs_handle_struct *handle, const char *path)
+{
+ char buf = '1';
+ int ret;
+
+ DEBUG(10, ("marking file %s as valid\n", path));
+
+ ret = SMB_VFS_NEXT_SETXATTR(handle, path, SAMBA_XATTR_MARKER,
+ &buf, sizeof(buf), 0);
+
+ if (ret == -1) {
+ DEBUG(10, ("SETXATTR failed: %s\n", strerror(errno)));
+ return false;
+ }
+
+ return true;
+}
+
+static char *stream_dir(vfs_handle_struct *handle, const char *base_path,
+ const SMB_STRUCT_STAT *base_sbuf, bool create_it)
+{
+ uint32_t hash;
+ char *result = NULL;
+ SMB_STRUCT_STAT sbuf;
+ uint8_t first, second;
+ char *tmp;
+ char *id_hex;
+ struct file_id id;
+ uint8 id_buf[16];
+
+ const char *rootdir = lp_parm_const_string(
+ SNUM(handle->conn), "streams", "directory",
+ handle->conn->connectpath);
+
+ if (base_sbuf == NULL) {
+ if (SMB_VFS_NEXT_STAT(handle, base_path, &sbuf) == -1) {
+ /*
+ * base file is not there
+ */
+ goto fail;
+ }
+ base_sbuf = &sbuf;
+ }
+
+ id = SMB_VFS_FILE_ID_CREATE(handle->conn, base_sbuf->st_dev,
+ base_sbuf->st_ino);
+
+ push_file_id_16((char *)id_buf, &id);
+
+ hash = hash_fn(data_blob_const(id_buf, sizeof(id_buf)));
+
+ first = hash & 0xff;
+ second = (hash >> 8) & 0xff;
+
+ id_hex = hex_encode(talloc_tos(), id_buf, sizeof(id_buf));
+
+ if (id_hex == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ result = talloc_asprintf(talloc_tos(), "%s/%2.2X/%2.2X/%s", rootdir,
+ first, second, id_hex);
+
+ TALLOC_FREE(id_hex);
+
+ if (result == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if (SMB_VFS_NEXT_STAT(handle, result, &sbuf) == 0) {
+ char *newname;
+
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (file_is_valid(handle, base_path)) {
+ return result;
+ }
+
+ /*
+ * Someone has recreated a file under an existing inode
+ * without deleting the streams directory. For now, just move
+ * it away.
+ */
+
+ again:
+ newname = talloc_asprintf(talloc_tos(), "lost-%lu", random());
+ if (newname == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ if (SMB_VFS_NEXT_RENAME(handle, result, newname) == -1) {
+ if ((errno == EEXIST) || (errno == ENOTEMPTY)) {
+ TALLOC_FREE(newname);
+ goto again;
+ }
+ goto fail;
+ }
+
+ TALLOC_FREE(newname);
+ }
+
+ if (!create_it) {
+ errno = ENOENT;
+ goto fail;
+ }
+
+ if ((SMB_VFS_NEXT_MKDIR(handle, rootdir, 0755) != 0)
+ && (errno != EEXIST)) {
+ goto fail;
+ }
+
+ tmp = talloc_asprintf(result, "%s/%2.2X", rootdir, first);
+ if (tmp == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ if ((SMB_VFS_NEXT_MKDIR(handle, tmp, 0755) != 0)
+ && (errno != EEXIST)) {
+ goto fail;
+ }
+
+ TALLOC_FREE(tmp);
+
+ tmp = talloc_asprintf(result, "%s/%2.2X/%2.2X", rootdir, first,
+ second);
+ if (tmp == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ if ((SMB_VFS_NEXT_MKDIR(handle, tmp, 0755) != 0)
+ && (errno != EEXIST)) {
+ goto fail;
+ }
+
+ TALLOC_FREE(tmp);
+
+ if ((SMB_VFS_NEXT_MKDIR(handle, result, 0755) != 0)
+ && (errno != EEXIST)) {
+ goto fail;
+ }
+
+ if (!mark_file_valid(handle, base_path)) {
+ goto fail;
+ }
+
+ return result;
+
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static char *stream_name(vfs_handle_struct *handle, const char *fname,
+ bool create_dir)
+{
+ char *base = NULL;
+ char *sname = NULL;
+ char *id_hex = NULL;
+ char *dirname, *stream_fname;
+
+ if (!NT_STATUS_IS_OK(split_ntfs_stream_name(talloc_tos(), fname,
+ &base, &sname))) {
+ DEBUG(10, ("split_ntfs_stream_name failed\n"));
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ dirname = stream_dir(handle, base, NULL, create_dir);
+
+ if (dirname == NULL) {
+ goto fail;
+ }
+
+ stream_fname = talloc_asprintf(talloc_tos(), "%s/:%s", dirname, sname);
+
+ if (stream_fname == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ DEBUG(10, ("stream filename = %s\n", stream_fname));
+
+ TALLOC_FREE(base);
+ TALLOC_FREE(sname);
+ TALLOC_FREE(id_hex);
+
+ return stream_fname;
+
+ fail:
+ DEBUG(5, ("stream_name failed: %s\n", strerror(errno)));
+ TALLOC_FREE(base);
+ TALLOC_FREE(sname);
+ TALLOC_FREE(id_hex);
+ return NULL;
+}
+
+static NTSTATUS walk_streams(vfs_handle_struct *handle,
+ const char *fname,
+ const SMB_STRUCT_STAT *sbuf,
+ char **pdirname,
+ bool (*fn)(const char *dirname,
+ const char *dirent,
+ void *private_data),
+ void *private_data)
+{
+ char *dirname;
+ SMB_STRUCT_DIR *dirhandle = NULL;
+ char *dirent;
+
+ dirname = stream_dir(handle, fname, sbuf, false);
+
+ if (dirname == NULL) {
+ if (errno == ENOENT) {
+ /*
+ * no stream around
+ */
+ return NT_STATUS_OK;
+ }
+ return map_nt_error_from_unix(errno);
+ }
+
+ DEBUG(10, ("walk_streams: dirname=%s\n", dirname));
+
+ dirhandle = SMB_VFS_NEXT_OPENDIR(handle, dirname, NULL, 0);
+
+ if (dirhandle == NULL) {
+ TALLOC_FREE(dirname);
+ return map_nt_error_from_unix(errno);
+ }
+
+ while ((dirent = vfs_readdirname(handle->conn, dirhandle)) != NULL) {
+
+ if (ISDOT(dirent) || ISDOTDOT(dirent)) {
+ continue;
+ }
+
+ DEBUG(10, ("walk_streams: dirent=%s\n", dirent));
+
+ if (!fn(dirname, dirent, private_data)) {
+ break;
+ }
+ }
+
+ SMB_VFS_NEXT_CLOSEDIR(handle, dirhandle);
+
+ if (pdirname != NULL) {
+ *pdirname = dirname;
+ }
+ else {
+ TALLOC_FREE(dirname);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static int streams_depot_stat(vfs_handle_struct *handle, const char *fname,
+ SMB_STRUCT_STAT *sbuf)
+{
+ char *stream_fname;
+ int ret = -1;
+
+ DEBUG(10, ("streams_depot_stat called for [%s]\n", fname));
+
+ if (!is_ntfs_stream_name(fname)) {
+ return SMB_VFS_NEXT_STAT(handle, fname, sbuf);
+ }
+
+ stream_fname = stream_name(handle, fname, false);
+ if (stream_fname == NULL) {
+ goto done;
+ }
+
+ ret = SMB_VFS_NEXT_STAT(handle, stream_fname, sbuf);
+
+ done:
+ TALLOC_FREE(stream_fname);
+ return ret;
+}
+
+static int streams_depot_lstat(vfs_handle_struct *handle, const char *fname,
+ SMB_STRUCT_STAT *sbuf)
+{
+ char *stream_fname;
+ int ret = -1;
+
+ if (!is_ntfs_stream_name(fname)) {
+ return SMB_VFS_NEXT_LSTAT(handle, fname, sbuf);
+ }
+
+ stream_fname = stream_name(handle, fname, false);
+ if (stream_fname == NULL) {
+ goto done;
+ }
+
+ ret = SMB_VFS_NEXT_LSTAT(handle, stream_fname, sbuf);
+
+ done:
+ TALLOC_FREE(stream_fname);
+ return ret;
+}
+
+static int streams_depot_open(vfs_handle_struct *handle, const char *fname,
+ files_struct *fsp, int flags, mode_t mode)
+{
+ TALLOC_CTX *frame;
+ char *base = NULL;
+ SMB_STRUCT_STAT base_sbuf;
+ char *stream_fname;
+ int ret = -1;
+
+ if (!is_ntfs_stream_name(fname)) {
+ return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
+ }
+
+ frame = talloc_stackframe();
+
+ if (!NT_STATUS_IS_OK(split_ntfs_stream_name(talloc_tos(), fname,
+ &base, NULL))) {
+ errno = ENOMEM;
+ goto done;
+ }
+
+ ret = SMB_VFS_NEXT_STAT(handle, base, &base_sbuf);
+
+ if (ret == -1) {
+ goto done;
+ }
+
+ TALLOC_FREE(base);
+
+ stream_fname = stream_name(handle, fname, true);
+ if (stream_fname == NULL) {
+ goto done;
+ }
+
+ ret = SMB_VFS_NEXT_OPEN(handle, stream_fname, fsp, flags, mode);
+
+ done:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static int streams_depot_unlink(vfs_handle_struct *handle, const char *fname)
+{
+ int ret = -1;
+ SMB_STRUCT_STAT sbuf;
+
+ DEBUG(10, ("streams_depot_unlink called for %s\n", fname));
+
+ if (is_ntfs_stream_name(fname)) {
+ char *stream_fname;
+
+ stream_fname = stream_name(handle, fname, false);
+ if (stream_fname == NULL) {
+ return -1;
+ }
+
+ ret = SMB_VFS_NEXT_UNLINK(handle, stream_fname);
+
+ TALLOC_FREE(stream_fname);
+ return ret;
+ }
+
+ /*
+ * We potentially need to delete the per-inode streams directory
+ */
+
+ if (SMB_VFS_NEXT_STAT(handle, fname, &sbuf) == -1) {
+ return -1;
+ }
+
+ if (sbuf.st_nlink == 1) {
+ char *dirname = stream_dir(handle, fname, &sbuf, false);
+
+ if (dirname != NULL) {
+ SMB_VFS_NEXT_RMDIR(handle, dirname);
+ }
+ TALLOC_FREE(dirname);
+ }
+
+ return SMB_VFS_NEXT_UNLINK(handle, fname);
+}
+
+static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
+ struct stream_struct **streams,
+ const char *name, SMB_OFF_T size,
+ SMB_OFF_T alloc_size)
+{
+ struct stream_struct *tmp;
+
+ tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
+ (*num_streams)+1);
+ if (tmp == NULL) {
+ return false;
+ }
+
+ tmp[*num_streams].name = talloc_strdup(tmp, name);
+ if (tmp[*num_streams].name == NULL) {
+ return false;
+ }
+
+ tmp[*num_streams].size = size;
+ tmp[*num_streams].alloc_size = alloc_size;
+
+ *streams = tmp;
+ *num_streams += 1;
+ return true;
+}
+
+struct streaminfo_state {
+ TALLOC_CTX *mem_ctx;
+ vfs_handle_struct *handle;
+ unsigned int num_streams;
+ struct stream_struct *streams;
+ NTSTATUS status;
+};
+
+static bool collect_one_stream(const char *dirname,
+ const char *dirent,
+ void *private_data)
+{
+ struct streaminfo_state *state =
+ (struct streaminfo_state *)private_data;
+ char *full_sname;
+ SMB_STRUCT_STAT sbuf;
+
+ if (asprintf(&full_sname, "%s/%s", dirname, dirent) == -1) {
+ state->status = NT_STATUS_NO_MEMORY;
+ return false;
+ }
+ if (SMB_VFS_NEXT_STAT(state->handle, full_sname, &sbuf) == -1) {
+ DEBUG(10, ("Could not stat %s: %s\n", full_sname,
+ strerror(errno)));
+ SAFE_FREE(full_sname);
+ return true;
+ }
+
+ SAFE_FREE(full_sname);
+
+ if (!add_one_stream(state->mem_ctx,
+ &state->num_streams, &state->streams,
+ dirent, sbuf.st_size,
+ get_allocation_size(
+ state->handle->conn, NULL, &sbuf))) {
+ state->status = NT_STATUS_NO_MEMORY;
+ return false;
+ }
+
+ return true;
+}
+
+static NTSTATUS streams_depot_streaminfo(vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ const char *fname,
+ TALLOC_CTX *mem_ctx,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams)
+{
+ SMB_STRUCT_STAT sbuf;
+ int ret;
+ NTSTATUS status;
+ struct streaminfo_state state;
+
+ if ((fsp != NULL) && (fsp->fh->fd != -1)) {
+ if (is_ntfs_stream_name(fsp->fsp_name)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &sbuf);
+ }
+ else {
+ if (is_ntfs_stream_name(fname)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ret = SMB_VFS_NEXT_STAT(handle, fname, &sbuf);
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ state.streams = NULL;
+ state.num_streams = 0;
+
+ if (!S_ISDIR(sbuf.st_mode)) {
+ if (!add_one_stream(mem_ctx,
+ &state.num_streams, &state.streams,
+ "::$DATA", sbuf.st_size,
+ get_allocation_size(handle->conn, fsp,
+ &sbuf))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ state.mem_ctx = mem_ctx;
+ state.handle = handle;
+ state.status = NT_STATUS_OK;
+
+ status = walk_streams(handle, fname, &sbuf, NULL, collect_one_stream,
+ &state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(state.streams);
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(state.status)) {
+ TALLOC_FREE(state.streams);
+ return state.status;
+ }
+
+ *pnum_streams = state.num_streams;
+ *pstreams = state.streams;
+ return NT_STATUS_OK;
+}
+
+static uint32_t streams_depot_fs_capabilities(struct vfs_handle_struct *handle)
+{
+ return SMB_VFS_NEXT_FS_CAPABILITIES(handle) | FILE_NAMED_STREAMS;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple streams_depot_ops[] = {
+ {SMB_VFS_OP(streams_depot_fs_capabilities), SMB_VFS_OP_FS_CAPABILITIES,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_depot_open), SMB_VFS_OP_OPEN,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_depot_stat), SMB_VFS_OP_STAT,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_depot_lstat), SMB_VFS_OP_LSTAT,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_depot_unlink), SMB_VFS_OP_UNLINK,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_depot_streaminfo), SMB_VFS_OP_STREAMINFO,
+ SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_streams_depot_init(void);
+NTSTATUS vfs_streams_depot_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "streams_depot",
+ streams_depot_ops);
+}
diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c
new file mode 100644
index 0000000000..766e7d10ab
--- /dev/null
+++ b/source3/modules/vfs_streams_xattr.c
@@ -0,0 +1,685 @@
+/*
+ * Store streams in xattrs
+ *
+ * Copyright (C) Volker Lendecke, 2008
+ *
+ * Partly based on James Peach's Darwin module, which is
+ *
+ * Copyright (C) James Peach 2006-2007
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+struct stream_io {
+ char *base;
+ char *xattr_name;
+};
+
+static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
+{
+ struct MD5Context ctx;
+ unsigned char hash[16];
+ SMB_INO_T result;
+ char *upper_sname;
+
+ DEBUG(10, ("stream_inode called for %lu/%lu [%s]\n",
+ (unsigned long)sbuf->st_dev,
+ (unsigned long)sbuf->st_ino, sname));
+
+ upper_sname = talloc_strdup_upper(talloc_tos(), sname);
+ SMB_ASSERT(upper_sname != NULL);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char *)&(sbuf->st_dev),
+ sizeof(sbuf->st_dev));
+ MD5Update(&ctx, (unsigned char *)&(sbuf->st_ino),
+ sizeof(sbuf->st_ino));
+ MD5Update(&ctx, (unsigned char *)upper_sname,
+ talloc_get_size(upper_sname)-1);
+ MD5Final(hash, &ctx);
+
+ TALLOC_FREE(upper_sname);
+
+ /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
+ memcpy(&result, hash, sizeof(result));
+
+ DEBUG(10, ("stream_inode returns %lu\n", (unsigned long)result));
+
+ return result;
+}
+
+static ssize_t get_xattr_size(connection_struct *conn, const char *fname,
+ const char *xattr_name)
+{
+ NTSTATUS status;
+ struct ea_struct ea;
+ ssize_t result;
+
+ status = get_ea_value(talloc_tos(), conn, NULL, fname,
+ xattr_name, &ea);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ result = ea.value.length-1;
+ TALLOC_FREE(ea.value.data);
+ return result;
+}
+
+
+static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
+ SMB_STRUCT_STAT *sbuf)
+{
+ struct stream_io *io = (struct stream_io *)
+ VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+ DEBUG(10, ("streams_xattr_fstat called for %d\n", fsp->fh->fd));
+
+ if (io == NULL) {
+ return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+ }
+
+ if (SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf) == -1) {
+ return -1;
+ }
+
+ sbuf->st_size = get_xattr_size(handle->conn, io->base, io->xattr_name);
+ if (sbuf->st_size == -1) {
+ return -1;
+ }
+
+ DEBUG(10, ("sbuf->st_size = %d\n", (int)sbuf->st_size));
+
+ sbuf->st_ino = stream_inode(sbuf, io->xattr_name);
+ sbuf->st_mode &= ~S_IFMT;
+ sbuf->st_mode |= S_IFREG;
+ sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
+
+ return 0;
+}
+
+static int streams_xattr_stat(vfs_handle_struct *handle, const char *fname,
+ SMB_STRUCT_STAT *sbuf)
+{
+ NTSTATUS status;
+ char *base = NULL, *sname = NULL;
+ int result = -1;
+ char *xattr_name;
+
+ if (!is_ntfs_stream_name(fname)) {
+ return SMB_VFS_NEXT_STAT(handle, fname, sbuf);
+ }
+
+ status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (SMB_VFS_STAT(handle->conn, base, sbuf) == -1) {
+ goto fail;
+ }
+
+ xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
+ SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
+ if (xattr_name == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ sbuf->st_size = get_xattr_size(handle->conn, base, xattr_name);
+ if (sbuf->st_size == -1) {
+ errno = ENOENT;
+ goto fail;
+ }
+
+ sbuf->st_ino = stream_inode(sbuf, xattr_name);
+ sbuf->st_mode &= ~S_IFMT;
+ sbuf->st_mode |= S_IFREG;
+ sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
+
+ result = 0;
+ fail:
+ TALLOC_FREE(base);
+ TALLOC_FREE(sname);
+ return result;
+}
+
+static int streams_xattr_lstat(vfs_handle_struct *handle, const char *fname,
+ SMB_STRUCT_STAT *sbuf)
+{
+ NTSTATUS status;
+ char *base, *sname;
+ int result = -1;
+ char *xattr_name;
+
+ if (!is_ntfs_stream_name(fname)) {
+ return SMB_VFS_NEXT_LSTAT(handle, fname, sbuf);
+ }
+
+ status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (SMB_VFS_LSTAT(handle->conn, base, sbuf) == -1) {
+ goto fail;
+ }
+
+ xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
+ SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
+ if (xattr_name == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ sbuf->st_size = get_xattr_size(handle->conn, base, xattr_name);
+ if (sbuf->st_size == -1) {
+ errno = ENOENT;
+ goto fail;
+ }
+
+ sbuf->st_ino = stream_inode(sbuf, xattr_name);
+ sbuf->st_mode &= ~S_IFMT;
+ sbuf->st_mode |= S_IFREG;
+ sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
+
+ result = 0;
+ fail:
+ TALLOC_FREE(base);
+ TALLOC_FREE(sname);
+ return result;
+}
+
+static int streams_xattr_open(vfs_handle_struct *handle, const char *fname,
+ files_struct *fsp, int flags, mode_t mode)
+{
+ TALLOC_CTX *frame;
+ NTSTATUS status;
+ struct stream_io *sio;
+ char *base, *sname;
+ struct ea_struct ea;
+ char *xattr_name;
+ int baseflags;
+ int hostfd = -1;
+
+ DEBUG(10, ("streams_xattr_open called for %s\n", fname));
+
+ if (!is_ntfs_stream_name(fname)) {
+ return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
+ }
+
+ frame = talloc_stackframe();
+
+ status = split_ntfs_stream_name(talloc_tos(), fname,
+ &base, &sname);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
+ SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
+ if (xattr_name == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ /*
+ * We use baseflags to turn off nasty side-effects when opening the
+ * underlying file.
+ */
+ baseflags = flags;
+ baseflags &= ~O_TRUNC;
+ baseflags &= ~O_EXCL;
+ baseflags &= ~O_CREAT;
+
+ hostfd = SMB_VFS_OPEN(handle->conn, base, fsp, baseflags, mode);
+
+ /* It is legit to open a stream on a directory, but the base
+ * fd has to be read-only.
+ */
+ if ((hostfd == -1) && (errno == EISDIR)) {
+ baseflags &= ~O_ACCMODE;
+ baseflags |= O_RDONLY;
+ hostfd = SMB_VFS_OPEN(handle->conn, fname, fsp, baseflags,
+ mode);
+ }
+
+ if (hostfd == -1) {
+ goto fail;
+ }
+
+ status = get_ea_value(talloc_tos(), handle->conn, NULL, base,
+ xattr_name, &ea);
+
+ DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status)));
+
+ if (!NT_STATUS_IS_OK(status)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ /*
+ * The base file is not there. This is an error even if we got
+ * O_CREAT, the higher levels should have created the base
+ * file for us.
+ */
+ DEBUG(10, ("streams_xattr_open: base file %s not around, "
+ "returning ENOENT\n", base));
+ errno = ENOENT;
+ goto fail;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * The attribute does not exist
+ */
+
+ if (flags & O_CREAT) {
+ /*
+ * Darn, xattrs need at least 1 byte
+ */
+ char null = '\0';
+
+ DEBUG(10, ("creating attribute %s on file %s\n",
+ xattr_name, base));
+
+ if (SMB_VFS_SETXATTR(
+ handle->conn, base, xattr_name,
+ &null, sizeof(null),
+ flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
+ goto fail;
+ }
+ }
+ }
+
+ if (flags & O_TRUNC) {
+ char null = '\0';
+ if (SMB_VFS_SETXATTR(
+ handle->conn, base, xattr_name,
+ &null, sizeof(null),
+ flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
+ goto fail;
+ }
+ }
+
+ sio = (struct stream_io *)VFS_ADD_FSP_EXTENSION(handle, fsp,
+ struct stream_io);
+ if (sio == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
+ xattr_name);
+ sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
+ base);
+
+ if ((sio->xattr_name == NULL) || (sio->base == NULL)) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ TALLOC_FREE(frame);
+ return hostfd;
+
+ fail:
+ if (hostfd >= 0) {
+ /*
+ * BUGBUGBUG -- we would need to call fd_close_posix here, but
+ * we don't have a full fsp yet
+ */
+ SMB_VFS_CLOSE(fsp, hostfd);
+ }
+
+ TALLOC_FREE(frame);
+ return -1;
+}
+
+static int streams_xattr_unlink(vfs_handle_struct *handle, const char *fname)
+{
+ NTSTATUS status;
+ char *base = NULL;
+ char *sname = NULL;
+ int ret = -1;
+ char *xattr_name;
+
+ if (!is_ntfs_stream_name(fname)) {
+ return SMB_VFS_NEXT_UNLINK(handle, fname);
+ }
+
+ status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
+ SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
+ if (xattr_name == NULL) {
+ errno = ENOMEM;
+ goto fail;
+ }
+
+ ret = SMB_VFS_REMOVEXATTR(handle->conn, base, xattr_name);
+
+ if ((ret == -1) && (errno == ENOATTR)) {
+ errno = ENOENT;
+ goto fail;
+ }
+
+ ret = 0;
+
+ fail:
+ TALLOC_FREE(base);
+ TALLOC_FREE(sname);
+ return ret;
+}
+
+static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
+ const char *fname,
+ bool (*fn)(struct ea_struct *ea,
+ void *private_data),
+ void *private_data)
+{
+ NTSTATUS status;
+ char **names;
+ size_t i, num_names;
+ size_t prefix_len = strlen(SAMBA_XATTR_DOSSTREAM_PREFIX);
+
+ status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname,
+ &names, &num_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (i=0; i<num_names; i++) {
+ struct ea_struct ea;
+
+ if (strncmp(names[i], SAMBA_XATTR_DOSSTREAM_PREFIX,
+ prefix_len) != 0) {
+ continue;
+ }
+
+ status = get_ea_value(names, conn, fsp, fname, names[i], &ea);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Could not get ea %s for file %s: %s\n",
+ names[i], fname, nt_errstr(status)));
+ continue;
+ }
+
+ ea.name = talloc_asprintf(ea.value.data, ":%s",
+ names[i] + prefix_len);
+ if (ea.name == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ continue;
+ }
+
+ if (!fn(&ea, private_data)) {
+ TALLOC_FREE(ea.value.data);
+ return NT_STATUS_OK;
+ }
+
+ TALLOC_FREE(ea.value.data);
+ }
+
+ TALLOC_FREE(names);
+ return NT_STATUS_OK;
+}
+
+static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
+ struct stream_struct **streams,
+ const char *name, SMB_OFF_T size,
+ SMB_OFF_T alloc_size)
+{
+ struct stream_struct *tmp;
+
+ tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
+ (*num_streams)+1);
+ if (tmp == NULL) {
+ return false;
+ }
+
+ tmp[*num_streams].name = talloc_strdup(tmp, name);
+ if (tmp[*num_streams].name == NULL) {
+ return false;
+ }
+
+ tmp[*num_streams].size = size;
+ tmp[*num_streams].alloc_size = alloc_size;
+
+ *streams = tmp;
+ *num_streams += 1;
+ return true;
+}
+
+struct streaminfo_state {
+ TALLOC_CTX *mem_ctx;
+ vfs_handle_struct *handle;
+ unsigned int num_streams;
+ struct stream_struct *streams;
+ NTSTATUS status;
+};
+
+static bool collect_one_stream(struct ea_struct *ea, void *private_data)
+{
+ struct streaminfo_state *state =
+ (struct streaminfo_state *)private_data;
+
+ if (!add_one_stream(state->mem_ctx,
+ &state->num_streams, &state->streams,
+ ea->name, ea->value.length-1,
+ smb_roundup(state->handle->conn,
+ ea->value.length-1))) {
+ state->status = NT_STATUS_NO_MEMORY;
+ return false;
+ }
+
+ return true;
+}
+
+static NTSTATUS streams_xattr_streaminfo(vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ const char *fname,
+ TALLOC_CTX *mem_ctx,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams)
+{
+ SMB_STRUCT_STAT sbuf;
+ int ret;
+ NTSTATUS status;
+ struct streaminfo_state state;
+
+ if ((fsp != NULL) && (fsp->fh->fd != -1)) {
+ if (is_ntfs_stream_name(fsp->fsp_name)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ret = SMB_VFS_FSTAT(fsp, &sbuf);
+ }
+ else {
+ if (is_ntfs_stream_name(fname)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ret = SMB_VFS_STAT(handle->conn, fname, &sbuf);
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ state.streams = NULL;
+ state.num_streams = 0;
+
+ if (!S_ISDIR(sbuf.st_mode)) {
+ if (!add_one_stream(mem_ctx,
+ &state.num_streams, &state.streams,
+ "::$DATA", sbuf.st_size,
+ get_allocation_size(handle->conn, fsp,
+ &sbuf))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ state.mem_ctx = mem_ctx;
+ state.handle = handle;
+ state.status = NT_STATUS_OK;
+
+ status = walk_xattr_streams(handle->conn, fsp, fname,
+ collect_one_stream, &state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(state.streams);
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(state.status)) {
+ TALLOC_FREE(state.streams);
+ return state.status;
+ }
+
+ *pnum_streams = state.num_streams;
+ *pstreams = state.streams;
+ return NT_STATUS_OK;
+}
+
+static uint32_t streams_xattr_fs_capabilities(struct vfs_handle_struct *handle)
+{
+ return SMB_VFS_NEXT_FS_CAPABILITIES(handle) | FILE_NAMED_STREAMS;
+}
+
+static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
+ files_struct *fsp, const void *data,
+ size_t n, SMB_OFF_T offset)
+{
+ struct stream_io *sio =
+ (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+ struct ea_struct ea;
+ NTSTATUS status;
+ int ret;
+
+ DEBUG(10, ("streams_xattr_pwrite called for %d bytes\n", (int)n));
+
+ if (sio == NULL) {
+ return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+ }
+
+ status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
+ sio->base, sio->xattr_name, &ea);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ if ((offset + n) > ea.value.length-1) {
+ uint8 *tmp;
+
+ tmp = TALLOC_REALLOC_ARRAY(talloc_tos(), ea.value.data, uint8,
+ offset + n + 1);
+
+ if (tmp == NULL) {
+ TALLOC_FREE(ea.value.data);
+ errno = ENOMEM;
+ return -1;
+ }
+ ea.value.data = tmp;
+ ea.value.length = offset + n + 1;
+ ea.value.data[offset+n] = 0;
+ }
+
+ memcpy(ea.value.data + offset, data, n);
+
+ ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name,
+ sio->xattr_name,
+ ea.value.data, ea.value.length, 0);
+
+ TALLOC_FREE(ea.value.data);
+
+ if (ret == -1) {
+ return -1;
+ }
+
+ return n;
+}
+
+static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
+ files_struct *fsp, void *data,
+ size_t n, SMB_OFF_T offset)
+{
+ struct stream_io *sio =
+ (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+ struct ea_struct ea;
+ NTSTATUS status;
+ size_t length, overlap;
+
+ if (sio == NULL) {
+ return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+ }
+
+ status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
+ sio->base, sio->xattr_name, &ea);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ length = ea.value.length-1;
+
+ /* Attempt to read past EOF. */
+ if (length <= offset) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ overlap = (offset + n) > length ? (length - offset) : n;
+ memcpy(data, ea.value.data + offset, overlap);
+
+ TALLOC_FREE(ea.value.data);
+ return overlap;
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple streams_xattr_ops[] = {
+ {SMB_VFS_OP(streams_xattr_fs_capabilities), SMB_VFS_OP_FS_CAPABILITIES,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_xattr_open), SMB_VFS_OP_OPEN,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_xattr_stat), SMB_VFS_OP_STAT,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_xattr_fstat), SMB_VFS_OP_FSTAT,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_xattr_lstat), SMB_VFS_OP_LSTAT,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_xattr_pread), SMB_VFS_OP_PREAD,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_xattr_pwrite), SMB_VFS_OP_PWRITE,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_xattr_lstat), SMB_VFS_OP_LSTAT,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_xattr_unlink), SMB_VFS_OP_UNLINK,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(streams_xattr_streaminfo), SMB_VFS_OP_STREAMINFO,
+ SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_streams_xattr_init(void);
+NTSTATUS vfs_streams_xattr_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "streams_xattr",
+ streams_xattr_ops);
+}
diff --git a/source3/modules/vfs_tsmsm.c b/source3/modules/vfs_tsmsm.c
new file mode 100644
index 0000000000..791e8cfcd0
--- /dev/null
+++ b/source3/modules/vfs_tsmsm.c
@@ -0,0 +1,365 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba VFS module for handling offline files
+ with Tivoli Storage Manager Space Management
+
+ (c) Alexander Bokovoy, 2007, 2008
+ (c) Andrew Tridgell, 2007, 2008
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+/*
+ This VFS module accepts following options:
+ tsmsm: hsm script = <path to hsm script> (default does nothing)
+ hsm script should point to a shell script which accepts two arguments:
+ <operation> <filepath>
+ where <operation> is currently 'offline' to set offline status of the <filepath>
+
+ tsmsm: online ratio = ratio to check reported size against actual file size (0.5 by default)
+ tsmsm: attribute name = name of DMAPI attribute that is present when a file is offline.
+ Default is "IBMobj" (which is what GPFS uses)
+
+ The TSMSM VFS module tries to avoid calling expensive DMAPI calls with some heuristics
+ based on the fact that number of blocks reported of a file multiplied by 512 will be
+ bigger than 'online ratio' of actual size for online (non-migrated) files.
+
+ If checks fail, we call DMAPI and ask for specific attribute which present for
+ offline (migrated) files. If this attribute presents, we consider file offline.
+ */
+
+#include "includes.h"
+
+#ifndef USE_DMAPI
+#error "This module requires DMAPI support!"
+#endif
+
+#ifdef HAVE_XFS_DMAPI_H
+#include <xfs/dmapi.h>
+#elif defined(HAVE_SYS_DMI_H)
+#include <sys/dmi.h>
+#elif defined(HAVE_SYS_JFSDMAPI_H)
+#include <sys/jfsdmapi.h>
+#elif defined(HAVE_SYS_DMAPI_H)
+#include <sys/dmapi.h>
+#elif defined(HAVE_DMAPI_H)
+#include <dmapi.h>
+#endif
+
+#ifndef _ISOC99_SOURCE
+#define _ISOC99_SOURCE
+#endif
+
+#include <math.h>
+
+/* optimisation tunables - used to avoid the DMAPI slow path */
+#define FILE_IS_ONLINE_RATIO 0.5
+
+/* default attribute name to look for */
+#define DM_ATTRIB_OBJECT "IBMObj"
+
+struct tsmsm_struct {
+ float online_ratio;
+ char *hsmscript;
+ const char *attrib_name;
+};
+
+static void tsmsm_free_data(void **pptr) {
+ struct tsmsm_struct **tsmd = (struct tsmsm_struct **)pptr;
+ if(!tsmd) return;
+ TALLOC_FREE(*tsmd);
+}
+
+/*
+ called when a client connects to a share
+*/
+static int tsmsm_connect(struct vfs_handle_struct *handle,
+ const char *service,
+ const char *user) {
+ struct tsmsm_struct *tsmd = TALLOC_ZERO_P(handle, struct tsmsm_struct);
+ const char *fres;
+ const char *tsmname;
+
+ if (!tsmd) {
+ DEBUG(0,("tsmsm_connect: out of memory!\n"));
+ return -1;
+ }
+
+ if (!dmapi_have_session()) {
+ DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n"));
+ TALLOC_FREE(tsmd);
+ return -1;
+ }
+
+ tsmname = (handle->param ? handle->param : "tsmsm");
+
+ /* Get 'hsm script' and 'dmapi attribute' parameters to tsmd context */
+ tsmd->hsmscript = lp_parm_talloc_string(SNUM(handle->conn), tsmname,
+ "hsm script", NULL);
+ talloc_steal(tsmd, tsmd->hsmscript);
+
+ tsmd->attrib_name = lp_parm_talloc_string(SNUM(handle->conn), tsmname,
+ "dmapi attribute", DM_ATTRIB_OBJECT);
+ talloc_steal(tsmd, tsmd->attrib_name);
+
+ /* retrieve 'online ratio'. In case of error default to FILE_IS_ONLINE_RATIO */
+ fres = lp_parm_const_string(SNUM(handle->conn), tsmname,
+ "online ratio", NULL);
+ if (fres == NULL) {
+ tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
+ } else {
+ tsmd->online_ratio = strtof(fres, NULL);
+ if (tsmd->online_ratio > 1.0 ||
+ tsmd->online_ratio <= 0.0) {
+ DEBUG(1, ("tsmsm_connect: invalid online ration %f - using %f.\n",
+ tsmd->online_ratio, (float)FILE_IS_ONLINE_RATIO));
+ }
+ }
+
+ /* Store the private data. */
+ SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data,
+ struct tsmsm_struct, return -1);
+ return SMB_VFS_NEXT_CONNECT(handle, service, user);
+}
+
+static bool tsmsm_is_offline(struct vfs_handle_struct *handle,
+ const char *path,
+ SMB_STRUCT_STAT *stbuf) {
+ struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
+ const dm_sessid_t *dmsession_id;
+ void *dmhandle = NULL;
+ size_t dmhandle_len = 0;
+ size_t rlen;
+ dm_attrname_t dmname;
+ int ret, lerrno;
+ bool offline;
+ char buf[1];
+
+ /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
+ then assume it is not offline (it may not be 100%, as it could be sparse) */
+ if (512 * (off_t)stbuf->st_blocks >= stbuf->st_size * tsmd->online_ratio) {
+ DEBUG(10,("%s not offline: st_blocks=%ld st_size=%ld online_ratio=%.2f\n",
+ path, stbuf->st_blocks, stbuf->st_size, tsmd->online_ratio));
+ return false;
+ }
+
+ dmsession_id = dmapi_get_current_session();
+ if (dmsession_id == NULL) {
+ DEBUG(2, ("tsmsm_is_offline: no DMAPI session available? "
+ "Assume file is online.\n"));
+ return false;
+ }
+
+ /* using POSIX capabilities does not work here. It's a slow path, so
+ * become_root() is just as good anyway (tridge)
+ */
+
+ /* Also, AIX has DMAPI but no POSIX capablities support. In this case,
+ * we need to be root to do DMAPI manipulations.
+ */
+ become_root();
+
+ /* go the slow DMAPI route */
+ if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) {
+ DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n",
+ path, strerror(errno)));
+ offline = true;
+ goto done;
+ }
+
+ memset(&dmname, 0, sizeof(dmname));
+ strlcpy((char *)&dmname.an_chars[0], tsmd->attrib_name, sizeof(dmname.an_chars));
+
+ lerrno = 0;
+
+ do {
+ ret = dm_get_dmattr(*dmsession_id, dmhandle, dmhandle_len,
+ DM_NO_TOKEN, &dmname, sizeof(buf), buf, &rlen);
+ if (ret == -1 && errno == EINVAL) {
+ DEBUG(0, ("Stale DMAPI session, re-creating it.\n"));
+ lerrno = EINVAL;
+ if (dmapi_new_session()) {
+ dmsession_id = dmapi_get_current_session();
+ } else {
+ DEBUG(0,
+ ("Unable to re-create DMAPI session, assuming offline (%s) - %s\n",
+ path, strerror(errno)));
+ offline = true;
+ dm_handle_free(dmhandle, dmhandle_len);
+ goto done;
+ }
+ }
+ } while (ret == -1 && lerrno == EINVAL);
+
+ /* its offline if the specified DMAPI attribute exists */
+ offline = (ret == 0 || (ret == -1 && errno == E2BIG));
+
+ DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
+
+ ret = 0;
+
+ dm_handle_free(dmhandle, dmhandle_len);
+
+done:
+ unbecome_root();
+ return offline;
+}
+
+
+static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
+{
+ SMB_STRUCT_STAT sbuf;
+ struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
+ /* see if the file might be offline. This is called before each IO
+ to ensure we use AIO if the file is offline. We don't do the full dmapi
+ call as that would be too slow, instead we err on the side of using AIO
+ if the file might be offline
+ */
+ if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
+ DEBUG(10,("tsmsm_aio_force st_blocks=%ld st_size=%ld online_ratio=%.2f\n",
+ sbuf.st_blocks, sbuf.st_size, tsmd->online_ratio));
+ return !(512 * (off_t)sbuf.st_blocks >= sbuf.st_size * tsmd->online_ratio);
+ }
+ return false;
+}
+
+static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp,
+ SMB_STRUCT_AIOCB *aiocb)
+{
+ ssize_t result;
+
+ result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb);
+ if(result >= 0) {
+ notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_ATTRIBUTES,
+ fsp->fsp_name);
+ }
+
+ return result;
+}
+
+static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
+ SMB_OFF_T offset, size_t n)
+{
+ bool file_online = tsmsm_aio_force(handle, fsp);
+
+ if(!file_online)
+ return ENOSYS;
+
+ return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
+}
+
+/* We do overload pread to allow notification when file becomes online after offline status */
+/* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
+static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp,
+ void *data, size_t n, SMB_OFF_T offset) {
+ ssize_t result;
+ bool notify_online = tsmsm_aio_force(handle, fsp);
+
+ result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+ if((result != -1) && notify_online) {
+ /* We can't actually force AIO at this point (came here not from reply_read_and_X)
+ what we can do is to send notification that file became online
+ */
+ notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_ATTRIBUTES,
+ fsp->fsp_name);
+ }
+
+ return result;
+}
+
+static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp,
+ void *data, size_t n, SMB_OFF_T offset) {
+ ssize_t result;
+ bool notify_online = tsmsm_aio_force(handle, fsp);
+
+ result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+ if((result != -1) && notify_online) {
+ /* We can't actually force AIO at this point (came here not from reply_read_and_X)
+ what we can do is to send notification that file became online
+ */
+ notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_ATTRIBUTES,
+ fsp->fsp_name);
+ }
+
+ return result;
+}
+
+static int tsmsm_set_offline(struct vfs_handle_struct *handle,
+ const char *path) {
+ struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
+ int result = 0;
+ char *command;
+
+ if (tsmd->hsmscript == NULL) {
+ /* no script enabled */
+ DEBUG(1, ("tsmsm_set_offline: No tsmsm:hsmscript configured\n"));
+ return 0;
+ }
+
+ /* Now, call the script */
+ command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
+ if(!command) {
+ DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
+ return -1;
+ }
+ DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
+ if((result = smbrun(command, NULL)) != 0) {
+ DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
+ }
+ TALLOC_FREE(command);
+ return result;
+}
+
+static uint32_t tsmsm_fs_capabilities(struct vfs_handle_struct *handle)
+{
+ return SMB_VFS_NEXT_FS_CAPABILITIES(handle) | FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
+}
+
+static vfs_op_tuple vfs_tsmsm_ops[] = {
+
+ /* Disk operations */
+
+ {SMB_VFS_OP(tsmsm_connect), SMB_VFS_OP_CONNECT,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(tsmsm_fs_capabilities), SMB_VFS_OP_FS_CAPABILITIES,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(tsmsm_aio_force), SMB_VFS_OP_AIO_FORCE,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(tsmsm_aio_return), SMB_VFS_OP_AIO_RETURN,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(tsmsm_pread), SMB_VFS_OP_PREAD,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(tsmsm_pwrite), SMB_VFS_OP_PWRITE,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(tsmsm_sendfile), SMB_VFS_OP_SENDFILE,
+ SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(tsmsm_is_offline), SMB_VFS_OP_IS_OFFLINE,
+ SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(tsmsm_set_offline), SMB_VFS_OP_SET_OFFLINE,
+ SMB_VFS_LAYER_OPAQUE},
+
+ /* Finish VFS operations definition */
+
+ {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP,
+ SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_tsmsm_init(void);
+NTSTATUS vfs_tsmsm_init(void)
+{
+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
+ "tsmsm", vfs_tsmsm_ops);
+}
diff --git a/source3/modules/vfs_xattr_tdb.c b/source3/modules/vfs_xattr_tdb.c
index 3a72831b5b..7b5e510747 100644
--- a/source3/modules/vfs_xattr_tdb.c
+++ b/source3/modules/vfs_xattr_tdb.c
@@ -110,7 +110,7 @@ static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx,
status = xattr_tdb_pull_attrs(mem_ctx, &data, presult);
TALLOC_FREE(data.dptr);
- return NT_STATUS_OK;
+ return status;
}
/*
@@ -134,7 +134,7 @@ static struct db_record *xattr_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
const struct tdb_xattrs *attribs)
{
- TDB_DATA data;
+ TDB_DATA data = tdb_null;
NTSTATUS status;
status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
@@ -165,6 +165,9 @@ static ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
ssize_t result = -1;
NTSTATUS status;
+ DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
+ file_id_string_tos(id), name));
+
status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
if (!NT_STATUS_IS_OK(status)) {
@@ -250,6 +253,9 @@ static int xattr_tdb_setattr(struct db_context *db_ctx,
struct tdb_xattrs *attribs;
uint32_t i;
+ DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
+ file_id_string_tos(id), name));
+
rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
if (rec == NULL) {
@@ -269,6 +275,11 @@ static int xattr_tdb_setattr(struct db_context *db_ctx,
for (i=0; i<attribs->num_xattrs; i++) {
if (strcmp(attribs->xattrs[i].name, name) == 0) {
+ if (flags & XATTR_CREATE) {
+ TALLOC_FREE(rec);
+ errno = EEXIST;
+ return -1;
+ }
break;
}
}
@@ -276,6 +287,12 @@ static int xattr_tdb_setattr(struct db_context *db_ctx,
if (i == attribs->num_xattrs) {
struct tdb_xattr *tmp;
+ if (flags & XATTR_REPLACE) {
+ TALLOC_FREE(rec);
+ errno = ENOATTR;
+ return -1;
+ }
+
tmp = TALLOC_REALLOC_ARRAY(
attribs, attribs->xattrs, struct tdb_xattr,
attribs->num_xattrs + 1);
@@ -558,10 +575,11 @@ static bool xattr_tdb_init(int snum, struct db_context **p_db)
struct db_context *db;
const char *dbname;
- dbname = lp_parm_const_string(snum, "ea", "tdb", lock_path("eas.tdb"));
+ dbname = lp_parm_const_string(snum, "xattr_tdb", "file",
+ lock_path("xattr.tdb"));
if (dbname == NULL) {
- errno = ENOTSUP;
+ errno = ENOSYS;
return false;
}
@@ -570,7 +588,11 @@ static bool xattr_tdb_init(int snum, struct db_context **p_db)
unbecome_root();
if (db == NULL) {
+#if defined(ENOTSUP)
errno = ENOTSUP;
+#else
+ errno = ENOSYS;
+#endif
return false;
}
@@ -660,7 +682,7 @@ static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
* Destructor for the VFS private data
*/
-static void close_ea_db(void **data)
+static void close_xattr_db(void **data)
{
struct db_context **p_db = (struct db_context **)data;
TALLOC_FREE(*p_db);
@@ -688,14 +710,14 @@ static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
}
if (!xattr_tdb_init(snum, &db)) {
- DEBUG(5, ("Could not init ea tdb\n"));
+ DEBUG(5, ("Could not init xattr tdb\n"));
lp_do_parameter(snum, "ea support", "False");
return 0;
}
lp_do_parameter(snum, "ea support", "True");
- SMB_VFS_HANDLE_SET_DATA(handle, db, close_ea_db,
+ SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
struct db_context, return -1);
return 0;