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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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
30 #include "include/includes.h"
31 #include "vfs_posix.h"
32 #include "system/dir.h"
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)
41 ret = StrCaseCmp(comp, name);
44 char *shortname = pvfs_short_name_component(pvfs, name);
46 ret = StrCaseCmp(comp, shortname);
47 talloc_free(shortname);
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, struct pvfs_filename *name)
62 /* break into a series of components */
65 char *p, *partial_name;
68 /* break up the full name info pathname components */
70 p = name->full_name + strlen(pvfs->base_directory) + 1;
78 components = talloc_array_p(name, char *, num_components);
79 p = name->full_name + strlen(pvfs->base_directory);
82 components[0] = name->full_name;
84 for (i=1;i<num_components;i++) {
88 if (pvfs_is_reserved_name(pvfs, components[i])) {
89 return NT_STATUS_ACCESS_DENIED;
93 partial_name = talloc_strdup(name, components[0]);
95 return NT_STATUS_NO_MEMORY;
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++) {
104 char *long_component;
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;
112 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
114 return NT_STATUS_NO_MEMORY;
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;
122 talloc_free(partial_name);
123 partial_name = test_name;
124 if (i == num_components - 1) {
130 /* the filesystem might be case insensitive, in which
131 case a search is pointless unless the name is
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;
138 partial_name = test_name;
142 dir = opendir(partial_name);
144 return pvfs_map_errno(pvfs, errno);
147 while ((de = readdir(dir))) {
148 if (component_compare(pvfs, components[i], de->d_name) == 0) {
154 if (i < num_components-1) {
156 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
159 components[i] = talloc_strdup(name, de->d_name);
161 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
162 talloc_free(partial_name);
163 partial_name = test_name;
169 if (stat(partial_name, &name->st) == 0) {
174 talloc_free(name->full_name);
175 name->full_name = partial_name;
178 return pvfs_fill_dos_info(pvfs, name);
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
191 errors are returned if the filename is illegal given the flags
193 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
194 uint_t flags, struct pvfs_filename *name)
196 char *ret, *p, *p_start;
198 name->original_name = talloc_strdup(name, cifs_name);
199 name->stream_name = NULL;
200 name->has_wildcard = False;
202 while (*cifs_name == '\\') {
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;
214 ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
216 return NT_STATUS_NO_MEMORY;
219 p = ret + strlen(pvfs->base_directory) + 1;
221 /* now do an in-place conversion of '\' to '/', checking
222 for legal characters */
227 codepoint_t c = next_codepoint(p, &c_size);
230 if (name->has_wildcard) {
231 /* wildcards are only allowed in the last part
233 return NT_STATUS_ILLEGAL_CHARACTER;
235 if (p > p_start && p[1] == 0) {
242 if (!(flags & PVFS_RESOLVE_STREAMS)) {
243 return NT_STATUS_ILLEGAL_CHARACTER;
245 name->stream_name = talloc_strdup(name, p+1);
246 if (name->stream_name == NULL) {
247 return NT_STATUS_NO_MEMORY;
256 if (flags & PVFS_RESOLVE_NO_WILDCARD) {
257 return NT_STATUS_OBJECT_NAME_INVALID;
259 name->has_wildcard = True;
263 return NT_STATUS_ILLEGAL_CHARACTER;
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 */
272 (p[2] == 0 || p[2] == '\\') &&
273 (p == p_start || p[-1] == '/')) {
274 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
276 if ((p[1] == 0 || p[1] == '\\') &&
277 (p == p_start || p[-1] == '/')) {
278 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
286 name->full_name = ret;
293 reduce a name that contains .. components or repeated \ separators
294 return NULL if it can't be reduced
296 static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char **fname, uint_t flags)
300 int i, num_components, err_count;
304 s = talloc_strdup(mem_ctx, *fname);
305 if (s == NULL) return NT_STATUS_NO_MEMORY;
307 for (num_components=1, p=s; *p; p += c_size) {
308 c = next_codepoint(p, &c_size);
309 if (c == '\\') num_components++;
312 components = talloc_array_p(s, char *, num_components+1);
313 if (components == NULL) {
315 return NT_STATUS_NO_MEMORY;
319 for (i=0, p=s; *p; p += c_size) {
320 c = next_codepoint(p, &c_size);
323 components[++i] = p+1;
326 components[i+1] = NULL;
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
335 for (err_count=i=0;components[i];i++) {
336 if (strcmp(components[i], "") == 0) {
339 if (strcmp(components[i], ".") == 0 || err_count) {
344 if (!(flags & PVFS_RESOLVE_NO_WILDCARD)) err_count--;
347 return NT_STATUS_OBJECT_NAME_INVALID;
349 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
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));
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)));
368 if (components[0] == NULL) {
370 *fname = talloc_strdup(mem_ctx, "\\");
374 for (len=i=0;components[i];i++) {
375 len += strlen(components[i]) + 1;
378 /* rebuild the name */
379 ret = talloc(mem_ctx, len+1);
382 return NT_STATUS_NO_MEMORY;
385 for (len=0,i=0;components[i];i++) {
386 size_t len1 = strlen(components[i]);
388 memcpy(ret+len+1, components[i], len1);
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'
406 PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
407 PVFS_RESOLVE_STREAMS = stream names are allowed
409 TODO: ../ collapsing, and outside share checking
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)
417 *name = talloc_p(mem_ctx, struct pvfs_filename);
419 return NT_STATUS_NO_MEMORY;
422 (*name)->exists = False;
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);
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)) {
434 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
437 if (!NT_STATUS_IS_OK(status)) {
441 /* if it has a wildcard then no point doing a stat() */
442 if ((*name)->has_wildcard) {
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);
452 /* search for a matching filename */
453 status = pvfs_case_search(pvfs, *name);
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.
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
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)
473 *name = talloc_p(mem_ctx, struct pvfs_filename);
475 return NT_STATUS_NO_MEMORY;
478 (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
479 if ((*name)->full_name == NULL) {
480 return NT_STATUS_NO_MEMORY;
483 if (stat((*name)->full_name, &(*name)->st) == -1) {
484 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
487 (*name)->exists = True;
488 (*name)->has_wildcard = False;
489 (*name)->original_name = talloc_strdup(*name, fname);
490 (*name)->stream_name = NULL;
492 status = pvfs_fill_dos_info(pvfs, *name);
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()
503 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
504 struct pvfs_filename *name)
510 device = name->st.st_dev;
511 inode = name->st.st_ino;
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;
519 if (fstat(fd, &name->st) == -1) {
520 return NT_STATUS_INVALID_HANDLE;
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",
532 return NT_STATUS_UNEXPECTED_IO_ERROR;
537 return pvfs_fill_dos_info(pvfs, name);