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 "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 */
component_compare(struct pvfs_state * pvfs,const char * comp,const char * name)37 static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
38 {
39 int ret;
40
41 ret = strcasecmp_m(comp, name);
42
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);
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 */
pvfs_case_search(struct pvfs_state * pvfs,struct pvfs_filename * name)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(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, -1);
179 }
180
181 return NT_STATUS_OK;
182 }
183
184 /*
185 parse a alternate data stream name
186 */
parse_stream_name(struct pvfs_filename * name,const char * s)187 static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
188 {
189 char *p;
190 name->stream_name = talloc_strdup(name, s+1);
191 if (name->stream_name == NULL) {
192 return NT_STATUS_NO_MEMORY;
193 }
194 p = strchr_m(name->stream_name, ':');
195 if (p == NULL) {
196 name->stream_id = pvfs_name_hash(name->stream_name,
197 strlen(name->stream_name));
198 return NT_STATUS_OK;
199 }
200 if (strcasecmp_m(p, ":$DATA") != 0) {
201 return NT_STATUS_OBJECT_NAME_INVALID;
202 }
203 *p = 0;
204 if (strcmp(name->stream_name, "") == 0) {
205 name->stream_name = NULL;
206 name->stream_id = 0;
207 } else {
208 name->stream_id = pvfs_name_hash(name->stream_name,
209 strlen(name->stream_name));
210 }
211
212 return NT_STATUS_OK;
213 }
214
215
216 /*
217 convert a CIFS pathname to a unix pathname. Note that this does NOT
218 take into account case insensitivity, and in fact does not access
219 the filesystem at all. It is merely a reformatting and charset
220 checking routine.
221
222 errors are returned if the filename is illegal given the flags
223 */
pvfs_unix_path(struct pvfs_state * pvfs,const char * cifs_name,uint_t flags,struct pvfs_filename * name)224 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
225 uint_t flags, struct pvfs_filename *name)
226 {
227 char *ret, *p, *p_start;
228 NTSTATUS status;
229
230 name->original_name = talloc_strdup(name, cifs_name);
231 name->stream_name = NULL;
232 name->stream_id = 0;
233 name->has_wildcard = False;
234
235 while (*cifs_name == '\\') {
236 cifs_name++;
237 }
238
239 if (*cifs_name == 0) {
240 name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
241 if (name->full_name == NULL) {
242 return NT_STATUS_NO_MEMORY;
243 }
244 return NT_STATUS_OK;
245 }
246
247 ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
248 if (ret == NULL) {
249 return NT_STATUS_NO_MEMORY;
250 }
251
252 p = ret + strlen(pvfs->base_directory) + 1;
253
254 /* now do an in-place conversion of '\' to '/', checking
255 for legal characters */
256 p_start = p;
257
258 while (*p) {
259 size_t c_size;
260 codepoint_t c = next_codepoint(p, &c_size);
261 switch (c) {
262 case '\\':
263 if (name->has_wildcard) {
264 /* wildcards are only allowed in the last part
265 of a name */
266 return NT_STATUS_ILLEGAL_CHARACTER;
267 }
268 if (p > p_start && p[1] == 0) {
269 *p = 0;
270 } else {
271 *p = '/';
272 }
273 break;
274 case ':':
275 if (!(flags & PVFS_RESOLVE_STREAMS)) {
276 return NT_STATUS_ILLEGAL_CHARACTER;
277 }
278 if (name->has_wildcard) {
279 return NT_STATUS_ILLEGAL_CHARACTER;
280 }
281 status = parse_stream_name(name, p);
282 if (!NT_STATUS_IS_OK(status)) {
283 return status;
284 }
285 *p-- = 0;
286 break;
287 case '*':
288 case '>':
289 case '<':
290 case '?':
291 case '"':
292 if (!(flags & PVFS_RESOLVE_WILDCARD)) {
293 return NT_STATUS_OBJECT_NAME_INVALID;
294 }
295 name->has_wildcard = True;
296 break;
297 case '/':
298 case '|':
299 return NT_STATUS_ILLEGAL_CHARACTER;
300 case '.':
301 /* see if it is definately a .. or
302 . component. If it is then fail here, and
303 let the next layer up try again after
304 pvfs_reduce_name() if it wants to. This is
305 much more efficient on average than always
306 scanning for these separately */
307 if (p[1] == '.' &&
308 (p[2] == 0 || p[2] == '\\') &&
309 (p == p_start || p[-1] == '/')) {
310 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
311 }
312 if ((p[1] == 0 || p[1] == '\\') &&
313 (p == p_start || p[-1] == '/')) {
314 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
315 }
316 break;
317 }
318
319 p += c_size;
320 }
321
322 name->full_name = ret;
323
324 return NT_STATUS_OK;
325 }
326
327
328 /*
329 reduce a name that contains .. components or repeated \ separators
330 return NULL if it can't be reduced
331 */
pvfs_reduce_name(TALLOC_CTX * mem_ctx,const char ** fname,uint_t flags)332 static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char **fname, uint_t flags)
333 {
334 codepoint_t c;
335 size_t c_size, len;
336 int i, num_components, err_count;
337 char **components;
338 char *p, *s, *ret;
339
340 s = talloc_strdup(mem_ctx, *fname);
341 if (s == NULL) return NT_STATUS_NO_MEMORY;
342
343 for (num_components=1, p=s; *p; p += c_size) {
344 c = next_codepoint(p, &c_size);
345 if (c == '\\') num_components++;
346 }
347
348 components = talloc_array(s, char *, num_components+1);
349 if (components == NULL) {
350 talloc_free(s);
351 return NT_STATUS_NO_MEMORY;
352 }
353
354 components[0] = s;
355 for (i=0, p=s; *p; p += c_size) {
356 c = next_codepoint(p, &c_size);
357 if (c == '\\') {
358 *p = 0;
359 components[++i] = p+1;
360 }
361 }
362 components[i+1] = NULL;
363
364 /*
365 rather bizarre!
366
367 '.' components are not allowed, but the rules for what error
368 code to give don't seem to make sense. This is a close
369 approximation.
370 */
371 for (err_count=i=0;components[i];i++) {
372 if (strcmp(components[i], "") == 0) {
373 continue;
374 }
375 if (ISDOT(components[i]) || err_count) {
376 err_count++;
377 }
378 }
379 if (err_count) {
380 if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
381
382 if (err_count==1) {
383 return NT_STATUS_OBJECT_NAME_INVALID;
384 } else {
385 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
386 }
387 }
388
389 /* remove any null components */
390 for (i=0;components[i];i++) {
391 if (strcmp(components[i], "") == 0) {
392 memmove(&components[i], &components[i+1],
393 sizeof(char *)*(num_components-i));
394 i--;
395 continue;
396 }
397 if (ISDOTDOT(components[i])) {
398 if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
399 memmove(&components[i-1], &components[i+1],
400 sizeof(char *)*(num_components-(i+1)));
401 i -= 2;
402 continue;
403 }
404 }
405
406 if (components[0] == NULL) {
407 talloc_free(s);
408 *fname = talloc_strdup(mem_ctx, "\\");
409 return NT_STATUS_OK;
410 }
411
412 for (len=i=0;components[i];i++) {
413 len += strlen(components[i]) + 1;
414 }
415
416 /* rebuild the name */
417 ret = talloc_size(mem_ctx, len+1);
418 if (ret == NULL) {
419 talloc_free(s);
420 return NT_STATUS_NO_MEMORY;
421 }
422
423 for (len=0,i=0;components[i];i++) {
424 size_t len1 = strlen(components[i]);
425 ret[len] = '\\';
426 memcpy(ret+len+1, components[i], len1);
427 len += len1 + 1;
428 }
429 ret[len] = 0;
430
431 talloc_free(s);
432
433 *fname = ret;
434
435 return NT_STATUS_OK;
436 }
437
438
439 /*
440 resolve a name from relative client format to a struct pvfs_filename
441 the memory for the filename is made as a talloc child of 'name'
442
443 flags include:
444 PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
445 PVFS_RESOLVE_STREAMS = stream names are allowed
446
447 TODO: ../ collapsing, and outside share checking
448 */
pvfs_resolve_name(struct pvfs_state * pvfs,TALLOC_CTX * mem_ctx,const char * cifs_name,uint_t flags,struct pvfs_filename ** name)449 NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
450 const char *cifs_name,
451 uint_t flags, struct pvfs_filename **name)
452 {
453 NTSTATUS status;
454
455 *name = talloc(mem_ctx, struct pvfs_filename);
456 if (*name == NULL) {
457 return NT_STATUS_NO_MEMORY;
458 }
459
460 (*name)->exists = False;
461 (*name)->stream_exists = False;
462
463 if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
464 flags &= ~PVFS_RESOLVE_STREAMS;
465 }
466
467 /* do the basic conversion to a unix formatted path,
468 also checking for allowable characters */
469 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
470
471 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
472 /* it might contain .. components which need to be reduced */
473 status = pvfs_reduce_name(*name, &cifs_name, flags);
474 if (!NT_STATUS_IS_OK(status)) {
475 return status;
476 }
477 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
478 }
479
480 if (!NT_STATUS_IS_OK(status)) {
481 return status;
482 }
483
484 /* if it has a wildcard then no point doing a stat() */
485 if ((*name)->has_wildcard) {
486 return NT_STATUS_OK;
487 }
488
489 /* if we can stat() the full name now then we are done */
490 if (stat((*name)->full_name, &(*name)->st) == 0) {
491 (*name)->exists = True;
492 return pvfs_fill_dos_info(pvfs, *name, -1);
493 }
494
495 /* search for a matching filename */
496 status = pvfs_case_search(pvfs, *name);
497
498 return status;
499 }
500
501
502 /*
503 do a partial resolve, returning a pvfs_filename structure given a
504 base path and a relative component. It is an error if the file does
505 not exist. No case-insensitive matching is done.
506
507 this is used in places like directory searching where we need a pvfs_filename
508 to pass to a function, but already know the unix base directory and component
509 */
pvfs_resolve_partial(struct pvfs_state * pvfs,TALLOC_CTX * mem_ctx,const char * unix_dir,const char * fname,struct pvfs_filename ** name)510 NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
511 const char *unix_dir, const char *fname,
512 struct pvfs_filename **name)
513 {
514 NTSTATUS status;
515
516 *name = talloc(mem_ctx, struct pvfs_filename);
517 if (*name == NULL) {
518 return NT_STATUS_NO_MEMORY;
519 }
520
521 (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
522 if ((*name)->full_name == NULL) {
523 return NT_STATUS_NO_MEMORY;
524 }
525
526 if (stat((*name)->full_name, &(*name)->st) == -1) {
527 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
528 }
529
530 (*name)->exists = True;
531 (*name)->stream_exists = True;
532 (*name)->has_wildcard = False;
533 (*name)->original_name = talloc_strdup(*name, fname);
534 (*name)->stream_name = NULL;
535 (*name)->stream_id = 0;
536
537 status = pvfs_fill_dos_info(pvfs, *name, -1);
538
539 return status;
540 }
541
542
543 /*
544 fill in the pvfs_filename info for an open file, given the current
545 info for a (possibly) non-open file. This is used by places that need
546 to update the pvfs_filename stat information, and by pvfs_open()
547 */
pvfs_resolve_name_fd(struct pvfs_state * pvfs,int fd,struct pvfs_filename * name)548 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
549 struct pvfs_filename *name)
550 {
551 dev_t device = (dev_t)0;
552 ino_t inode = 0;
553
554 if (name->exists) {
555 device = name->st.st_dev;
556 inode = name->st.st_ino;
557 }
558
559 if (fd == -1) {
560 if (stat(name->full_name, &name->st) == -1) {
561 return NT_STATUS_INVALID_HANDLE;
562 }
563 } else {
564 if (fstat(fd, &name->st) == -1) {
565 return NT_STATUS_INVALID_HANDLE;
566 }
567 }
568
569 if (name->exists &&
570 (device != name->st.st_dev || inode != name->st.st_ino)) {
571 /* the file we are looking at has changed! this could
572 be someone trying to exploit a race
573 condition. Certainly we don't want to continue
574 operating on this file */
575 DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
576 name->full_name));
577 return NT_STATUS_UNEXPECTED_IO_ERROR;
578 }
579
580 name->exists = True;
581
582 return pvfs_fill_dos_info(pvfs, name, fd);
583 }
584
585
586 /*
587 resolve the parent of a given name
588 */
pvfs_resolve_parent(struct pvfs_state * pvfs,TALLOC_CTX * mem_ctx,const struct pvfs_filename * child,struct pvfs_filename ** name)589 NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
590 const struct pvfs_filename *child,
591 struct pvfs_filename **name)
592 {
593 NTSTATUS status;
594 char *p;
595
596 *name = talloc(mem_ctx, struct pvfs_filename);
597 if (*name == NULL) {
598 return NT_STATUS_NO_MEMORY;
599 }
600
601 (*name)->full_name = talloc_strdup(*name, child->full_name);
602 if ((*name)->full_name == NULL) {
603 return NT_STATUS_NO_MEMORY;
604 }
605
606 p = strrchr_m((*name)->full_name, '/');
607 if (p == NULL) {
608 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
609 }
610
611 /* this handles the root directory */
612 if (p == (*name)->full_name) {
613 p[1] = 0;
614 } else {
615 p[0] = 0;
616 }
617
618 if (stat((*name)->full_name, &(*name)->st) == -1) {
619 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
620 }
621
622 (*name)->exists = True;
623 (*name)->stream_exists = True;
624 (*name)->has_wildcard = False;
625 /* we can't get the correct 'original_name', but for the purposes
626 of this call this is close enough */
627 (*name)->original_name = talloc_reference(*name, child->original_name);
628 (*name)->stream_name = NULL;
629 (*name)->stream_id = 0;
630
631 status = pvfs_fill_dos_info(pvfs, *name, -1);
632
633 return status;
634 }
635