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"
34 compare two filename components. This is where the name mangling hook will go
36 static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
40 ret = StrCaseCmp(comp, name);
43 char *shortname = pvfs_short_name_component(pvfs, name);
45 ret = StrCaseCmp(comp, shortname);
46 talloc_free(shortname);
54 search for a filename in a case insensitive fashion
56 TODO: add a cache for previously resolved case-insensitive names
57 TODO: add mangled name support
59 static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name)
61 /* break into a series of components */
64 char *p, *partial_name;
67 /* break up the full name info pathname components */
69 p = name->full_name + strlen(pvfs->base_directory) + 1;
77 components = talloc_array_p(name, char *, num_components);
78 p = name->full_name + strlen(pvfs->base_directory);
81 components[0] = name->full_name;
83 for (i=1;i<num_components;i++) {
89 partial_name = talloc_strdup(name, components[0]);
91 return NT_STATUS_NO_MEMORY;
94 /* for each component, check if it exists as-is, and if not then
95 do a directory scan */
96 for (i=1;i<num_components;i++) {
100 char *long_component;
102 /* possibly remap from the short name cache */
103 long_component = pvfs_mangled_lookup(pvfs, name, components[i]);
104 if (long_component) {
105 components[i] = long_component;
108 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
110 return NT_STATUS_NO_MEMORY;
113 /* check if this component exists as-is */
114 if (stat(test_name, &name->st) == 0) {
115 if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
116 return NT_STATUS_NOT_A_DIRECTORY;
118 talloc_free(partial_name);
119 partial_name = test_name;
120 if (i == num_components - 1) {
126 dir = opendir(partial_name);
128 return pvfs_map_errno(pvfs, errno);
131 while ((de = readdir(dir))) {
132 if (component_compare(pvfs, components[i], de->d_name) == 0) {
138 if (i < num_components-1) {
140 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
143 components[i] = talloc_strdup(name, de->d_name);
145 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
146 talloc_free(partial_name);
147 partial_name = test_name;
153 if (stat(partial_name, &name->st) == 0) {
158 talloc_free(name->full_name);
159 name->full_name = partial_name;
162 return pvfs_fill_dos_info(pvfs, name);
170 convert a CIFS pathname to a unix pathname. Note that this does NOT
171 take into account case insensitivity, and in fact does not access
172 the filesystem at all. It is merely a reformatting and charset
175 errors are returned if the filename is illegal given the flags
177 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
178 uint_t flags, struct pvfs_filename *name)
183 name->original_name = talloc_strdup(name, cifs_name);
184 name->stream_name = NULL;
185 name->has_wildcard = False;
187 while (*cifs_name == '\\') {
191 if (*cifs_name == 0) {
192 name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
193 if (name->full_name == NULL) {
194 return NT_STATUS_NO_MEMORY;
199 ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
201 return NT_STATUS_NO_MEMORY;
204 p = ret + strlen(pvfs->base_directory) + 1;
206 len = strlen(cifs_name);
207 if (len>0 && p[len-1] == '\\') {
211 if (len>1 && p[len-1] == '.' && p[len-2] == '\\') {
212 return NT_STATUS_OBJECT_NAME_INVALID;
215 /* now do an in-place conversion of '\' to '/', checking
216 for legal characters */
220 if (name->has_wildcard) {
221 /* wildcards are only allowed in the last part
223 return NT_STATUS_ILLEGAL_CHARACTER;
228 if (!(flags & PVFS_RESOLVE_STREAMS)) {
229 return NT_STATUS_ILLEGAL_CHARACTER;
231 name->stream_name = talloc_strdup(name, p+1);
232 if (name->stream_name == NULL) {
233 return NT_STATUS_NO_MEMORY;
242 if (flags & PVFS_RESOLVE_NO_WILDCARD) {
243 return NT_STATUS_ILLEGAL_CHARACTER;
245 name->has_wildcard = True;
249 return NT_STATUS_ILLEGAL_CHARACTER;
253 name->full_name = ret;
260 resolve a name from relative client format to a struct pvfs_filename
261 the memory for the filename is made as a talloc child of 'name'
264 PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
265 PVFS_RESOLVE_STREAMS = stream names are allowed
267 TODO: add reserved name checking (for things like LPT1)
268 TODO: ../ collapsing, and outside share checking
270 NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
271 const char *cifs_name,
272 uint_t flags, struct pvfs_filename **name)
276 *name = talloc_p(mem_ctx, struct pvfs_filename);
278 return NT_STATUS_NO_MEMORY;
281 (*name)->exists = False;
283 /* do the basic conversion to a unix formatted path,
284 also checking for allowable characters */
285 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
286 if (!NT_STATUS_IS_OK(status)) {
290 /* if it has a wildcard then no point doing a stat() */
291 if ((*name)->has_wildcard) {
295 /* if we can stat() the full name now then we are done */
296 if (stat((*name)->full_name, &(*name)->st) == 0) {
297 (*name)->exists = True;
298 return pvfs_fill_dos_info(pvfs, *name);
301 /* the filesystem might be case insensitive, in which
302 case a search is pointless */
303 if (pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) {
307 /* search for a matching filename */
308 status = pvfs_case_search(pvfs, *name);
315 do a partial resolve, returning a pvfs_filename structure given a
316 base path and a relative component. It is an error if the file does
317 not exist. No case-insensitive matching is done.
319 this is used in places like directory searching where we need a pvfs_filename
320 to pass to a function, but already know the unix base directory and component
322 NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
323 const char *unix_dir, const char *fname,
324 struct pvfs_filename **name)
328 *name = talloc_p(mem_ctx, struct pvfs_filename);
330 return NT_STATUS_NO_MEMORY;
333 (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
334 if ((*name)->full_name == NULL) {
335 return NT_STATUS_NO_MEMORY;
338 if (stat((*name)->full_name, &(*name)->st) == -1) {
339 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
342 (*name)->exists = True;
343 (*name)->has_wildcard = False;
344 (*name)->original_name = talloc_strdup(*name, fname);
345 (*name)->stream_name = NULL;
347 status = pvfs_fill_dos_info(pvfs, *name);
354 fill in the pvfs_filename info for an open file, given the current
355 info for a (possibly) non-open file. This is used by places that need
356 to update the pvfs_filename stat information, and by pvfs_open()
358 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
359 struct pvfs_filename *name)
361 if (fstat(fd, &name->st) == -1) {
362 return NT_STATUS_INVALID_HANDLE;
367 return pvfs_fill_dos_info(pvfs, name);