r2927: imported the hash2 name mangling code from Samba3 into Samba4, but
[ira/wip.git] / source4 / 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
33 /*
34   compare two filename components. This is where the name mangling hook will go
35 */
36 static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
37 {
38         int ret;
39
40         ret = StrCaseCmp(comp, name);
41
42         if (ret != 0) {
43                 char *shortname = pvfs_short_name_component(pvfs, name);
44                 if (shortname) {
45                         ret = StrCaseCmp(comp, shortname);
46                         talloc_free(shortname);
47                 }
48         }
49
50         return ret;
51 }
52
53 /*
54   search for a filename in a case insensitive fashion
55
56   TODO: add a cache for previously resolved case-insensitive names
57   TODO: add mangled name support
58 */
59 static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name)
60 {
61         /* break into a series of components */
62         int num_components;
63         char **components;
64         char *p, *partial_name;
65         int i;
66
67         /* break up the full name info pathname components */
68         num_components=2;
69         p = name->full_name + strlen(pvfs->base_directory) + 1;
70
71         for (;*p;p++) {
72                 if (*p == '/') {
73                         num_components++;
74                 }
75         }
76
77         components = talloc_array_p(name, char *, num_components);
78         p = name->full_name + strlen(pvfs->base_directory);
79         *p++ = 0;
80
81         components[0] = name->full_name;
82
83         for (i=1;i<num_components;i++) {
84                 components[i] = p;
85                 p = strchr(p, '/');
86                 if (p) *p++ = 0;
87         }
88
89         partial_name = talloc_strdup(name, components[0]);
90         if (!partial_name) {
91                 return NT_STATUS_NO_MEMORY;
92         }
93
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++) {
97                 char *test_name;
98                 DIR *dir;
99                 struct dirent *de;
100                 char *long_component;
101
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;
106                 }
107
108                 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
109                 if (!test_name) {
110                         return NT_STATUS_NO_MEMORY;
111                 }
112
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;
117                         }
118                         talloc_free(partial_name);
119                         partial_name = test_name;
120                         if (i == num_components - 1) {
121                                 name->exists = True;
122                         }
123                         continue;
124                 }
125                 
126                 dir = opendir(partial_name);
127                 if (!dir) {
128                         return pvfs_map_errno(pvfs, errno);
129                 }
130
131                 while ((de = readdir(dir))) {
132                         if (component_compare(pvfs, components[i], de->d_name) == 0) {
133                                 break;
134                         }
135                 }
136
137                 if (!de) {
138                         if (i < num_components-1) {
139                                 closedir(dir);
140                                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
141                         }
142                 } else {
143                         components[i] = talloc_strdup(name, de->d_name);
144                 }
145                 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
146                 talloc_free(partial_name);
147                 partial_name = test_name;
148
149                 closedir(dir);
150         }
151
152         if (!name->exists) {
153                 if (stat(partial_name, &name->st) == 0) {
154                         name->exists = True;
155                 }
156         }
157
158         talloc_free(name->full_name);
159         name->full_name = partial_name;
160
161         if (name->exists) {
162                 return pvfs_fill_dos_info(pvfs, name);
163         }
164
165         return NT_STATUS_OK;
166 }
167
168
169 /*
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
173   checking routine.
174
175   errors are returned if the filename is illegal given the flags
176 */
177 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
178                                uint_t flags, struct pvfs_filename *name)
179 {
180         char *ret, *p;
181         size_t len;
182
183         name->original_name = talloc_strdup(name, cifs_name);
184         name->stream_name = NULL;
185         name->has_wildcard = False;
186
187         while (*cifs_name == '\\') {
188                 cifs_name++;
189         }
190
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;
195                 }
196                 return NT_STATUS_OK;
197         }
198
199         ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
200         if (ret == NULL) {
201                 return NT_STATUS_NO_MEMORY;
202         }
203
204         p = ret + strlen(pvfs->base_directory) + 1;
205
206         len = strlen(cifs_name);
207         if (len>0 && p[len-1] == '\\') {
208                 p[len-1] = 0;
209                 len--;
210         }
211         if (len>1 && p[len-1] == '.' && p[len-2] == '\\') {
212                 return NT_STATUS_OBJECT_NAME_INVALID;
213         }
214
215         /* now do an in-place conversion of '\' to '/', checking
216            for legal characters */
217         for (;*p;p++) {
218                 switch (*p) {
219                 case '\\':
220                         if (name->has_wildcard) {
221                                 /* wildcards are only allowed in the last part
222                                    of a name */
223                                 return NT_STATUS_ILLEGAL_CHARACTER;
224                         }
225                         *p = '/';
226                         break;
227                 case ':':
228                         if (!(flags & PVFS_RESOLVE_STREAMS)) {
229                                 return NT_STATUS_ILLEGAL_CHARACTER;
230                         }
231                         name->stream_name = talloc_strdup(name, p+1);
232                         if (name->stream_name == NULL) {
233                                 return NT_STATUS_NO_MEMORY;
234                         }
235                         *p-- = 0;
236                         break;
237                 case '*':
238                 case '>':
239                 case '<':
240                 case '?':
241                 case '"':
242                         if (flags & PVFS_RESOLVE_NO_WILDCARD) {
243                                 return NT_STATUS_ILLEGAL_CHARACTER;
244                         }
245                         name->has_wildcard = True;
246                         break;
247                 case '/':
248                 case '|':
249                         return NT_STATUS_ILLEGAL_CHARACTER;
250                 }
251         }
252
253         name->full_name = ret;
254
255         return NT_STATUS_OK;
256 }
257
258
259 /*
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'
262
263   flags include:
264      PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
265      PVFS_RESOLVE_STREAMS     = stream names are allowed
266
267      TODO: add reserved name checking (for things like LPT1)
268      TODO: ../ collapsing, and outside share checking
269 */
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)
273 {
274         NTSTATUS status;
275
276         *name = talloc_p(mem_ctx, struct pvfs_filename);
277         if (*name == NULL) {
278                 return NT_STATUS_NO_MEMORY;
279         }
280
281         (*name)->exists = False;
282
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)) {
287                 return status;
288         }
289
290         /* if it has a wildcard then no point doing a stat() */
291         if ((*name)->has_wildcard) {
292                 return NT_STATUS_OK;
293         }
294
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);
299         }
300
301         /* the filesystem might be case insensitive, in which
302            case a search is pointless */
303         if (pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) {
304                 return NT_STATUS_OK;
305         }
306
307         /* search for a matching filename */
308         status = pvfs_case_search(pvfs, *name);
309
310         return status;
311 }
312
313
314 /*
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.
318
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
321 */
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)
325 {
326         NTSTATUS status;
327
328         *name = talloc_p(mem_ctx, struct pvfs_filename);
329         if (*name == NULL) {
330                 return NT_STATUS_NO_MEMORY;
331         }
332
333         (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
334         if ((*name)->full_name == NULL) {
335                 return NT_STATUS_NO_MEMORY;
336         }
337
338         if (stat((*name)->full_name, &(*name)->st) == -1) {
339                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
340         }
341
342         (*name)->exists = True;
343         (*name)->has_wildcard = False;
344         (*name)->original_name = talloc_strdup(*name, fname);
345         (*name)->stream_name = NULL;
346
347         status = pvfs_fill_dos_info(pvfs, *name);
348
349         return status;
350 }
351
352
353 /*
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()
357 */
358 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
359                               struct pvfs_filename *name)
360 {
361         if (fstat(fd, &name->st) == -1) {
362                 return NT_STATUS_INVALID_HANDLE;
363         }
364
365         name->exists = True;
366         
367         return pvfs_fill_dos_info(pvfs, name);
368 }