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