s3:streams_xattr: recheck fsp->fsp_name after a rename
[kai/samba.git] / source3 / modules / vfs_streams_xattr.c
index d0112a5d740e13c81c2319c86616500e0f160108..6530a1813bd29bc16187d4d5670c78f3d7d055ac 100644 (file)
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
 
-#define XATTR_DOSSTREAM_PREFIX "user.DosStream."
-
 struct stream_io {
        char *base;
        char *xattr_name;
+       void *fsp_name_ptr;
+       files_struct *fsp;
+       vfs_handle_struct *handle;
 };
 
 static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
@@ -66,14 +67,16 @@ static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
        return result;
 }
 
-static ssize_t get_xattr_size(connection_struct *conn, const char *fname,
-                             const char *xattr_name)
+static ssize_t get_xattr_size(connection_struct *conn,
+                               files_struct *fsp,
+                               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,
+       status = get_ea_value(talloc_tos(), conn, fsp, fname,
                              xattr_name, &ea);
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -85,6 +88,49 @@ static ssize_t get_xattr_size(connection_struct *conn, const char *fname,
        return result;
 }
 
+static bool streams_xattr_recheck(struct stream_io *sio)
+{
+       NTSTATUS status;
+       char *base = NULL;
+       char *sname = NULL;
+       char *xattr_name = NULL;
+
+       if (sio->fsp->fsp_name == sio->fsp_name_ptr) {
+               return true;
+       }
+
+       status = split_ntfs_stream_name(talloc_tos(), sio->fsp->fsp_name,
+                                       &base, &sname);
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
+
+       if (sname == NULL) {
+               /* how can this happen */
+               errno = EINVAL;
+               return false;
+       }
+
+       xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
+                                    SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
+       if (xattr_name == NULL) {
+               return false;
+       }
+
+       TALLOC_FREE(sio->xattr_name);
+       TALLOC_FREE(sio->base);
+       sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(sio->handle, sio->fsp),
+                                       xattr_name);
+       sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(sio->handle, sio->fsp),
+                                 base);
+       sio->fsp_name_ptr = sio->fsp->fsp_name;
+
+       if ((sio->xattr_name == NULL) || (sio->base == NULL)) {
+               return false;
+       }
+
+       return true;
+}
 
 static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
                               SMB_STRUCT_STAT *sbuf)
@@ -94,15 +140,20 @@ static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
 
        DEBUG(10, ("streams_xattr_fstat called for %d\n", fsp->fh->fd));
 
-       if (io == NULL) {
+       if (io == NULL || fsp->base_fsp == NULL) {
                return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
        }
 
+       if (!streams_xattr_recheck(io)) {
+               return -1;
+       }
+
        if (SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf) == -1) {
                return -1;
        }
 
-       sbuf->st_size = get_xattr_size(handle->conn, io->base, io->xattr_name);
+       sbuf->st_size = get_xattr_size(handle->conn, fsp->base_fsp,
+                                       io->base, io->xattr_name);
        if (sbuf->st_size == -1) {
                return -1;
        }
@@ -121,7 +172,7 @@ static int streams_xattr_stat(vfs_handle_struct *handle, const char *fname,
                              SMB_STRUCT_STAT *sbuf)
 {
        NTSTATUS status;
-       char *base, *sname;
+       char *base = NULL, *sname = NULL;
        int result = -1;
        char *xattr_name;
 
@@ -132,7 +183,11 @@ static int streams_xattr_stat(vfs_handle_struct *handle, const char *fname,
        status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
        if (!NT_STATUS_IS_OK(status)) {
                errno = EINVAL;
-               goto fail;
+               return -1;
+       }
+
+       if (sname == NULL){
+               return SMB_VFS_NEXT_STAT(handle, base, sbuf);
        }
 
        if (SMB_VFS_STAT(handle->conn, base, sbuf) == -1) {
@@ -140,13 +195,13 @@ static int streams_xattr_stat(vfs_handle_struct *handle, const char *fname,
        }
 
        xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
-                                    XATTR_DOSSTREAM_PREFIX, sname);
+                                    SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
        if (xattr_name == NULL) {
                errno = ENOMEM;
                goto fail;
        }
 
-       sbuf->st_size = get_xattr_size(handle->conn, base, xattr_name);
+       sbuf->st_size = get_xattr_size(handle->conn, NULL, base, xattr_name);
        if (sbuf->st_size == -1) {
                errno = ENOENT;
                goto fail;
@@ -182,18 +237,22 @@ static int streams_xattr_lstat(vfs_handle_struct *handle, const char *fname,
                goto fail;
        }
 
+       if (sname == NULL){
+               return SMB_VFS_NEXT_LSTAT(handle, base, sbuf);
+       }
+
        if (SMB_VFS_LSTAT(handle->conn, base, sbuf) == -1) {
                goto fail;
        }
 
        xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
-                                    XATTR_DOSSTREAM_PREFIX, sname);
+                                    SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
        if (xattr_name == NULL) {
                errno = ENOMEM;
                goto fail;
        }
 
-       sbuf->st_size = get_xattr_size(handle->conn, base, xattr_name);
+       sbuf->st_size = get_xattr_size(handle->conn, NULL, base, xattr_name);
        if (sbuf->st_size == -1) {
                errno = ENOENT;
                goto fail;
@@ -238,8 +297,14 @@ static int streams_xattr_open(vfs_handle_struct *handle,  const char *fname,
                goto fail;
        }
 
+       if (sname == NULL) {
+               hostfd = SMB_VFS_NEXT_OPEN(handle, base, fsp, flags, mode);
+               talloc_free(frame);
+               return hostfd;
+       }
+
        xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
-                                    XATTR_DOSSTREAM_PREFIX, sname);
+                                    SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
        if (xattr_name == NULL) {
                errno = ENOMEM;
                goto fail;
@@ -302,22 +367,40 @@ static int streams_xattr_open(vfs_handle_struct *handle,  const char *fname,
                        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 (fsp->base_fsp->fh->fd != -1) {
+                               if (SMB_VFS_FSETXATTR(
+                                       fsp->base_fsp, xattr_name,
+                                       &null, sizeof(null),
+                                       flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
+                                       goto fail;
+                               }
+                       } else {
+                               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;
+               if (fsp->base_fsp->fh->fd != -1) {
+                       if (SMB_VFS_FSETXATTR(
+                                       fsp->base_fsp, xattr_name,
+                                       &null, sizeof(null),
+                                       flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
+                               goto fail;
+                       }
+               } else {
+                       if (SMB_VFS_SETXATTR(
+                                       handle->conn, base, xattr_name,
+                                       &null, sizeof(null),
+                                       flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
+                               goto fail;
+                       }
                }
        }
 
@@ -332,6 +415,9 @@ static int streams_xattr_open(vfs_handle_struct *handle,  const char *fname,
                                        xattr_name);
         sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
                                  base);
+       sio->fsp_name_ptr = fsp->fsp_name;
+       sio->handle = handle;
+       sio->fsp = fsp;
 
        if ((sio->xattr_name == NULL) || (sio->base == NULL)) {
                errno = ENOMEM;
@@ -347,7 +433,7 @@ static int streams_xattr_open(vfs_handle_struct *handle,  const char *fname,
                 * BUGBUGBUG -- we would need to call fd_close_posix here, but
                 * we don't have a full fsp yet
                 */
-               SMB_VFS_CLOSE(fsp, hostfd);
+               SMB_VFS_CLOSE(fsp);
        }
 
        TALLOC_FREE(frame);
@@ -372,8 +458,12 @@ static int streams_xattr_unlink(vfs_handle_struct *handle,  const char *fname)
                goto fail;
        }
 
+       if (sname == NULL){
+               return SMB_VFS_NEXT_UNLINK(handle, base);
+       }
+
        xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
-                                    XATTR_DOSSTREAM_PREFIX, sname);
+                                    SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
        if (xattr_name == NULL) {
                errno = ENOMEM;
                goto fail;
@@ -394,6 +484,127 @@ static int streams_xattr_unlink(vfs_handle_struct *handle,  const char *fname)
        return ret;
 }
 
+static int streams_xattr_rename(vfs_handle_struct *handle,
+                               const char *oldname,
+                               const char *newname)
+{
+       NTSTATUS status;
+       TALLOC_CTX *frame = NULL;
+       char *obase;
+       char *ostream;
+       char *nbase;
+       char *nstream;
+       const char *base;
+       int ret = -1;
+       char *oxattr_name;
+       char *nxattr_name;
+       bool o_is_stream;
+       bool n_is_stream;
+       ssize_t oret;
+       ssize_t nret;
+       struct ea_struct ea;
+
+       o_is_stream = is_ntfs_stream_name(oldname);
+       n_is_stream = is_ntfs_stream_name(newname);
+
+       if (!o_is_stream && !n_is_stream) {
+               return SMB_VFS_NEXT_RENAME(handle, oldname, newname);
+       }
+
+       if (!(o_is_stream && n_is_stream)) {
+               errno = ENOSYS;
+               goto fail;
+       }
+
+       frame = talloc_stackframe();
+       if (!frame) {
+               goto fail;
+       }
+
+       status = split_ntfs_stream_name(talloc_tos(), oldname, &obase, &ostream);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = EINVAL;
+               goto fail;
+       }
+
+       status = split_ntfs_stream_name(talloc_tos(), newname, &nbase, &nstream);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = EINVAL;
+               goto fail;
+       }
+
+       /*TODO: maybe call SMB_VFS_NEXT_RENAME() both streams are NULL (::$DATA) */
+       if (ostream == NULL) {
+               errno = ENOSYS;
+               goto fail;
+       }
+
+       if (nstream == NULL) {
+               errno = ENOSYS;
+               goto fail;
+       }
+
+       /* the new base should be empty */
+       if (StrCaseCmp(obase, nbase) != 0) {
+               errno = ENOSYS;
+               goto fail;
+       }
+
+       if (StrCaseCmp(ostream, nstream) == 0) {
+               goto done;
+       }
+
+       base = obase;
+
+       oxattr_name = talloc_asprintf(talloc_tos(), "%s%s",
+                                     SAMBA_XATTR_DOSSTREAM_PREFIX, ostream);
+       if (oxattr_name == NULL) {
+               errno = ENOMEM;
+               goto fail;
+       }
+
+       nxattr_name = talloc_asprintf(talloc_tos(), "%s%s",
+                                     SAMBA_XATTR_DOSSTREAM_PREFIX, nstream);
+       if (nxattr_name == NULL) {
+               errno = ENOMEM;
+               goto fail;
+       }
+
+       /* read the old stream */
+       status = get_ea_value(talloc_tos(), handle->conn, NULL,
+                             base, oxattr_name, &ea);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = ENOENT;
+               goto fail;
+       }
+
+       /* (over)write the new stream */
+       nret = SMB_VFS_SETXATTR(handle->conn, base, nxattr_name,
+                               ea.value.data, ea.value.length, 0);
+       if (nret < 0) {
+               if (errno == ENOATTR) {
+                       errno = ENOENT;
+               }
+               goto fail;
+       }
+
+       /* remove the old stream */
+       oret = SMB_VFS_REMOVEXATTR(handle->conn, base, oxattr_name);
+       if (oret < 0) {
+               if (errno == ENOATTR) {
+                       errno = ENOENT;
+               }
+               goto fail;
+       }
+
+ done:
+       errno = 0;
+       ret = 0;
+ fail:
+       TALLOC_FREE(frame);
+       return ret;
+}
+
 static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
                                   const char *fname,
                                   bool (*fn)(struct ea_struct *ea,
@@ -403,7 +614,7 @@ static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
        NTSTATUS status;
        char **names;
        size_t i, num_names;
-       size_t prefix_len = strlen(XATTR_DOSSTREAM_PREFIX);
+       size_t prefix_len = strlen(SAMBA_XATTR_DOSSTREAM_PREFIX);
 
        status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname,
                                        &names, &num_names);
@@ -414,7 +625,7 @@ static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
        for (i=0; i<num_names; i++) {
                struct ea_struct ea;
 
-               if (strncmp(names[i], XATTR_DOSSTREAM_PREFIX,
+               if (strncmp(names[i], SAMBA_XATTR_DOSSTREAM_PREFIX,
                            prefix_len) != 0) {
                        continue;
                }
@@ -560,16 +771,9 @@ static NTSTATUS streams_xattr_streaminfo(vfs_handle_struct *handle,
        return NT_STATUS_OK;
 }
 
-static int streams_xattr_statvfs(struct vfs_handle_struct *handle,
-                                const char *path,
-                                struct vfs_statvfs_struct *statbuf)
+static uint32_t streams_xattr_fs_capabilities(struct vfs_handle_struct *handle)
 {
-       int ret;
-
-       ret = SMB_VFS_NEXT_STATVFS(handle, path, statbuf);
-       statbuf->FsCapabilities |= FILE_NAMED_STREAMS;
-       return ret;
-
+       return SMB_VFS_NEXT_FS_CAPABILITIES(handle) | FILE_NAMED_STREAMS;
 }
 
 static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
@@ -588,6 +792,10 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
                return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
        }
 
+       if (!streams_xattr_recheck(sio)) {
+               return -1;
+       }
+
        status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
                              sio->base, sio->xattr_name, &ea);
        if (!NT_STATUS_IS_OK(status)) {
@@ -612,12 +820,21 @@ static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
 
         memcpy(ea.value.data + offset, data, n);
 
-       ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name,
+       if (fsp->base_fsp->fh->fd != -1) {
+               ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
                                sio->xattr_name,
                                ea.value.data, ea.value.length, 0);
-
+       } else {
+               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;
 }
 
@@ -629,12 +846,16 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
                (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
        struct ea_struct ea;
        NTSTATUS status;
-        size_t length, overlap;
+       size_t length, overlap;
 
        if (sio == NULL) {
                return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
        }
 
+       if (!streams_xattr_recheck(sio)) {
+               return -1;
+       }
+
        status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
                              sio->base, sio->xattr_name, &ea);
        if (!NT_STATUS_IS_OK(status)) {
@@ -656,10 +877,77 @@ static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
         return overlap;
 }
 
+static int streams_xattr_ftruncate(struct vfs_handle_struct *handle,
+                                       struct files_struct *fsp,
+                                       SMB_OFF_T offset)
+{
+       int ret;
+       uint8 *tmp;
+       struct ea_struct ea;
+       NTSTATUS status;
+        struct stream_io *sio =
+               (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+       DEBUG(10, ("streams_xattr_ftruncate called for file %s offset %.0f\n",
+               fsp->fsp_name,
+               (double)offset ));
+
+       if (sio == NULL) {
+               return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
+       }
+
+       if (!streams_xattr_recheck(sio)) {
+               return -1;
+       }
+
+       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;
+       }
+
+       tmp = TALLOC_REALLOC_ARRAY(talloc_tos(), ea.value.data, uint8,
+                                  offset + 1);
+
+       if (tmp == NULL) {
+               TALLOC_FREE(ea.value.data);
+               errno = ENOMEM;
+               return -1;
+       }
+
+       /* Did we expand ? */
+       if (ea.value.length < offset + 1) {
+               memset(&tmp[ea.value.length], '\0',
+                       offset + 1 - ea.value.length);
+       }
+
+       ea.value.data = tmp;
+       ea.value.length = offset + 1;
+       ea.value.data[offset] = 0;
+
+       if (fsp->base_fsp->fh->fd != -1) {
+               ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
+                               sio->xattr_name,
+                               ea.value.data, ea.value.length, 0);
+       } else {
+               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 0;
+}
+
 /* VFS operations structure */
 
 static vfs_op_tuple streams_xattr_ops[] = {
-       {SMB_VFS_OP(streams_xattr_statvfs), SMB_VFS_OP_STATVFS,
+       {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},
@@ -677,6 +965,10 @@ static vfs_op_tuple streams_xattr_ops[] = {
         SMB_VFS_LAYER_TRANSPARENT},
        {SMB_VFS_OP(streams_xattr_unlink), SMB_VFS_OP_UNLINK,
         SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(streams_xattr_rename), SMB_VFS_OP_RENAME,
+        SMB_VFS_LAYER_TRANSPARENT},
+        {SMB_VFS_OP(streams_xattr_ftruncate),  SMB_VFS_OP_FTRUNCATE,
+         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}