s3/lib: fsp_str_dbg() doesn't show a possible stream name
[amitay/samba.git] / source3 / lib / filename_util.c
index 2405183d53bb08d7798c04f317894160aa73ea9b..84a248eb766a443ec593a2526debacc41a195c8e 100644 (file)
@@ -47,63 +47,87 @@ NTSTATUS get_full_smb_filename(TALLOC_CTX *ctx,
 
 /**
  * There are actually legitimate callers of this such as functions that
- * enumerate streams using the SMB_VFS_STREAMINFO interface and then want to
+ * enumerate streams using the vfs_streaminfo interface and then want to
  * operate on each stream.
  */
-NTSTATUS create_synthetic_smb_fname(TALLOC_CTX *ctx, const char *base_name,
-                                   const char *stream_name,
-                                   const SMB_STRUCT_STAT *psbuf,
-                                   struct smb_filename **smb_fname_out)
+struct smb_filename *synthetic_smb_fname(TALLOC_CTX *mem_ctx,
+                                        const char *base_name,
+                                        const char *stream_name,
+                                        const SMB_STRUCT_STAT *psbuf,
+                                        NTTIME twrp,
+                                        uint32_t flags)
 {
-       struct smb_filename smb_fname_loc;
-
-       ZERO_STRUCT(smb_fname_loc);
+       struct smb_filename smb_fname_loc = { 0, };
 
        /* Setup the base_name/stream_name. */
        smb_fname_loc.base_name = discard_const_p(char, base_name);
        smb_fname_loc.stream_name = discard_const_p(char, stream_name);
+       smb_fname_loc.flags = flags;
+       smb_fname_loc.twrp = twrp;
 
        /* Copy the psbuf if one was given. */
        if (psbuf)
                smb_fname_loc.st = *psbuf;
 
-       /* Let copy_smb_filename() do the heavy lifting. */
-       return copy_smb_filename(ctx, &smb_fname_loc, smb_fname_out);
+       /* Let cp_smb_filename() do the heavy lifting. */
+       return cp_smb_filename(mem_ctx, &smb_fname_loc);
 }
 
 /**
- * XXX: This is temporary and there should be no callers of this once
- * smb_filename is plumbed through all path based operations.
+ * Utility function used by VFS calls that must *NOT* operate
+ * on a stream filename, only the base_name.
  */
-NTSTATUS create_synthetic_smb_fname_split(TALLOC_CTX *ctx,
-                                         const char *fname,
-                                         const SMB_STRUCT_STAT *psbuf,
-                                         struct smb_filename **smb_fname_out)
+struct smb_filename *cp_smb_filename_nostream(TALLOC_CTX *mem_ctx,
+                                       const struct smb_filename *smb_fname_in)
 {
-       NTSTATUS status;
-       const char *stream_name = NULL;
-       char *base_name = NULL;
-
-       if (!lp_posix_pathnames()) {
-               stream_name = strchr_m(fname, ':');
+       struct smb_filename *smb_fname = cp_smb_filename(mem_ctx,
+                                                       smb_fname_in);
+       if (smb_fname == NULL) {
+               return NULL;
        }
+       TALLOC_FREE(smb_fname->stream_name);
+       return smb_fname;
+}
 
-       /* Setup the base_name/stream_name. */
-       if (stream_name) {
-               base_name = talloc_strndup(ctx, fname,
-                                          PTR_DIFF(stream_name, fname));
-       } else {
-               base_name = talloc_strdup(ctx, fname);
+/**
+ * There are a few legitimate users of this.
+ */
+struct smb_filename *synthetic_smb_fname_split(TALLOC_CTX *ctx,
+                                               const char *fname,
+                                               bool posix_path)
+{
+       char *stream_name = NULL;
+       char *base_name = NULL;
+       struct smb_filename *ret;
+       bool ok;
+
+       if (posix_path) {
+               /* No stream name looked for. */
+               return synthetic_smb_fname(ctx,
+                               fname,
+                               NULL,
+                               NULL,
+                               0,
+                               SMB_FILENAME_POSIX_PATH);
        }
 
-       if (!base_name) {
-               return NT_STATUS_NO_MEMORY;
+       ok = split_stream_filename(ctx,
+                               fname,
+                               &base_name,
+                               &stream_name);
+       if (!ok) {
+               return NULL;
        }
 
-       status = create_synthetic_smb_fname(ctx, base_name, stream_name, psbuf,
-                                           smb_fname_out);
+       ret = synthetic_smb_fname(ctx,
+                                 base_name,
+                                 stream_name,
+                                 NULL,
+                                 0,
+                                 0);
        TALLOC_FREE(base_name);
-       return status;
+       TALLOC_FREE(stream_name);
+       return ret;
 }
 
 /**
@@ -112,6 +136,11 @@ NTSTATUS create_synthetic_smb_fname_split(TALLOC_CTX *ctx,
 const char *smb_fname_str_dbg(const struct smb_filename *smb_fname)
 {
        char *fname = NULL;
+       time_t t;
+       struct tm tm;
+       struct tm *ptm = NULL;
+       fstring tstr;
+       ssize_t slen;
        NTSTATUS status;
 
        if (smb_fname == NULL) {
@@ -121,76 +150,262 @@ const char *smb_fname_str_dbg(const struct smb_filename *smb_fname)
        if (!NT_STATUS_IS_OK(status)) {
                return "";
        }
+       if (smb_fname->twrp == 0) {
+               return fname;
+       }
+
+       t = nt_time_to_unix(smb_fname->twrp);
+       ptm = gmtime_r(&t, &tm);
+       if (ptm == NULL) {
+               return "";
+       }
+
+       slen = strftime(tstr, sizeof(tstr), GMT_FORMAT, &tm);
+       if (slen == 0) {
+               return "";
+       }
+
+       fname = talloc_asprintf(talloc_tos(),
+                               "%s {%s}",
+                               fname,
+                               tstr);
+       if (fname == NULL) {
+               return "";
+       }
        return fname;
 }
 
 /**
- * Return a debug string using the talloc_tos().  This can only be called from
- * DEBUG() macros due to the debut_ctx().
+ * Return a debug string of the path name of an fsp using the talloc_tos().
  */
 const char *fsp_str_dbg(const struct files_struct *fsp)
 {
-       return smb_fname_str_dbg(fsp->fsp_name);
+       const char *name = NULL;
+
+       name = smb_fname_str_dbg(fsp->fsp_name);
+       if (name == NULL) {
+               return "";
+       }
+
+       if (fsp->dirfsp == NULL || fsp->dirfsp == fsp->conn->cwd_fsp) {
+               return name;
+       }
+
+       if (ISDOT(fsp->dirfsp->fsp_name->base_name)) {
+               return name;
+       }
+
+       name = smb_fname_str_dbg(fsp->fsp_name);
+       if (name == NULL) {
+               return "";
+       }
+
+       name = talloc_asprintf(talloc_tos(),
+                              "%s/%s",
+                              fsp->dirfsp->fsp_name->base_name,
+                              name);
+       if (name == NULL) {
+               return "";
+       }
+       return name;
+}
+
+/**
+ * Create a debug string for the fnum of an fsp.
+ *
+ * This is allocated to talloc_tos() or a string constant
+ * in certain corner cases. The returned string should
+ * hence not be free'd directly but only via the talloc stack.
+ */
+const char *fsp_fnum_dbg(const struct files_struct *fsp)
+{
+       char *str;
+
+       if (fsp == NULL) {
+               return "fnum [fsp is NULL]";
+       }
+
+       if (fsp->fnum == FNUM_FIELD_INVALID) {
+               return "fnum [invalid value]";
+       }
+
+       str = talloc_asprintf(talloc_tos(), "fnum %llu",
+                             (unsigned long long)fsp->fnum);
+       if (str == NULL) {
+               DEBUG(1, ("%s: talloc_asprintf failed\n", __FUNCTION__));
+               return "fnum [talloc failed!]";
+       }
+
+       return str;
 }
 
-NTSTATUS copy_smb_filename(TALLOC_CTX *ctx,
-                          const struct smb_filename *smb_fname_in,
-                          struct smb_filename **smb_fname_out)
+struct smb_filename *cp_smb_filename(TALLOC_CTX *mem_ctx,
+                                    const struct smb_filename *in)
 {
+       struct smb_filename *out;
+       size_t base_len = 0;
+       size_t stream_len = 0;
+       int num = 0;
+
        /* stream_name must always be NULL if there is no stream. */
-       if (smb_fname_in->stream_name) {
-               SMB_ASSERT(smb_fname_in->stream_name[0] != '\0');
+       if (in->stream_name) {
+               SMB_ASSERT(in->stream_name[0] != '\0');
        }
 
-       *smb_fname_out = talloc_zero(ctx, struct smb_filename);
-       if (*smb_fname_out == NULL) {
-               return NT_STATUS_NO_MEMORY;
+       if (in->base_name != NULL) {
+               base_len = strlen(in->base_name) + 1;
+               num += 1;
+       }
+       if (in->stream_name != NULL) {
+               stream_len = strlen(in->stream_name) + 1;
+               num += 1;
        }
 
-       if (smb_fname_in->base_name) {
-               (*smb_fname_out)->base_name =
-                   talloc_strdup(*smb_fname_out, smb_fname_in->base_name);
-               if (!(*smb_fname_out)->base_name)
-                       goto no_mem_err;
+       out = talloc_pooled_object(mem_ctx, struct smb_filename,
+                               num, stream_len + base_len);
+       if (out == NULL) {
+               return NULL;
+       }
+       ZERO_STRUCTP(out);
+
+       /*
+        * The following allocations cannot fail as we
+        * pre-allocated space for them in the out pooled
+        * object.
+        */
+       if (in->base_name != NULL) {
+               out->base_name = talloc_memdup(
+                               out, in->base_name, base_len);
+               talloc_set_name_const(out->base_name,
+                                     out->base_name);
+       }
+       if (in->stream_name != NULL) {
+               out->stream_name = talloc_memdup(
+                               out, in->stream_name, stream_len);
+               talloc_set_name_const(out->stream_name,
+                                     out->stream_name);
        }
+       out->flags = in->flags;
+       out->st = in->st;
+       out->twrp = in->twrp;
+       return out;
+}
 
-       if (smb_fname_in->stream_name) {
-               (*smb_fname_out)->stream_name =
-                   talloc_strdup(*smb_fname_out, smb_fname_in->stream_name);
-               if (!(*smb_fname_out)->stream_name)
-                       goto no_mem_err;
+/**
+ * Return allocated parent directory and basename of path
+ *
+ * Note: if requesting name, it is returned as talloc child of the
+ * parent. Freeing the parent is thus sufficient to free both.
+ */
+bool parent_smb_fname(TALLOC_CTX *mem_ctx,
+                     const struct smb_filename *path,
+                     struct smb_filename **_parent,
+                     struct smb_filename  **_name)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct smb_filename *parent = NULL;
+       struct smb_filename *name = NULL;
+       char *p = NULL;
+
+       parent = cp_smb_filename(frame, path);
+       if (parent == NULL) {
+               TALLOC_FREE(frame);
+               return false;
+       }
+       TALLOC_FREE(parent->stream_name);
+       SET_STAT_INVALID(parent->st);
+
+       p = strrchr_m(parent->base_name, '/'); /* Find final '/', if any */
+       if (p == NULL) {
+               TALLOC_FREE(parent->base_name);
+               parent->base_name = talloc_strdup(parent, ".");
+               if (parent->base_name == NULL) {
+                       TALLOC_FREE(frame);
+                       return false;
+               }
+               p = path->base_name;
+       } else {
+               *p = '\0';
+               p++;
        }
 
-       if (smb_fname_in->original_lcomp) {
-               (*smb_fname_out)->original_lcomp =
-                   talloc_strdup(*smb_fname_out, smb_fname_in->original_lcomp);
-               if (!(*smb_fname_out)->original_lcomp)
-                       goto no_mem_err;
+       if (_name == NULL) {
+               *_parent = talloc_move(mem_ctx, &parent);
+               TALLOC_FREE(frame);
+               return true;
        }
 
-       (*smb_fname_out)->st = smb_fname_in->st;
-       return NT_STATUS_OK;
+       name = cp_smb_filename(frame, path);
+       if (name == NULL) {
+               TALLOC_FREE(frame);
+               return false;
+       }
+       TALLOC_FREE(name->base_name);
+
+       name->base_name = talloc_strdup(mem_ctx, p);
+       if (name == NULL) {
+               TALLOC_FREE(frame);
+               return false;
+       }
 
- no_mem_err:
-       TALLOC_FREE(*smb_fname_out);
-       return NT_STATUS_NO_MEMORY;
+       *_parent = talloc_move(mem_ctx, &parent);
+       *_name = talloc_move(*_parent, &name);
+       TALLOC_FREE(frame);
+       return true;
 }
 
-/****************************************************************************
- Simple check to determine if the filename is a stream.
- ***************************************************************************/
-bool is_ntfs_stream_smb_fname(const struct smb_filename *smb_fname)
+static void assert_valid_stream_smb_fname(const struct smb_filename *smb_fname)
 {
        /* stream_name must always be NULL if there is no stream. */
        if (smb_fname->stream_name) {
                SMB_ASSERT(smb_fname->stream_name[0] != '\0');
        }
 
-       if (lp_posix_pathnames()) {
+       if (smb_fname->flags & SMB_FILENAME_POSIX_PATH) {
+               SMB_ASSERT(smb_fname->stream_name == NULL);
+       }
+}
+
+/****************************************************************************
+ Simple check to determine if a smb_fname is a real named stream or the
+ default stream.
+ ***************************************************************************/
+
+bool is_ntfs_stream_smb_fname(const struct smb_filename *smb_fname)
+{
+       assert_valid_stream_smb_fname(smb_fname);
+
+       if (smb_fname->stream_name == NULL) {
                return false;
        }
 
-       return smb_fname->stream_name != NULL;
+       return true;
+}
+
+/****************************************************************************
+ Simple check to determine if a smb_fname is pointing to a normal file or
+ a named stream that is not the default stream "::$DATA".
+
+  foo           -> false
+  foo::$DATA    -> false
+  foo:bar       -> true
+  foo:bar:$DATA -> true
+
+ ***************************************************************************/
+
+bool is_named_stream(const struct smb_filename *smb_fname)
+{
+       assert_valid_stream_smb_fname(smb_fname);
+
+       if (smb_fname->stream_name == NULL) {
+               return false;
+       }
+
+       if (strequal_m(smb_fname->stream_name, "::$DATA")) {
+               return false;
+       }
+
+       return true;
 }
 
 /****************************************************************************
@@ -198,9 +413,83 @@ bool is_ntfs_stream_smb_fname(const struct smb_filename *smb_fname)
  ***************************************************************************/
 bool is_ntfs_default_stream_smb_fname(const struct smb_filename *smb_fname)
 {
-       if (!is_ntfs_stream_smb_fname(smb_fname)) {
+       assert_valid_stream_smb_fname(smb_fname);
+
+       if (smb_fname->stream_name == NULL) {
+               return false;
+       }
+
+       return strequal_m(smb_fname->stream_name, "::$DATA");
+}
+
+/****************************************************************************
+ Filter out Windows invalid EA names (list probed from Windows 2012).
+****************************************************************************/
+
+static char bad_ea_name_chars[] = "\"*+,/:;<=>?[\\]|";
+
+bool is_invalid_windows_ea_name(const char *name)
+{
+       int i;
+       /* EA name is pulled as ascii so we can examine
+          individual bytes here. */
+       for (i = 0; name[i] != 0; i++) {
+               int val = (name[i] & 0xff);
+               if (val < ' ' || strchr(bad_ea_name_chars, val)) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+bool ea_list_has_invalid_name(struct ea_list *ea_list)
+{
+       for (;ea_list; ea_list = ea_list->next) {
+               if (is_invalid_windows_ea_name(ea_list->ea.name)) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+/****************************************************************************
+ Split an incoming name into tallocd filename and stream components.
+ Returns true on success, false on out of memory.
+****************************************************************************/
+
+bool split_stream_filename(TALLOC_CTX *ctx,
+                               const char *filename_in,
+                               char **filename_out,
+                               char **streamname_out)
+{
+       const char *stream_name = NULL;
+       char *stream_out = NULL;
+       char *file_out = NULL;
+
+       stream_name = strchr_m(filename_in, ':');
+
+       if (stream_name) {
+               stream_out = talloc_strdup(ctx, stream_name);
+               if (stream_out == NULL) {
+                       return false;
+               }
+               file_out = talloc_strndup(ctx,
+                                       filename_in,
+                                       PTR_DIFF(stream_name, filename_in));
+       } else {
+               file_out = talloc_strdup(ctx, filename_in);
+       }
+
+       if (file_out == NULL) {
+               TALLOC_FREE(stream_out);
                return false;
        }
 
-       return strcasecmp_m(smb_fname->stream_name, "::$DATA") == 0;
+       if (filename_out) {
+               *filename_out = file_out;
+       }
+       if (streamname_out) {
+               *streamname_out = stream_out;
+       }
+       return true;
 }