SMB2 signing now works. The spec was wrong (and will be fixed in the
[kai/samba.git] / source / 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 3 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, see <http://www.gnu.org/licenses/>.
20 */
21
22 /*
23   this is the core code for converting a filename from the format as
24   given by a client to a posix filename, including any case-matching
25   required, and checks for legal characters
26 */
27
28
29 #include "includes.h"
30 #include "vfs_posix.h"
31 #include "system/dir.h"
32 #include "param/param.h"
33
34 /**
35   compare two filename components. This is where the name mangling hook will go
36 */
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 */
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 */
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                 /*
206                  * we don't set stream_name to NULL, here
207                  * as this would be wrong for directories
208                  *
209                  * pvfs_fill_dos_info() will set it to NULL
210                  * if it's not a directory.
211                  */
212                 name->stream_id = 0;
213         } else {
214                 name->stream_id = pvfs_name_hash(name->stream_name, 
215                                                  strlen(name->stream_name));
216         }
217                                                  
218         return NT_STATUS_OK;    
219 }
220
221
222 /*
223   convert a CIFS pathname to a unix pathname. Note that this does NOT
224   take into account case insensitivity, and in fact does not access
225   the filesystem at all. It is merely a reformatting and charset
226   checking routine.
227
228   errors are returned if the filename is illegal given the flags
229 */
230 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
231                                uint_t flags, struct pvfs_filename *name)
232 {
233         char *ret, *p, *p_start;
234         NTSTATUS status;
235
236         name->original_name = talloc_strdup(name, cifs_name);
237         name->stream_name = NULL;
238         name->stream_id = 0;
239         name->has_wildcard = false;
240
241         while (*cifs_name == '\\') {
242                 cifs_name++;
243         }
244
245         if (*cifs_name == 0) {
246                 name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
247                 if (name->full_name == NULL) {
248                         return NT_STATUS_NO_MEMORY;
249                 }
250                 return NT_STATUS_OK;
251         }
252
253         ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
254         if (ret == NULL) {
255                 return NT_STATUS_NO_MEMORY;
256         }
257
258         p = ret + strlen(pvfs->base_directory) + 1;
259
260         /* now do an in-place conversion of '\' to '/', checking
261            for legal characters */
262         p_start = p;
263
264         while (*p) {
265                 size_t c_size;
266                 codepoint_t c = next_codepoint(lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), p, &c_size);
267                 switch (c) {
268                 case '\\':
269                         if (name->has_wildcard) {
270                                 /* wildcards are only allowed in the last part
271                                    of a name */
272                                 return NT_STATUS_ILLEGAL_CHARACTER;
273                         }
274                         if (p > p_start && (p[1] == '\\' || p[1] == '\0')) {
275                                 /* see if it is definately a "\\" or
276                                  * a trailing "\". If it is then fail here,
277                                  * and let the next layer up try again after
278                                  * pvfs_reduce_name() if it wants to. This is
279                                  * much more efficient on average than always
280                                  * scanning for these separately
281                                  */
282                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
283                         } else {
284                                 *p = '/';
285                         }
286                         break;
287                 case ':':
288                         if (!(flags & PVFS_RESOLVE_STREAMS)) {
289                                 return NT_STATUS_ILLEGAL_CHARACTER;
290                         }
291                         if (name->has_wildcard) {
292                                 return NT_STATUS_ILLEGAL_CHARACTER;
293                         }
294                         status = parse_stream_name(name, p);
295                         if (!NT_STATUS_IS_OK(status)) {
296                                 return status;
297                         }
298                         *p-- = 0;
299                         break;
300                 case '*':
301                 case '>':
302                 case '<':
303                 case '?':
304                 case '"':
305                         if (!(flags & PVFS_RESOLVE_WILDCARD)) {
306                                 return NT_STATUS_OBJECT_NAME_INVALID;
307                         }
308                         name->has_wildcard = true;
309                         break;
310                 case '/':
311                 case '|':
312                         return NT_STATUS_ILLEGAL_CHARACTER;
313                 case '.':
314                         /* see if it is definately a .. or
315                            . component. If it is then fail here, and
316                            let the next layer up try again after
317                            pvfs_reduce_name() if it wants to. This is
318                            much more efficient on average than always
319                            scanning for these separately */
320                         if (p[1] == '.' && 
321                             (p[2] == 0 || p[2] == '\\') &&
322                             (p == p_start || p[-1] == '/')) {
323                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
324                         }
325                         if ((p[1] == 0 || p[1] == '\\') &&
326                             (p == p_start || p[-1] == '/')) {
327                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
328                         }
329                         break;
330                 }
331
332                 p += c_size;
333         }
334
335         name->full_name = ret;
336
337         return NT_STATUS_OK;
338 }
339
340
341 /*
342   reduce a name that contains .. components or repeated \ separators
343   return NULL if it can't be reduced
344 */
345 static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, 
346                                  struct smb_iconv_convenience *iconv_convenience, 
347                                  const char **fname, uint_t flags)
348 {
349         codepoint_t c;
350         size_t c_size, len;
351         int i, num_components, err_count;
352         char **components;
353         char *p, *s, *ret;
354
355         s = talloc_strdup(mem_ctx, *fname);
356         if (s == NULL) return NT_STATUS_NO_MEMORY;
357
358         for (num_components=1, p=s; *p; p += c_size) {
359                 c = next_codepoint(iconv_convenience, p, &c_size);
360                 if (c == '\\') num_components++;
361         }
362
363         components = talloc_array(s, char *, num_components+1);
364         if (components == NULL) {
365                 talloc_free(s);
366                 return NT_STATUS_NO_MEMORY;
367         }
368
369         components[0] = s;
370         for (i=0, p=s; *p; p += c_size) {
371                 c = next_codepoint(iconv_convenience, p, &c_size);
372                 if (c == '\\') {
373                         *p = 0;
374                         components[++i] = p+1;
375                 }
376         }
377         components[i+1] = NULL;
378
379         /*
380           rather bizarre!
381
382           '.' components are not allowed, but the rules for what error
383           code to give don't seem to make sense. This is a close
384           approximation.
385         */
386         for (err_count=i=0;components[i];i++) {
387                 if (strcmp(components[i], "") == 0) {
388                         continue;
389                 }
390                 if (ISDOT(components[i]) || err_count) {
391                         err_count++;
392                 }
393         }
394         if (err_count) {
395                 if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
396
397                 if (err_count==1) {
398                         return NT_STATUS_OBJECT_NAME_INVALID;
399                 } else {
400                         return NT_STATUS_OBJECT_PATH_NOT_FOUND;
401                 }
402         }
403
404         /* remove any null components */
405         for (i=0;components[i];i++) {
406                 if (strcmp(components[i], "") == 0) {
407                         memmove(&components[i], &components[i+1], 
408                                 sizeof(char *)*(num_components-i));
409                         i--;
410                         continue;
411                 }
412                 if (ISDOTDOT(components[i])) {
413                         if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
414                         memmove(&components[i-1], &components[i+1], 
415                                 sizeof(char *)*(num_components-i));
416                         i -= 2;
417                         continue;
418                 }
419         }
420
421         if (components[0] == NULL) {
422                 talloc_free(s);
423                 *fname = talloc_strdup(mem_ctx, "\\");
424                 return NT_STATUS_OK;
425         }
426
427         for (len=i=0;components[i];i++) {
428                 len += strlen(components[i]) + 1;
429         }
430
431         /* rebuild the name */
432         ret = talloc_size(mem_ctx, len+1);
433         if (ret == NULL) {
434                 talloc_free(s);
435                 return NT_STATUS_NO_MEMORY;
436         }
437
438         for (len=0,i=0;components[i];i++) {
439                 size_t len1 = strlen(components[i]);
440                 ret[len] = '\\';
441                 memcpy(ret+len+1, components[i], len1);
442                 len += len1 + 1;
443         }       
444         ret[len] = 0;
445
446         talloc_free(s);
447
448         *fname = ret;
449         
450         return NT_STATUS_OK;
451 }
452
453
454 /*
455   resolve a name from relative client format to a struct pvfs_filename
456   the memory for the filename is made as a talloc child of 'name'
457
458   flags include:
459      PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
460      PVFS_RESOLVE_STREAMS     = stream names are allowed
461
462      TODO: ../ collapsing, and outside share checking
463 */
464 NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
465                            const char *cifs_name,
466                            uint_t flags, struct pvfs_filename **name)
467 {
468         NTSTATUS status;
469
470         *name = talloc(mem_ctx, struct pvfs_filename);
471         if (*name == NULL) {
472                 return NT_STATUS_NO_MEMORY;
473         }
474
475         (*name)->exists = false;
476         (*name)->stream_exists = false;
477
478         if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
479                 flags &= ~PVFS_RESOLVE_STREAMS;
480         }
481
482         /* do the basic conversion to a unix formatted path,
483            also checking for allowable characters */
484         status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
485
486         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
487                 /* it might contain .. components which need to be reduced */
488                 status = pvfs_reduce_name(*name, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), &cifs_name, flags);
489                 if (!NT_STATUS_IS_OK(status)) {
490                         return status;
491                 }
492                 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
493         }
494
495         if (!NT_STATUS_IS_OK(status)) {
496                 return status;
497         }
498
499         /* if it has a wildcard then no point doing a stat() of the
500            full name. Instead We need check if the directory exists 
501          */
502         if ((*name)->has_wildcard) {
503                 const char *p;
504                 char *dir_name, *saved_name;
505                 p = strrchr((*name)->full_name, '/');
506                 if (p == NULL) {
507                         /* root directory wildcard is OK */
508                         return NT_STATUS_OK;
509                 }
510                 dir_name = talloc_strndup(*name, (*name)->full_name, (p-(*name)->full_name));
511                 if (stat(dir_name, &(*name)->st) == 0) {
512                         talloc_free(dir_name);
513                         return NT_STATUS_OK;
514                 }
515                 /* we need to search for a matching name */
516                 saved_name = (*name)->full_name;
517                 (*name)->full_name = dir_name;
518                 status = pvfs_case_search(pvfs, *name);
519                 if (!NT_STATUS_IS_OK(status)) {
520                         /* the directory doesn't exist */
521                         (*name)->full_name = saved_name;
522                         return status;
523                 }
524                 /* it does exist, but might need a case change */
525                 if (dir_name != (*name)->full_name) {
526                         (*name)->full_name = talloc_asprintf(*name, "%s%s",
527                                                              (*name)->full_name, p);
528                         NT_STATUS_HAVE_NO_MEMORY((*name)->full_name);
529                 } else {
530                         (*name)->full_name = saved_name;
531                         talloc_free(dir_name);
532                 }
533                 return NT_STATUS_OK;
534         }
535
536         /* if we can stat() the full name now then we are done */
537         if (stat((*name)->full_name, &(*name)->st) == 0) {
538                 (*name)->exists = true;
539                 return pvfs_fill_dos_info(pvfs, *name, -1);
540         }
541
542         /* search for a matching filename */
543         status = pvfs_case_search(pvfs, *name);
544
545         return status;
546 }
547
548
549 /*
550   do a partial resolve, returning a pvfs_filename structure given a
551   base path and a relative component. It is an error if the file does
552   not exist. No case-insensitive matching is done.
553
554   this is used in places like directory searching where we need a pvfs_filename
555   to pass to a function, but already know the unix base directory and component
556 */
557 NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
558                               const char *unix_dir, const char *fname,
559                               struct pvfs_filename **name)
560 {
561         NTSTATUS status;
562
563         *name = talloc(mem_ctx, struct pvfs_filename);
564         if (*name == NULL) {
565                 return NT_STATUS_NO_MEMORY;
566         }
567
568         (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
569         if ((*name)->full_name == NULL) {
570                 return NT_STATUS_NO_MEMORY;
571         }
572
573         if (stat((*name)->full_name, &(*name)->st) == -1) {
574                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
575         }
576
577         (*name)->exists = true;
578         (*name)->stream_exists = true;
579         (*name)->has_wildcard = false;
580         (*name)->original_name = talloc_strdup(*name, fname);
581         (*name)->stream_name = NULL;
582         (*name)->stream_id = 0;
583
584         status = pvfs_fill_dos_info(pvfs, *name, -1);
585
586         return status;
587 }
588
589
590 /*
591   fill in the pvfs_filename info for an open file, given the current
592   info for a (possibly) non-open file. This is used by places that need
593   to update the pvfs_filename stat information, and by pvfs_open()
594 */
595 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
596                               struct pvfs_filename *name)
597 {
598         dev_t device = (dev_t)0;
599         ino_t inode = 0;
600
601         if (name->exists) {
602                 device = name->st.st_dev;
603                 inode = name->st.st_ino;
604         }
605
606         if (fd == -1) {
607                 if (stat(name->full_name, &name->st) == -1) {
608                         return NT_STATUS_INVALID_HANDLE;
609                 }
610         } else {
611                 if (fstat(fd, &name->st) == -1) {
612                         return NT_STATUS_INVALID_HANDLE;
613                 }
614         }
615
616         if (name->exists &&
617             (device != name->st.st_dev || inode != name->st.st_ino)) {
618                 /* the file we are looking at has changed! this could
619                  be someone trying to exploit a race
620                  condition. Certainly we don't want to continue
621                  operating on this file */
622                 DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
623                          name->full_name));
624                 return NT_STATUS_UNEXPECTED_IO_ERROR;
625         }
626
627         name->exists = true;
628         
629         return pvfs_fill_dos_info(pvfs, name, fd);
630 }
631
632 /*
633   fill in the pvfs_filename info for an open file, given the current
634   info for a (possibly) non-open file. This is used by places that need
635   to update the pvfs_filename stat information, and the path
636   after a possible rename on a different handle.
637 */
638 NTSTATUS pvfs_resolve_name_handle(struct pvfs_state *pvfs,
639                                   struct pvfs_file_handle *h)
640 {
641         NTSTATUS status;
642
643         if (h->have_opendb_entry) {
644                 struct odb_lock *lck;
645                 const char *name = NULL;
646
647                 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
648                 if (lck == NULL) {
649                         DEBUG(0,("%s: failed to lock file '%s' in opendb\n",
650                                  __FUNCTION__, h->name->full_name));
651                         /* we were supposed to do a blocking lock, so something
652                            is badly wrong! */
653                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
654                 }
655
656                 status = odb_get_path(lck, &name);
657                 if (NT_STATUS_IS_OK(status)) {
658                         /*
659                          * This relies an the fact that
660                          * renames of open files are only
661                          * allowed by setpathinfo() and setfileinfo()
662                          * and there're only renames within the same
663                          * directory supported
664                          */
665                         if (strcmp(h->name->full_name, name) != 0) {
666                                 const char *orig_dir;
667                                 const char *new_file;
668                                 const char *new_orig;
669                                 char *delim;
670
671                                 delim = strrchr(name, '/');
672                                 if (!delim) {
673                                         talloc_free(lck);
674                                         return NT_STATUS_INTERNAL_ERROR;
675                                 }
676
677                                 new_file = delim + 1;
678                                 delim = strrchr(h->name->original_name, '\\');
679                                 if (delim) {
680                                         delim[0] = '\0';
681                                         orig_dir = h->name->original_name;
682                                         new_orig = talloc_asprintf(h->name, "%s\\%s",
683                                                                    orig_dir, new_file);
684                                         if (!new_orig) {
685                                                 talloc_free(lck);
686                                                 return NT_STATUS_NO_MEMORY;
687                                         }
688                                 } else {
689                                         new_orig = talloc_strdup(h->name, new_file);
690                                         if (!new_orig) {
691                                                 talloc_free(lck);
692                                                 return NT_STATUS_NO_MEMORY;
693                                         }
694                                 }
695
696                                 talloc_free(h->name->original_name);
697                                 talloc_free(h->name->full_name);
698                                 h->name->full_name = talloc_steal(h->name, name);
699                                 h->name->original_name = new_orig;
700                         }
701                 }
702
703                 talloc_free(lck);
704         }
705
706         status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
707         NT_STATUS_NOT_OK_RETURN(status);
708
709         return NT_STATUS_OK;
710 }
711
712
713 /*
714   resolve the parent of a given name
715 */
716 NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
717                              const struct pvfs_filename *child,
718                              struct pvfs_filename **name)
719 {
720         NTSTATUS status;
721         char *p;
722
723         *name = talloc(mem_ctx, struct pvfs_filename);
724         if (*name == NULL) {
725                 return NT_STATUS_NO_MEMORY;
726         }
727
728         (*name)->full_name = talloc_strdup(*name, child->full_name);
729         if ((*name)->full_name == NULL) {
730                 return NT_STATUS_NO_MEMORY;
731         }
732
733         p = strrchr_m((*name)->full_name, '/');
734         if (p == NULL) {
735                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
736         }
737
738         /* this handles the root directory */
739         if (p == (*name)->full_name) {
740                 p[1] = 0;
741         } else {
742                 p[0] = 0;
743         }
744
745         if (stat((*name)->full_name, &(*name)->st) == -1) {
746                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
747         }
748
749         (*name)->exists = true;
750         (*name)->stream_exists = true;
751         (*name)->has_wildcard = false;
752         /* we can't get the correct 'original_name', but for the purposes
753            of this call this is close enough */
754         (*name)->original_name = talloc_reference(*name, child->original_name);
755         (*name)->stream_name = NULL;
756         (*name)->stream_id = 0;
757
758         status = pvfs_fill_dos_info(pvfs, *name, -1);
759
760         return status;
761 }