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
,
192 char *p
, *stream_name
;
194 return NT_STATUS_OBJECT_NAME_INVALID
;
196 name
->stream_name
= stream_name
= talloc_strdup(name
, s
+1);
197 if (name
->stream_name
== NULL
) {
198 return NT_STATUS_NO_MEMORY
;
205 codepoint_t c
= next_codepoint(p
, &c_size
);
210 return NT_STATUS_OBJECT_NAME_INVALID
;
215 return NT_STATUS_OBJECT_NAME_INVALID
;
217 if (strcasecmp_m(p
, "$DATA") != 0) {
218 if (strchr_m(p
, ':')) {
219 return NT_STATUS_OBJECT_NAME_INVALID
;
221 return NT_STATUS_INVALID_PARAMETER
;
231 if (strcmp(name
->stream_name
, "") == 0) {
233 * we don't set stream_name to NULL, here
234 * as this would be wrong for directories
236 * pvfs_fill_dos_info() will set it to NULL
237 * if it's not a directory.
241 name
->stream_id
= pvfs_name_hash(name
->stream_name
,
242 strlen(name
->stream_name
));
250 convert a CIFS pathname to a unix pathname. Note that this does NOT
251 take into account case insensitivity, and in fact does not access
252 the filesystem at all. It is merely a reformatting and charset
255 errors are returned if the filename is illegal given the flags
257 static NTSTATUS
pvfs_unix_path(struct pvfs_state
*pvfs
, const char *cifs_name
,
258 unsigned int flags
, struct pvfs_filename
*name
)
260 char *ret
, *p
, *p_start
;
263 name
->original_name
= talloc_strdup(name
, cifs_name
);
265 /* remove any :$DATA */
266 p
= strrchr(name
->original_name
, ':');
267 if (p
&& strcasecmp_m(p
, ":$DATA") == 0) {
268 if (p
> name
->original_name
&& p
[-1] == ':') {
274 name
->stream_name
= NULL
;
276 name
->has_wildcard
= false;
278 while (*cifs_name
== '\\') {
282 if (*cifs_name
== 0) {
283 name
->full_name
= talloc_asprintf(name
, "%s/.", pvfs
->base_directory
);
284 if (name
->full_name
== NULL
) {
285 return NT_STATUS_NO_MEMORY
;
290 ret
= talloc_asprintf(name
, "%s/%s", pvfs
->base_directory
, cifs_name
);
292 return NT_STATUS_NO_MEMORY
;
295 p
= ret
+ strlen(pvfs
->base_directory
) + 1;
297 /* now do an in-place conversion of '\' to '/', checking
298 for legal characters */
303 codepoint_t c
= next_codepoint(p
, &c_size
);
306 return NT_STATUS_OBJECT_NAME_INVALID
;
311 if (name
->has_wildcard
) {
312 /* wildcards are only allowed in the last part
314 return NT_STATUS_OBJECT_NAME_INVALID
;
316 if (p
> p_start
&& (p
[1] == '\\' || p
[1] == '\0')) {
317 /* see if it is definately a "\\" or
318 * a trailing "\". If it is then fail here,
319 * and let the next layer up try again after
320 * pvfs_reduce_name() if it wants to. This is
321 * much more efficient on average than always
322 * scanning for these separately
324 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD
;
330 if (!(flags
& PVFS_RESOLVE_STREAMS
)) {
331 return NT_STATUS_OBJECT_NAME_INVALID
;
333 if (name
->has_wildcard
) {
334 return NT_STATUS_OBJECT_NAME_INVALID
;
336 status
= parse_stream_name(name
, p
);
337 if (!NT_STATUS_IS_OK(status
)) {
347 if (!(flags
& PVFS_RESOLVE_WILDCARD
)) {
348 return NT_STATUS_OBJECT_NAME_INVALID
;
350 name
->has_wildcard
= true;
354 return NT_STATUS_OBJECT_NAME_INVALID
;
356 /* see if it is definately a .. or
357 . component. If it is then fail here, and
358 let the next layer up try again after
359 pvfs_reduce_name() if it wants to. This is
360 much more efficient on average than always
361 scanning for these separately */
363 (p
[2] == 0 || p
[2] == '\\') &&
364 (p
== p_start
|| p
[-1] == '/')) {
365 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD
;
367 if ((p
[1] == 0 || p
[1] == '\\') &&
368 (p
== p_start
|| p
[-1] == '/')) {
369 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD
;
377 name
->full_name
= ret
;
384 reduce a name that contains .. components or repeated \ separators
385 return NULL if it can't be reduced
387 static NTSTATUS
pvfs_reduce_name(TALLOC_CTX
*mem_ctx
,
388 const char **fname
, unsigned int flags
)
392 int i
, num_components
, err_count
;
396 s
= talloc_strdup(mem_ctx
, *fname
);
397 if (s
== NULL
) return NT_STATUS_NO_MEMORY
;
399 for (num_components
=1, p
=s
; *p
; p
+= c_size
) {
400 c
= next_codepoint(p
, &c_size
);
401 if (c
== '\\') num_components
++;
404 components
= talloc_array(s
, char *, num_components
+1);
405 if (components
== NULL
) {
407 return NT_STATUS_NO_MEMORY
;
411 for (i
=0, p
=s
; *p
; p
+= c_size
) {
412 c
= next_codepoint(p
, &c_size
);
415 components
[++i
] = p
+1;
418 components
[i
+1] = NULL
;
423 '.' components are not allowed, but the rules for what error
424 code to give don't seem to make sense. This is a close
427 for (err_count
=i
=0;components
[i
];i
++) {
428 if (strcmp(components
[i
], "") == 0) {
431 if (ISDOT(components
[i
]) || err_count
) {
436 if (flags
& PVFS_RESOLVE_WILDCARD
) err_count
--;
439 return NT_STATUS_OBJECT_NAME_INVALID
;
441 return NT_STATUS_OBJECT_PATH_NOT_FOUND
;
445 /* remove any null components */
446 for (i
=0;components
[i
];i
++) {
447 if (strcmp(components
[i
], "") == 0) {
448 memmove(&components
[i
], &components
[i
+1],
449 sizeof(char *)*(num_components
-i
));
453 if (ISDOTDOT(components
[i
])) {
454 if (i
< 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD
;
455 memmove(&components
[i
-1], &components
[i
+1],
456 sizeof(char *)*(num_components
-i
));
462 if (components
[0] == NULL
) {
464 *fname
= talloc_strdup(mem_ctx
, "\\");
468 for (len
=i
=0;components
[i
];i
++) {
469 len
+= strlen(components
[i
]) + 1;
472 /* rebuild the name */
473 ret
= talloc_array(mem_ctx
, char, len
+1);
476 return NT_STATUS_NO_MEMORY
;
479 for (len
=0,i
=0;components
[i
];i
++) {
480 size_t len1
= strlen(components
[i
]);
482 memcpy(ret
+len
+1, components
[i
], len1
);
487 talloc_set_name_const(ret
, ret
);
498 resolve a name from relative client format to a struct pvfs_filename
499 the memory for the filename is made as a talloc child of 'name'
502 PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
503 PVFS_RESOLVE_STREAMS = stream names are allowed
505 TODO: ../ collapsing, and outside share checking
507 NTSTATUS
pvfs_resolve_name(struct pvfs_state
*pvfs
,
508 struct ntvfs_request
*req
,
509 const char *cifs_name
,
510 unsigned int flags
, struct pvfs_filename
**name
)
514 *name
= talloc(req
, struct pvfs_filename
);
516 return NT_STATUS_NO_MEMORY
;
519 (*name
)->exists
= false;
520 (*name
)->stream_exists
= false;
521 (*name
)->allow_override
= false;
523 if (!(pvfs
->fs_attribs
& FS_ATTR_NAMED_STREAMS
)) {
524 flags
&= ~PVFS_RESOLVE_STREAMS
;
527 /* SMB2 doesn't allow a leading slash */
528 if (req
->ctx
->protocol
>= PROTOCOL_SMB2_02
&&
529 *cifs_name
== '\\') {
530 return NT_STATUS_INVALID_PARAMETER
;
533 /* do the basic conversion to a unix formatted path,
534 also checking for allowable characters */
535 status
= pvfs_unix_path(pvfs
, cifs_name
, flags
, *name
);
537 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_PATH_SYNTAX_BAD
)) {
538 /* it might contain .. components which need to be reduced */
539 status
= pvfs_reduce_name(*name
, &cifs_name
, flags
);
540 if (!NT_STATUS_IS_OK(status
)) {
543 status
= pvfs_unix_path(pvfs
, cifs_name
, flags
, *name
);
546 if (!NT_STATUS_IS_OK(status
)) {
550 /* if it has a wildcard then no point doing a stat() of the
551 full name. Instead We need check if the directory exists
553 if ((*name
)->has_wildcard
) {
555 char *dir_name
, *saved_name
;
556 p
= strrchr((*name
)->full_name
, '/');
558 /* root directory wildcard is OK */
561 dir_name
= talloc_strndup(*name
, (*name
)->full_name
, (p
-(*name
)->full_name
));
562 if (stat(dir_name
, &(*name
)->st
) == 0) {
563 talloc_free(dir_name
);
566 /* we need to search for a matching name */
567 saved_name
= (*name
)->full_name
;
568 (*name
)->full_name
= dir_name
;
569 status
= pvfs_case_search(pvfs
, *name
, flags
);
570 if (!NT_STATUS_IS_OK(status
)) {
571 /* the directory doesn't exist */
572 (*name
)->full_name
= saved_name
;
575 /* it does exist, but might need a case change */
576 if (dir_name
!= (*name
)->full_name
) {
577 (*name
)->full_name
= talloc_asprintf(*name
, "%s%s",
578 (*name
)->full_name
, p
);
579 NT_STATUS_HAVE_NO_MEMORY((*name
)->full_name
);
581 (*name
)->full_name
= saved_name
;
582 talloc_free(dir_name
);
587 /* if we can stat() the full name now then we are done */
588 if (stat((*name
)->full_name
, &(*name
)->st
) == 0) {
589 (*name
)->exists
= true;
590 return pvfs_fill_dos_info(pvfs
, *name
, flags
, -1);
593 /* search for a matching filename */
594 status
= pvfs_case_search(pvfs
, *name
, flags
);
601 do a partial resolve, returning a pvfs_filename structure given a
602 base path and a relative component. It is an error if the file does
603 not exist. No case-insensitive matching is done.
605 this is used in places like directory searching where we need a pvfs_filename
606 to pass to a function, but already know the unix base directory and component
608 NTSTATUS
pvfs_resolve_partial(struct pvfs_state
*pvfs
, TALLOC_CTX
*mem_ctx
,
609 const char *unix_dir
, const char *fname
,
610 unsigned int flags
, struct pvfs_filename
**name
)
614 *name
= talloc(mem_ctx
, struct pvfs_filename
);
616 return NT_STATUS_NO_MEMORY
;
619 (*name
)->full_name
= talloc_asprintf(*name
, "%s/%s", unix_dir
, fname
);
620 if ((*name
)->full_name
== NULL
) {
621 return NT_STATUS_NO_MEMORY
;
624 if (stat((*name
)->full_name
, &(*name
)->st
) == -1) {
625 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
628 (*name
)->exists
= true;
629 (*name
)->stream_exists
= true;
630 (*name
)->has_wildcard
= false;
631 (*name
)->original_name
= talloc_strdup(*name
, fname
);
632 (*name
)->stream_name
= NULL
;
633 (*name
)->stream_id
= 0;
634 (*name
)->allow_override
= false;
636 status
= pvfs_fill_dos_info(pvfs
, *name
, flags
, -1);
643 fill in the pvfs_filename info for an open file, given the current
644 info for a (possibly) non-open file. This is used by places that need
645 to update the pvfs_filename stat information, and by pvfs_open()
647 NTSTATUS
pvfs_resolve_name_fd(struct pvfs_state
*pvfs
, int fd
,
648 struct pvfs_filename
*name
, unsigned int flags
)
650 dev_t device
= (dev_t
)0;
654 device
= name
->st
.st_dev
;
655 inode
= name
->st
.st_ino
;
659 if (stat(name
->full_name
, &name
->st
) == -1) {
660 return NT_STATUS_INVALID_HANDLE
;
663 if (fstat(fd
, &name
->st
) == -1) {
664 return NT_STATUS_INVALID_HANDLE
;
669 (device
!= name
->st
.st_dev
|| inode
!= name
->st
.st_ino
)) {
670 /* the file we are looking at has changed! this could
671 be someone trying to exploit a race
672 condition. Certainly we don't want to continue
673 operating on this file */
674 DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
676 return NT_STATUS_UNEXPECTED_IO_ERROR
;
681 return pvfs_fill_dos_info(pvfs
, name
, flags
, fd
);
685 fill in the pvfs_filename info for an open file, given the current
686 info for a (possibly) non-open file. This is used by places that need
687 to update the pvfs_filename stat information, and the path
688 after a possible rename on a different handle.
690 NTSTATUS
pvfs_resolve_name_handle(struct pvfs_state
*pvfs
,
691 struct pvfs_file_handle
*h
)
695 if (h
->have_opendb_entry
) {
696 struct odb_lock
*lck
;
699 lck
= odb_lock(h
, h
->pvfs
->odb_context
, &h
->odb_locking_key
);
701 DEBUG(0,("%s: failed to lock file '%s' in opendb\n",
702 __FUNCTION__
, h
->name
->full_name
));
703 /* we were supposed to do a blocking lock, so something
705 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
708 status
= odb_get_path(lck
, (const char **) &name
);
709 if (NT_STATUS_IS_OK(status
)) {
711 * This relies an the fact that
712 * renames of open files are only
713 * allowed by setpathinfo() and setfileinfo()
714 * and there're only renames within the same
715 * directory supported
717 if (strcmp(h
->name
->full_name
, name
) != 0) {
718 const char *orig_dir
;
719 const char *new_file
;
723 delim
= strrchr(name
, '/');
726 return NT_STATUS_INTERNAL_ERROR
;
729 new_file
= delim
+ 1;
730 delim
= strrchr(h
->name
->original_name
, '\\');
733 orig_dir
= h
->name
->original_name
;
734 new_orig
= talloc_asprintf(h
->name
, "%s\\%s",
738 return NT_STATUS_NO_MEMORY
;
741 new_orig
= talloc_strdup(h
->name
, new_file
);
744 return NT_STATUS_NO_MEMORY
;
748 talloc_free(h
->name
->original_name
);
749 talloc_free(h
->name
->full_name
);
750 h
->name
->full_name
= talloc_steal(h
->name
, name
);
751 h
->name
->original_name
= new_orig
;
759 * TODO: pass PVFS_RESOLVE_NO_OPENDB and get
760 * the write time from odb_lock() above.
762 status
= pvfs_resolve_name_fd(pvfs
, h
->fd
, h
->name
, 0);
763 NT_STATUS_NOT_OK_RETURN(status
);
765 if (!null_nttime(h
->write_time
.close_time
)) {
766 h
->name
->dos
.write_time
= h
->write_time
.close_time
;
774 resolve the parent of a given name
776 NTSTATUS
pvfs_resolve_parent(struct pvfs_state
*pvfs
, TALLOC_CTX
*mem_ctx
,
777 const struct pvfs_filename
*child
,
778 struct pvfs_filename
**name
)
783 *name
= talloc(mem_ctx
, struct pvfs_filename
);
785 return NT_STATUS_NO_MEMORY
;
788 (*name
)->full_name
= talloc_strdup(*name
, child
->full_name
);
789 if ((*name
)->full_name
== NULL
) {
790 return NT_STATUS_NO_MEMORY
;
793 p
= strrchr_m((*name
)->full_name
, '/');
795 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD
;
798 /* this handles the root directory */
799 if (p
== (*name
)->full_name
) {
805 if (stat((*name
)->full_name
, &(*name
)->st
) == -1) {
806 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
809 (*name
)->exists
= true;
810 (*name
)->stream_exists
= true;
811 (*name
)->has_wildcard
= false;
812 /* we can't get the correct 'original_name', but for the purposes
813 of this call this is close enough */
814 (*name
)->original_name
= talloc_strdup(*name
, child
->original_name
);
815 if ((*name
)->original_name
== NULL
) {
816 return NT_STATUS_NO_MEMORY
;
818 (*name
)->stream_name
= NULL
;
819 (*name
)->stream_id
= 0;
820 (*name
)->allow_override
= false;
822 status
= pvfs_fill_dos_info(pvfs
, *name
, PVFS_RESOLVE_NO_OPENDB
, -1);