2 * $Id: directory.c,v 1.125 2010-01-06 11:08:53 franklahm Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
7 * 19 jan 2000 implemented red-black trees for directory lookups
13 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
37 #include <sys/param.h>
41 #include <atalk/adouble.h>
42 #include <atalk/vfs.h>
43 #include <atalk/afp.h>
44 #include <atalk/util.h>
45 #include <atalk/cnid.h>
46 #include <atalk/logger.h>
47 #include <atalk/uuid.h>
48 #include <atalk/unix.h>
50 #include "directory.h"
61 #ifdef HAVE_NFSv4_ACLS
62 extern void addir_inherit_acl(const struct vol *vol);
68 #define SENTINEL (&sentinel)
69 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
70 NULL, NULL, NULL, NULL, NULL, 0, 0,
71 0, 0, NULL, NULL, 0, NULL};
72 static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
73 NULL, NULL, NULL, NULL, NULL, 0, 0,
74 0, 0, NULL, NULL, 0, NULL};
76 /* (from IM: Toolbox Essentials)
77 * dirFinderInfo (DInfo) fields:
79 * frRect 8 folder's window rectangle
81 * frLocation 4 folder's location in window
82 * frView 2 folder's view (default == closedView (256))
84 * extended dirFinderInfo (DXInfo) fields:
85 * frScroll 4 scroll position
86 * frOpenChain: 4 directory ID chain of open folders
87 * frScript: 1 script flag and code
88 * frXFlags: 1 reserved
89 * frComment: 2 comment ID
90 * frPutAway: 4 home directory ID
94 vol_tree_root(const struct vol *vol, u_int32_t did)
98 if (vol->v_curdir && vol->v_curdir->d_did == did) {
108 * redid did assignment for directories. now we use red-black trees.
112 dirsearch(const struct vol *vol, u_int32_t did)
117 /* check for 0 did */
119 afp_errno = AFPERR_PARAM;
122 if ( did == DIRDID_ROOT_PARENT ) {
124 rootpar.d_did = DIRDID_ROOT_PARENT;
125 rootpar.d_child = vol->v_dir;
129 dir = vol_tree_root(vol, did);
131 afp_errno = AFPERR_NOOBJ;
132 while ( dir != SENTINEL ) {
133 if (dir->d_did == did)
134 return dir->d_m_name ? dir : NULL;
135 dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
140 /* ------------------- */
141 int get_afp_errno(const int param)
143 if (afp_errno != AFPERR_DID1)
148 /* ------------------- */
150 dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
152 struct dir *dir = NULL;
154 if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
160 key.d_u_name_len = strlen(name);
161 hn = hash_lookup(vol->v_hash, &key);
169 /* -----------------------------------------
170 * if did is not in the cache resolve it with cnid
173 * OSX call it with bogus id, ie file ID not folder ID,
174 * and we are really bad in this case.
177 dirlookup( struct vol *vol, u_int32_t did)
182 static char path[MAXPATHLEN + 1];
185 static char buffer[12 + MAXPATHLEN + 1];
186 int buflen = 12 + MAXPATHLEN + 1;
191 ret = dirsearch(vol, did);
192 if (ret != NULL || afp_errno == AFPERR_PARAM)
195 utf8 = utf8_encoding();
196 maxpath = (utf8)?MAXPATHLEN -7:255;
198 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
199 afp_errno = AFPERR_NOOBJ;
202 ptr = path + MAXPATHLEN;
203 if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
204 afp_errno = AFPERR_NOOBJ;
208 pathlen = len; /* no 0 in the last part */
210 strcpy(ptr - len, mpath);
213 ret = dirsearch(vol,id);
218 if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
220 NULL == (mpath = utompath(vol, upath, cnid, utf8))
222 afp_errno = AFPERR_NOOBJ;
226 len = strlen(mpath) + 1;
228 if (pathlen > maxpath) {
229 afp_errno = AFPERR_PARAM;
232 strcpy(ptr - len, mpath);
236 /* fill the cache, another place where we know about the path type */
242 temp16 = htons(pathlen);
243 memcpy(ptr, &temp16, sizeof(temp16));
245 temp = htonl(kTextEncodingUTF8);
247 memcpy(ptr, &temp, sizeof(temp));
253 *ptr = (unsigned char)pathlen;
257 /* cname is not efficient */
258 if (cname( vol, ret, &ptr ) == NULL )
261 return dirsearch(vol, did);
264 /* child addition/removal */
265 static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
270 b->d_next = a->d_child;
271 b->d_prev = b->d_next->d_prev;
272 b->d_next->d_prev = b;
273 b->d_prev->d_next = b;
275 if (!hash_alloc_insert(vol->v_hash, b, b)) {
276 LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
280 static void dirchildremove(struct dir *a,struct dir *b)
283 a->d_child = (b == b->d_next) ? NULL : b->d_next;
284 b->d_next->d_prev = b->d_prev;
285 b->d_prev->d_next = b->d_next;
286 b->d_next = b->d_prev = b;
289 /* --------------------------- */
290 /* rotate the tree to the left */
291 static void dir_leftrotate(struct vol *vol, struct dir *dir)
293 struct dir *right = dir->d_right;
295 /* whee. move the right's left tree into dir's right tree */
296 dir->d_right = right->d_left;
297 if (right->d_left != SENTINEL)
298 right->d_left->d_back = dir;
300 if (right != SENTINEL) {
301 right->d_back = dir->d_back;
305 if (!dir->d_back) /* no parent. move the right tree to the top. */
307 else if (dir == dir->d_back->d_left) /* we were on the left */
308 dir->d_back->d_left = right;
310 dir->d_back->d_right = right; /* we were on the right */
312 /* re-insert dir on the left tree */
319 /* rotate the tree to the right */
320 static void dir_rightrotate(struct vol *vol, struct dir *dir)
322 struct dir *left = dir->d_left;
324 /* whee. move the left's right tree into dir's left tree */
325 dir->d_left = left->d_right;
326 if (left->d_right != SENTINEL)
327 left->d_right->d_back = dir;
329 if (left != SENTINEL) {
330 left->d_back = dir->d_back;
334 if (!dir->d_back) /* no parent. move the left tree to the top. */
336 else if (dir == dir->d_back->d_right) /* we were on the right */
337 dir->d_back->d_right = left;
339 dir->d_back->d_left = left; /* we were on the left */
341 /* re-insert dir on the right tree */
347 /* recolor after a removal */
348 static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
352 while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
353 /* are we on the left tree? */
354 if (dir == dir->d_back->d_left) {
355 leaf = dir->d_back->d_right; /* get right side */
356 if (leaf->d_color == DIRTREE_COLOR_RED) {
357 /* we're red. we need to change to black. */
358 leaf->d_color = DIRTREE_COLOR_BLACK;
359 dir->d_back->d_color = DIRTREE_COLOR_RED;
360 dir_leftrotate(vol, dir->d_back);
361 leaf = dir->d_back->d_right;
364 /* right leaf has black end nodes */
365 if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
366 (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
367 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
368 dir = dir->d_back; /* ascend */
370 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
371 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
372 leaf->d_color = DIRTREE_COLOR_RED;
373 dir_rightrotate(vol, leaf);
374 leaf = dir->d_back->d_right;
376 leaf->d_color = dir->d_back->d_color;
377 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
378 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
379 dir_leftrotate(vol, dir->d_back);
382 } else { /* right tree */
383 leaf = dir->d_back->d_left; /* left tree */
384 if (leaf->d_color == DIRTREE_COLOR_RED) {
385 leaf->d_color = DIRTREE_COLOR_BLACK;
386 dir->d_back->d_color = DIRTREE_COLOR_RED;
387 dir_rightrotate(vol, dir->d_back);
388 leaf = dir->d_back->d_left;
391 /* left leaf has black end nodes */
392 if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
393 (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
394 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
395 dir = dir->d_back; /* ascend */
397 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
398 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
399 leaf->d_color = DIRTREE_COLOR_RED;
400 dir_leftrotate(vol, leaf);
401 leaf = dir->d_back->d_left;
403 leaf->d_color = dir->d_back->d_color;
404 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
405 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
406 dir_rightrotate(vol, dir->d_back);
411 dir->d_color = DIRTREE_COLOR_BLACK;
417 /* --------------------- */
418 static void dir_hash_del(const struct vol *vol, struct dir *dir)
422 hn = hash_lookup(vol->v_hash, dir);
424 LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
427 hash_delete(vol->v_hash, hn);
431 /* remove the node from the tree. this is just like insertion, but
432 * different. actually, it has to worry about a bunch of things that
433 * insertion doesn't care about. */
435 static void dir_remove( struct vol *vol, struct dir *dir)
438 struct ofork *of, *last;
439 struct dir *node, *leaf;
440 #endif /* REMOVE_NODES */
442 if (!dir || (dir == SENTINEL))
445 /* i'm not sure if it really helps to delete stuff. */
446 dir_hash_del(vol, dir);
447 vol->v_curdir = NULL;
450 dir->d_m_name = NULL;
451 dir->d_u_name = NULL;
452 dir->d_m_name_ucs2 = NULL;
453 #else /* ! REMOVE_NODES */
455 /* go searching for a node with at most one child */
456 if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
460 while (node->d_left != SENTINEL)
465 leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
468 leaf->d_back = node->d_back;
471 } else if (node == node->d_back->d_left) { /* left tree */
472 node->d_back->d_left = leaf;
474 node->d_back->d_right = leaf;
477 /* we want to free node, but we also want to free the data in dir.
478 * currently, that's d_name and the directory traversal bits.
479 * we just copy the necessary bits and then fix up all the
480 * various pointers to the directory. needless to say, there are
481 * a bunch of places that store the directory struct. */
483 struct dir save, *tmp;
485 memcpy(&save, dir, sizeof(save));
486 memcpy(dir, node, sizeof(struct dir));
488 /* restore the red-black bits */
489 dir->d_left = save.d_left;
490 dir->d_right = save.d_right;
491 dir->d_back = save.d_back;
492 dir->d_color = save.d_color;
494 if (node == vol->v_dir) {/* we may need to fix up this pointer */
496 rootpar.d_child = vol->v_dir;
498 /* if we aren't the root directory, we have parents and
499 * siblings to worry about */
500 if (dir->d_parent->d_child == node)
501 dir->d_parent->d_child = dir;
502 dir->d_next->d_prev = dir;
503 dir->d_prev->d_next = dir;
506 /* fix up children. */
510 tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
513 if (node == curdir) /* another pointer to fixup */
516 /* we also need to fix up oforks. bleah */
517 if ((of = dir->d_ofork)) {
518 last = of->of_d_prev;
521 of = (last == of) ? NULL : of->of_d_next;
525 /* set the node's d_name */
526 node->d_m_name = save.d_m_name;
527 node->d_u_name = save.d_u_name;
528 node->d_m_name_ucs2 = save.d_m_name_ucs2;
531 if (node->d_color == DIRTREE_COLOR_BLACK)
532 dir_rmrecolor(vol, leaf);
534 if (node->d_m_name_ucs2)
535 free(node->d_u_name_ucs2);
536 if (node->d_u_name != node->d_m_name) {
537 free(node->d_u_name);
539 free(node->d_m_name);
541 #endif /* ! REMOVE_NODES */
544 /* ---------------------------------------
545 * remove the node and its childs from the tree
547 * FIXME what about opened forks with refs to it?
548 * it's an afp specs violation because you can't delete
549 * an opened forks. Now afpd doesn't care about forks opened by other
550 * process. It's fixable within afpd if fnctl_lock, doable with smb and
551 * next to impossible for nfs and local filesystem access.
553 static void dir_invalidate( struct vol *vol, struct dir *dir)
556 /* v_root can't be deleted */
557 if (movecwd(vol, vol->v_root) < 0) {
558 LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
562 dirchildremove(dir->d_parent, dir);
563 dir_remove( vol, dir );
566 /* ------------------------------------ */
567 static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
571 pdir = vol_tree_root(vol, dir->d_did);
572 while (pdir->d_did != dir->d_did ) {
573 if ( pdir->d_did > dir->d_did ) {
574 if ( pdir->d_left == SENTINEL ) {
581 if ( pdir->d_right == SENTINEL ) {
586 pdir = pdir->d_right;
592 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
595 caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
600 static u_int32_t did = 0;
601 static char cname[MAXPATHLEN];
602 static char lname[MAXPATHLEN];
603 ucs2_t u2_path[MAXPATHLEN];
604 ucs2_t u2_dename[MAXPATHLEN];
605 char *tmp, *savepath;
607 if (!(vol->v_flags & AFPVOL_CASEINSEN))
610 if (veto_file(ENUMVETO, path->u_name))
613 savepath = path->u_name;
615 /* very simple cache */
616 if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
617 path->u_name = cname;
619 if (of_stat( path ) == 0 ) {
622 /* something changed, we cannot stat ... */
626 if (NULL == ( dp = opendir( "." )) ) {
627 LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
632 /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
633 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, -1, u2_path, sizeof(u2_path)) )
634 LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
636 /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
638 for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
639 if (NULL == check_dirent(vol, de->d_name))
642 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, -1, u2_dename, sizeof(u2_dename)) )
645 if (strcasecmp_w( u2_path, u2_dename) == 0) {
647 strlcpy(cname, de->d_name, sizeof(cname));
648 path->u_name = cname;
650 if (of_stat( path ) == 0 ) {
651 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
652 strlcpy(lname, tmp, sizeof(lname));
665 /* invalidate cache */
668 path->u_name = savepath;
670 /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
676 * attempt to extend the current dir. tree to include path
677 * as a side-effect, movecwd to that point and return the new dir
680 extenddir(struct vol *vol, struct dir *dir, struct path *path)
684 if ( path->u_name == NULL) {
685 afp_errno = AFPERR_PARAM;
689 if (check_name(vol, path->u_name)) {
690 /* the name is illegal */
691 LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
693 afp_errno = AFPERR_PARAM;
697 if (of_stat( path ) != 0 ) {
698 if (!(vol->v_flags & AFPVOL_CASEINSEN))
700 else if(caseenumerate(vol, path, dir) != 0)
704 if (!S_ISDIR(path->st.st_mode)) {
708 /* mac name is always with the right encoding (from cname()) */
709 if (( dir = adddir( vol, dir, path)) == NULL ) {
714 if ( movecwd( vol, dir ) < 0 ) {
721 /* -------------------------
722 appledouble mkdir afp error code.
724 static int netatalk_mkdir(const char *name)
726 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
729 return( AFPERR_NOOBJ );
731 return( AFPERR_VLOCK );
734 return( AFPERR_ACCESS );
736 return( AFPERR_EXIST );
739 return( AFPERR_DFULL );
741 return( AFPERR_PARAM );
747 /* ------------------- */
748 static int deletedir(char *dir)
750 char path[MAXPATHLEN + 1];
758 if ((len = strlen(dir)) +2 > sizeof(path))
762 if ((dp = opendir(dir)) == NULL)
768 remain = sizeof(path) -len -1;
769 while ((de = readdir(dp)) && err == AFP_OK) {
770 /* skip this and previous directory */
771 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
774 if (strlen(de->d_name) > remain) {
778 strcpy(path + len, de->d_name);
779 if (stat(path, &st)) {
782 if (S_ISDIR(st.st_mode)) {
783 err = deletedir(path);
785 err = netatalk_unlink(path);
790 /* okay. the directory is empty. delete it. note: we already got rid
793 err = netatalk_rmdir(dir);
798 /* do a recursive copy. */
799 static int copydir(const struct vol *vol, char *src, char *dst)
801 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
810 /* doesn't exist or the path is too long. */
811 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
812 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
813 ((dp = opendir(src)) == NULL))
816 /* try to create the destination directory */
817 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
822 /* set things up to copy */
826 srem = sizeof(spath) - slen -1;
831 drem = sizeof(dpath) - dlen -1;
834 while ((de = readdir(dp))) {
835 /* skip this and previous directory */
836 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
839 if (strlen(de->d_name) > srem) {
843 strcpy(spath + slen, de->d_name);
845 if (stat(spath, &st) == 0) {
846 if (strlen(de->d_name) > drem) {
850 strcpy(dpath + dlen, de->d_name);
852 if (S_ISDIR(st.st_mode)) {
853 if (AFP_OK != (err = copydir(vol, spath, dpath)))
855 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
859 /* keep the same time stamp. */
860 ut.actime = ut.modtime = st.st_mtime;
866 /* keep the same time stamp. */
867 if (stat(src, &st) == 0) {
868 ut.actime = ut.modtime = st.st_mtime;
878 /* --- public functions follow --- */
880 /* NOTE: we start off with at least one node (the root directory). */
881 static struct dir *dirinsert(struct vol *vol, struct dir *dir)
885 if ((node = dir_insert(vol, dir)))
888 /* recolor the tree. the current node is red. */
889 dir->d_color = DIRTREE_COLOR_RED;
891 /* parent of this node has to be black. if the parent node
892 * is red, then we have a grandparent. */
893 while ((dir != vol->v_root) &&
894 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
895 /* are we on the left tree? */
896 if (dir->d_back == dir->d_back->d_back->d_left) {
897 node = dir->d_back->d_back->d_right; /* get the right node */
898 if (node->d_color == DIRTREE_COLOR_RED) {
899 /* we're red. we need to change to black. */
900 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
901 node->d_color = DIRTREE_COLOR_BLACK;
902 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
903 dir = dir->d_back->d_back; /* finished. go up. */
905 if (dir == dir->d_back->d_right) {
907 dir_leftrotate(vol, dir);
909 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
910 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
911 dir_rightrotate(vol, dir->d_back->d_back);
914 node = dir->d_back->d_back->d_left;
915 if (node->d_color == DIRTREE_COLOR_RED) {
916 /* we're red. we need to change to black. */
917 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
918 node->d_color = DIRTREE_COLOR_BLACK;
919 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
920 dir = dir->d_back->d_back; /* finished. ascend */
922 if (dir == dir->d_back->d_left) {
924 dir_rightrotate(vol, dir);
926 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
927 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
928 dir_leftrotate(vol, dir->d_back->d_back);
933 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
937 /* ---------------------------- */
939 adddir(struct vol *vol, struct dir *dir, struct path *path)
941 struct dir *cdir, *edir;
949 upath = path->u_name;
951 upathlen = strlen(upath);
953 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
957 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
961 if ((cdir = dirnew(name, upath)) == NULL) {
962 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
965 if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, path->m_name, -1, (char **)&cdir->d_m_name_ucs2)) {
966 LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
967 cdir->d_m_name_ucs2 = NULL;
972 if ((edir = dirinsert( vol, cdir ))) {
973 /* it's not possible with LASTDID
975 - someone else have moved the directory.
976 - it's a symlink inside the share.
977 - it's an ID reused, the old directory was deleted but not
978 the cnid record and the server've reused the inode for
980 for HASH (we should get ride of HASH)
981 - someone else have moved the directory.
982 - it's an ID reused as above
983 - it's a hash duplicate and we are in big trouble
985 deleted = (edir->d_m_name == NULL);
987 dir_hash_del(vol, edir);
989 edir->d_m_name = cdir->d_m_name;
990 edir->d_u_name = cdir->d_u_name;
991 edir->d_u_name_len = cdir->d_u_name_len;
992 edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
995 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
996 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
997 hash_alloc_insert(vol->v_hash, cdir, cdir);
1000 /* the old was not in the same folder */
1002 dirchildremove(cdir->d_parent, cdir);
1005 /* parent/child directories */
1006 cdir->d_parent = dir;
1007 dirchildadd(vol, dir, cdir);
1011 /* --- public functions follow --- */
1012 /* free everything down. we don't bother to recolor as this is only
1013 * called to free the entire tree */
1014 void dirfreename(struct dir *dir)
1016 if (dir->d_u_name != dir->d_m_name) {
1017 free(dir->d_u_name);
1019 if (dir->d_m_name_ucs2)
1020 free(dir->d_m_name_ucs2);
1021 free(dir->d_m_name);
1024 void dirfree(struct dir *dir)
1026 if (!dir || (dir == SENTINEL))
1029 if ( dir->d_left != SENTINEL ) {
1030 dirfree( dir->d_left );
1032 if ( dir->d_right != SENTINEL ) {
1033 dirfree( dir->d_right );
1036 if (dir != SENTINEL) {
1042 /* --------------------------------------------
1043 * most of the time mac name and unix name are the same
1045 struct dir *dirnew(const char *m_name, const char *u_name)
1049 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1053 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1058 if (m_name == u_name || !strcmp(m_name, u_name)) {
1059 dir->d_u_name = dir->d_m_name;
1061 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1062 free(dir->d_m_name);
1067 dir->d_u_name_len = strlen(dir->d_u_name);
1068 dir->d_m_name_ucs2 = NULL;
1069 dir->d_left = dir->d_right = SENTINEL;
1070 dir->d_next = dir->d_prev = dir;
1074 /* ------------------ */
1075 static hash_val_t hash_fun_dir(const void *key)
1077 const struct dir *k = key;
1079 static unsigned long randbox[] = {
1080 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1081 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1082 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1083 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1086 const unsigned char *str = (unsigned char *)(k->d_u_name);
1087 hash_val_t acc = k->d_parent->d_did;
1090 acc ^= randbox[(*str + acc) & 0xf];
1091 acc = (acc << 1) | (acc >> 31);
1093 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1094 acc = (acc << 2) | (acc >> 30);
1101 #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
1102 || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
1103 #define get16bits(d) (*((const uint16_t *) (d)))
1106 #if !defined (get16bits)
1107 #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
1108 +(uint32_t)(((const uint8_t *)(d))[0]) )
1111 static hash_val_t hash_fun2_dir(const void *key)
1113 const struct dir *k = key;
1114 const char *data = k->d_u_name;
1115 int len = k->d_u_name_len;
1116 hash_val_t hash = k->d_parent->d_did, tmp;
1122 for (;len > 0; len--) {
1123 hash += get16bits (data);
1124 tmp = (get16bits (data+2) << 11) ^ hash;
1125 hash = (hash << 16) ^ tmp;
1126 data += 2*sizeof (uint16_t);
1130 /* Handle end cases */
1132 case 3: hash += get16bits (data);
1134 hash ^= data[sizeof (uint16_t)] << 18;
1137 case 2: hash += get16bits (data);
1141 case 1: hash += *data;
1146 /* Force "avalanching" of final 127 bits */
1157 /* ---------------- */
1158 static int hash_comp_dir(const void *key1, const void *key2)
1160 const struct dir *k1 = key1;
1161 const struct dir *k2 = key2;
1163 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1166 /* ---------------- */
1170 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun2_dir);
1173 /* ------------------ */
1174 static struct path *invalidate (struct vol *vol, struct dir *dir, struct path *ret)
1177 movecwd failed some of dir path are not there anymore.
1178 FIXME Is it true with other errors?
1179 so we remove dir from the cache
1181 if (dir->d_did == DIRDID_ROOT_PARENT)
1183 if (afp_errno == AFPERR_ACCESS) {
1184 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1187 /* FIXME should we set these?, don't need to call stat() after:
1189 ret->st_errno = EACCES;
1191 ret->m_name = dir->d_m_name;
1192 ret->u_name = dir->d_u_name;
1195 } else if (afp_errno == AFPERR_NOOBJ) {
1196 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1199 strcpy(ret->m_name, dir->d_m_name);
1200 if (dir->d_m_name == dir->d_u_name) {
1201 ret->u_name = ret->m_name;
1204 size_t tp = strlen(ret->m_name)+1;
1206 ret->u_name = ret->m_name +tp;
1207 strcpy(ret->u_name, dir->d_u_name);
1209 /* FIXME should we set :
1211 ret->st_errno = ENOENT;
1213 dir_invalidate(vol, dir);
1216 dir_invalidate(vol, dir);
1220 /* -------------------------------------------------- */
1226 stat the file or errno
1229 curdir: filename parent directory
1235 stat the dir or errno
1239 curdir: dir parent directory
1247 curdir: dir parent directory
1254 cname(struct vol *vol, struct dir *dir, char **cpath)
1256 struct dir *cdir, *scdir=NULL;
1257 static char path[ MAXPATHLEN + 1];
1258 static struct path ret;
1270 afp_errno = AFPERR_NOOBJ;
1271 memset(&ret, 0, sizeof(ret));
1272 switch (ret.m_type = *data) { /* path type */
1275 len = (unsigned char) *data++;
1278 if (afp_version >= 30) {
1284 if (afp_version >= 30) {
1286 memcpy(&hint, data, sizeof(hint));
1288 data += sizeof(hint);
1290 memcpy(&len16, data, sizeof(len16));
1297 /* else it's an error */
1299 afp_errno = AFPERR_PARAM;
1302 *cpath += len + size;
1307 if (movecwd( vol, dir ) < 0 ) {
1308 return invalidate(vol, dir, &ret );
1310 if (*path == '\0') {
1317 if (*data == sep ) {
1321 while (*data == sep && len > 0 ) {
1322 if ( dir->d_parent == NULL ) {
1325 dir = dir->d_parent;
1330 /* would this be faster with strlen + strncpy? */
1332 while ( *data != sep && len > 0 ) {
1334 if (p > &path[ MAXPATHLEN]) {
1335 afp_errno = AFPERR_PARAM;
1341 /* short cut bits by chopping off a trailing \0. this also
1342 makes the traversal happy w/ filenames at the end of the
1349 if ( p == path ) { /* end of the name parameter */
1353 if (afp_version >= 30) {
1358 static char temp[ MAXPATHLEN + 1];
1360 if (dir->d_did == DIRDID_ROOT_PARENT) {
1362 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1363 So we compare it with the longname from the current volume and if they match
1364 we overwrite the requested path with the utf8 volume name so that the following
1367 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1368 if (strcasecmp( path, temp) == 0)
1369 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1372 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1373 afp_errno = AFPERR_PARAM;
1379 /* check for OS X mangled filename :( */
1381 t = demangle_osx(vol, path, dir->d_did, &fileid);
1384 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1385 * flags weren't the same
1387 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1388 /* at last got our view of mac name */
1393 if (ret.u_name == NULL) {
1394 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1395 afp_errno = AFPERR_PARAM;
1401 cdir = dir->d_child;
1403 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1404 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1405 CH_UCS2, path, -1, (char **)&tmpname) )
1408 if (!cdir->d_m_name_ucs2) {
1409 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1410 /* this shouldn't happen !!!! */
1414 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1417 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1420 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1426 if (dir->d_did == DIRDID_ROOT_PARENT) {
1428 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1429 must check against the volume name.
1431 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1437 cdir = dirsearch_byname(vol, dir, ret.u_name);
1441 if (cdir == NULL && scdir != NULL) {
1443 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1446 if ( cdir == NULL ) {
1448 /* if dir == curdir it always succeed,
1449 even if curdir is deleted.
1450 it's not a pb because it will fail in extenddir
1452 if ( movecwd( vol, dir ) < 0 ) {
1453 /* dir is not valid anymore
1454 we delete dir from the cache and abort.
1456 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1457 afp_errno = AFPERR_NOOBJ;
1460 if (afp_errno == AFPERR_ACCESS)
1462 dir_invalidate(vol, dir);
1465 cdir = extenddir( vol, dir, &ret );
1469 cdir = extenddir( vol, dir, &ret );
1470 } /* if (!extend) */
1472 if ( cdir == NULL ) {
1474 if ( len > 0 || !ret.u_name ) {
1486 * Move curdir to dir, with a possible chdir()
1488 int movecwd(struct vol *vol, struct dir *dir)
1490 char path[MAXPATHLEN + 1];
1495 if ( dir == curdir ) {
1498 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1499 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1503 p = path + sizeof(path) - 1;
1506 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1509 /* parent directory is deleted */
1510 afp_errno = AFPERR_NOOBJ;
1514 if (p -n -1 < path) {
1515 afp_errno = AFPERR_PARAM;
1522 if ( d != curdir ) {
1523 n = strlen( vol->v_path );
1524 if (p -n -1 < path) {
1525 afp_errno = AFPERR_PARAM;
1530 memcpy( p, vol->v_path, n );
1532 if ( chdir( p ) < 0 ) {
1536 afp_errno = AFPERR_ACCESS;
1539 afp_errno = AFPERR_NOOBJ;
1544 vol->v_curdir = curdir = dir;
1549 * We can't use unix file's perm to support Apple's inherited protection modes.
1550 * If we aren't the file's owner we can't change its perms when moving it and smb
1551 * nfs,... don't even try.
1553 #define AFP_CHECK_ACCESS
1555 int check_access(char *path, int mode)
1557 #ifdef AFP_CHECK_ACCESS
1565 accessmode(p, &ma, curdir, NULL);
1566 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1568 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1574 /* --------------------- */
1575 int file_access(struct path *path, int mode)
1579 accessmode(path->u_name, &ma, curdir, &path->st);
1580 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1582 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1588 /* --------------------- */
1589 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1591 dir->offcnt = count;
1592 dir->ctime = st->st_ctime;
1593 dir->d_flags &= ~DIRF_CNID;
1596 /* ---------------------
1597 * is our cached offspring count valid?
1600 static int diroffcnt(struct dir *dir, struct stat *st)
1602 return st->st_ctime == dir->ctime;
1605 /* ---------------------
1606 * is our cached also for reenumerate id?
1609 int dirreenumerate(struct dir *dir, struct stat *st)
1611 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1614 /* --------------------- */
1615 static int invisible_dots(const struct vol *vol, const char *name)
1617 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1620 /* ------------------------------
1622 (name, dir) with curdir:name == dir, from afp_enumerate
1625 int getdirparams(const struct vol *vol,
1626 u_int16_t bitmap, struct path *s_path,
1628 char *buf, size_t *buflen )
1632 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1633 int bit = 0, isad = 0;
1639 struct stat *st = &s_path->st;
1640 char *upath = s_path->u_name;
1642 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1643 (1 << DIRPBIT_CDATE) |
1644 (1 << DIRPBIT_MDATE) |
1645 (1 << DIRPBIT_BDATE) |
1646 (1 << DIRPBIT_FINFO)))) {
1648 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1649 if ( !ad_metadata( upath, ADFLAGS_CREATE|ADFLAGS_DIR, &ad) ) {
1654 if ( dir->d_did == DIRDID_ROOT) {
1655 pdid = DIRDID_ROOT_PARENT;
1656 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1659 pdid = dir->d_parent->d_did;
1663 while ( bitmap != 0 ) {
1664 while (( bitmap & 1 ) == 0 ) {
1672 ad_getattr(&ad, &ashort);
1673 } else if (invisible_dots(vol, dir->d_u_name)) {
1674 ashort = htons(ATTRBIT_INVISIBLE);
1677 ashort |= htons(ATTRBIT_SHARED);
1678 memcpy( data, &ashort, sizeof( ashort ));
1679 data += sizeof( ashort );
1683 memcpy( data, &pdid, sizeof( pdid ));
1684 data += sizeof( pdid );
1687 case DIRPBIT_CDATE :
1688 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1689 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1690 memcpy( data, &aint, sizeof( aint ));
1691 data += sizeof( aint );
1694 case DIRPBIT_MDATE :
1695 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1696 memcpy( data, &aint, sizeof( aint ));
1697 data += sizeof( aint );
1700 case DIRPBIT_BDATE :
1701 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1702 aint = AD_DATE_START;
1703 memcpy( data, &aint, sizeof( aint ));
1704 data += sizeof( aint );
1707 case DIRPBIT_FINFO :
1709 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1710 } else { /* no appledouble */
1711 memset( data, 0, 32 );
1712 /* set default view -- this also gets done in ad_open() */
1713 ashort = htons(FINDERINFO_CLOSEDVIEW);
1714 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1716 /* dot files are by default visible */
1717 if (invisible_dots(vol, dir->d_u_name)) {
1718 ashort = htons(FINDERINFO_INVISIBLE);
1719 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1725 case DIRPBIT_LNAME :
1726 if (dir->d_m_name) /* root of parent can have a null name */
1729 memset(data, 0, sizeof(u_int16_t));
1730 data += sizeof( u_int16_t );
1733 case DIRPBIT_SNAME :
1734 memset(data, 0, sizeof(u_int16_t));
1735 data += sizeof( u_int16_t );
1739 memcpy( data, &dir->d_did, sizeof( aint ));
1740 data += sizeof( aint );
1743 case DIRPBIT_OFFCNT :
1745 /* this needs to handle current directory access rights */
1746 if (diroffcnt(dir, st)) {
1747 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1749 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1750 setdiroffcnt(dir, st, ret);
1751 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1753 ashort = htons( ashort );
1754 memcpy( data, &ashort, sizeof( ashort ));
1755 data += sizeof( ashort );
1759 aint = htonl(st->st_uid);
1760 memcpy( data, &aint, sizeof( aint ));
1761 data += sizeof( aint );
1765 aint = htonl(st->st_gid);
1766 memcpy( data, &aint, sizeof( aint ));
1767 data += sizeof( aint );
1770 case DIRPBIT_ACCESS :
1771 accessmode( upath, &ma, dir , st);
1773 *data++ = ma.ma_user;
1774 *data++ = ma.ma_world;
1775 *data++ = ma.ma_group;
1776 *data++ = ma.ma_owner;
1779 /* Client has requested the ProDOS information block.
1780 Just pass back the same basic block for all
1781 directories. <shirsch@ibm.net> */
1782 case DIRPBIT_PDINFO :
1783 if (afp_version >= 30) { /* UTF8 name */
1784 utf8 = kTextEncodingUTF8;
1785 if (dir->d_m_name) /* root of parent can have a null name */
1788 memset(data, 0, sizeof(u_int16_t));
1789 data += sizeof( u_int16_t );
1791 memcpy(data, &aint, sizeof( aint ));
1792 data += sizeof( aint );
1794 else { /* ProDOS Info Block */
1797 ashort = htons( 0x0200 );
1798 memcpy( data, &ashort, sizeof( ashort ));
1799 data += sizeof( ashort );
1800 memset( data, 0, sizeof( ashort ));
1801 data += sizeof( ashort );
1805 case DIRPBIT_UNIXPR :
1806 aint = htonl(st->st_uid);
1807 memcpy( data, &aint, sizeof( aint ));
1808 data += sizeof( aint );
1809 aint = htonl(st->st_gid);
1810 memcpy( data, &aint, sizeof( aint ));
1811 data += sizeof( aint );
1814 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1815 memcpy( data, &aint, sizeof( aint ));
1816 data += sizeof( aint );
1818 accessmode( upath, &ma, dir , st);
1820 *data++ = ma.ma_user;
1821 *data++ = ma.ma_world;
1822 *data++ = ma.ma_group;
1823 *data++ = ma.ma_owner;
1828 ad_close_metadata( &ad );
1830 return( AFPERR_BITMAP );
1836 ashort = htons( data - buf );
1837 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1838 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1840 if ( utf_nameoff ) {
1841 ashort = htons( data - buf );
1842 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1843 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1846 ad_close_metadata( &ad );
1848 *buflen = data - buf;
1852 /* ----------------------------- */
1853 int path_error(struct path *path, int error)
1855 /* - a dir with access error
1856 * - no error it's a file
1859 if (path_isadir(path))
1861 if (path->st_valid && path->st_errno)
1863 return AFPERR_BADTYPE ;
1866 /* ----------------------------- */
1867 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1872 u_int16_t vid, bitmap;
1878 memcpy( &vid, ibuf, sizeof( vid ));
1879 ibuf += sizeof( vid );
1881 if (NULL == ( vol = getvolbyvid( vid )) ) {
1882 return( AFPERR_PARAM );
1885 if (vol->v_flags & AFPVOL_RO)
1886 return AFPERR_VLOCK;
1888 memcpy( &did, ibuf, sizeof( did ));
1889 ibuf += sizeof( int );
1891 if (NULL == ( dir = dirlookup( vol, did )) ) {
1895 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1896 bitmap = ntohs( bitmap );
1897 ibuf += sizeof( bitmap );
1899 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1900 return get_afp_errno(AFPERR_NOOBJ);
1903 if ( *path->m_name != '\0' ) {
1904 rc = path_error(path, AFPERR_NOOBJ);
1905 /* maybe we are trying to set perms back */
1906 if (rc != AFPERR_ACCESS)
1911 * If ibuf is odd, make it even.
1913 if ((u_long)ibuf & 1 ) {
1917 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1918 setvoltime(obj, vol );
1924 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1926 * assume path == '\0' eg. it's a directory in canonical form
1929 struct path Cur_Path = {
1932 ".", /* unix name */
1934 NULL,/* struct dir */
1935 0, /* stat is not set */
1938 /* ------------------ */
1939 static int set_dir_errors(struct path *path, const char *where, int err)
1944 return AFPERR_ACCESS;
1946 return AFPERR_VLOCK;
1948 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1949 return AFPERR_PARAM;
1952 /* ------------------ */
1953 int setdirparams(struct vol *vol,
1954 struct path *path, u_int16_t d_bitmap, char *buf )
1966 u_int16_t ashort, bshort;
1968 int change_mdate = 0;
1969 int change_parent_mdate = 0;
1971 u_int16_t bitmap = d_bitmap;
1972 u_char finder_buf[32];
1975 u_int16_t upriv_bit = 0;
1978 upath = path->u_name;
1980 while ( bitmap != 0 ) {
1981 while (( bitmap & 1 ) == 0 ) {
1989 memcpy( &ashort, buf, sizeof( ashort ));
1990 buf += sizeof( ashort );
1992 case DIRPBIT_CDATE :
1994 memcpy(&cdate, buf, sizeof(cdate));
1995 buf += sizeof( cdate );
1997 case DIRPBIT_MDATE :
1998 memcpy(&newdate, buf, sizeof(newdate));
1999 buf += sizeof( newdate );
2001 case DIRPBIT_BDATE :
2003 memcpy(&bdate, buf, sizeof(bdate));
2004 buf += sizeof( bdate );
2006 case DIRPBIT_FINFO :
2008 memcpy( finder_buf, buf, 32 );
2011 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2012 change_parent_mdate = 1;
2013 memcpy( &owner, buf, sizeof(owner));
2014 buf += sizeof( owner );
2017 change_parent_mdate = 1;
2018 memcpy( &group, buf, sizeof( group ));
2019 buf += sizeof( group );
2021 case DIRPBIT_ACCESS :
2023 change_parent_mdate = 1;
2024 ma.ma_user = *buf++;
2025 ma.ma_world = *buf++;
2026 ma.ma_group = *buf++;
2027 ma.ma_owner = *buf++;
2028 mpriv = mtoumode( &ma ) | vol->v_dperm;
2029 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2030 err = set_dir_errors(path, "setdirmode", errno);
2034 /* Ignore what the client thinks we should do to the
2035 ProDOS information block. Skip over the data and
2036 report nothing amiss. <shirsch@ibm.net> */
2037 case DIRPBIT_PDINFO :
2038 if (afp_version < 30) {
2042 err = AFPERR_BITMAP;
2046 case DIRPBIT_UNIXPR :
2047 if (vol_unix_priv(vol)) {
2048 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2049 buf += sizeof( owner );
2050 memcpy( &group, buf, sizeof( group ));
2051 buf += sizeof( group );
2054 change_parent_mdate = 1;
2055 memcpy( &upriv, buf, sizeof( upriv ));
2056 buf += sizeof( upriv );
2057 upriv = ntohl (upriv) | vol->v_dperm;
2058 if (dir_rx_set(upriv)) {
2059 /* maybe we are trying to set perms back */
2060 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2062 err = set_dir_errors(path, "setdirunixmode", errno);
2073 err = AFPERR_BITMAP;
2081 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2083 if (ad_open_metadata( upath, ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2085 * Check to see what we're trying to set. If it's anything
2086 * but ACCESS, UID, or GID, give an error. If it's any of those
2087 * three, we don't need the ad to be open, so just continue.
2089 * note: we also don't need to worry about mdate. also, be quiet
2090 * if we're using the noadouble option.
2092 if (!vol_noadouble(vol) && (d_bitmap &
2093 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2094 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2095 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2096 return AFPERR_ACCESS;
2102 * Check to see if a create was necessary. If it was, we'll want
2103 * to set our name, etc.
2105 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2106 ad_setname(&ad, curdir->d_m_name);
2112 while ( bitmap != 0 ) {
2113 while (( bitmap & 1 ) == 0 ) {
2121 ad_getattr(&ad, &bshort);
2122 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2123 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2124 change_parent_mdate = 1;
2125 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2126 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2130 ad_setattr(&ad, bshort);
2133 case DIRPBIT_CDATE :
2135 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2138 case DIRPBIT_MDATE :
2140 case DIRPBIT_BDATE :
2142 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2145 case DIRPBIT_FINFO :
2147 /* Fixes #2802236 */
2148 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2149 *fflags &= htons(~FINDERINFO_ISHARED);
2151 if ( dir->d_did == DIRDID_ROOT ) {
2153 * Alright, we admit it, this is *really* sick!
2154 * The 4 bytes that we don't copy, when we're dealing
2155 * with the root of a volume, are the directory's
2156 * location information. This eliminates that annoying
2157 * behavior one sees when mounting above another mount
2160 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2161 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2163 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2167 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2168 if ( (dir->d_did == DIRDID_ROOT) &&
2169 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2170 err = set_dir_errors(path, "setdeskowner", errno);
2171 if (isad && err == AFPERR_PARAM) {
2172 err = AFP_OK; /* ???*/
2175 goto setdirparam_done;
2178 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2179 err = set_dir_errors(path, "setdirowner", errno);
2180 goto setdirparam_done;
2184 if (dir->d_did == DIRDID_ROOT)
2185 setdeskowner( -1, ntohl(group) );
2186 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2187 err = set_dir_errors(path, "setdirowner", errno);
2188 goto setdirparam_done;
2191 case DIRPBIT_ACCESS :
2192 if (dir->d_did == DIRDID_ROOT) {
2194 if (!dir_rx_set(mpriv)) {
2195 /* we can't remove read and search for owner on volume root */
2196 err = AFPERR_ACCESS;
2197 goto setdirparam_done;
2201 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2202 err = set_dir_errors(path, "setdirmode", errno);
2203 goto setdirparam_done;
2206 case DIRPBIT_PDINFO :
2207 if (afp_version >= 30) {
2208 err = AFPERR_BITMAP;
2209 goto setdirparam_done;
2212 case DIRPBIT_UNIXPR :
2213 if (vol_unix_priv(vol)) {
2214 if (dir->d_did == DIRDID_ROOT) {
2215 if (!dir_rx_set(upriv)) {
2216 /* we can't remove read and search for owner on volume root */
2217 err = AFPERR_ACCESS;
2218 goto setdirparam_done;
2220 setdeskowner( -1, ntohl(group) );
2221 setdeskmode( upriv );
2223 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2224 err = set_dir_errors(path, "setdirowner", errno);
2225 goto setdirparam_done;
2228 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2229 err = set_dir_errors(path, "setdirunixmode", errno);
2230 goto setdirparam_done;
2234 err = AFPERR_BITMAP;
2235 goto setdirparam_done;
2239 err = AFPERR_BITMAP;
2240 goto setdirparam_done;
2249 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2250 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2254 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2255 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2260 if (path->st_valid && !path->st_errno) {
2261 struct stat *st = &path->st;
2263 if (dir && dir->d_parent) {
2264 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2268 ad_close_metadata( &ad);
2271 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2272 && gettimeofday(&tv, NULL) == 0) {
2273 if (!movecwd(vol, dir->d_parent)) {
2274 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2275 /* be careful with bitmap because now dir is null */
2276 bitmap = 1<<DIRPBIT_MDATE;
2277 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2278 /* should we reset curdir ?*/
2285 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2299 memcpy( &vid, ibuf, sizeof( vid ));
2300 ibuf += sizeof( vid );
2301 if (NULL == (vol = getvolbyvid( vid )) ) {
2302 return( AFPERR_PARAM );
2305 memcpy( &did, ibuf, sizeof( did ));
2306 ibuf += sizeof( did );
2310 * if it's CNID 2 our only choice to meet the specs is call sync.
2311 * For any other CNID just sync that dir. To my knowledge the
2312 * intended use of FPSyncDir is to sync the volume so all we're
2313 * ever going to see here is probably CNID 2. Anyway, we' prepared.
2316 if ( ntohl(did) == 2 ) {
2319 if (NULL == ( dir = dirlookup( vol, did )) ) {
2320 return afp_errno; /* was AFPERR_NOOBJ */
2323 if (movecwd( vol, dir ) < 0 )
2324 return ( AFPERR_NOOBJ );
2327 * Assuming only OSens that have dirfd also may require fsyncing directories
2328 * in order to flush metadata e.g. Linux.
2332 if (NULL == ( dp = opendir( "." )) ) {
2335 return( AFPERR_NOOBJ );
2337 return( AFPERR_ACCESS );
2339 return( AFPERR_PARAM );
2343 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2346 if ( fsync ( dfd ) < 0 )
2347 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2348 dir->d_u_name, strerror(errno) );
2349 closedir(dp); /* closes dfd too */
2352 if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2355 return( AFPERR_NOOBJ );
2357 return( AFPERR_ACCESS );
2359 return( AFPERR_PARAM );
2363 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2364 vol->ad_path(".", ADFLAGS_DIR) );
2366 if ( fsync(dfd) < 0 )
2367 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2368 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2375 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2381 struct path *s_path;
2389 memcpy( &vid, ibuf, sizeof( vid ));
2390 ibuf += sizeof( vid );
2391 if (NULL == ( vol = getvolbyvid( vid )) ) {
2392 return( AFPERR_PARAM );
2395 if (vol->v_flags & AFPVOL_RO)
2396 return AFPERR_VLOCK;
2398 memcpy( &did, ibuf, sizeof( did ));
2399 ibuf += sizeof( did );
2400 if (NULL == ( dir = dirlookup( vol, did )) ) {
2401 return afp_errno; /* was AFPERR_NOOBJ */
2403 /* for concurrent access we need to be sure we are not in the
2404 * folder we want to create...
2408 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2409 return get_afp_errno(AFPERR_PARAM);
2411 /* cname was able to move curdir to it! */
2412 if (*s_path->m_name == '\0')
2413 return AFPERR_EXIST;
2415 upath = s_path->u_name;
2417 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2421 if (of_stat(s_path) < 0) {
2425 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2429 if ( movecwd( vol, dir ) < 0 ) {
2430 return( AFPERR_PARAM );
2433 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2434 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2435 if (vol_noadouble(vol))
2436 goto createdir_done;
2437 return( AFPERR_ACCESS );
2439 ad_setname(&ad, s_path->m_name);
2440 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2443 ad_close_metadata( &ad);
2446 #ifdef HAVE_NFSv4_ACLS
2447 /* FIXME: are we really inside the created dir? */
2448 addir_inherit_acl(vol);
2451 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2452 *rbuflen = sizeof( u_int32_t );
2453 setvoltime(obj, vol );
2458 * dst new unix filename (not a pathname)
2459 * newname new mac name
2463 int renamedir(const struct vol *vol, char *src, char *dst,
2465 struct dir *newparent,
2473 /* existence check moved to afp_moveandrename */
2474 if ( unix_rename( src, dst ) < 0 ) {
2477 return( AFPERR_NOOBJ );
2479 return( AFPERR_ACCESS );
2481 return AFPERR_VLOCK;
2483 /* tried to move directory into a subdirectory of itself */
2484 return AFPERR_CANTMOVE;
2486 /* this needs to copy and delete. bleah. that means we have
2487 * to deal with entire directory hierarchies. */
2488 if ((err = copydir(vol, src, dst)) < 0) {
2492 if ((err = deletedir(src)) < 0)
2496 return( AFPERR_PARAM );
2500 vol->vfs->vfs_renamedir(vol, src, dst);
2502 len = strlen( newname );
2503 /* rename() succeeded so we need to update our tree even if we can't open
2507 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2509 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2510 ad_setname(&ad, newname);
2512 ad_close_metadata( &ad);
2515 dir_hash_del(vol, dir);
2516 if (dir->d_m_name == dir->d_u_name)
2517 dir->d_u_name = NULL;
2519 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2520 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2521 /* FIXME : fatal ? */
2524 dir->d_m_name = buf;
2525 strcpy( dir->d_m_name, newname );
2527 if (newname == dst) {
2528 free(dir->d_u_name);
2529 dir->d_u_name = dir->d_m_name;
2532 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2533 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2536 dir->d_u_name = buf;
2537 strcpy( dir->d_u_name, dst );
2539 dir->d_u_name_len = strlen(dir->d_u_name);
2541 if (dir->d_m_name_ucs2)
2542 free(dir->d_m_name_ucs2);
2544 dir->d_m_name_ucs2 = NULL;
2545 if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, -1, (char**)&dir->d_m_name_ucs2))
2546 dir->d_m_name_ucs2 = NULL;
2548 if (( parent = dir->d_parent ) == NULL ) {
2551 if ( parent == newparent ) {
2552 hash_alloc_insert(vol->v_hash, dir, dir);
2556 /* detach from old parent and add to new one. */
2557 dirchildremove(parent, dir);
2558 dir->d_parent = newparent;
2559 dirchildadd(vol, newparent, dir);
2563 /* delete an empty directory */
2564 int deletecurdir(struct vol *vol)
2574 if ( curdir->d_parent == NULL ) {
2575 return( AFPERR_ACCESS );
2580 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2581 /* we never want to create a resource fork here, we are going to delete it */
2582 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2584 ad_getattr(&ad, &ashort);
2585 ad_close( &ad, ADFLAGS_HF );
2586 if ((ashort & htons(ATTRBIT_NODELETE))) {
2587 return AFPERR_OLOCK;
2590 err = vol->vfs->vfs_deletecurdir(vol);
2595 /* now get rid of dangling symlinks */
2596 if ((dp = opendir("."))) {
2597 while ((de = readdir(dp))) {
2598 /* skip this and previous directory */
2599 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2602 /* bail if it's not a symlink */
2603 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2605 return AFPERR_DIRNEMPT;
2608 if ((err = netatalk_unlink(de->d_name))) {
2615 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2620 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2621 dirchildremove(curdir, fdir);
2622 cnid_delete(vol->v_cdb, fdir->d_did);
2623 dir_remove( vol, fdir );
2628 /* inode is used as key for cnid.
2629 * Close the descriptor only after cnid_delete
2637 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2647 sfunc = (unsigned char) *ibuf++;
2651 if (sfunc >= 3 && sfunc <= 6) {
2652 if (afp_version < 30) {
2653 return( AFPERR_PARAM );
2660 case 3 :/* unicode */
2661 memcpy( &id, ibuf, sizeof( id ));
2664 if (( pw = getpwuid( id )) == NULL ) {
2665 return( AFPERR_NOITEM );
2667 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2668 pw->pw_name, -1, &name);
2675 case 4 : /* unicode */
2676 memcpy( &id, ibuf, sizeof( id ));
2679 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2680 return( AFPERR_NOITEM );
2682 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2683 gr->gr_name, -1, &name);
2689 #ifdef HAVE_NFSv4_ACLS
2690 case 5 : /* UUID -> username */
2691 case 6 : /* UUID -> groupname */
2692 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2693 return AFPERR_PARAM;
2694 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2696 len = getnamefromuuid( ibuf, &name, &type);
2697 if (len != 0) /* its a error code, not len */
2698 return AFPERR_NOITEM;
2699 if (type == UUID_USER) {
2700 if (( pw = getpwnam( name )) == NULL )
2701 return( AFPERR_NOITEM );
2702 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2703 id = htonl(UUID_USER);
2704 memcpy( rbuf, &id, sizeof( id ));
2705 id = htonl( pw->pw_uid);
2706 rbuf += sizeof( id );
2707 memcpy( rbuf, &id, sizeof( id ));
2708 rbuf += sizeof( id );
2709 *rbuflen = 2 * sizeof( id );
2710 } else { /* type == UUID_GROUP */
2711 if (( gr = getgrnam( name )) == NULL )
2712 return( AFPERR_NOITEM );
2713 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2714 id = htonl(UUID_GROUP);
2715 memcpy( rbuf, &id, sizeof( id ));
2716 rbuf += sizeof( id );
2717 id = htonl( gr->gr_gid);
2718 memcpy( rbuf, &id, sizeof( id ));
2719 rbuf += sizeof( id );
2720 *rbuflen = 2 * sizeof( id );
2725 return( AFPERR_PARAM );
2729 len = strlen( name );
2732 u_int16_t tp = htons(len);
2733 memcpy(rbuf, &tp, sizeof(tp));
2742 memcpy( rbuf, name, len );
2750 int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2759 sfunc = (unsigned char) *ibuf++;
2761 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2764 case 2 : /* unicode */
2765 if (afp_version < 30) {
2766 return( AFPERR_PARAM );
2768 memcpy(&ulen, ibuf, sizeof(ulen));
2771 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2775 len = (unsigned char) *ibuf++;
2777 #ifdef HAVE_NFSv4_ACLS
2778 case 5 : /* username -> UUID */
2779 case 6 : /* groupname -> UUID */
2780 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2781 return AFPERR_PARAM;
2782 memcpy(&ulen, ibuf, sizeof(ulen));
2788 return( AFPERR_PARAM );
2794 return AFPERR_PARAM;
2797 case 1 : /* unicode */
2799 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2800 return( AFPERR_NOITEM );
2804 memcpy( rbuf, &id, sizeof( id ));
2805 *rbuflen = sizeof( id );
2808 case 2 : /* unicode */
2810 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2811 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2812 return( AFPERR_NOITEM );
2815 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2817 memcpy( rbuf, &id, sizeof( id ));
2818 *rbuflen = sizeof( id );
2820 #ifdef HAVE_NFSv4_ACLS
2821 case 5 : /* username -> UUID */
2822 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2823 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2824 return AFPERR_NOITEM;
2825 *rbuflen = UUID_BINSIZE;
2827 case 6 : /* groupname -> UUID */
2828 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2829 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2830 return AFPERR_NOITEM;
2831 *rbuflen = UUID_BINSIZE;
2839 /* ------------------------------------
2840 variable DID support
2842 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2853 /* do nothing as dids are static for the life of the process. */
2857 memcpy(&vid, ibuf, sizeof( vid ));
2858 ibuf += sizeof( vid );
2859 if (( vol = getvolbyvid( vid )) == NULL ) {
2860 return( AFPERR_PARAM );
2863 memcpy( &did, ibuf, sizeof( did ));
2864 ibuf += sizeof( did );
2865 if (( dir = dirlookup( vol, did )) == NULL ) {
2866 return( AFPERR_PARAM );
2869 /* dir_remove -- deletedid */
2875 /* did creation gets done automatically
2876 * there's a pb again with case but move it to cname
2878 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2881 struct dir *parentdir;
2889 memcpy(&vid, ibuf, sizeof(vid));
2890 ibuf += sizeof( vid );
2892 if (NULL == ( vol = getvolbyvid( vid )) ) {
2893 return( AFPERR_PARAM );
2896 memcpy(&did, ibuf, sizeof(did));
2897 ibuf += sizeof(did);
2899 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2903 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2904 return get_afp_errno(AFPERR_PARAM);
2907 if ( *path->m_name != '\0' ) {
2908 return path_error(path, AFPERR_NOOBJ);
2911 if ( !path->st_valid && of_stat(path ) < 0 ) {
2912 return( AFPERR_NOOBJ );
2914 if ( path->st_errno ) {
2915 return( AFPERR_NOOBJ );
2918 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2919 *rbuflen = sizeof(curdir->d_did);