s3: Modify SMB_VFS_FILE_ID_CREATE to take a stat struct
[jra/samba/.git] / source3 / modules / vfs_streams_depot.c
index d8c476f96f4c95cfd1a5f0893b622a76da73a842..9329be7a9c110a94c8a8d60f2745b2b2ce8dbf65 100644 (file)
@@ -67,10 +67,15 @@ static uint32_t hash_fn(DATA_BLOB key)
 
 #define SAMBA_XATTR_MARKER "user.SAMBA_STREAMS"
 
-static bool file_is_valid(vfs_handle_struct *handle, const char *path)
+static bool file_is_valid(vfs_handle_struct *handle, const char *path,
+                         bool check_valid)
 {
        char buf;
 
+       if (!check_valid) {
+               return true;
+       }
+
        DEBUG(10, ("file_is_valid (%s) called\n", path));
 
        if (SMB_VFS_NEXT_GETXATTR(handle, path, SAMBA_XATTR_MARKER,
@@ -87,11 +92,16 @@ static bool file_is_valid(vfs_handle_struct *handle, const char *path)
        return true;
 }
 
-static bool mark_file_valid(vfs_handle_struct *handle, const char *path)
+static bool mark_file_valid(vfs_handle_struct *handle, const char *path,
+                           bool check_valid)
 {
        char buf = '1';
        int ret;
 
+       if (!check_valid) {
+               return true;
+       }
+
        DEBUG(10, ("marking file %s as valid\n", path));
 
        ret = SMB_VFS_NEXT_SETXATTR(handle, path, SAMBA_XATTR_MARKER,
@@ -116,10 +126,22 @@ static char *stream_dir(vfs_handle_struct *handle, const char *base_path,
        char *id_hex;
        struct file_id id;
        uint8 id_buf[16];
+       bool check_valid;
+       const char *rootdir;
+
+       check_valid = lp_parm_bool(SNUM(handle->conn),
+                     "streams_depot", "check_valid", true);
+
+       tmp = talloc_asprintf(talloc_tos(), "%s/.streams", handle->conn->connectpath);
 
-       const char *rootdir = lp_parm_const_string(
+       if (tmp == NULL) {
+               errno = ENOMEM;
+               goto fail;
+       }
+
+       rootdir = lp_parm_const_string(
                SNUM(handle->conn), "streams_depot", "directory",
-               handle->conn->connectpath);
+               tmp);
 
        if (base_sbuf == NULL) {
                if (SMB_VFS_NEXT_STAT(handle, base_path, &sbuf) == -1) {
@@ -131,8 +153,7 @@ static char *stream_dir(vfs_handle_struct *handle, const char *base_path,
                base_sbuf = &sbuf;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(handle->conn, base_sbuf->st_dev,
-                                   base_sbuf->st_ino);
+       id = SMB_VFS_FILE_ID_CREATE(handle->conn, base_sbuf);
 
        push_file_id_16((char *)id_buf, &id);
 
@@ -141,7 +162,7 @@ static char *stream_dir(vfs_handle_struct *handle, const char *base_path,
        first = hash & 0xff;
        second = (hash >> 8) & 0xff;
 
-       id_hex = hex_encode(talloc_tos(), id_buf, sizeof(id_buf));
+       id_hex = hex_encode_talloc(talloc_tos(), id_buf, sizeof(id_buf));
 
        if (id_hex == NULL) {
                errno = ENOMEM;
@@ -166,7 +187,7 @@ static char *stream_dir(vfs_handle_struct *handle, const char *base_path,
                        goto fail;
                }
 
-               if (file_is_valid(handle, base_path)) {
+               if (file_is_valid(handle, base_path, check_valid)) {
                        return result;
                }
 
@@ -236,7 +257,7 @@ static char *stream_dir(vfs_handle_struct *handle, const char *base_path,
                goto fail;
        }
 
-       if (!mark_file_valid(handle, base_path)) {
+       if (!mark_file_valid(handle, base_path, check_valid)) {
                goto fail;
        }
 
@@ -262,6 +283,11 @@ static char *stream_name(vfs_handle_struct *handle, const char *fname,
                goto fail;
        }
 
+       /* if it's the ::$DATA stream just return the base file name */
+       if (!sname) {
+               return base;
+       }
+
        dirname = stream_dir(handle, base, NULL, create_dir);
 
        if (dirname == NULL) {
@@ -325,7 +351,7 @@ static NTSTATUS walk_streams(vfs_handle_struct *handle,
                return map_nt_error_from_unix(errno);
        }
 
-       while ((dirent = vfs_readdirname(handle->conn, dirhandle)) != NULL) {
+       while ((dirent = vfs_readdirname(handle->conn, dirhandle, NULL)) != NULL) {
 
                if (ISDOT(dirent) || ISDOTDOT(dirent)) {
                        continue;
@@ -401,6 +427,7 @@ static int streams_depot_open(vfs_handle_struct *handle,  const char *fname,
 {
        TALLOC_CTX *frame;
        char *base = NULL;
+       char *sname = NULL;
        SMB_STRUCT_STAT base_sbuf;
        char *stream_fname;
        int ret = -1;
@@ -412,11 +439,16 @@ static int streams_depot_open(vfs_handle_struct *handle,  const char *fname,
        frame = talloc_stackframe();
 
        if (!NT_STATUS_IS_OK(split_ntfs_stream_name(talloc_tos(), fname,
-                                                   &base, NULL))) {
+                                                   &base, &sname))) {
                errno = ENOMEM;
                goto done;
        }
 
+       if (!sname) {
+               ret = SMB_VFS_NEXT_OPEN(handle, base, fsp, flags, mode);
+               goto done;
+       }
+
        ret = SMB_VFS_NEXT_STAT(handle, base, &base_sbuf);
 
        if (ret == -1) {
@@ -478,6 +510,84 @@ static int streams_depot_unlink(vfs_handle_struct *handle,  const char *fname)
        return SMB_VFS_NEXT_UNLINK(handle, fname);
 }
 
+static int streams_depot_rename(vfs_handle_struct *handle,
+                               const char *oldname,
+                               const char *newname)
+{
+       TALLOC_CTX *frame = NULL;
+       int ret = -1;
+       bool old_is_stream;
+       bool new_is_stream;
+       char *obase = NULL;
+       char *osname = NULL;
+       char *nbase = NULL;
+       char *nsname = NULL;
+       char *ostream_fname = NULL;
+       char *nstream_fname = NULL;
+       char *newname_full = NULL;
+
+       DEBUG(10, ("streams_depot_rename called for %s => %s\n",
+                  oldname, newname));
+
+       old_is_stream = is_ntfs_stream_name(oldname);
+       new_is_stream = is_ntfs_stream_name(newname);
+
+       if (!old_is_stream && !new_is_stream) {
+               return SMB_VFS_NEXT_RENAME(handle, oldname, newname);
+       }
+
+       frame = talloc_stackframe();
+
+       if (!NT_STATUS_IS_OK(split_ntfs_stream_name(talloc_tos(), oldname,
+                                                   &obase, &osname))) {
+               errno = ENOMEM;
+               goto done;
+       }
+
+       if (!NT_STATUS_IS_OK(split_ntfs_stream_name(talloc_tos(), newname,
+                                                   &nbase, &nsname))) {
+               errno = ENOMEM;
+               goto done;
+       }
+
+       /* for now don't allow renames from or to the default stream */
+       if (!osname || !nsname) {
+               errno = ENOSYS;
+               goto done;
+       }
+
+       ostream_fname = stream_name(handle, oldname, false);
+       if (ostream_fname == NULL) {
+               return -1;
+       }
+
+       /*
+        * Handle passing in a stream name without the base file.  This is
+        * exercised by the NTRENAME streams rename path.
+        */
+       if (StrCaseCmp(nbase, "./") == 0) {
+               newname_full = talloc_asprintf(talloc_tos(), "%s:%s", obase,
+                                              nsname);
+               if (newname_full == NULL) {
+                       errno = ENOMEM;
+                       goto done;
+               }
+       }
+
+       nstream_fname = stream_name(handle,
+                                   newname_full ? newname_full : newname,
+                                   false);
+       if (nstream_fname == NULL) {
+               return -1;
+       }
+
+       ret = SMB_VFS_NEXT_RENAME(handle, ostream_fname, nstream_fname);
+
+done:
+       TALLOC_FREE(frame);
+       return ret;
+}
+
 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
                           struct stream_struct **streams,
                           const char *name, SMB_OFF_T size,
@@ -537,8 +647,8 @@ static bool collect_one_stream(const char *dirname,
        if (!add_one_stream(state->mem_ctx,
                            &state->num_streams, &state->streams,
                            dirent, sbuf.st_size,
-                           get_allocation_size(
-                                   state->handle->conn, NULL, &sbuf))) {
+                           SMB_VFS_GET_ALLOC_SIZE(state->handle->conn, NULL,
+                                                  &sbuf))) {
                state->status = NT_STATUS_NO_MEMORY;
                return false;
        }
@@ -582,8 +692,8 @@ static NTSTATUS streams_depot_streaminfo(vfs_handle_struct *handle,
                if (!add_one_stream(mem_ctx,
                                    &state.num_streams, &state.streams,
                                    "::$DATA", sbuf.st_size,
-                                   get_allocation_size(handle->conn, fsp,
-                                                       &sbuf))) {
+                                   SMB_VFS_GET_ALLOC_SIZE(handle->conn, fsp,
+                                                          &sbuf))) {
                        return NT_STATUS_NO_MEMORY;
                }
        }
@@ -628,6 +738,8 @@ static vfs_op_tuple streams_depot_ops[] = {
         SMB_VFS_LAYER_TRANSPARENT},
        {SMB_VFS_OP(streams_depot_unlink), SMB_VFS_OP_UNLINK,
         SMB_VFS_LAYER_TRANSPARENT},
+       {SMB_VFS_OP(streams_depot_rename), SMB_VFS_OP_RENAME,
+        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}