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"
34 compare two filename components. This is where the name mangling hook will go
36 static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
40 ret = strcasecmp_m(comp, name);
43 char *shortname = pvfs_short_name_component(pvfs, name);
45 ret = strcasecmp_m(comp, shortname);
46 talloc_free(shortname);
54 search for a filename in a case insensitive fashion
56 TODO: add a cache for previously resolved case-insensitive names
57 TODO: add mangled name support
59 static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name)
61 /* break into a series of components */
64 char *p, *partial_name;
67 /* break up the full name info pathname components */
69 p = name->full_name + strlen(pvfs->base_directory) + 1;
77 components = talloc_array(name, char *, num_components);
78 p = name->full_name + strlen(pvfs->base_directory);
81 components[0] = name->full_name;
83 for (i=1;i<num_components;i++) {
87 if (pvfs_is_reserved_name(pvfs, components[i])) {
88 return NT_STATUS_ACCESS_DENIED;
92 partial_name = talloc_strdup(name, components[0]);
94 return NT_STATUS_NO_MEMORY;
97 /* for each component, check if it exists as-is, and if not then
98 do a directory scan */
99 for (i=1;i<num_components;i++) {
103 char *long_component;
105 /* possibly remap from the short name cache */
106 long_component = pvfs_mangled_lookup(pvfs, name, components[i]);
107 if (long_component) {
108 components[i] = long_component;
111 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
113 return NT_STATUS_NO_MEMORY;
116 /* check if this component exists as-is */
117 if (stat(test_name, &name->st) == 0) {
118 if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
119 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
121 talloc_free(partial_name);
122 partial_name = test_name;
123 if (i == num_components - 1) {
129 /* the filesystem might be case insensitive, in which
130 case a search is pointless unless the name is
132 if ((pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) &&
133 !pvfs_is_mangled_component(pvfs, components[i])) {
134 if (i < num_components-1) {
135 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
137 partial_name = test_name;
141 dir = opendir(partial_name);
143 return pvfs_map_errno(pvfs, errno);
146 while ((de = readdir(dir))) {
147 if (component_compare(pvfs, components[i], de->d_name) == 0) {
153 if (i < num_components-1) {
155 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
158 components[i] = talloc_strdup(name, de->d_name);
160 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
161 talloc_free(partial_name);
162 partial_name = test_name;
168 if (stat(partial_name, &name->st) == 0) {
173 talloc_free(name->full_name);
174 name->full_name = partial_name;
177 return pvfs_fill_dos_info(pvfs, name, -1);
184 parse a alternate data stream name
186 static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
189 name->stream_name = talloc_strdup(name, s+1);
190 if (name->stream_name == NULL) {
191 return NT_STATUS_NO_MEMORY;
193 p = strchr_m(name->stream_name, ':');
195 name->stream_id = pvfs_name_hash(name->stream_name,
196 strlen(name->stream_name));
199 if (strcasecmp_m(p, ":$DATA") != 0) {
200 return NT_STATUS_OBJECT_NAME_INVALID;
203 if (strcmp(name->stream_name, "") == 0) {
204 name->stream_name = NULL;
207 name->stream_id = pvfs_name_hash(name->stream_name,
208 strlen(name->stream_name));
216 convert a CIFS pathname to a unix pathname. Note that this does NOT
217 take into account case insensitivity, and in fact does not access
218 the filesystem at all. It is merely a reformatting and charset
221 errors are returned if the filename is illegal given the flags
223 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
224 uint_t flags, struct pvfs_filename *name)
226 char *ret, *p, *p_start;
229 name->original_name = talloc_strdup(name, cifs_name);
230 name->stream_name = NULL;
232 name->has_wildcard = false;
234 while (*cifs_name == '\\') {
238 if (*cifs_name == 0) {
239 name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
240 if (name->full_name == NULL) {
241 return NT_STATUS_NO_MEMORY;
246 ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
248 return NT_STATUS_NO_MEMORY;
251 p = ret + strlen(pvfs->base_directory) + 1;
253 /* now do an in-place conversion of '\' to '/', checking
254 for legal characters */
259 codepoint_t c = next_codepoint(p, &c_size);
262 if (name->has_wildcard) {
263 /* wildcards are only allowed in the last part
265 return NT_STATUS_ILLEGAL_CHARACTER;
267 if (p > p_start && p[1] == 0) {
274 if (!(flags & PVFS_RESOLVE_STREAMS)) {
275 return NT_STATUS_ILLEGAL_CHARACTER;
277 if (name->has_wildcard) {
278 return NT_STATUS_ILLEGAL_CHARACTER;
280 status = parse_stream_name(name, p);
281 if (!NT_STATUS_IS_OK(status)) {
291 if (!(flags & PVFS_RESOLVE_WILDCARD)) {
292 return NT_STATUS_OBJECT_NAME_INVALID;
294 name->has_wildcard = true;
298 return NT_STATUS_ILLEGAL_CHARACTER;
300 /* see if it is definately a .. or
301 . component. If it is then fail here, and
302 let the next layer up try again after
303 pvfs_reduce_name() if it wants to. This is
304 much more efficient on average than always
305 scanning for these separately */
307 (p[2] == 0 || p[2] == '\\') &&
308 (p == p_start || p[-1] == '/')) {
309 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
311 if ((p[1] == 0 || p[1] == '\\') &&
312 (p == p_start || p[-1] == '/')) {
313 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
321 name->full_name = ret;
328 reduce a name that contains .. components or repeated \ separators
329 return NULL if it can't be reduced
331 static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char **fname, uint_t flags)
335 int i, num_components, err_count;
339 s = talloc_strdup(mem_ctx, *fname);
340 if (s == NULL) return NT_STATUS_NO_MEMORY;
342 for (num_components=1, p=s; *p; p += c_size) {
343 c = next_codepoint(p, &c_size);
344 if (c == '\\') num_components++;
347 components = talloc_array(s, char *, num_components+1);
348 if (components == NULL) {
350 return NT_STATUS_NO_MEMORY;
354 for (i=0, p=s; *p; p += c_size) {
355 c = next_codepoint(p, &c_size);
358 components[++i] = p+1;
361 components[i+1] = NULL;
366 '.' components are not allowed, but the rules for what error
367 code to give don't seem to make sense. This is a close
370 for (err_count=i=0;components[i];i++) {
371 if (strcmp(components[i], "") == 0) {
374 if (ISDOT(components[i]) || err_count) {
379 if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
382 return NT_STATUS_OBJECT_NAME_INVALID;
384 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
388 /* remove any null components */
389 for (i=0;components[i];i++) {
390 if (strcmp(components[i], "") == 0) {
391 memmove(&components[i], &components[i+1],
392 sizeof(char *)*(num_components-i));
396 if (ISDOTDOT(components[i])) {
397 if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
398 memmove(&components[i-1], &components[i+1],
399 sizeof(char *)*(num_components-(i+1)));
405 if (components[0] == NULL) {
407 *fname = talloc_strdup(mem_ctx, "\\");
411 for (len=i=0;components[i];i++) {
412 len += strlen(components[i]) + 1;
415 /* rebuild the name */
416 ret = talloc_size(mem_ctx, len+1);
419 return NT_STATUS_NO_MEMORY;
422 for (len=0,i=0;components[i];i++) {
423 size_t len1 = strlen(components[i]);
425 memcpy(ret+len+1, components[i], len1);
439 resolve a name from relative client format to a struct pvfs_filename
440 the memory for the filename is made as a talloc child of 'name'
443 PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
444 PVFS_RESOLVE_STREAMS = stream names are allowed
446 TODO: ../ collapsing, and outside share checking
448 NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
449 const char *cifs_name,
450 uint_t flags, struct pvfs_filename **name)
454 *name = talloc(mem_ctx, struct pvfs_filename);
456 return NT_STATUS_NO_MEMORY;
459 (*name)->exists = false;
460 (*name)->stream_exists = false;
462 if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
463 flags &= ~PVFS_RESOLVE_STREAMS;
466 /* do the basic conversion to a unix formatted path,
467 also checking for allowable characters */
468 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
470 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
471 /* it might contain .. components which need to be reduced */
472 status = pvfs_reduce_name(*name, &cifs_name, flags);
473 if (!NT_STATUS_IS_OK(status)) {
476 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
479 if (!NT_STATUS_IS_OK(status)) {
483 /* if it has a wildcard then no point doing a stat() of the
484 full name. Instead We need check if the directory exists
486 if ((*name)->has_wildcard) {
488 char *dir_name, *saved_name;
489 p = strrchr((*name)->full_name, '/');
491 /* root directory wildcard is OK */
494 dir_name = talloc_strndup(*name, (*name)->full_name, (p-(*name)->full_name));
495 if (stat(dir_name, &(*name)->st) == 0) {
496 talloc_free(dir_name);
499 /* we need to search for a matching name */
500 saved_name = (*name)->full_name;
501 (*name)->full_name = dir_name;
502 status = pvfs_case_search(pvfs, *name);
503 if (!NT_STATUS_IS_OK(status)) {
504 /* the directory doesn't exist */
505 (*name)->full_name = saved_name;
508 /* it does exist, but might need a case change */
509 if (dir_name != (*name)->full_name) {
510 (*name)->full_name = talloc_asprintf(*name, "%s%s",
511 (*name)->full_name, p);
512 NT_STATUS_HAVE_NO_MEMORY((*name)->full_name);
514 (*name)->full_name = saved_name;
515 talloc_free(dir_name);
520 /* if we can stat() the full name now then we are done */
521 if (stat((*name)->full_name, &(*name)->st) == 0) {
522 (*name)->exists = true;
523 return pvfs_fill_dos_info(pvfs, *name, -1);
526 /* search for a matching filename */
527 status = pvfs_case_search(pvfs, *name);
534 do a partial resolve, returning a pvfs_filename structure given a
535 base path and a relative component. It is an error if the file does
536 not exist. No case-insensitive matching is done.
538 this is used in places like directory searching where we need a pvfs_filename
539 to pass to a function, but already know the unix base directory and component
541 NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
542 const char *unix_dir, const char *fname,
543 struct pvfs_filename **name)
547 *name = talloc(mem_ctx, struct pvfs_filename);
549 return NT_STATUS_NO_MEMORY;
552 (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
553 if ((*name)->full_name == NULL) {
554 return NT_STATUS_NO_MEMORY;
557 if (stat((*name)->full_name, &(*name)->st) == -1) {
558 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
561 (*name)->exists = true;
562 (*name)->stream_exists = true;
563 (*name)->has_wildcard = false;
564 (*name)->original_name = talloc_strdup(*name, fname);
565 (*name)->stream_name = NULL;
566 (*name)->stream_id = 0;
568 status = pvfs_fill_dos_info(pvfs, *name, -1);
575 fill in the pvfs_filename info for an open file, given the current
576 info for a (possibly) non-open file. This is used by places that need
577 to update the pvfs_filename stat information, and by pvfs_open()
579 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
580 struct pvfs_filename *name)
582 dev_t device = (dev_t)0;
586 device = name->st.st_dev;
587 inode = name->st.st_ino;
591 if (stat(name->full_name, &name->st) == -1) {
592 return NT_STATUS_INVALID_HANDLE;
595 if (fstat(fd, &name->st) == -1) {
596 return NT_STATUS_INVALID_HANDLE;
601 (device != name->st.st_dev || inode != name->st.st_ino)) {
602 /* the file we are looking at has changed! this could
603 be someone trying to exploit a race
604 condition. Certainly we don't want to continue
605 operating on this file */
606 DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
608 return NT_STATUS_UNEXPECTED_IO_ERROR;
613 return pvfs_fill_dos_info(pvfs, name, fd);
618 resolve the parent of a given name
620 NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
621 const struct pvfs_filename *child,
622 struct pvfs_filename **name)
627 *name = talloc(mem_ctx, struct pvfs_filename);
629 return NT_STATUS_NO_MEMORY;
632 (*name)->full_name = talloc_strdup(*name, child->full_name);
633 if ((*name)->full_name == NULL) {
634 return NT_STATUS_NO_MEMORY;
637 p = strrchr_m((*name)->full_name, '/');
639 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
642 /* this handles the root directory */
643 if (p == (*name)->full_name) {
649 if (stat((*name)->full_name, &(*name)->st) == -1) {
650 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
653 (*name)->exists = true;
654 (*name)->stream_exists = true;
655 (*name)->has_wildcard = false;
656 /* we can't get the correct 'original_name', but for the purposes
657 of this call this is close enough */
658 (*name)->original_name = talloc_reference(*name, child->original_name);
659 (*name)->stream_name = NULL;
660 (*name)->stream_id = 0;
662 status = pvfs_fill_dos_info(pvfs, *name, -1);