s4-heimdal: Fix typos in comment.
[Samba.git] / source4 / ntvfs / posix / pvfs_resolve.c
blob9613b7078aacc5873ab87111e8a2034a5ce1c061
1 /*
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
29 #include "includes.h"
30 #include "vfs_posix.h"
31 #include "system/dir.h"
32 #include "param/param.h"
34 /**
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)
39 int ret;
41 ret = strcasecmp_m(comp, name);
43 if (ret != 0) {
44 char *shortname = pvfs_short_name_component(pvfs, name);
45 if (shortname) {
46 ret = strcasecmp_m(comp, shortname);
47 talloc_free(shortname);
51 return ret;
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,
62 unsigned int flags)
64 /* break into a series of components */
65 int num_components;
66 char **components;
67 char *p, *partial_name;
68 int i;
70 /* break up the full name info pathname components */
71 num_components=2;
72 p = name->full_name + strlen(pvfs->base_directory) + 1;
74 for (;*p;p++) {
75 if (*p == '/') {
76 num_components++;
80 components = talloc_array(name, char *, num_components);
81 p = name->full_name + strlen(pvfs->base_directory);
82 *p++ = 0;
84 components[0] = name->full_name;
86 for (i=1;i<num_components;i++) {
87 components[i] = p;
88 p = strchr(p, '/');
89 if (p) *p++ = 0;
90 if (pvfs_is_reserved_name(pvfs, components[i])) {
91 return NT_STATUS_ACCESS_DENIED;
95 partial_name = talloc_strdup(name, components[0]);
96 if (!partial_name) {
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++) {
103 char *test_name;
104 DIR *dir;
105 struct dirent *de;
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]);
115 if (!test_name) {
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) {
127 name->exists = true;
129 continue;
132 /* the filesystem might be case insensitive, in which
133 case a search is pointless unless the name is
134 mangled */
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;
141 continue;
144 dir = opendir(partial_name);
145 if (!dir) {
146 return pvfs_map_errno(pvfs, errno);
149 while ((de = readdir(dir))) {
150 if (component_compare(pvfs, components[i], de->d_name) == 0) {
151 break;
155 if (!de) {
156 if (i < num_components-1) {
157 closedir(dir);
158 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
160 } else {
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;
167 closedir(dir);
170 if (!name->exists) {
171 if (stat(partial_name, &name->st) == 0) {
172 name->exists = true;
176 talloc_free(name->full_name);
177 name->full_name = partial_name;
179 if (name->exists) {
180 return pvfs_fill_dos_info(pvfs, name, flags, -1);
183 return NT_STATUS_OK;
187 parse a alternate data stream name
189 static NTSTATUS parse_stream_name(struct smb_iconv_convenience *ic,
190 struct pvfs_filename *name,
191 const char *s)
193 char *p, *stream_name;
194 if (s[1] == '\0') {
195 return NT_STATUS_OBJECT_NAME_INVALID;
197 name->stream_name = stream_name = talloc_strdup(name, s+1);
198 if (name->stream_name == NULL) {
199 return NT_STATUS_NO_MEMORY;
202 p = stream_name;
204 while (*p) {
205 size_t c_size;
206 codepoint_t c = next_codepoint_convenience(ic, p, &c_size);
208 switch (c) {
209 case '/':
210 case '\\':
211 return NT_STATUS_OBJECT_NAME_INVALID;
212 case ':':
213 *p= 0;
214 p++;
215 if (*p == '\0') {
216 return NT_STATUS_OBJECT_NAME_INVALID;
218 if (strcasecmp_m(p, "$DATA") != 0) {
219 if (strchr_m(p, ':')) {
220 return NT_STATUS_OBJECT_NAME_INVALID;
222 return NT_STATUS_INVALID_PARAMETER;
224 c_size = 0;
225 p--;
226 break;
229 p += c_size;
232 if (strcmp(name->stream_name, "") == 0) {
234 * we don't set stream_name to NULL, here
235 * as this would be wrong for directories
237 * pvfs_fill_dos_info() will set it to NULL
238 * if it's not a directory.
240 name->stream_id = 0;
241 } else {
242 name->stream_id = pvfs_name_hash(name->stream_name,
243 strlen(name->stream_name));
246 return NT_STATUS_OK;
251 convert a CIFS pathname to a unix pathname. Note that this does NOT
252 take into account case insensitivity, and in fact does not access
253 the filesystem at all. It is merely a reformatting and charset
254 checking routine.
256 errors are returned if the filename is illegal given the flags
258 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
259 unsigned int flags, struct pvfs_filename *name)
261 char *ret, *p, *p_start;
262 struct smb_iconv_convenience *ic = NULL;
263 NTSTATUS status;
265 name->original_name = talloc_strdup(name, cifs_name);
267 /* remove any :$DATA */
268 p = strrchr(name->original_name, ':');
269 if (p && strcasecmp_m(p, ":$DATA") == 0) {
270 if (p > name->original_name && p[-1] == ':') {
271 p--;
273 *p = 0;
276 name->stream_name = NULL;
277 name->stream_id = 0;
278 name->has_wildcard = false;
280 while (*cifs_name == '\\') {
281 cifs_name++;
284 if (*cifs_name == 0) {
285 name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
286 if (name->full_name == NULL) {
287 return NT_STATUS_NO_MEMORY;
289 return NT_STATUS_OK;
292 ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
293 if (ret == NULL) {
294 return NT_STATUS_NO_MEMORY;
297 p = ret + strlen(pvfs->base_directory) + 1;
299 /* now do an in-place conversion of '\' to '/', checking
300 for legal characters */
301 p_start = p;
303 ic = lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx);
304 while (*p) {
305 size_t c_size;
306 codepoint_t c = next_codepoint_convenience(ic, p, &c_size);
308 if (c <= 0x1F) {
309 return NT_STATUS_OBJECT_NAME_INVALID;
312 switch (c) {
313 case '\\':
314 if (name->has_wildcard) {
315 /* wildcards are only allowed in the last part
316 of a name */
317 return NT_STATUS_OBJECT_NAME_INVALID;
319 if (p > p_start && (p[1] == '\\' || p[1] == '\0')) {
320 /* see if it is definately a "\\" or
321 * a trailing "\". If it is then fail here,
322 * and let the next layer up try again after
323 * pvfs_reduce_name() if it wants to. This is
324 * much more efficient on average than always
325 * scanning for these separately
327 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
328 } else {
329 *p = '/';
331 break;
332 case ':':
333 if (!(flags & PVFS_RESOLVE_STREAMS)) {
334 return NT_STATUS_OBJECT_NAME_INVALID;
336 if (name->has_wildcard) {
337 return NT_STATUS_OBJECT_NAME_INVALID;
339 status = parse_stream_name(ic, name, p);
340 if (!NT_STATUS_IS_OK(status)) {
341 return status;
343 *p-- = 0;
344 break;
345 case '*':
346 case '>':
347 case '<':
348 case '?':
349 case '"':
350 if (!(flags & PVFS_RESOLVE_WILDCARD)) {
351 return NT_STATUS_OBJECT_NAME_INVALID;
353 name->has_wildcard = true;
354 break;
355 case '/':
356 case '|':
357 return NT_STATUS_OBJECT_NAME_INVALID;
358 case '.':
359 /* see if it is definately a .. or
360 . component. If it is then fail here, and
361 let the next layer up try again after
362 pvfs_reduce_name() if it wants to. This is
363 much more efficient on average than always
364 scanning for these separately */
365 if (p[1] == '.' &&
366 (p[2] == 0 || p[2] == '\\') &&
367 (p == p_start || p[-1] == '/')) {
368 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
370 if ((p[1] == 0 || p[1] == '\\') &&
371 (p == p_start || p[-1] == '/')) {
372 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
374 break;
377 p += c_size;
380 name->full_name = ret;
382 return NT_STATUS_OK;
387 reduce a name that contains .. components or repeated \ separators
388 return NULL if it can't be reduced
390 static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx,
391 struct smb_iconv_convenience *iconv_convenience,
392 const char **fname, unsigned int flags)
394 codepoint_t c;
395 size_t c_size, len;
396 int i, num_components, err_count;
397 char **components;
398 char *p, *s, *ret;
400 s = talloc_strdup(mem_ctx, *fname);
401 if (s == NULL) return NT_STATUS_NO_MEMORY;
403 for (num_components=1, p=s; *p; p += c_size) {
404 c = next_codepoint_convenience(iconv_convenience, p, &c_size);
405 if (c == '\\') num_components++;
408 components = talloc_array(s, char *, num_components+1);
409 if (components == NULL) {
410 talloc_free(s);
411 return NT_STATUS_NO_MEMORY;
414 components[0] = s;
415 for (i=0, p=s; *p; p += c_size) {
416 c = next_codepoint_convenience(iconv_convenience, p, &c_size);
417 if (c == '\\') {
418 *p = 0;
419 components[++i] = p+1;
422 components[i+1] = NULL;
425 rather bizarre!
427 '.' components are not allowed, but the rules for what error
428 code to give don't seem to make sense. This is a close
429 approximation.
431 for (err_count=i=0;components[i];i++) {
432 if (strcmp(components[i], "") == 0) {
433 continue;
435 if (ISDOT(components[i]) || err_count) {
436 err_count++;
439 if (err_count) {
440 if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
442 if (err_count==1) {
443 return NT_STATUS_OBJECT_NAME_INVALID;
444 } else {
445 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
449 /* remove any null components */
450 for (i=0;components[i];i++) {
451 if (strcmp(components[i], "") == 0) {
452 memmove(&components[i], &components[i+1],
453 sizeof(char *)*(num_components-i));
454 i--;
455 continue;
457 if (ISDOTDOT(components[i])) {
458 if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
459 memmove(&components[i-1], &components[i+1],
460 sizeof(char *)*(num_components-i));
461 i -= 2;
462 continue;
466 if (components[0] == NULL) {
467 talloc_free(s);
468 *fname = talloc_strdup(mem_ctx, "\\");
469 return NT_STATUS_OK;
472 for (len=i=0;components[i];i++) {
473 len += strlen(components[i]) + 1;
476 /* rebuild the name */
477 ret = talloc_array(mem_ctx, char, len+1);
478 if (ret == NULL) {
479 talloc_free(s);
480 return NT_STATUS_NO_MEMORY;
483 for (len=0,i=0;components[i];i++) {
484 size_t len1 = strlen(components[i]);
485 ret[len] = '\\';
486 memcpy(ret+len+1, components[i], len1);
487 len += len1 + 1;
489 ret[len] = 0;
491 talloc_set_name_const(ret, ret);
493 talloc_free(s);
495 *fname = ret;
497 return NT_STATUS_OK;
502 resolve a name from relative client format to a struct pvfs_filename
503 the memory for the filename is made as a talloc child of 'name'
505 flags include:
506 PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
507 PVFS_RESOLVE_STREAMS = stream names are allowed
509 TODO: ../ collapsing, and outside share checking
511 NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs,
512 struct ntvfs_request *req,
513 const char *cifs_name,
514 unsigned int flags, struct pvfs_filename **name)
516 NTSTATUS status;
518 *name = talloc(req, struct pvfs_filename);
519 if (*name == NULL) {
520 return NT_STATUS_NO_MEMORY;
523 (*name)->exists = false;
524 (*name)->stream_exists = false;
526 if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
527 flags &= ~PVFS_RESOLVE_STREAMS;
530 /* SMB2 doesn't allow a leading slash */
531 if (req->ctx->protocol == PROTOCOL_SMB2 &&
532 *cifs_name == '\\') {
533 return NT_STATUS_INVALID_PARAMETER;
536 /* do the basic conversion to a unix formatted path,
537 also checking for allowable characters */
538 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
540 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
541 /* it might contain .. components which need to be reduced */
542 status = pvfs_reduce_name(*name, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), &cifs_name, flags);
543 if (!NT_STATUS_IS_OK(status)) {
544 return status;
546 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
549 if (!NT_STATUS_IS_OK(status)) {
550 return status;
553 /* if it has a wildcard then no point doing a stat() of the
554 full name. Instead We need check if the directory exists
556 if ((*name)->has_wildcard) {
557 const char *p;
558 char *dir_name, *saved_name;
559 p = strrchr((*name)->full_name, '/');
560 if (p == NULL) {
561 /* root directory wildcard is OK */
562 return NT_STATUS_OK;
564 dir_name = talloc_strndup(*name, (*name)->full_name, (p-(*name)->full_name));
565 if (stat(dir_name, &(*name)->st) == 0) {
566 talloc_free(dir_name);
567 return NT_STATUS_OK;
569 /* we need to search for a matching name */
570 saved_name = (*name)->full_name;
571 (*name)->full_name = dir_name;
572 status = pvfs_case_search(pvfs, *name, flags);
573 if (!NT_STATUS_IS_OK(status)) {
574 /* the directory doesn't exist */
575 (*name)->full_name = saved_name;
576 return status;
578 /* it does exist, but might need a case change */
579 if (dir_name != (*name)->full_name) {
580 (*name)->full_name = talloc_asprintf(*name, "%s%s",
581 (*name)->full_name, p);
582 NT_STATUS_HAVE_NO_MEMORY((*name)->full_name);
583 } else {
584 (*name)->full_name = saved_name;
585 talloc_free(dir_name);
587 return NT_STATUS_OK;
590 /* if we can stat() the full name now then we are done */
591 if (stat((*name)->full_name, &(*name)->st) == 0) {
592 (*name)->exists = true;
593 return pvfs_fill_dos_info(pvfs, *name, flags, -1);
596 /* search for a matching filename */
597 status = pvfs_case_search(pvfs, *name, flags);
599 return status;
604 do a partial resolve, returning a pvfs_filename structure given a
605 base path and a relative component. It is an error if the file does
606 not exist. No case-insensitive matching is done.
608 this is used in places like directory searching where we need a pvfs_filename
609 to pass to a function, but already know the unix base directory and component
611 NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
612 const char *unix_dir, const char *fname,
613 unsigned int flags, struct pvfs_filename **name)
615 NTSTATUS status;
617 *name = talloc(mem_ctx, struct pvfs_filename);
618 if (*name == NULL) {
619 return NT_STATUS_NO_MEMORY;
622 (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
623 if ((*name)->full_name == NULL) {
624 return NT_STATUS_NO_MEMORY;
627 if (stat((*name)->full_name, &(*name)->st) == -1) {
628 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
631 (*name)->exists = true;
632 (*name)->stream_exists = true;
633 (*name)->has_wildcard = false;
634 (*name)->original_name = talloc_strdup(*name, fname);
635 (*name)->stream_name = NULL;
636 (*name)->stream_id = 0;
638 status = pvfs_fill_dos_info(pvfs, *name, flags, -1);
640 return status;
645 fill in the pvfs_filename info for an open file, given the current
646 info for a (possibly) non-open file. This is used by places that need
647 to update the pvfs_filename stat information, and by pvfs_open()
649 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
650 struct pvfs_filename *name, unsigned int flags)
652 dev_t device = (dev_t)0;
653 ino_t inode = 0;
655 if (name->exists) {
656 device = name->st.st_dev;
657 inode = name->st.st_ino;
660 if (fd == -1) {
661 if (stat(name->full_name, &name->st) == -1) {
662 return NT_STATUS_INVALID_HANDLE;
664 } else {
665 if (fstat(fd, &name->st) == -1) {
666 return NT_STATUS_INVALID_HANDLE;
670 if (name->exists &&
671 (device != name->st.st_dev || inode != name->st.st_ino)) {
672 /* the file we are looking at has changed! this could
673 be someone trying to exploit a race
674 condition. Certainly we don't want to continue
675 operating on this file */
676 DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
677 name->full_name));
678 return NT_STATUS_UNEXPECTED_IO_ERROR;
681 name->exists = true;
683 return pvfs_fill_dos_info(pvfs, name, flags, fd);
687 fill in the pvfs_filename info for an open file, given the current
688 info for a (possibly) non-open file. This is used by places that need
689 to update the pvfs_filename stat information, and the path
690 after a possible rename on a different handle.
692 NTSTATUS pvfs_resolve_name_handle(struct pvfs_state *pvfs,
693 struct pvfs_file_handle *h)
695 NTSTATUS status;
697 if (h->have_opendb_entry) {
698 struct odb_lock *lck;
699 char *name = NULL;
701 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
702 if (lck == NULL) {
703 DEBUG(0,("%s: failed to lock file '%s' in opendb\n",
704 __FUNCTION__, h->name->full_name));
705 /* we were supposed to do a blocking lock, so something
706 is badly wrong! */
707 return NT_STATUS_INTERNAL_DB_CORRUPTION;
710 status = odb_get_path(lck, (const char **) &name);
711 if (NT_STATUS_IS_OK(status)) {
713 * This relies an the fact that
714 * renames of open files are only
715 * allowed by setpathinfo() and setfileinfo()
716 * and there're only renames within the same
717 * directory supported
719 if (strcmp(h->name->full_name, name) != 0) {
720 const char *orig_dir;
721 const char *new_file;
722 char *new_orig;
723 char *delim;
725 delim = strrchr(name, '/');
726 if (!delim) {
727 talloc_free(lck);
728 return NT_STATUS_INTERNAL_ERROR;
731 new_file = delim + 1;
732 delim = strrchr(h->name->original_name, '\\');
733 if (delim) {
734 delim[0] = '\0';
735 orig_dir = h->name->original_name;
736 new_orig = talloc_asprintf(h->name, "%s\\%s",
737 orig_dir, new_file);
738 if (!new_orig) {
739 talloc_free(lck);
740 return NT_STATUS_NO_MEMORY;
742 } else {
743 new_orig = talloc_strdup(h->name, new_file);
744 if (!new_orig) {
745 talloc_free(lck);
746 return NT_STATUS_NO_MEMORY;
750 talloc_free(h->name->original_name);
751 talloc_free(h->name->full_name);
752 h->name->full_name = talloc_steal(h->name, name);
753 h->name->original_name = new_orig;
757 talloc_free(lck);
761 * TODO: pass PVFS_RESOLVE_NO_OPENDB and get
762 * the write time from odb_lock() above.
764 status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, 0);
765 NT_STATUS_NOT_OK_RETURN(status);
767 if (!null_nttime(h->write_time.close_time)) {
768 h->name->dos.write_time = h->write_time.close_time;
771 return NT_STATUS_OK;
776 resolve the parent of a given name
778 NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
779 const struct pvfs_filename *child,
780 struct pvfs_filename **name)
782 NTSTATUS status;
783 char *p;
785 *name = talloc(mem_ctx, struct pvfs_filename);
786 if (*name == NULL) {
787 return NT_STATUS_NO_MEMORY;
790 (*name)->full_name = talloc_strdup(*name, child->full_name);
791 if ((*name)->full_name == NULL) {
792 return NT_STATUS_NO_MEMORY;
795 p = strrchr_m((*name)->full_name, '/');
796 if (p == NULL) {
797 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
800 /* this handles the root directory */
801 if (p == (*name)->full_name) {
802 p[1] = 0;
803 } else {
804 p[0] = 0;
807 if (stat((*name)->full_name, &(*name)->st) == -1) {
808 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
811 (*name)->exists = true;
812 (*name)->stream_exists = true;
813 (*name)->has_wildcard = false;
814 /* we can't get the correct 'original_name', but for the purposes
815 of this call this is close enough */
816 (*name)->original_name = talloc_strdup(*name, child->original_name);
817 if ((*name)->original_name == NULL) {
818 return NT_STATUS_NO_MEMORY;
820 (*name)->stream_name = NULL;
821 (*name)->stream_id = 0;
823 status = pvfs_fill_dos_info(pvfs, *name, PVFS_RESOLVE_NO_OPENDB, -1);
825 return status;