2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - filename resolution
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 this is the core code for converting a filename from the format as
24 given by a client to a posix filename, including any case-matching
25 required, and checks for legal characters
30 #include "vfs_posix.h"
31 #include "system/dir.h"
32 #include "param/param.h"
35 compare two filename components. This is where the name mangling hook will go
37 static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
41 ret = strcasecmp_m(comp, name);
44 char *shortname = pvfs_short_name_component(pvfs, name);
46 ret = strcasecmp_m(comp, shortname);
47 talloc_free(shortname);
55 search for a filename in a case insensitive fashion
57 TODO: add a cache for previously resolved case-insensitive names
58 TODO: add mangled name support
60 static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs,
61 struct pvfs_filename *name,
64 /* break into a series of components */
67 char *p, *partial_name;
70 /* break up the full name info pathname components */
72 p = name->full_name + strlen(pvfs->base_directory) + 1;
80 components = talloc_array(name, char *, num_components);
81 p = name->full_name + strlen(pvfs->base_directory);
84 components[0] = name->full_name;
86 for (i=1;i<num_components;i++) {
90 if (pvfs_is_reserved_name(pvfs, components[i])) {
91 return NT_STATUS_ACCESS_DENIED;
95 partial_name = talloc_strdup(name, components[0]);
97 return NT_STATUS_NO_MEMORY;
100 /* for each component, check if it exists as-is, and if not then
101 do a directory scan */
102 for (i=1;i<num_components;i++) {
106 char *long_component;
108 /* possibly remap from the short name cache */
109 long_component = pvfs_mangled_lookup(pvfs, name, components[i]);
110 if (long_component) {
111 components[i] = long_component;
114 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
116 return NT_STATUS_NO_MEMORY;
119 /* check if this component exists as-is */
120 if (stat(test_name, &name->st) == 0) {
121 if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
122 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
124 talloc_free(partial_name);
125 partial_name = test_name;
126 if (i == num_components - 1) {
132 /* the filesystem might be case insensitive, in which
133 case a search is pointless unless the name is
135 if ((pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) &&
136 !pvfs_is_mangled_component(pvfs, components[i])) {
137 if (i < num_components-1) {
138 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
140 partial_name = test_name;
144 dir = opendir(partial_name);
146 return pvfs_map_errno(pvfs, errno);
149 while ((de = readdir(dir))) {
150 if (component_compare(pvfs, components[i], de->d_name) == 0) {
156 if (i < num_components-1) {
158 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
161 components[i] = talloc_strdup(name, de->d_name);
163 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
164 talloc_free(partial_name);
165 partial_name = test_name;
171 if (stat(partial_name, &name->st) == 0) {
176 talloc_free(name->full_name);
177 name->full_name = partial_name;
180 return pvfs_fill_dos_info(pvfs, name, flags, -1);
187 parse a alternate data stream name
189 static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
193 return NT_STATUS_OBJECT_NAME_INVALID;
195 name->stream_name = talloc_strdup(name, s+1);
196 if (name->stream_name == NULL) {
197 return NT_STATUS_NO_MEMORY;
199 p = strchr_m(name->stream_name, ':');
201 name->stream_id = pvfs_name_hash(name->stream_name,
202 strlen(name->stream_name));
206 return NT_STATUS_OBJECT_NAME_INVALID;
208 if (strcasecmp_m(p, ":$DATA") != 0) {
209 return NT_STATUS_INVALID_PARAMETER;
212 if (strcmp(name->stream_name, "") == 0) {
214 * we don't set stream_name to NULL, here
215 * as this would be wrong for directories
217 * pvfs_fill_dos_info() will set it to NULL
218 * if it's not a directory.
222 name->stream_id = pvfs_name_hash(name->stream_name,
223 strlen(name->stream_name));
231 convert a CIFS pathname to a unix pathname. Note that this does NOT
232 take into account case insensitivity, and in fact does not access
233 the filesystem at all. It is merely a reformatting and charset
236 errors are returned if the filename is illegal given the flags
238 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
239 uint_t flags, struct pvfs_filename *name)
241 char *ret, *p, *p_start;
244 name->original_name = talloc_strdup(name, cifs_name);
245 name->stream_name = NULL;
247 name->has_wildcard = false;
249 while (*cifs_name == '\\') {
253 if (*cifs_name == 0) {
254 name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
255 if (name->full_name == NULL) {
256 return NT_STATUS_NO_MEMORY;
261 ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
263 return NT_STATUS_NO_MEMORY;
266 p = ret + strlen(pvfs->base_directory) + 1;
268 /* now do an in-place conversion of '\' to '/', checking
269 for legal characters */
274 codepoint_t c = next_codepoint_convenience(lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), p, &c_size);
277 return NT_STATUS_OBJECT_NAME_INVALID;
282 if (name->has_wildcard) {
283 /* wildcards are only allowed in the last part
285 return NT_STATUS_OBJECT_NAME_INVALID;
287 if (p > p_start && (p[1] == '\\' || p[1] == '\0')) {
288 /* see if it is definately a "\\" or
289 * a trailing "\". If it is then fail here,
290 * and let the next layer up try again after
291 * pvfs_reduce_name() if it wants to. This is
292 * much more efficient on average than always
293 * scanning for these separately
295 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
301 if (!(flags & PVFS_RESOLVE_STREAMS)) {
302 return NT_STATUS_OBJECT_NAME_INVALID;
304 if (name->has_wildcard) {
305 return NT_STATUS_OBJECT_NAME_INVALID;
307 status = parse_stream_name(name, p);
308 if (!NT_STATUS_IS_OK(status)) {
318 if (!(flags & PVFS_RESOLVE_WILDCARD)) {
319 return NT_STATUS_OBJECT_NAME_INVALID;
321 name->has_wildcard = true;
325 return NT_STATUS_OBJECT_NAME_INVALID;
327 /* see if it is definately a .. or
328 . component. If it is then fail here, and
329 let the next layer up try again after
330 pvfs_reduce_name() if it wants to. This is
331 much more efficient on average than always
332 scanning for these separately */
334 (p[2] == 0 || p[2] == '\\') &&
335 (p == p_start || p[-1] == '/')) {
336 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
338 if ((p[1] == 0 || p[1] == '\\') &&
339 (p == p_start || p[-1] == '/')) {
340 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
348 name->full_name = ret;
355 reduce a name that contains .. components or repeated \ separators
356 return NULL if it can't be reduced
358 static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx,
359 struct smb_iconv_convenience *iconv_convenience,
360 const char **fname, uint_t flags)
364 int i, num_components, err_count;
368 s = talloc_strdup(mem_ctx, *fname);
369 if (s == NULL) return NT_STATUS_NO_MEMORY;
371 for (num_components=1, p=s; *p; p += c_size) {
372 c = next_codepoint_convenience(iconv_convenience, p, &c_size);
373 if (c == '\\') num_components++;
376 components = talloc_array(s, char *, num_components+1);
377 if (components == NULL) {
379 return NT_STATUS_NO_MEMORY;
383 for (i=0, p=s; *p; p += c_size) {
384 c = next_codepoint_convenience(iconv_convenience, p, &c_size);
387 components[++i] = p+1;
390 components[i+1] = NULL;
395 '.' components are not allowed, but the rules for what error
396 code to give don't seem to make sense. This is a close
399 for (err_count=i=0;components[i];i++) {
400 if (strcmp(components[i], "") == 0) {
403 if (ISDOT(components[i]) || err_count) {
408 if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
411 return NT_STATUS_OBJECT_NAME_INVALID;
413 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
417 /* remove any null components */
418 for (i=0;components[i];i++) {
419 if (strcmp(components[i], "") == 0) {
420 memmove(&components[i], &components[i+1],
421 sizeof(char *)*(num_components-i));
425 if (ISDOTDOT(components[i])) {
426 if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
427 memmove(&components[i-1], &components[i+1],
428 sizeof(char *)*(num_components-i));
434 if (components[0] == NULL) {
436 *fname = talloc_strdup(mem_ctx, "\\");
440 for (len=i=0;components[i];i++) {
441 len += strlen(components[i]) + 1;
444 /* rebuild the name */
445 ret = talloc_size(mem_ctx, len+1);
448 return NT_STATUS_NO_MEMORY;
451 for (len=0,i=0;components[i];i++) {
452 size_t len1 = strlen(components[i]);
454 memcpy(ret+len+1, components[i], len1);
468 resolve a name from relative client format to a struct pvfs_filename
469 the memory for the filename is made as a talloc child of 'name'
472 PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
473 PVFS_RESOLVE_STREAMS = stream names are allowed
475 TODO: ../ collapsing, and outside share checking
477 NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
478 const char *cifs_name,
479 uint_t flags, struct pvfs_filename **name)
483 *name = talloc(mem_ctx, struct pvfs_filename);
485 return NT_STATUS_NO_MEMORY;
488 (*name)->exists = false;
489 (*name)->stream_exists = false;
491 if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
492 flags &= ~PVFS_RESOLVE_STREAMS;
495 /* do the basic conversion to a unix formatted path,
496 also checking for allowable characters */
497 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
499 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
500 /* it might contain .. components which need to be reduced */
501 status = pvfs_reduce_name(*name, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), &cifs_name, flags);
502 if (!NT_STATUS_IS_OK(status)) {
505 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
508 if (!NT_STATUS_IS_OK(status)) {
512 /* if it has a wildcard then no point doing a stat() of the
513 full name. Instead We need check if the directory exists
515 if ((*name)->has_wildcard) {
517 char *dir_name, *saved_name;
518 p = strrchr((*name)->full_name, '/');
520 /* root directory wildcard is OK */
523 dir_name = talloc_strndup(*name, (*name)->full_name, (p-(*name)->full_name));
524 if (stat(dir_name, &(*name)->st) == 0) {
525 talloc_free(dir_name);
528 /* we need to search for a matching name */
529 saved_name = (*name)->full_name;
530 (*name)->full_name = dir_name;
531 status = pvfs_case_search(pvfs, *name, flags);
532 if (!NT_STATUS_IS_OK(status)) {
533 /* the directory doesn't exist */
534 (*name)->full_name = saved_name;
537 /* it does exist, but might need a case change */
538 if (dir_name != (*name)->full_name) {
539 (*name)->full_name = talloc_asprintf(*name, "%s%s",
540 (*name)->full_name, p);
541 NT_STATUS_HAVE_NO_MEMORY((*name)->full_name);
543 (*name)->full_name = saved_name;
544 talloc_free(dir_name);
549 /* if we can stat() the full name now then we are done */
550 if (stat((*name)->full_name, &(*name)->st) == 0) {
551 (*name)->exists = true;
552 return pvfs_fill_dos_info(pvfs, *name, flags, -1);
555 /* search for a matching filename */
556 status = pvfs_case_search(pvfs, *name, flags);
563 do a partial resolve, returning a pvfs_filename structure given a
564 base path and a relative component. It is an error if the file does
565 not exist. No case-insensitive matching is done.
567 this is used in places like directory searching where we need a pvfs_filename
568 to pass to a function, but already know the unix base directory and component
570 NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
571 const char *unix_dir, const char *fname,
572 uint_t flags, struct pvfs_filename **name)
576 *name = talloc(mem_ctx, struct pvfs_filename);
578 return NT_STATUS_NO_MEMORY;
581 (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
582 if ((*name)->full_name == NULL) {
583 return NT_STATUS_NO_MEMORY;
586 if (stat((*name)->full_name, &(*name)->st) == -1) {
587 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
590 (*name)->exists = true;
591 (*name)->stream_exists = true;
592 (*name)->has_wildcard = false;
593 (*name)->original_name = talloc_strdup(*name, fname);
594 (*name)->stream_name = NULL;
595 (*name)->stream_id = 0;
597 status = pvfs_fill_dos_info(pvfs, *name, flags, -1);
604 fill in the pvfs_filename info for an open file, given the current
605 info for a (possibly) non-open file. This is used by places that need
606 to update the pvfs_filename stat information, and by pvfs_open()
608 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
609 struct pvfs_filename *name, uint_t flags)
611 dev_t device = (dev_t)0;
615 device = name->st.st_dev;
616 inode = name->st.st_ino;
620 if (stat(name->full_name, &name->st) == -1) {
621 return NT_STATUS_INVALID_HANDLE;
624 if (fstat(fd, &name->st) == -1) {
625 return NT_STATUS_INVALID_HANDLE;
630 (device != name->st.st_dev || inode != name->st.st_ino)) {
631 /* the file we are looking at has changed! this could
632 be someone trying to exploit a race
633 condition. Certainly we don't want to continue
634 operating on this file */
635 DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
637 return NT_STATUS_UNEXPECTED_IO_ERROR;
642 return pvfs_fill_dos_info(pvfs, name, flags, fd);
646 fill in the pvfs_filename info for an open file, given the current
647 info for a (possibly) non-open file. This is used by places that need
648 to update the pvfs_filename stat information, and the path
649 after a possible rename on a different handle.
651 NTSTATUS pvfs_resolve_name_handle(struct pvfs_state *pvfs,
652 struct pvfs_file_handle *h)
656 if (h->have_opendb_entry) {
657 struct odb_lock *lck;
658 const char *name = NULL;
660 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
662 DEBUG(0,("%s: failed to lock file '%s' in opendb\n",
663 __FUNCTION__, h->name->full_name));
664 /* we were supposed to do a blocking lock, so something
666 return NT_STATUS_INTERNAL_DB_CORRUPTION;
669 status = odb_get_path(lck, &name);
670 if (NT_STATUS_IS_OK(status)) {
672 * This relies an the fact that
673 * renames of open files are only
674 * allowed by setpathinfo() and setfileinfo()
675 * and there're only renames within the same
676 * directory supported
678 if (strcmp(h->name->full_name, name) != 0) {
679 const char *orig_dir;
680 const char *new_file;
681 const char *new_orig;
684 delim = strrchr(name, '/');
687 return NT_STATUS_INTERNAL_ERROR;
690 new_file = delim + 1;
691 delim = strrchr(h->name->original_name, '\\');
694 orig_dir = h->name->original_name;
695 new_orig = talloc_asprintf(h->name, "%s\\%s",
699 return NT_STATUS_NO_MEMORY;
702 new_orig = talloc_strdup(h->name, new_file);
705 return NT_STATUS_NO_MEMORY;
709 talloc_free(h->name->original_name);
710 talloc_free(h->name->full_name);
711 h->name->full_name = talloc_steal(h->name, name);
712 h->name->original_name = new_orig;
720 * TODO: pass PVFS_RESOLVE_NO_OPENDB and get
721 * the write time from odb_lock() above.
723 status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, 0);
724 NT_STATUS_NOT_OK_RETURN(status);
726 if (!null_nttime(h->write_time.close_time)) {
727 h->name->dos.write_time = h->write_time.close_time;
735 resolve the parent of a given name
737 NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
738 const struct pvfs_filename *child,
739 struct pvfs_filename **name)
744 *name = talloc(mem_ctx, struct pvfs_filename);
746 return NT_STATUS_NO_MEMORY;
749 (*name)->full_name = talloc_strdup(*name, child->full_name);
750 if ((*name)->full_name == NULL) {
751 return NT_STATUS_NO_MEMORY;
754 p = strrchr_m((*name)->full_name, '/');
756 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
759 /* this handles the root directory */
760 if (p == (*name)->full_name) {
766 if (stat((*name)->full_name, &(*name)->st) == -1) {
767 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
770 (*name)->exists = true;
771 (*name)->stream_exists = true;
772 (*name)->has_wildcard = false;
773 /* we can't get the correct 'original_name', but for the purposes
774 of this call this is close enough */
775 (*name)->original_name = talloc_reference(*name, child->original_name);
776 (*name)->stream_name = NULL;
777 (*name)->stream_id = 0;
779 status = pvfs_fill_dos_info(pvfs, *name, PVFS_RESOLVE_NO_OPENDB, -1);