s4:ntvfs: Fix size type in pvfs functions
[metze/samba/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 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,
61                                  struct pvfs_filename *name,
62                                  unsigned int flags)
63 {
64         /* break into a series of components */
65         size_t num_components;
66         char **components;
67         char *p, *partial_name;
68         size_t i;
69
70         /* break up the full name info pathname components */
71         num_components=2;
72         p = name->full_name + strlen(pvfs->base_directory) + 1;
73
74         for (;*p;p++) {
75                 if (*p == '/') {
76                         num_components++;
77                 }
78         }
79
80         components = talloc_array(name, char *, num_components);
81         p = name->full_name + strlen(pvfs->base_directory);
82         *p++ = 0;
83
84         components[0] = name->full_name;
85
86         for (i=1;i<num_components;i++) {
87                 components[i] = p;
88                 p = strchr(p, '/');
89                 if (p) *p++ = 0;
90                 if (pvfs_is_reserved_name(pvfs, components[i])) {
91                         return NT_STATUS_ACCESS_DENIED;
92                 }
93         }
94
95         partial_name = talloc_strdup(name, components[0]);
96         if (!partial_name) {
97                 return NT_STATUS_NO_MEMORY;
98         }
99
100         /* for each component, check if it exists as-is, and if not then
101            do a directory scan */
102         for (i=1;i<num_components;i++) {
103                 char *test_name;
104                 DIR *dir;
105                 struct dirent *de;
106                 char *long_component;
107
108                 /* possibly remap from the short name cache */
109                 long_component = pvfs_mangled_lookup(pvfs, name, components[i]);
110                 if (long_component) {
111                         components[i] = long_component;
112                 }
113
114                 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
115                 if (!test_name) {
116                         return NT_STATUS_NO_MEMORY;
117                 }
118
119                 /* check if this component exists as-is */
120                 if (stat(test_name, &name->st) == 0) {
121                         if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
122                                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
123                         }
124                         talloc_free(partial_name);
125                         partial_name = test_name;
126                         if (i == num_components - 1) {
127                                 name->exists = true;
128                         }
129                         continue;
130                 }
131
132                 /* the filesystem might be case insensitive, in which
133                    case a search is pointless unless the name is
134                    mangled */
135                 if ((pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) &&
136                     !pvfs_is_mangled_component(pvfs, components[i])) {
137                         if (i < num_components-1) {
138                                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
139                         }
140                         partial_name = test_name;
141                         continue;
142                 }
143                 
144                 dir = opendir(partial_name);
145                 if (!dir) {
146                         return pvfs_map_errno(pvfs, errno);
147                 }
148
149                 while ((de = readdir(dir))) {
150                         if (component_compare(pvfs, components[i], de->d_name) == 0) {
151                                 break;
152                         }
153                 }
154
155                 if (!de) {
156                         if (i < num_components-1) {
157                                 closedir(dir);
158                                 return NT_STATUS_OBJECT_PATH_NOT_FOUND;
159                         }
160                 } else {
161                         components[i] = talloc_strdup(name, de->d_name);
162                 }
163                 test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
164                 talloc_free(partial_name);
165                 partial_name = test_name;
166
167                 closedir(dir);
168         }
169
170         if (!name->exists) {
171                 if (stat(partial_name, &name->st) == 0) {
172                         name->exists = true;
173                 }
174         }
175
176         talloc_free(name->full_name);
177         name->full_name = partial_name;
178
179         if (name->exists) {
180                 return pvfs_fill_dos_info(pvfs, name, flags, -1);
181         }
182
183         return NT_STATUS_OK;
184 }
185
186 /*
187   parse a alternate data stream name
188 */
189 static NTSTATUS parse_stream_name(struct pvfs_filename *name,
190                                   const char *s)
191 {
192         char *p, *stream_name;
193         if (s[1] == '\0') {
194                 return NT_STATUS_OBJECT_NAME_INVALID;
195         }
196         name->stream_name = stream_name = talloc_strdup(name, s+1);
197         if (name->stream_name == NULL) {
198                 return NT_STATUS_NO_MEMORY;
199         }
200
201         p = stream_name;
202
203         while (*p) {
204                 size_t c_size;
205                 codepoint_t c = next_codepoint(p, &c_size);
206
207                 switch (c) {
208                 case '/':
209                 case '\\':
210                         return NT_STATUS_OBJECT_NAME_INVALID;
211                 case ':':
212                         *p= 0;
213                         p++;
214                         if (*p == '\0') {
215                                 return NT_STATUS_OBJECT_NAME_INVALID;
216                         }
217                         if (strcasecmp_m(p, "$DATA") != 0) {
218                                 if (strchr_m(p, ':')) {
219                                         return NT_STATUS_OBJECT_NAME_INVALID;
220                                 }
221                                 return NT_STATUS_INVALID_PARAMETER;
222                         }
223                         c_size = 0;
224                         p--;
225                         break;
226                 }
227
228                 p += c_size;
229         }
230
231         if (strcmp(name->stream_name, "") == 0) {
232                 /*
233                  * we don't set stream_name to NULL, here
234                  * as this would be wrong for directories
235                  *
236                  * pvfs_fill_dos_info() will set it to NULL
237                  * if it's not a directory.
238                  */
239                 name->stream_id = 0;
240         } else {
241                 name->stream_id = pvfs_name_hash(name->stream_name, 
242                                                  strlen(name->stream_name));
243         }
244                                                  
245         return NT_STATUS_OK;    
246 }
247
248
249 /*
250   convert a CIFS pathname to a unix pathname. Note that this does NOT
251   take into account case insensitivity, and in fact does not access
252   the filesystem at all. It is merely a reformatting and charset
253   checking routine.
254
255   errors are returned if the filename is illegal given the flags
256 */
257 static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
258                                unsigned int flags, struct pvfs_filename *name)
259 {
260         char *ret, *p, *p_start;
261         NTSTATUS status;
262
263         name->original_name = talloc_strdup(name, cifs_name);
264
265         /* remove any :$DATA */
266         p = strrchr(name->original_name, ':');
267         if (p && strcasecmp_m(p, ":$DATA") == 0) {
268                 if (p > name->original_name && p[-1] == ':') {
269                         p--;
270                 }
271                 *p = 0;
272         }
273
274         name->stream_name = NULL;
275         name->stream_id = 0;
276         name->has_wildcard = false;
277
278         while (*cifs_name == '\\') {
279                 cifs_name++;
280         }
281
282         if (*cifs_name == 0) {
283                 name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
284                 if (name->full_name == NULL) {
285                         return NT_STATUS_NO_MEMORY;
286                 }
287                 return NT_STATUS_OK;
288         }
289
290         ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
291         if (ret == NULL) {
292                 return NT_STATUS_NO_MEMORY;
293         }
294
295         p = ret + strlen(pvfs->base_directory) + 1;
296
297         /* now do an in-place conversion of '\' to '/', checking
298            for legal characters */
299         p_start = p;
300
301         while (*p) {
302                 size_t c_size;
303                 codepoint_t c = next_codepoint(p, &c_size);
304
305                 if (c <= 0x1F) {
306                         return NT_STATUS_OBJECT_NAME_INVALID;
307                 }
308
309                 switch (c) {
310                 case '\\':
311                         if (name->has_wildcard) {
312                                 /* wildcards are only allowed in the last part
313                                    of a name */
314                                 return NT_STATUS_OBJECT_NAME_INVALID;
315                         }
316                         if (p > p_start && (p[1] == '\\' || p[1] == '\0')) {
317                                 /* see if it is definately a "\\" or
318                                  * a trailing "\". If it is then fail here,
319                                  * and let the next layer up try again after
320                                  * pvfs_reduce_name() if it wants to. This is
321                                  * much more efficient on average than always
322                                  * scanning for these separately
323                                  */
324                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
325                         } else {
326                                 *p = '/';
327                         }
328                         break;
329                 case ':':
330                         if (!(flags & PVFS_RESOLVE_STREAMS)) {
331                                 return NT_STATUS_OBJECT_NAME_INVALID;
332                         }
333                         if (name->has_wildcard) {
334                                 return NT_STATUS_OBJECT_NAME_INVALID;
335                         }
336                         status = parse_stream_name(name, p);
337                         if (!NT_STATUS_IS_OK(status)) {
338                                 return status;
339                         }
340                         *p-- = 0;
341                         break;
342                 case '*':
343                 case '>':
344                 case '<':
345                 case '?':
346                 case '"':
347                         if (!(flags & PVFS_RESOLVE_WILDCARD)) {
348                                 return NT_STATUS_OBJECT_NAME_INVALID;
349                         }
350                         name->has_wildcard = true;
351                         break;
352                 case '/':
353                 case '|':
354                         return NT_STATUS_OBJECT_NAME_INVALID;
355                 case '.':
356                         /* see if it is definately a .. or
357                            . component. If it is then fail here, and
358                            let the next layer up try again after
359                            pvfs_reduce_name() if it wants to. This is
360                            much more efficient on average than always
361                            scanning for these separately */
362                         if (p[1] == '.' && 
363                             (p[2] == 0 || p[2] == '\\') &&
364                             (p == p_start || p[-1] == '/')) {
365                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
366                         }
367                         if ((p[1] == 0 || p[1] == '\\') &&
368                             (p == p_start || p[-1] == '/')) {
369                                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
370                         }
371                         break;
372                 }
373
374                 p += c_size;
375         }
376
377         name->full_name = ret;
378
379         return NT_STATUS_OK;
380 }
381
382
383 /*
384   reduce a name that contains .. components or repeated \ separators
385   return NULL if it can't be reduced
386 */
387 static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, 
388                                  const char **fname, unsigned int flags)
389 {
390         codepoint_t c;
391         size_t c_size, len;
392         size_t i, num_components, err_count;
393         char **components;
394         char *p, *s, *ret;
395
396         s = talloc_strdup(mem_ctx, *fname);
397         if (s == NULL) return NT_STATUS_NO_MEMORY;
398
399         for (num_components=1, p=s; *p; p += c_size) {
400                 c = next_codepoint(p, &c_size);
401                 if (c == '\\') num_components++;
402         }
403
404         components = talloc_array(s, char *, num_components+1);
405         if (components == NULL) {
406                 talloc_free(s);
407                 return NT_STATUS_NO_MEMORY;
408         }
409
410         components[0] = s;
411         for (i=0, p=s; *p; p += c_size) {
412                 c = next_codepoint(p, &c_size);
413                 if (c == '\\') {
414                         *p = 0;
415                         components[++i] = p+1;
416                 }
417         }
418         components[i+1] = NULL;
419
420         /*
421           rather bizarre!
422
423           '.' components are not allowed, but the rules for what error
424           code to give don't seem to make sense. This is a close
425           approximation.
426         */
427         for (err_count=i=0;components[i];i++) {
428                 if (strcmp(components[i], "") == 0) {
429                         continue;
430                 }
431                 if (ISDOT(components[i]) || err_count) {
432                         err_count++;
433                 }
434         }
435         if (err_count > 0) {
436                 if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
437
438                 if (err_count==1) {
439                         return NT_STATUS_OBJECT_NAME_INVALID;
440                 } else {
441                         return NT_STATUS_OBJECT_PATH_NOT_FOUND;
442                 }
443         }
444
445         /* remove any null components */
446         for (i=0;components[i];i++) {
447                 if (strcmp(components[i], "") == 0) {
448                         memmove(&components[i], &components[i+1], 
449                                 sizeof(char *)*(num_components-i));
450                         i--;
451                         continue;
452                 }
453                 if (ISDOTDOT(components[i])) {
454                         if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
455                         memmove(&components[i-1], &components[i+1], 
456                                 sizeof(char *)*(num_components-i));
457                         i -= 2;
458                         continue;
459                 }
460         }
461
462         if (components[0] == NULL) {
463                 talloc_free(s);
464                 *fname = talloc_strdup(mem_ctx, "\\");
465                 return NT_STATUS_OK;
466         }
467
468         for (len=i=0;components[i];i++) {
469                 len += strlen(components[i]) + 1;
470         }
471
472         /* rebuild the name */
473         ret = talloc_array(mem_ctx, char, len+1);
474         if (ret == NULL) {
475                 talloc_free(s);
476                 return NT_STATUS_NO_MEMORY;
477         }
478
479         for (len=0,i=0;components[i];i++) {
480                 size_t len1 = strlen(components[i]);
481                 ret[len] = '\\';
482                 memcpy(ret+len+1, components[i], len1);
483                 len += len1 + 1;
484         }       
485         ret[len] = 0;
486
487         talloc_set_name_const(ret, ret);
488
489         talloc_free(s);
490
491         *fname = ret;
492         
493         return NT_STATUS_OK;
494 }
495
496
497 /*
498   resolve a name from relative client format to a struct pvfs_filename
499   the memory for the filename is made as a talloc child of 'name'
500
501   flags include:
502      PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
503      PVFS_RESOLVE_STREAMS     = stream names are allowed
504
505      TODO: ../ collapsing, and outside share checking
506 */
507 NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, 
508                            struct ntvfs_request *req,
509                            const char *cifs_name,
510                            unsigned int flags, struct pvfs_filename **name)
511 {
512         NTSTATUS status;
513
514         *name = talloc(req, struct pvfs_filename);
515         if (*name == NULL) {
516                 return NT_STATUS_NO_MEMORY;
517         }
518
519         (*name)->exists = false;
520         (*name)->stream_exists = false;
521         (*name)->allow_override = false;
522
523         if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
524                 flags &= ~PVFS_RESOLVE_STREAMS;
525         }
526
527         /* SMB2 doesn't allow a leading slash */
528         if (req->ctx->protocol >= PROTOCOL_SMB2_02 &&
529             *cifs_name == '\\') {
530                 return NT_STATUS_INVALID_PARAMETER;
531         }
532
533         /* do the basic conversion to a unix formatted path,
534            also checking for allowable characters */
535         status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
536
537         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
538                 /* it might contain .. components which need to be reduced */
539                 status = pvfs_reduce_name(*name, &cifs_name, flags);
540                 if (!NT_STATUS_IS_OK(status)) {
541                         return status;
542                 }
543                 status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
544         }
545
546         if (!NT_STATUS_IS_OK(status)) {
547                 return status;
548         }
549
550         /* if it has a wildcard then no point doing a stat() of the
551            full name. Instead We need check if the directory exists 
552          */
553         if ((*name)->has_wildcard) {
554                 const char *p;
555                 char *dir_name, *saved_name;
556                 p = strrchr((*name)->full_name, '/');
557                 if (p == NULL) {
558                         /* root directory wildcard is OK */
559                         return NT_STATUS_OK;
560                 }
561                 dir_name = talloc_strndup(*name, (*name)->full_name, (p-(*name)->full_name));
562                 if (stat(dir_name, &(*name)->st) == 0) {
563                         talloc_free(dir_name);
564                         return NT_STATUS_OK;
565                 }
566                 /* we need to search for a matching name */
567                 saved_name = (*name)->full_name;
568                 (*name)->full_name = dir_name;
569                 status = pvfs_case_search(pvfs, *name, flags);
570                 if (!NT_STATUS_IS_OK(status)) {
571                         /* the directory doesn't exist */
572                         (*name)->full_name = saved_name;
573                         return status;
574                 }
575                 /* it does exist, but might need a case change */
576                 if (dir_name != (*name)->full_name) {
577                         (*name)->full_name = talloc_asprintf(*name, "%s%s",
578                                                              (*name)->full_name, p);
579                         NT_STATUS_HAVE_NO_MEMORY((*name)->full_name);
580                 } else {
581                         (*name)->full_name = saved_name;
582                         talloc_free(dir_name);
583                 }
584                 return NT_STATUS_OK;
585         }
586
587         /* if we can stat() the full name now then we are done */
588         if (stat((*name)->full_name, &(*name)->st) == 0) {
589                 (*name)->exists = true;
590                 return pvfs_fill_dos_info(pvfs, *name, flags, -1);
591         }
592
593         /* search for a matching filename */
594         status = pvfs_case_search(pvfs, *name, flags);
595
596         return status;
597 }
598
599
600 /*
601   do a partial resolve, returning a pvfs_filename structure given a
602   base path and a relative component. It is an error if the file does
603   not exist. No case-insensitive matching is done.
604
605   this is used in places like directory searching where we need a pvfs_filename
606   to pass to a function, but already know the unix base directory and component
607 */
608 NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
609                               const char *unix_dir, const char *fname,
610                               unsigned int flags, struct pvfs_filename **name)
611 {
612         NTSTATUS status;
613
614         *name = talloc(mem_ctx, struct pvfs_filename);
615         if (*name == NULL) {
616                 return NT_STATUS_NO_MEMORY;
617         }
618
619         (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
620         if ((*name)->full_name == NULL) {
621                 return NT_STATUS_NO_MEMORY;
622         }
623
624         if (stat((*name)->full_name, &(*name)->st) == -1) {
625                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
626         }
627
628         (*name)->exists = true;
629         (*name)->stream_exists = true;
630         (*name)->has_wildcard = false;
631         (*name)->original_name = talloc_strdup(*name, fname);
632         (*name)->stream_name = NULL;
633         (*name)->stream_id = 0;
634         (*name)->allow_override = false;
635
636         status = pvfs_fill_dos_info(pvfs, *name, flags, -1);
637
638         return status;
639 }
640
641
642 /*
643   fill in the pvfs_filename info for an open file, given the current
644   info for a (possibly) non-open file. This is used by places that need
645   to update the pvfs_filename stat information, and by pvfs_open()
646 */
647 NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
648                               struct pvfs_filename *name, unsigned int flags)
649 {
650         dev_t device = (dev_t)0;
651         ino_t inode = 0;
652
653         if (name->exists) {
654                 device = name->st.st_dev;
655                 inode = name->st.st_ino;
656         }
657
658         if (fd == -1) {
659                 if (stat(name->full_name, &name->st) == -1) {
660                         return NT_STATUS_INVALID_HANDLE;
661                 }
662         } else {
663                 if (fstat(fd, &name->st) == -1) {
664                         return NT_STATUS_INVALID_HANDLE;
665                 }
666         }
667
668         if (name->exists &&
669             (device != name->st.st_dev || inode != name->st.st_ino)) {
670                 /* the file we are looking at has changed! this could
671                  be someone trying to exploit a race
672                  condition. Certainly we don't want to continue
673                  operating on this file */
674                 DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
675                          name->full_name));
676                 return NT_STATUS_UNEXPECTED_IO_ERROR;
677         }
678
679         name->exists = true;
680         
681         return pvfs_fill_dos_info(pvfs, name, flags, fd);
682 }
683
684 /*
685   fill in the pvfs_filename info for an open file, given the current
686   info for a (possibly) non-open file. This is used by places that need
687   to update the pvfs_filename stat information, and the path
688   after a possible rename on a different handle.
689 */
690 NTSTATUS pvfs_resolve_name_handle(struct pvfs_state *pvfs,
691                                   struct pvfs_file_handle *h)
692 {
693         NTSTATUS status;
694
695         if (h->have_opendb_entry) {
696                 struct odb_lock *lck;
697                 const char *name = NULL;
698
699                 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
700                 if (lck == NULL) {
701                         DEBUG(0,("%s: failed to lock file '%s' in opendb\n",
702                                  __FUNCTION__, h->name->full_name));
703                         /* we were supposed to do a blocking lock, so something
704                            is badly wrong! */
705                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
706                 }
707
708                 status = odb_get_path(lck, &name);
709                 if (NT_STATUS_IS_OK(status)) {
710                         /*
711                          * This relies an the fact that
712                          * renames of open files are only
713                          * allowed by setpathinfo() and setfileinfo()
714                          * and there're only renames within the same
715                          * directory supported
716                          */
717                         if (strcmp(h->name->full_name, name) != 0) {
718                                 const char *orig_dir;
719                                 const char *new_file;
720                                 char *new_orig;
721                                 char *delim;
722                                 char *full_name = discard_const_p(char, name);
723
724                                 delim = strrchr(name, '/');
725                                 if (!delim) {
726                                         talloc_free(lck);
727                                         return NT_STATUS_INTERNAL_ERROR;
728                                 }
729
730                                 new_file = delim + 1;
731                                 delim = strrchr(h->name->original_name, '\\');
732                                 if (delim) {
733                                         delim[0] = '\0';
734                                         orig_dir = h->name->original_name;
735                                         new_orig = talloc_asprintf(h->name, "%s\\%s",
736                                                                    orig_dir, new_file);
737                                         if (!new_orig) {
738                                                 talloc_free(lck);
739                                                 return NT_STATUS_NO_MEMORY;
740                                         }
741                                 } else {
742                                         new_orig = talloc_strdup(h->name, new_file);
743                                         if (!new_orig) {
744                                                 talloc_free(lck);
745                                                 return NT_STATUS_NO_MEMORY;
746                                         }
747                                 }
748
749                                 talloc_free(h->name->original_name);
750                                 talloc_free(h->name->full_name);
751                                 h->name->full_name = talloc_steal(h->name, full_name);
752                                 h->name->original_name = new_orig;
753                         }
754                 }
755
756                 talloc_free(lck);
757         }
758
759         /*
760          * TODO: pass PVFS_RESOLVE_NO_OPENDB and get
761          *       the write time from odb_lock() above.
762          */
763         status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, 0);
764         NT_STATUS_NOT_OK_RETURN(status);
765
766         if (!null_nttime(h->write_time.close_time)) {
767                 h->name->dos.write_time = h->write_time.close_time;
768         }
769
770         return NT_STATUS_OK;
771 }
772
773
774 /*
775   resolve the parent of a given name
776 */
777 NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
778                              const struct pvfs_filename *child,
779                              struct pvfs_filename **name)
780 {
781         NTSTATUS status;
782         char *p;
783
784         *name = talloc(mem_ctx, struct pvfs_filename);
785         if (*name == NULL) {
786                 return NT_STATUS_NO_MEMORY;
787         }
788
789         (*name)->full_name = talloc_strdup(*name, child->full_name);
790         if ((*name)->full_name == NULL) {
791                 return NT_STATUS_NO_MEMORY;
792         }
793
794         p = strrchr_m((*name)->full_name, '/');
795         if (p == NULL) {
796                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
797         }
798
799         /* this handles the root directory */
800         if (p == (*name)->full_name) {
801                 p[1] = 0;
802         } else {
803                 p[0] = 0;
804         }
805
806         if (stat((*name)->full_name, &(*name)->st) == -1) {
807                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
808         }
809
810         (*name)->exists = true;
811         (*name)->stream_exists = true;
812         (*name)->has_wildcard = false;
813         /* we can't get the correct 'original_name', but for the purposes
814            of this call this is close enough */
815         (*name)->original_name = talloc_strdup(*name, child->original_name);
816         if ((*name)->original_name == NULL) {
817                 return NT_STATUS_NO_MEMORY;
818         }
819         (*name)->stream_name = NULL;
820         (*name)->stream_id = 0;
821         (*name)->allow_override = false;
822
823         status = pvfs_fill_dos_info(pvfs, *name, PVFS_RESOLVE_NO_OPENDB, -1);
824
825         return status;
826 }