Merge commit 'release-4-0-0alpha1' into v4-0-test
[jelmer/samba4-debian.git] / source / ntvfs / posix / pvfs_resolve.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - filename resolution
5
6    Copyright (C) Andrew Tridgell 2004
7
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.
12    
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.
17    
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/>.
20 */
21
22 /*
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
26 */
27
28
29 #include "includes.h"
30 #include "vfs_posix.h"
31 #include "system/dir.h"
32
33 /*
34   compare two filename components. This is where the name mangling hook will go
35 */
36 static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
37 {
38         int ret;
39
40         ret = strcasecmp_m(comp, name);
41
42         if (ret != 0) {
43                 char *shortname = pvfs_short_name_component(pvfs, name);
44                 if (shortname) {
45                         ret = strcasecmp_m(comp, shortname);
46                         talloc_free(shortname);
47                 }
48         }
49
50         return ret;
51 }
52
53 /*
54   search for a filename in a case insensitive fashion
55
56   TODO: add a cache for previously resolved case-insensitive names
57   TODO: add mangled name support
58 */
59 static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name)
60 {
61         /* break into a series of components */
62         int num_components;
63         char **components;
64         char *p, *partial_name;
65         int i;
66
67         /* break up the full name info pathname components */
68         num_components=2;
69         p = name->full_name + strlen(pvfs->base_directory) + 1;
70
71         for (;*p;p++) {
72                 if (*p == '/') {
73                         num_components++;
74                 }
75         }
76
77         components = talloc_array(name, char *, num_components);
78         p = name->full_name + strlen(pvfs->base_directory);
79         *p++ = 0;
80
81         components[0] = name->full_name;
82
83         for (i=1;i<num_components;i++) {
84                 components[i] = p;
85                 p = strchr(p, '/');
86                 if (p) *p++ = 0;
87                 if (pvfs_is_reserved_name(pvfs, components[i])) {
88                         return NT_STATUS_ACCESS_DENIED;
89                 }
90         }
91
92         partial_name = talloc_strdup(name, components[0]);
93         if (!partial_name) {
94                 return NT_STATUS_NO_MEMORY;
95         }
96
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++) {
100                 char *test_name;
101                 DIR *dir;
102                 struct dirent *de;
103                 char *long_component;
104
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;
109                 }
110
111                 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
112                 if (!test_name) {
113                         return NT_STATUS_NO_MEMORY;
114                 }
115
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;
120                         }
121                         talloc_free(partial_name);
122                         partial_name = test_name;
123                         if (i == num_components - 1) {
124                                 name->exists = true;
125                         }
126                         continue;
127                 }
128
129                 /* the filesystem might be case insensitive, in which
130                    case a search is pointless unless the name is
131                    mangled */
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;
136                         }
137                         partial_name = test_name;
138                         continue;
139                 }
140                 
141                 dir = opendir(partial_name);
142                 if (!dir) {
143                         return pvfs_map_errno(pvfs, errno);
144                 }
145
146                 while ((de = readdir(dir))) {
147                         if (component_compare(pvfs, components[i], de->d_name) == 0) {
148                                 break;
149                         }
150                 }
151
152                 if (!de) {
153                         if (i < num_components-1) {
154                                 closedir(dir);
155                                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
156                         }
157                 } else {
158                         components[i] = talloc_strdup(name, de->d_name);
159                 }
160                 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
161                 talloc_free(partial_name);
162                 partial_name = test_name;
163
164                 closedir(dir);
165         }
166
167         if (!name->exists) {
168                 if (stat(partial_name, &name->st) == 0) {
169                         name->exists = true;
170                 }
171         }
172
173         talloc_free(name->full_name);
174         name->full_name = partial_name;
175
176         if (name->exists) {
177                 return pvfs_fill_dos_info(pvfs, name, -1);
178         }
179
180         return NT_STATUS_OK;
181 }
182
183 /*
184   parse a alternate data stream name
185 */
186 static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
187 {
188         char *p;
189         name->stream_name = talloc_strdup(name, s+1);
190         if (name->stream_name == NULL) {
191                 return NT_STATUS_NO_MEMORY;
192         }
193         p = strchr_m(name->stream_name, ':');
194         if (p == NULL) {
195                 name->stream_id = pvfs_name_hash(name->stream_name, 
196                                                  strlen(name->stream_name));
197                 return NT_STATUS_OK;
198         }
199         if (strcasecmp_m(p, ":$DATA") != 0) {
200                 return NT_STATUS_OBJECT_NAME_INVALID;
201         }
202         *p = 0;
203         if (strcmp(name->stream_name, "") == 0) {
204                 name->stream_name = NULL;
205                 name->stream_id = 0;
206         } else {
207                 name->stream_id = pvfs_name_hash(name->stream_name, 
208                                                  strlen(name->stream_name));
209         }
210                                                  
211         return NT_STATUS_OK;    
212 }
213
214
215 /*
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
219   checking routine.
220
221   errors are returned if the filename is illegal given the flags
222 */
223 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
224                                uint_t flags, struct pvfs_filename *name)
225 {
226         char *ret, *p, *p_start;
227         NTSTATUS status;
228
229         name->original_name = talloc_strdup(name, cifs_name);
230         name->stream_name = NULL;
231         name->stream_id = 0;
232         name->has_wildcard = false;
233
234         while (*cifs_name == '\\') {
235                 cifs_name++;
236         }
237
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;
242                 }
243                 return NT_STATUS_OK;
244         }
245
246         ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
247         if (ret == NULL) {
248                 return NT_STATUS_NO_MEMORY;
249         }
250
251         p = ret + strlen(pvfs->base_directory) + 1;
252
253         /* now do an in-place conversion of '\' to '/', checking
254            for legal characters */
255         p_start = p;
256
257         while (*p) {
258                 size_t c_size;
259                 codepoint_t c = next_codepoint(p, &c_size);
260                 switch (c) {
261                 case '\\':
262                         if (name->has_wildcard) {
263                                 /* wildcards are only allowed in the last part
264                                    of a name */
265                                 return NT_STATUS_ILLEGAL_CHARACTER;
266                         }
267                         if (p > p_start && p[1] == 0) {
268                                 *p = 0;
269                         } else {
270                                 *p = '/';
271                         }
272                         break;
273                 case ':':
274                         if (!(flags & PVFS_RESOLVE_STREAMS)) {
275                                 return NT_STATUS_ILLEGAL_CHARACTER;
276                         }
277                         if (name->has_wildcard) {
278                                 return NT_STATUS_ILLEGAL_CHARACTER;
279                         }
280                         status = parse_stream_name(name, p);
281                         if (!NT_STATUS_IS_OK(status)) {
282                                 return status;
283                         }
284                         *p-- = 0;
285                         break;
286                 case '*':
287                 case '>':
288                 case '<':
289                 case '?':
290                 case '"':
291                         if (!(flags & PVFS_RESOLVE_WILDCARD)) {
292                                 return NT_STATUS_OBJECT_NAME_INVALID;
293                         }
294                         name->has_wildcard = true;
295                         break;
296                 case '/':
297                 case '|':
298                         return NT_STATUS_ILLEGAL_CHARACTER;
299                 case '.':
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 */
306                         if (p[1] == '.' && 
307                             (p[2] == 0 || p[2] == '\\') &&
308                             (p == p_start || p[-1] == '/')) {
309                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
310                         }
311                         if ((p[1] == 0 || p[1] == '\\') &&
312                             (p == p_start || p[-1] == '/')) {
313                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
314                         }
315                         break;
316                 }
317
318                 p += c_size;
319         }
320
321         name->full_name = ret;
322
323         return NT_STATUS_OK;
324 }
325
326
327 /*
328   reduce a name that contains .. components or repeated \ separators
329   return NULL if it can't be reduced
330 */
331 static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char **fname, uint_t flags)
332 {
333         codepoint_t c;
334         size_t c_size, len;
335         int i, num_components, err_count;
336         char **components;
337         char *p, *s, *ret;
338
339         s = talloc_strdup(mem_ctx, *fname);
340         if (s == NULL) return NT_STATUS_NO_MEMORY;
341
342         for (num_components=1, p=s; *p; p += c_size) {
343                 c = next_codepoint(p, &c_size);
344                 if (c == '\\') num_components++;
345         }
346
347         components = talloc_array(s, char *, num_components+1);
348         if (components == NULL) {
349                 talloc_free(s);
350                 return NT_STATUS_NO_MEMORY;
351         }
352
353         components[0] = s;
354         for (i=0, p=s; *p; p += c_size) {
355                 c = next_codepoint(p, &c_size);
356                 if (c == '\\') {
357                         *p = 0;
358                         components[++i] = p+1;
359                 }
360         }
361         components[i+1] = NULL;
362
363         /*
364           rather bizarre!
365
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
368           approximation.
369         */
370         for (err_count=i=0;components[i];i++) {
371                 if (strcmp(components[i], "") == 0) {
372                         continue;
373                 }
374                 if (ISDOT(components[i]) || err_count) {
375                         err_count++;
376                 }
377         }
378         if (err_count) {
379                 if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
380
381                 if (err_count==1) {
382                         return NT_STATUS_OBJECT_NAME_INVALID;
383                 } else {
384                         return NT_STATUS_OBJECT_PATH_NOT_FOUND;
385                 }
386         }
387
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));
393                         i--;
394                         continue;
395                 }
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)));
400                         i -= 2;
401                         continue;
402                 }
403         }
404
405         if (components[0] == NULL) {
406                 talloc_free(s);
407                 *fname = talloc_strdup(mem_ctx, "\\");
408                 return NT_STATUS_OK;
409         }
410
411         for (len=i=0;components[i];i++) {
412                 len += strlen(components[i]) + 1;
413         }
414
415         /* rebuild the name */
416         ret = talloc_size(mem_ctx, len+1);
417         if (ret == NULL) {
418                 talloc_free(s);
419                 return NT_STATUS_NO_MEMORY;
420         }
421
422         for (len=0,i=0;components[i];i++) {
423                 size_t len1 = strlen(components[i]);
424                 ret[len] = '\\';
425                 memcpy(ret+len+1, components[i], len1);
426                 len += len1 + 1;
427         }       
428         ret[len] = 0;
429
430         talloc_free(s);
431
432         *fname = ret;
433         
434         return NT_STATUS_OK;
435 }
436
437
438 /*
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'
441
442   flags include:
443      PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
444      PVFS_RESOLVE_STREAMS     = stream names are allowed
445
446      TODO: ../ collapsing, and outside share checking
447 */
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)
451 {
452         NTSTATUS status;
453
454         *name = talloc(mem_ctx, struct pvfs_filename);
455         if (*name == NULL) {
456                 return NT_STATUS_NO_MEMORY;
457         }
458
459         (*name)->exists = false;
460         (*name)->stream_exists = false;
461
462         if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
463                 flags &= ~PVFS_RESOLVE_STREAMS;
464         }
465
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);
469
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)) {
474                         return status;
475                 }
476                 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
477         }
478
479         if (!NT_STATUS_IS_OK(status)) {
480                 return status;
481         }
482
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 
485          */
486         if ((*name)->has_wildcard) {
487                 const char *p;
488                 char *dir_name, *saved_name;
489                 p = strrchr((*name)->full_name, '/');
490                 if (p == NULL) {
491                         /* root directory wildcard is OK */
492                         return NT_STATUS_OK;
493                 }
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);
497                         return NT_STATUS_OK;
498                 }
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;
506                         return status;
507                 }
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);
513                 } else {
514                         (*name)->full_name = saved_name;
515                         talloc_free(dir_name);
516                 }
517                 return NT_STATUS_OK;
518         }
519
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);
524         }
525
526         /* search for a matching filename */
527         status = pvfs_case_search(pvfs, *name);
528
529         return status;
530 }
531
532
533 /*
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.
537
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
540 */
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)
544 {
545         NTSTATUS status;
546
547         *name = talloc(mem_ctx, struct pvfs_filename);
548         if (*name == NULL) {
549                 return NT_STATUS_NO_MEMORY;
550         }
551
552         (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
553         if ((*name)->full_name == NULL) {
554                 return NT_STATUS_NO_MEMORY;
555         }
556
557         if (stat((*name)->full_name, &(*name)->st) == -1) {
558                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
559         }
560
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;
567
568         status = pvfs_fill_dos_info(pvfs, *name, -1);
569
570         return status;
571 }
572
573
574 /*
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()
578 */
579 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
580                               struct pvfs_filename *name)
581 {
582         dev_t device = (dev_t)0;
583         ino_t inode = 0;
584
585         if (name->exists) {
586                 device = name->st.st_dev;
587                 inode = name->st.st_ino;
588         }
589
590         if (fd == -1) {
591                 if (stat(name->full_name, &name->st) == -1) {
592                         return NT_STATUS_INVALID_HANDLE;
593                 }
594         } else {
595                 if (fstat(fd, &name->st) == -1) {
596                         return NT_STATUS_INVALID_HANDLE;
597                 }
598         }
599
600         if (name->exists &&
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",
607                          name->full_name));
608                 return NT_STATUS_UNEXPECTED_IO_ERROR;
609         }
610
611         name->exists = true;
612         
613         return pvfs_fill_dos_info(pvfs, name, fd);
614 }
615
616
617 /*
618   resolve the parent of a given name
619 */
620 NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
621                              const struct pvfs_filename *child,
622                              struct pvfs_filename **name)
623 {
624         NTSTATUS status;
625         char *p;
626
627         *name = talloc(mem_ctx, struct pvfs_filename);
628         if (*name == NULL) {
629                 return NT_STATUS_NO_MEMORY;
630         }
631
632         (*name)->full_name = talloc_strdup(*name, child->full_name);
633         if ((*name)->full_name == NULL) {
634                 return NT_STATUS_NO_MEMORY;
635         }
636
637         p = strrchr_m((*name)->full_name, '/');
638         if (p == NULL) {
639                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
640         }
641
642         /* this handles the root directory */
643         if (p == (*name)->full_name) {
644                 p[1] = 0;
645         } else {
646                 p[0] = 0;
647         }
648
649         if (stat((*name)->full_name, &(*name)->st) == -1) {
650                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
651         }
652
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;
661
662         status = pvfs_fill_dos_info(pvfs, *name, -1);
663
664         return status;
665 }