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