r3447: more include/system/XXX.h include files
[samba.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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 /*
24   this is the core code for converting a filename from the format as
25   given by a client to a posix filename, including any case-matching
26   required, and checks for legal characters
27 */
28
29
30 #include "include/includes.h"
31 #include "vfs_posix.h"
32 #include "system/dir.h"
33
34 /*
35   compare two filename components. This is where the name mangling hook will go
36 */
37 static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
38 {
39         int ret;
40
41         ret = StrCaseCmp(comp, name);
42
43         if (ret != 0) {
44                 char *shortname = pvfs_short_name_component(pvfs, name);
45                 if (shortname) {
46                         ret = StrCaseCmp(comp, shortname);
47                         talloc_free(shortname);
48                 }
49         }
50
51         return ret;
52 }
53
54 /*
55   search for a filename in a case insensitive fashion
56
57   TODO: add a cache for previously resolved case-insensitive names
58   TODO: add mangled name support
59 */
60 static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name)
61 {
62         /* break into a series of components */
63         int num_components;
64         char **components;
65         char *p, *partial_name;
66         int i;
67
68         /* break up the full name info pathname components */
69         num_components=2;
70         p = name->full_name + strlen(pvfs->base_directory) + 1;
71
72         for (;*p;p++) {
73                 if (*p == '/') {
74                         num_components++;
75                 }
76         }
77
78         components = talloc_array_p(name, char *, num_components);
79         p = name->full_name + strlen(pvfs->base_directory);
80         *p++ = 0;
81
82         components[0] = name->full_name;
83
84         for (i=1;i<num_components;i++) {
85                 components[i] = p;
86                 p = strchr(p, '/');
87                 if (p) *p++ = 0;
88                 if (pvfs_is_reserved_name(pvfs, components[i])) {
89                         return NT_STATUS_ACCESS_DENIED;
90                 }
91         }
92
93         partial_name = talloc_strdup(name, components[0]);
94         if (!partial_name) {
95                 return NT_STATUS_NO_MEMORY;
96         }
97
98         /* for each component, check if it exists as-is, and if not then
99            do a directory scan */
100         for (i=1;i<num_components;i++) {
101                 char *test_name;
102                 DIR *dir;
103                 struct dirent *de;
104                 char *long_component;
105
106                 /* possibly remap from the short name cache */
107                 long_component = pvfs_mangled_lookup(pvfs, name, components[i]);
108                 if (long_component) {
109                         components[i] = long_component;
110                 }
111
112                 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
113                 if (!test_name) {
114                         return NT_STATUS_NO_MEMORY;
115                 }
116
117                 /* check if this component exists as-is */
118                 if (stat(test_name, &name->st) == 0) {
119                         if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
120                                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
121                         }
122                         talloc_free(partial_name);
123                         partial_name = test_name;
124                         if (i == num_components - 1) {
125                                 name->exists = True;
126                         }
127                         continue;
128                 }
129
130                 /* the filesystem might be case insensitive, in which
131                    case a search is pointless unless the name is
132                    mangled */
133                 if ((pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) &&
134                     !pvfs_is_mangled_component(pvfs, components[i])) {
135                         if (i < num_components-1) {
136                                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
137                         }
138                         partial_name = test_name;
139                         continue;
140                 }
141                 
142                 dir = opendir(partial_name);
143                 if (!dir) {
144                         return pvfs_map_errno(pvfs, errno);
145                 }
146
147                 while ((de = readdir(dir))) {
148                         if (component_compare(pvfs, components[i], de->d_name) == 0) {
149                                 break;
150                         }
151                 }
152
153                 if (!de) {
154                         if (i < num_components-1) {
155                                 closedir(dir);
156                                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
157                         }
158                 } else {
159                         components[i] = talloc_strdup(name, de->d_name);
160                 }
161                 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
162                 talloc_free(partial_name);
163                 partial_name = test_name;
164
165                 closedir(dir);
166         }
167
168         if (!name->exists) {
169                 if (stat(partial_name, &name->st) == 0) {
170                         name->exists = True;
171                 }
172         }
173
174         talloc_free(name->full_name);
175         name->full_name = partial_name;
176
177         if (name->exists) {
178                 return pvfs_fill_dos_info(pvfs, name);
179         }
180
181         return NT_STATUS_OK;
182 }
183
184
185 /*
186   convert a CIFS pathname to a unix pathname. Note that this does NOT
187   take into account case insensitivity, and in fact does not access
188   the filesystem at all. It is merely a reformatting and charset
189   checking routine.
190
191   errors are returned if the filename is illegal given the flags
192 */
193 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
194                                uint_t flags, struct pvfs_filename *name)
195 {
196         char *ret, *p, *p_start;
197
198         name->original_name = talloc_strdup(name, cifs_name);
199         name->stream_name = NULL;
200         name->has_wildcard = False;
201
202         while (*cifs_name == '\\') {
203                 cifs_name++;
204         }
205
206         if (*cifs_name == 0) {
207                 name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
208                 if (name->full_name == NULL) {
209                         return NT_STATUS_NO_MEMORY;
210                 }
211                 return NT_STATUS_OK;
212         }
213
214         ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
215         if (ret == NULL) {
216                 return NT_STATUS_NO_MEMORY;
217         }
218
219         p = ret + strlen(pvfs->base_directory) + 1;
220
221         /* now do an in-place conversion of '\' to '/', checking
222            for legal characters */
223         p_start = p;
224
225         while (*p) {
226                 size_t c_size;
227                 codepoint_t c = next_codepoint(p, &c_size);
228                 switch (c) {
229                 case '\\':
230                         if (name->has_wildcard) {
231                                 /* wildcards are only allowed in the last part
232                                    of a name */
233                                 return NT_STATUS_ILLEGAL_CHARACTER;
234                         }
235                         if (p > p_start && p[1] == 0) {
236                                 *p = 0;
237                         } else {
238                                 *p = '/';
239                         }
240                         break;
241                 case ':':
242                         if (!(flags & PVFS_RESOLVE_STREAMS)) {
243                                 return NT_STATUS_ILLEGAL_CHARACTER;
244                         }
245                         name->stream_name = talloc_strdup(name, p+1);
246                         if (name->stream_name == NULL) {
247                                 return NT_STATUS_NO_MEMORY;
248                         }
249                         *p-- = 0;
250                         break;
251                 case '*':
252                 case '>':
253                 case '<':
254                 case '?':
255                 case '"':
256                         if (flags & PVFS_RESOLVE_NO_WILDCARD) {
257                                 return NT_STATUS_OBJECT_NAME_INVALID;
258                         }
259                         name->has_wildcard = True;
260                         break;
261                 case '/':
262                 case '|':
263                         return NT_STATUS_ILLEGAL_CHARACTER;
264                 case '.':
265                         /* see if it is definately a .. or
266                            . component. If it is then fail here, and
267                            let the next layer up try again after
268                            pvfs_reduce_name() if it wants to. This is
269                            much more efficient on average than always
270                            scanning for these separately */
271                         if (p[1] == '.' && 
272                             (p[2] == 0 || p[2] == '\\') &&
273                             (p == p_start || p[-1] == '/')) {
274                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
275                         }
276                         if ((p[1] == 0 || p[1] == '\\') &&
277                             (p == p_start || p[-1] == '/')) {
278                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
279                         }
280                         break;
281                 }
282
283                 p += c_size;
284         }
285
286         name->full_name = ret;
287
288         return NT_STATUS_OK;
289 }
290
291
292 /*
293   reduce a name that contains .. components or repeated \ separators
294   return NULL if it can't be reduced
295 */
296 static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char **fname, uint_t flags)
297 {
298         codepoint_t c;
299         size_t c_size, len;
300         int i, num_components, err_count;
301         char **components;
302         char *p, *s, *ret;
303
304         s = talloc_strdup(mem_ctx, *fname);
305         if (s == NULL) return NT_STATUS_NO_MEMORY;
306
307         for (num_components=1, p=s; *p; p += c_size) {
308                 c = next_codepoint(p, &c_size);
309                 if (c == '\\') num_components++;
310         }
311
312         components = talloc_array_p(s, char *, num_components+1);
313         if (components == NULL) {
314                 talloc_free(s);
315                 return NT_STATUS_NO_MEMORY;
316         }
317
318         components[0] = s;
319         for (i=0, p=s; *p; p += c_size) {
320                 c = next_codepoint(p, &c_size);
321                 if (c == '\\') {
322                         *p = 0;
323                         components[++i] = p+1;
324                 }
325         }
326         components[i+1] = NULL;
327
328         /*
329           rather bizarre!
330
331           '.' components are not allowed, but the rules for what error
332           code to give don't seem to make sense. This is a close
333           approximation.
334         */
335         for (err_count=i=0;components[i];i++) {
336                 if (strcmp(components[i], "") == 0) {
337                         continue;
338                 }
339                 if (strcmp(components[i], ".") == 0 || err_count) {
340                         err_count++;
341                 }
342         }
343         if (err_count) {
344                 if (!(flags & PVFS_RESOLVE_NO_WILDCARD)) err_count--;
345
346                 if (err_count==1) {
347                         return NT_STATUS_OBJECT_NAME_INVALID;
348                 } else {
349                         return NT_STATUS_OBJECT_PATH_NOT_FOUND;
350                 }
351         }
352
353         /* remove any null components */
354         for (i=0;components[i];i++) {
355                 if (strcmp(components[i], "") == 0) {
356                         memmove(&components[i], &components[i+1], 
357                                 sizeof(char *)*(num_components-i));
358                         i--;
359                 }
360                 if (strcmp(components[i], "..") == 0) {
361                         if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
362                         memmove(&components[i-1], &components[i+1], 
363                                 sizeof(char *)*(num_components-(i+1)));
364                         i -= 2;
365                 }
366         }
367
368         if (components[0] == NULL) {
369                 talloc_free(s);
370                 *fname = talloc_strdup(mem_ctx, "\\");
371                 return NT_STATUS_OK;
372         }
373
374         for (len=i=0;components[i];i++) {
375                 len += strlen(components[i]) + 1;
376         }
377
378         /* rebuild the name */
379         ret = talloc(mem_ctx, len+1);
380         if (ret == NULL) {
381                 talloc_free(s);
382                 return NT_STATUS_NO_MEMORY;
383         }
384
385         for (len=0,i=0;components[i];i++) {
386                 size_t len1 = strlen(components[i]);
387                 ret[len] = '\\';
388                 memcpy(ret+len+1, components[i], len1);
389                 len += len1 + 1;
390         }       
391         ret[len] = 0;
392
393         talloc_free(s);
394
395         *fname = ret;
396         
397         return NT_STATUS_OK;
398 }
399
400
401 /*
402   resolve a name from relative client format to a struct pvfs_filename
403   the memory for the filename is made as a talloc child of 'name'
404
405   flags include:
406      PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
407      PVFS_RESOLVE_STREAMS     = stream names are allowed
408
409      TODO: ../ collapsing, and outside share checking
410 */
411 NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
412                            const char *cifs_name,
413                            uint_t flags, struct pvfs_filename **name)
414 {
415         NTSTATUS status;
416
417         *name = talloc_p(mem_ctx, struct pvfs_filename);
418         if (*name == NULL) {
419                 return NT_STATUS_NO_MEMORY;
420         }
421
422         (*name)->exists = False;
423
424         /* do the basic conversion to a unix formatted path,
425            also checking for allowable characters */
426         status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
427
428         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
429                 /* it might contain .. components which need to be reduced */
430                 status = pvfs_reduce_name(*name, &cifs_name, flags);
431                 if (!NT_STATUS_IS_OK(status)) {
432                         return status;
433                 }
434                 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
435         }
436
437         if (!NT_STATUS_IS_OK(status)) {
438                 return status;
439         }
440
441         /* if it has a wildcard then no point doing a stat() */
442         if ((*name)->has_wildcard) {
443                 return NT_STATUS_OK;
444         }
445
446         /* if we can stat() the full name now then we are done */
447         if (stat((*name)->full_name, &(*name)->st) == 0) {
448                 (*name)->exists = True;
449                 return pvfs_fill_dos_info(pvfs, *name);
450         }
451
452         /* search for a matching filename */
453         status = pvfs_case_search(pvfs, *name);
454
455         return status;
456 }
457
458
459 /*
460   do a partial resolve, returning a pvfs_filename structure given a
461   base path and a relative component. It is an error if the file does
462   not exist. No case-insensitive matching is done.
463
464   this is used in places like directory searching where we need a pvfs_filename
465   to pass to a function, but already know the unix base directory and component
466 */
467 NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
468                               const char *unix_dir, const char *fname,
469                               struct pvfs_filename **name)
470 {
471         NTSTATUS status;
472
473         *name = talloc_p(mem_ctx, struct pvfs_filename);
474         if (*name == NULL) {
475                 return NT_STATUS_NO_MEMORY;
476         }
477
478         (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
479         if ((*name)->full_name == NULL) {
480                 return NT_STATUS_NO_MEMORY;
481         }
482
483         if (stat((*name)->full_name, &(*name)->st) == -1) {
484                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
485         }
486
487         (*name)->exists = True;
488         (*name)->has_wildcard = False;
489         (*name)->original_name = talloc_strdup(*name, fname);
490         (*name)->stream_name = NULL;
491
492         status = pvfs_fill_dos_info(pvfs, *name);
493
494         return status;
495 }
496
497
498 /*
499   fill in the pvfs_filename info for an open file, given the current
500   info for a (possibly) non-open file. This is used by places that need
501   to update the pvfs_filename stat information, and by pvfs_open()
502 */
503 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
504                               struct pvfs_filename *name)
505 {
506         dev_t device;
507         ino_t inode;
508
509         if (name->exists) {
510                 device = name->st.st_dev;
511                 inode = name->st.st_ino;
512         }
513
514         if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
515                 if (stat(name->full_name, &name->st) == -1) {
516                         return NT_STATUS_INVALID_HANDLE;
517                 }
518         } else {
519                 if (fstat(fd, &name->st) == -1) {
520                         return NT_STATUS_INVALID_HANDLE;
521                 }
522         }
523
524         if (name->exists &&
525             (device != name->st.st_dev || inode != name->st.st_ino)) {
526                 /* the file we are looking at has changed! this could
527                  be someone trying to exploit a race
528                  condition. Certainly we don't want to continue
529                  operating on this file */
530                 DEBUG(0,("pvfs: WARNING: file '%s' changed during resole - failing\n",
531                          name->full_name));
532                 return NT_STATUS_UNEXPECTED_IO_ERROR;
533         }
534
535         name->exists = True;
536         
537         return pvfs_fill_dos_info(pvfs, name);
538 }