TODO: add a cache for previously resolved case-insensitive names
TODO: add mangled name support
*/
-static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name)
+static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ uint_t flags)
{
/* break into a series of components */
int num_components;
name->full_name = partial_name;
if (name->exists) {
- return pvfs_fill_dos_info(pvfs, name, -1);
+ return pvfs_fill_dos_info(pvfs, name, flags, -1);
}
return NT_STATUS_OK;
/*
parse a alternate data stream name
*/
-static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
+static NTSTATUS parse_stream_name(struct smb_iconv_convenience *ic,
+ struct pvfs_filename *name,
+ const char *s)
{
- char *p;
- name->stream_name = talloc_strdup(name, s+1);
+ char *p, *stream_name;
+ if (s[1] == '\0') {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ name->stream_name = stream_name = talloc_strdup(name, s+1);
if (name->stream_name == NULL) {
return NT_STATUS_NO_MEMORY;
}
- p = strchr_m(name->stream_name, ':');
- if (p == NULL) {
- name->stream_id = pvfs_name_hash(name->stream_name,
- strlen(name->stream_name));
- return NT_STATUS_OK;
- }
- if (strcasecmp_m(p, ":$DATA") != 0) {
- return NT_STATUS_OBJECT_NAME_INVALID;
+
+ p = stream_name;
+
+ while (*p) {
+ size_t c_size;
+ codepoint_t c = next_codepoint_convenience(ic, p, &c_size);
+
+ switch (c) {
+ case '/':
+ case '\\':
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ case ':':
+ *p= 0;
+ p++;
+ if (*p == '\0') {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (strcasecmp_m(p, "$DATA") != 0) {
+ if (strchr_m(p, ':')) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ c_size = 0;
+ p--;
+ break;
+ }
+
+ p += c_size;
}
- *p = 0;
+
if (strcmp(name->stream_name, "") == 0) {
- name->stream_name = NULL;
+ /*
+ * we don't set stream_name to NULL, here
+ * as this would be wrong for directories
+ *
+ * pvfs_fill_dos_info() will set it to NULL
+ * if it's not a directory.
+ */
name->stream_id = 0;
} else {
name->stream_id = pvfs_name_hash(name->stream_name,
uint_t flags, struct pvfs_filename *name)
{
char *ret, *p, *p_start;
+ struct smb_iconv_convenience *ic = NULL;
NTSTATUS status;
name->original_name = talloc_strdup(name, cifs_name);
for legal characters */
p_start = p;
+ ic = lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx);
while (*p) {
size_t c_size;
- codepoint_t c = next_codepoint(lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), p, &c_size);
+ codepoint_t c = next_codepoint_convenience(ic, p, &c_size);
+
+ if (c <= 0x1F) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
switch (c) {
case '\\':
if (name->has_wildcard) {
/* wildcards are only allowed in the last part
of a name */
- return NT_STATUS_ILLEGAL_CHARACTER;
+ return NT_STATUS_OBJECT_NAME_INVALID;
}
- if (p > p_start && p[1] == 0) {
- *p = 0;
+ if (p > p_start && (p[1] == '\\' || p[1] == '\0')) {
+ /* see if it is definately a "\\" or
+ * a trailing "\". If it is then fail here,
+ * and let the next layer up try again after
+ * pvfs_reduce_name() if it wants to. This is
+ * much more efficient on average than always
+ * scanning for these separately
+ */
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
} else {
*p = '/';
}
break;
case ':':
if (!(flags & PVFS_RESOLVE_STREAMS)) {
- return NT_STATUS_ILLEGAL_CHARACTER;
+ return NT_STATUS_OBJECT_NAME_INVALID;
}
if (name->has_wildcard) {
- return NT_STATUS_ILLEGAL_CHARACTER;
+ return NT_STATUS_OBJECT_NAME_INVALID;
}
- status = parse_stream_name(name, p);
+ status = parse_stream_name(ic, name, p);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
break;
case '/':
case '|':
- return NT_STATUS_ILLEGAL_CHARACTER;
+ return NT_STATUS_OBJECT_NAME_INVALID;
case '.':
/* see if it is definately a .. or
. component. If it is then fail here, and
if (s == NULL) return NT_STATUS_NO_MEMORY;
for (num_components=1, p=s; *p; p += c_size) {
- c = next_codepoint(iconv_convenience, p, &c_size);
+ c = next_codepoint_convenience(iconv_convenience, p, &c_size);
if (c == '\\') num_components++;
}
components[0] = s;
for (i=0, p=s; *p; p += c_size) {
- c = next_codepoint(iconv_convenience, p, &c_size);
+ c = next_codepoint_convenience(iconv_convenience, p, &c_size);
if (c == '\\') {
*p = 0;
components[++i] = p+1;
}
/* rebuild the name */
- ret = talloc_size(mem_ctx, len+1);
+ ret = talloc_array(mem_ctx, char, len+1);
if (ret == NULL) {
talloc_free(s);
return NT_STATUS_NO_MEMORY;
}
ret[len] = 0;
+ talloc_set_name_const(ret, ret);
+
talloc_free(s);
*fname = ret;
TODO: ../ collapsing, and outside share checking
*/
-NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
const char *cifs_name,
uint_t flags, struct pvfs_filename **name)
{
NTSTATUS status;
- *name = talloc(mem_ctx, struct pvfs_filename);
+ *name = talloc(req, struct pvfs_filename);
if (*name == NULL) {
return NT_STATUS_NO_MEMORY;
}
flags &= ~PVFS_RESOLVE_STREAMS;
}
+ /* SMB2 doesn't allow a leading slash */
+ if (req->ctx->protocol == PROTOCOL_SMB2 &&
+ *cifs_name == '\\') {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
/* do the basic conversion to a unix formatted path,
also checking for allowable characters */
status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
/* we need to search for a matching name */
saved_name = (*name)->full_name;
(*name)->full_name = dir_name;
- status = pvfs_case_search(pvfs, *name);
+ status = pvfs_case_search(pvfs, *name, flags);
if (!NT_STATUS_IS_OK(status)) {
/* the directory doesn't exist */
(*name)->full_name = saved_name;
/* if we can stat() the full name now then we are done */
if (stat((*name)->full_name, &(*name)->st) == 0) {
(*name)->exists = true;
- return pvfs_fill_dos_info(pvfs, *name, -1);
+ return pvfs_fill_dos_info(pvfs, *name, flags, -1);
}
/* search for a matching filename */
- status = pvfs_case_search(pvfs, *name);
+ status = pvfs_case_search(pvfs, *name, flags);
return status;
}
*/
NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
const char *unix_dir, const char *fname,
- struct pvfs_filename **name)
+ uint_t flags, struct pvfs_filename **name)
{
NTSTATUS status;
(*name)->stream_name = NULL;
(*name)->stream_id = 0;
- status = pvfs_fill_dos_info(pvfs, *name, -1);
+ status = pvfs_fill_dos_info(pvfs, *name, flags, -1);
return status;
}
to update the pvfs_filename stat information, and by pvfs_open()
*/
NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
- struct pvfs_filename *name)
+ struct pvfs_filename *name, uint_t flags)
{
dev_t device = (dev_t)0;
ino_t inode = 0;
name->exists = true;
- return pvfs_fill_dos_info(pvfs, name, fd);
+ return pvfs_fill_dos_info(pvfs, name, flags, fd);
+}
+
+/*
+ fill in the pvfs_filename info for an open file, given the current
+ info for a (possibly) non-open file. This is used by places that need
+ to update the pvfs_filename stat information, and the path
+ after a possible rename on a different handle.
+*/
+NTSTATUS pvfs_resolve_name_handle(struct pvfs_state *pvfs,
+ struct pvfs_file_handle *h)
+{
+ NTSTATUS status;
+
+ if (h->have_opendb_entry) {
+ struct odb_lock *lck;
+ const char *name = NULL;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("%s: failed to lock file '%s' in opendb\n",
+ __FUNCTION__, h->name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = odb_get_path(lck, &name);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * This relies an the fact that
+ * renames of open files are only
+ * allowed by setpathinfo() and setfileinfo()
+ * and there're only renames within the same
+ * directory supported
+ */
+ if (strcmp(h->name->full_name, name) != 0) {
+ const char *orig_dir;
+ const char *new_file;
+ const char *new_orig;
+ char *delim;
+
+ delim = strrchr(name, '/');
+ if (!delim) {
+ talloc_free(lck);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ new_file = delim + 1;
+ delim = strrchr(h->name->original_name, '\\');
+ if (delim) {
+ delim[0] = '\0';
+ orig_dir = h->name->original_name;
+ new_orig = talloc_asprintf(h->name, "%s\\%s",
+ orig_dir, new_file);
+ if (!new_orig) {
+ talloc_free(lck);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ new_orig = talloc_strdup(h->name, new_file);
+ if (!new_orig) {
+ talloc_free(lck);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_free(h->name->original_name);
+ talloc_free(h->name->full_name);
+ h->name->full_name = talloc_steal(h->name, name);
+ h->name->original_name = new_orig;
+ }
+ }
+
+ talloc_free(lck);
+ }
+
+ /*
+ * TODO: pass PVFS_RESOLVE_NO_OPENDB and get
+ * the write time from odb_lock() above.
+ */
+ status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (!null_nttime(h->write_time.close_time)) {
+ h->name->dos.write_time = h->write_time.close_time;
+ }
+
+ return NT_STATUS_OK;
}
(*name)->has_wildcard = false;
/* we can't get the correct 'original_name', but for the purposes
of this call this is close enough */
- (*name)->original_name = talloc_reference(*name, child->original_name);
+ (*name)->original_name = talloc_strdup(*name, child->original_name);
+ if ((*name)->original_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
(*name)->stream_name = NULL;
(*name)->stream_id = 0;
- status = pvfs_fill_dos_info(pvfs, *name, -1);
+ status = pvfs_fill_dos_info(pvfs, *name, PVFS_RESOLVE_NO_OPENDB, -1);
return status;
}