2 * $Id: directory.c,v 1.123 2010-01-05 15:12:19 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_m_name_ucs2 = cdir->d_m_name_ucs2;
994 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
995 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
996 hash_alloc_insert(vol->v_hash, cdir, cdir);
999 /* the old was not in the same folder */
1001 dirchildremove(cdir->d_parent, cdir);
1004 /* parent/child directories */
1005 cdir->d_parent = dir;
1006 dirchildadd(vol, dir, cdir);
1010 /* --- public functions follow --- */
1011 /* free everything down. we don't bother to recolor as this is only
1012 * called to free the entire tree */
1013 void dirfreename(struct dir *dir)
1015 if (dir->d_u_name != dir->d_m_name) {
1016 free(dir->d_u_name);
1018 if (dir->d_m_name_ucs2)
1019 free(dir->d_m_name_ucs2);
1020 free(dir->d_m_name);
1023 void dirfree(struct dir *dir)
1025 if (!dir || (dir == SENTINEL))
1028 if ( dir->d_left != SENTINEL ) {
1029 dirfree( dir->d_left );
1031 if ( dir->d_right != SENTINEL ) {
1032 dirfree( dir->d_right );
1035 if (dir != SENTINEL) {
1041 /* --------------------------------------------
1042 * most of the time mac name and unix name are the same
1044 struct dir *dirnew(const char *m_name, const char *u_name)
1048 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1052 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1057 if (m_name == u_name || !strcmp(m_name, u_name)) {
1058 dir->d_u_name = dir->d_m_name;
1060 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1061 free(dir->d_m_name);
1066 dir->d_u_name_len = strlen(dir->d_u_name);
1067 dir->d_m_name_ucs2 = NULL;
1068 dir->d_left = dir->d_right = SENTINEL;
1069 dir->d_next = dir->d_prev = dir;
1073 /* ------------------ */
1074 static hash_val_t hash_fun_dir(const void *key)
1076 const struct dir *k = key;
1078 static unsigned long randbox[] = {
1079 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1080 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1081 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1082 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1085 const unsigned char *str = (unsigned char *)(k->d_u_name);
1086 hash_val_t acc = k->d_parent->d_did;
1089 acc ^= randbox[(*str + acc) & 0xf];
1090 acc = (acc << 1) | (acc >> 31);
1092 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1093 acc = (acc << 2) | (acc >> 30);
1100 #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
1101 || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
1102 #define get16bits(d) (*((const uint16_t *) (d)))
1105 #if !defined (get16bits)
1106 #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
1107 +(uint32_t)(((const uint8_t *)(d))[0]) )
1110 static hash_val_t hash_fun2_dir(const void *key)
1112 const struct dir *k = key;
1113 const char *data = k->d_u_name;
1114 int len = k->d_u_name_len;
1115 hash_val_t hash = k->d_parent->d_did, tmp;
1121 for (;len > 0; len--) {
1122 hash += get16bits (data);
1123 tmp = (get16bits (data+2) << 11) ^ hash;
1124 hash = (hash << 16) ^ tmp;
1125 data += 2*sizeof (uint16_t);
1129 /* Handle end cases */
1131 case 3: hash += get16bits (data);
1133 hash ^= data[sizeof (uint16_t)] << 18;
1136 case 2: hash += get16bits (data);
1140 case 1: hash += *data;
1145 /* Force "avalanching" of final 127 bits */
1156 /* ---------------- */
1157 static int hash_comp_dir(const void *key1, const void *key2)
1159 const struct dir *k1 = key1;
1160 const struct dir *k2 = key2;
1162 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1165 /* ---------------- */
1169 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun2_dir);
1172 /* ------------------ */
1173 static struct path *invalidate (struct vol *vol, struct dir *dir, struct path *ret)
1176 movecwd failed some of dir path are not there anymore.
1177 FIXME Is it true with other errors?
1178 so we remove dir from the cache
1180 if (dir->d_did == DIRDID_ROOT_PARENT)
1182 if (afp_errno == AFPERR_ACCESS) {
1183 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1186 /* FIXME should we set these?, don't need to call stat() after:
1188 ret->st_errno = EACCES;
1190 ret->m_name = dir->d_m_name;
1191 ret->u_name = dir->d_u_name;
1194 } else if (afp_errno == AFPERR_NOOBJ) {
1195 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1198 strcpy(ret->m_name, dir->d_m_name);
1199 if (dir->d_m_name == dir->d_u_name) {
1200 ret->u_name = ret->m_name;
1203 size_t tp = strlen(ret->m_name)+1;
1205 ret->u_name = ret->m_name +tp;
1206 strcpy(ret->u_name, dir->d_u_name);
1208 /* FIXME should we set :
1210 ret->st_errno = ENOENT;
1212 dir_invalidate(vol, dir);
1215 dir_invalidate(vol, dir);
1219 /* -------------------------------------------------- */
1225 stat the file or errno
1228 curdir: filename parent directory
1234 stat the dir or errno
1238 curdir: dir parent directory
1246 curdir: dir parent directory
1253 cname(struct vol *vol, struct dir *dir, char **cpath)
1255 struct dir *cdir, *scdir=NULL;
1256 static char path[ MAXPATHLEN + 1];
1257 static struct path ret;
1269 afp_errno = AFPERR_NOOBJ;
1270 memset(&ret, 0, sizeof(ret));
1271 switch (ret.m_type = *data) { /* path type */
1274 len = (unsigned char) *data++;
1277 if (afp_version >= 30) {
1283 if (afp_version >= 30) {
1285 memcpy(&hint, data, sizeof(hint));
1287 data += sizeof(hint);
1289 memcpy(&len16, data, sizeof(len16));
1296 /* else it's an error */
1298 afp_errno = AFPERR_PARAM;
1301 *cpath += len + size;
1306 if (movecwd( vol, dir ) < 0 ) {
1307 return invalidate(vol, dir, &ret );
1309 if (*path == '\0') {
1316 if (*data == sep ) {
1320 while (*data == sep && len > 0 ) {
1321 if ( dir->d_parent == NULL ) {
1324 dir = dir->d_parent;
1329 /* would this be faster with strlen + strncpy? */
1331 while ( *data != sep && len > 0 ) {
1333 if (p > &path[ MAXPATHLEN]) {
1334 afp_errno = AFPERR_PARAM;
1340 /* short cut bits by chopping off a trailing \0. this also
1341 makes the traversal happy w/ filenames at the end of the
1348 if ( p == path ) { /* end of the name parameter */
1352 if (afp_version >= 30) {
1357 static char temp[ MAXPATHLEN + 1];
1359 if (dir->d_did == DIRDID_ROOT_PARENT) {
1361 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1362 So we compare it with the longname from the current volume and if they match
1363 we overwrite the requested path with the utf8 volume name so that the following
1366 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1367 if (strcasecmp( path, temp) == 0)
1368 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1371 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1372 afp_errno = AFPERR_PARAM;
1378 /* check for OS X mangled filename :( */
1380 t = demangle_osx(vol, path, dir->d_did, &fileid);
1383 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1384 * flags weren't the same
1386 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1387 /* at last got our view of mac name */
1392 if (ret.u_name == NULL) {
1393 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1394 afp_errno = AFPERR_PARAM;
1400 cdir = dir->d_child;
1402 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1403 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1404 CH_UCS2, path, -1, (char **)&tmpname) )
1407 if (!cdir->d_m_name_ucs2) {
1408 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1409 /* this shouldn't happen !!!! */
1413 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1416 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1419 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1425 if (dir->d_did == DIRDID_ROOT_PARENT) {
1427 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1428 must check against the volume name.
1430 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1436 cdir = dirsearch_byname(vol, dir, ret.u_name);
1440 if (cdir == NULL && scdir != NULL) {
1442 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1445 if ( cdir == NULL ) {
1447 /* if dir == curdir it always succeed,
1448 even if curdir is deleted.
1449 it's not a pb because it will fail in extenddir
1451 if ( movecwd( vol, dir ) < 0 ) {
1452 /* dir is not valid anymore
1453 we delete dir from the cache and abort.
1455 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1456 afp_errno = AFPERR_NOOBJ;
1459 if (afp_errno == AFPERR_ACCESS)
1461 dir_invalidate(vol, dir);
1464 cdir = extenddir( vol, dir, &ret );
1468 cdir = extenddir( vol, dir, &ret );
1469 } /* if (!extend) */
1471 if ( cdir == NULL ) {
1473 if ( len > 0 || !ret.u_name ) {
1485 * Move curdir to dir, with a possible chdir()
1487 int movecwd(struct vol *vol, struct dir *dir)
1489 char path[MAXPATHLEN + 1];
1494 if ( dir == curdir ) {
1497 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1498 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1502 p = path + sizeof(path) - 1;
1505 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1508 /* parent directory is deleted */
1509 afp_errno = AFPERR_NOOBJ;
1513 if (p -n -1 < path) {
1514 afp_errno = AFPERR_PARAM;
1521 if ( d != curdir ) {
1522 n = strlen( vol->v_path );
1523 if (p -n -1 < path) {
1524 afp_errno = AFPERR_PARAM;
1529 memcpy( p, vol->v_path, n );
1531 if ( chdir( p ) < 0 ) {
1535 afp_errno = AFPERR_ACCESS;
1538 afp_errno = AFPERR_NOOBJ;
1543 vol->v_curdir = curdir = dir;
1548 * We can't use unix file's perm to support Apple's inherited protection modes.
1549 * If we aren't the file's owner we can't change its perms when moving it and smb
1550 * nfs,... don't even try.
1552 #define AFP_CHECK_ACCESS
1554 int check_access(char *path, int mode)
1556 #ifdef AFP_CHECK_ACCESS
1564 accessmode(p, &ma, curdir, NULL);
1565 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1567 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1573 /* --------------------- */
1574 int file_access(struct path *path, int mode)
1578 accessmode(path->u_name, &ma, curdir, &path->st);
1579 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1581 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1587 /* --------------------- */
1588 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1590 dir->offcnt = count;
1591 dir->ctime = st->st_ctime;
1592 dir->d_flags &= ~DIRF_CNID;
1595 /* ---------------------
1596 * is our cached offspring count valid?
1599 static int diroffcnt(struct dir *dir, struct stat *st)
1601 return st->st_ctime == dir->ctime;
1604 /* ---------------------
1605 * is our cached also for reenumerate id?
1608 int dirreenumerate(struct dir *dir, struct stat *st)
1610 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1613 /* --------------------- */
1614 static int invisible_dots(const struct vol *vol, const char *name)
1616 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1619 /* ------------------------------
1621 (name, dir) with curdir:name == dir, from afp_enumerate
1624 int getdirparams(const struct vol *vol,
1625 u_int16_t bitmap, struct path *s_path,
1627 char *buf, size_t *buflen )
1631 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1632 int bit = 0, isad = 0;
1638 struct stat *st = &s_path->st;
1639 char *upath = s_path->u_name;
1641 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1642 (1 << DIRPBIT_CDATE) |
1643 (1 << DIRPBIT_MDATE) |
1644 (1 << DIRPBIT_BDATE) |
1645 (1 << DIRPBIT_FINFO)))) {
1647 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1648 if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1653 if ( dir->d_did == DIRDID_ROOT) {
1654 pdid = DIRDID_ROOT_PARENT;
1655 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1658 pdid = dir->d_parent->d_did;
1662 while ( bitmap != 0 ) {
1663 while (( bitmap & 1 ) == 0 ) {
1671 ad_getattr(&ad, &ashort);
1672 } else if (invisible_dots(vol, dir->d_u_name)) {
1673 ashort = htons(ATTRBIT_INVISIBLE);
1676 ashort |= htons(ATTRBIT_SHARED);
1677 memcpy( data, &ashort, sizeof( ashort ));
1678 data += sizeof( ashort );
1682 memcpy( data, &pdid, sizeof( pdid ));
1683 data += sizeof( pdid );
1686 case DIRPBIT_CDATE :
1687 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1688 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1689 memcpy( data, &aint, sizeof( aint ));
1690 data += sizeof( aint );
1693 case DIRPBIT_MDATE :
1694 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1695 memcpy( data, &aint, sizeof( aint ));
1696 data += sizeof( aint );
1699 case DIRPBIT_BDATE :
1700 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1701 aint = AD_DATE_START;
1702 memcpy( data, &aint, sizeof( aint ));
1703 data += sizeof( aint );
1706 case DIRPBIT_FINFO :
1708 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1709 } else { /* no appledouble */
1710 memset( data, 0, 32 );
1711 /* set default view -- this also gets done in ad_open() */
1712 ashort = htons(FINDERINFO_CLOSEDVIEW);
1713 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1715 /* dot files are by default visible */
1716 if (invisible_dots(vol, dir->d_u_name)) {
1717 ashort = htons(FINDERINFO_INVISIBLE);
1718 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1724 case DIRPBIT_LNAME :
1725 if (dir->d_m_name) /* root of parent can have a null name */
1728 memset(data, 0, sizeof(u_int16_t));
1729 data += sizeof( u_int16_t );
1732 case DIRPBIT_SNAME :
1733 memset(data, 0, sizeof(u_int16_t));
1734 data += sizeof( u_int16_t );
1738 memcpy( data, &dir->d_did, sizeof( aint ));
1739 data += sizeof( aint );
1742 case DIRPBIT_OFFCNT :
1744 /* this needs to handle current directory access rights */
1745 if (diroffcnt(dir, st)) {
1746 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1748 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1749 setdiroffcnt(dir, st, ret);
1750 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1752 ashort = htons( ashort );
1753 memcpy( data, &ashort, sizeof( ashort ));
1754 data += sizeof( ashort );
1758 aint = htonl(st->st_uid);
1759 memcpy( data, &aint, sizeof( aint ));
1760 data += sizeof( aint );
1764 aint = htonl(st->st_gid);
1765 memcpy( data, &aint, sizeof( aint ));
1766 data += sizeof( aint );
1769 case DIRPBIT_ACCESS :
1770 accessmode( upath, &ma, dir , st);
1772 *data++ = ma.ma_user;
1773 *data++ = ma.ma_world;
1774 *data++ = ma.ma_group;
1775 *data++ = ma.ma_owner;
1778 /* Client has requested the ProDOS information block.
1779 Just pass back the same basic block for all
1780 directories. <shirsch@ibm.net> */
1781 case DIRPBIT_PDINFO :
1782 if (afp_version >= 30) { /* UTF8 name */
1783 utf8 = kTextEncodingUTF8;
1784 if (dir->d_m_name) /* root of parent can have a null name */
1787 memset(data, 0, sizeof(u_int16_t));
1788 data += sizeof( u_int16_t );
1790 memcpy(data, &aint, sizeof( aint ));
1791 data += sizeof( aint );
1793 else { /* ProDOS Info Block */
1796 ashort = htons( 0x0200 );
1797 memcpy( data, &ashort, sizeof( ashort ));
1798 data += sizeof( ashort );
1799 memset( data, 0, sizeof( ashort ));
1800 data += sizeof( ashort );
1804 case DIRPBIT_UNIXPR :
1805 aint = htonl(st->st_uid);
1806 memcpy( data, &aint, sizeof( aint ));
1807 data += sizeof( aint );
1808 aint = htonl(st->st_gid);
1809 memcpy( data, &aint, sizeof( aint ));
1810 data += sizeof( aint );
1813 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1814 memcpy( data, &aint, sizeof( aint ));
1815 data += sizeof( aint );
1817 accessmode( upath, &ma, dir , st);
1819 *data++ = ma.ma_user;
1820 *data++ = ma.ma_world;
1821 *data++ = ma.ma_group;
1822 *data++ = ma.ma_owner;
1827 ad_close_metadata( &ad );
1829 return( AFPERR_BITMAP );
1835 ashort = htons( data - buf );
1836 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1837 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1839 if ( utf_nameoff ) {
1840 ashort = htons( data - buf );
1841 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1842 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1845 ad_close_metadata( &ad );
1847 *buflen = data - buf;
1851 /* ----------------------------- */
1852 int path_error(struct path *path, int error)
1854 /* - a dir with access error
1855 * - no error it's a file
1858 if (path_isadir(path))
1860 if (path->st_valid && path->st_errno)
1862 return AFPERR_BADTYPE ;
1865 /* ----------------------------- */
1866 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1871 u_int16_t vid, bitmap;
1877 memcpy( &vid, ibuf, sizeof( vid ));
1878 ibuf += sizeof( vid );
1880 if (NULL == ( vol = getvolbyvid( vid )) ) {
1881 return( AFPERR_PARAM );
1884 if (vol->v_flags & AFPVOL_RO)
1885 return AFPERR_VLOCK;
1887 memcpy( &did, ibuf, sizeof( did ));
1888 ibuf += sizeof( int );
1890 if (NULL == ( dir = dirlookup( vol, did )) ) {
1894 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1895 bitmap = ntohs( bitmap );
1896 ibuf += sizeof( bitmap );
1898 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1899 return get_afp_errno(AFPERR_NOOBJ);
1902 if ( *path->m_name != '\0' ) {
1903 rc = path_error(path, AFPERR_NOOBJ);
1904 /* maybe we are trying to set perms back */
1905 if (rc != AFPERR_ACCESS)
1910 * If ibuf is odd, make it even.
1912 if ((u_long)ibuf & 1 ) {
1916 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1917 setvoltime(obj, vol );
1923 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1925 * assume path == '\0' eg. it's a directory in canonical form
1928 struct path Cur_Path = {
1931 ".", /* unix name */
1933 NULL,/* struct dir */
1934 0, /* stat is not set */
1937 /* ------------------ */
1938 static int set_dir_errors(struct path *path, const char *where, int err)
1943 return AFPERR_ACCESS;
1945 return AFPERR_VLOCK;
1947 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1948 return AFPERR_PARAM;
1951 /* ------------------ */
1952 int setdirparams(struct vol *vol,
1953 struct path *path, u_int16_t d_bitmap, char *buf )
1965 u_int16_t ashort, bshort;
1967 int change_mdate = 0;
1968 int change_parent_mdate = 0;
1970 u_int16_t bitmap = d_bitmap;
1971 u_char finder_buf[32];
1974 u_int16_t upriv_bit = 0;
1977 upath = path->u_name;
1979 while ( bitmap != 0 ) {
1980 while (( bitmap & 1 ) == 0 ) {
1988 memcpy( &ashort, buf, sizeof( ashort ));
1989 buf += sizeof( ashort );
1991 case DIRPBIT_CDATE :
1993 memcpy(&cdate, buf, sizeof(cdate));
1994 buf += sizeof( cdate );
1996 case DIRPBIT_MDATE :
1997 memcpy(&newdate, buf, sizeof(newdate));
1998 buf += sizeof( newdate );
2000 case DIRPBIT_BDATE :
2002 memcpy(&bdate, buf, sizeof(bdate));
2003 buf += sizeof( bdate );
2005 case DIRPBIT_FINFO :
2007 memcpy( finder_buf, buf, 32 );
2010 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2011 change_parent_mdate = 1;
2012 memcpy( &owner, buf, sizeof(owner));
2013 buf += sizeof( owner );
2016 change_parent_mdate = 1;
2017 memcpy( &group, buf, sizeof( group ));
2018 buf += sizeof( group );
2020 case DIRPBIT_ACCESS :
2022 change_parent_mdate = 1;
2023 ma.ma_user = *buf++;
2024 ma.ma_world = *buf++;
2025 ma.ma_group = *buf++;
2026 ma.ma_owner = *buf++;
2027 mpriv = mtoumode( &ma ) | vol->v_dperm;
2028 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2029 err = set_dir_errors(path, "setdirmode", errno);
2033 /* Ignore what the client thinks we should do to the
2034 ProDOS information block. Skip over the data and
2035 report nothing amiss. <shirsch@ibm.net> */
2036 case DIRPBIT_PDINFO :
2037 if (afp_version < 30) {
2041 err = AFPERR_BITMAP;
2045 case DIRPBIT_UNIXPR :
2046 if (vol_unix_priv(vol)) {
2047 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2048 buf += sizeof( owner );
2049 memcpy( &group, buf, sizeof( group ));
2050 buf += sizeof( group );
2053 change_parent_mdate = 1;
2054 memcpy( &upriv, buf, sizeof( upriv ));
2055 buf += sizeof( upriv );
2056 upriv = ntohl (upriv) | vol->v_dperm;
2057 if (dir_rx_set(upriv)) {
2058 /* maybe we are trying to set perms back */
2059 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2061 err = set_dir_errors(path, "setdirunixmode", errno);
2072 err = AFPERR_BITMAP;
2080 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2082 if (ad_open_metadata( upath, ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2084 * Check to see what we're trying to set. If it's anything
2085 * but ACCESS, UID, or GID, give an error. If it's any of those
2086 * three, we don't need the ad to be open, so just continue.
2088 * note: we also don't need to worry about mdate. also, be quiet
2089 * if we're using the noadouble option.
2091 if (!vol_noadouble(vol) && (d_bitmap &
2092 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2093 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2094 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2095 return AFPERR_ACCESS;
2101 * Check to see if a create was necessary. If it was, we'll want
2102 * to set our name, etc.
2104 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2105 ad_setname(&ad, curdir->d_m_name);
2111 while ( bitmap != 0 ) {
2112 while (( bitmap & 1 ) == 0 ) {
2120 ad_getattr(&ad, &bshort);
2121 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2122 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2123 change_parent_mdate = 1;
2124 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2125 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2129 ad_setattr(&ad, bshort);
2132 case DIRPBIT_CDATE :
2134 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2137 case DIRPBIT_MDATE :
2139 case DIRPBIT_BDATE :
2141 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2144 case DIRPBIT_FINFO :
2146 /* Fixes #2802236 */
2147 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2148 *fflags &= htons(~FINDERINFO_ISHARED);
2150 if ( dir->d_did == DIRDID_ROOT ) {
2152 * Alright, we admit it, this is *really* sick!
2153 * The 4 bytes that we don't copy, when we're dealing
2154 * with the root of a volume, are the directory's
2155 * location information. This eliminates that annoying
2156 * behavior one sees when mounting above another mount
2159 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2160 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2162 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2166 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2167 if ( (dir->d_did == DIRDID_ROOT) &&
2168 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2169 err = set_dir_errors(path, "setdeskowner", errno);
2170 if (isad && err == AFPERR_PARAM) {
2171 err = AFP_OK; /* ???*/
2174 goto setdirparam_done;
2177 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2178 err = set_dir_errors(path, "setdirowner", errno);
2179 goto setdirparam_done;
2183 if (dir->d_did == DIRDID_ROOT)
2184 setdeskowner( -1, ntohl(group) );
2185 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2186 err = set_dir_errors(path, "setdirowner", errno);
2187 goto setdirparam_done;
2190 case DIRPBIT_ACCESS :
2191 if (dir->d_did == DIRDID_ROOT) {
2193 if (!dir_rx_set(mpriv)) {
2194 /* we can't remove read and search for owner on volume root */
2195 err = AFPERR_ACCESS;
2196 goto setdirparam_done;
2200 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2201 err = set_dir_errors(path, "setdirmode", errno);
2202 goto setdirparam_done;
2205 case DIRPBIT_PDINFO :
2206 if (afp_version >= 30) {
2207 err = AFPERR_BITMAP;
2208 goto setdirparam_done;
2211 case DIRPBIT_UNIXPR :
2212 if (vol_unix_priv(vol)) {
2213 if (dir->d_did == DIRDID_ROOT) {
2214 if (!dir_rx_set(upriv)) {
2215 /* we can't remove read and search for owner on volume root */
2216 err = AFPERR_ACCESS;
2217 goto setdirparam_done;
2219 setdeskowner( -1, ntohl(group) );
2220 setdeskmode( upriv );
2222 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2223 err = set_dir_errors(path, "setdirowner", errno);
2224 goto setdirparam_done;
2227 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2228 err = set_dir_errors(path, "setdirunixmode", errno);
2229 goto setdirparam_done;
2233 err = AFPERR_BITMAP;
2234 goto setdirparam_done;
2238 err = AFPERR_BITMAP;
2239 goto setdirparam_done;
2248 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2249 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2253 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2254 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2259 if (path->st_valid && !path->st_errno) {
2260 struct stat *st = &path->st;
2262 if (dir && dir->d_parent) {
2263 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2267 ad_close_metadata( &ad);
2270 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2271 && gettimeofday(&tv, NULL) == 0) {
2272 if (!movecwd(vol, dir->d_parent)) {
2273 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2274 /* be careful with bitmap because now dir is null */
2275 bitmap = 1<<DIRPBIT_MDATE;
2276 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2277 /* should we reset curdir ?*/
2284 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2298 memcpy( &vid, ibuf, sizeof( vid ));
2299 ibuf += sizeof( vid );
2300 if (NULL == (vol = getvolbyvid( vid )) ) {
2301 return( AFPERR_PARAM );
2304 memcpy( &did, ibuf, sizeof( did ));
2305 ibuf += sizeof( did );
2309 * if it's CNID 2 our only choice to meet the specs is call sync.
2310 * For any other CNID just sync that dir. To my knowledge the
2311 * intended use of FPSyncDir is to sync the volume so all we're
2312 * ever going to see here is probably CNID 2. Anyway, we' prepared.
2315 if ( ntohl(did) == 2 ) {
2318 if (NULL == ( dir = dirlookup( vol, did )) ) {
2319 return afp_errno; /* was AFPERR_NOOBJ */
2322 if (movecwd( vol, dir ) < 0 )
2323 return ( AFPERR_NOOBJ );
2326 * Assuming only OSens that have dirfd also may require fsyncing directories
2327 * in order to flush metadata e.g. Linux.
2331 if (NULL == ( dp = opendir( "." )) ) {
2334 return( AFPERR_NOOBJ );
2336 return( AFPERR_ACCESS );
2338 return( AFPERR_PARAM );
2342 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2345 if ( fsync ( dfd ) < 0 )
2346 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2347 dir->d_u_name, strerror(errno) );
2348 closedir(dp); /* closes dfd too */
2351 if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2354 return( AFPERR_NOOBJ );
2356 return( AFPERR_ACCESS );
2358 return( AFPERR_PARAM );
2362 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2363 vol->ad_path(".", ADFLAGS_DIR) );
2365 if ( fsync(dfd) < 0 )
2366 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2367 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2374 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2380 struct path *s_path;
2388 memcpy( &vid, ibuf, sizeof( vid ));
2389 ibuf += sizeof( vid );
2390 if (NULL == ( vol = getvolbyvid( vid )) ) {
2391 return( AFPERR_PARAM );
2394 if (vol->v_flags & AFPVOL_RO)
2395 return AFPERR_VLOCK;
2397 memcpy( &did, ibuf, sizeof( did ));
2398 ibuf += sizeof( did );
2399 if (NULL == ( dir = dirlookup( vol, did )) ) {
2400 return afp_errno; /* was AFPERR_NOOBJ */
2402 /* for concurrent access we need to be sure we are not in the
2403 * folder we want to create...
2407 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2408 return get_afp_errno(AFPERR_PARAM);
2410 /* cname was able to move curdir to it! */
2411 if (*s_path->m_name == '\0')
2412 return AFPERR_EXIST;
2414 upath = s_path->u_name;
2416 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2420 if (of_stat(s_path) < 0) {
2424 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2428 if ( movecwd( vol, dir ) < 0 ) {
2429 return( AFPERR_PARAM );
2432 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2433 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2434 if (vol_noadouble(vol))
2435 goto createdir_done;
2436 return( AFPERR_ACCESS );
2438 ad_setname(&ad, s_path->m_name);
2439 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2442 ad_close_metadata( &ad);
2445 #ifdef HAVE_NFSv4_ACLS
2446 /* FIXME: are we really inside the created dir? */
2447 addir_inherit_acl(vol);
2450 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2451 *rbuflen = sizeof( u_int32_t );
2452 setvoltime(obj, vol );
2457 * dst new unix filename (not a pathname)
2458 * newname new mac name
2462 int renamedir(const struct vol *vol, char *src, char *dst,
2464 struct dir *newparent,
2472 /* existence check moved to afp_moveandrename */
2473 if ( unix_rename( src, dst ) < 0 ) {
2476 return( AFPERR_NOOBJ );
2478 return( AFPERR_ACCESS );
2480 return AFPERR_VLOCK;
2482 /* tried to move directory into a subdirectory of itself */
2483 return AFPERR_CANTMOVE;
2485 /* this needs to copy and delete. bleah. that means we have
2486 * to deal with entire directory hierarchies. */
2487 if ((err = copydir(vol, src, dst)) < 0) {
2491 if ((err = deletedir(src)) < 0)
2495 return( AFPERR_PARAM );
2499 vol->vfs->vfs_renamedir(vol, src, dst);
2501 len = strlen( newname );
2502 /* rename() succeeded so we need to update our tree even if we can't open
2506 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2508 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2509 ad_setname(&ad, newname);
2511 ad_close_metadata( &ad);
2514 dir_hash_del(vol, dir);
2515 if (dir->d_m_name == dir->d_u_name)
2516 dir->d_u_name = NULL;
2518 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2519 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2520 /* FIXME : fatal ? */
2523 dir->d_m_name = buf;
2524 strcpy( dir->d_m_name, newname );
2526 if (newname == dst) {
2527 free(dir->d_u_name);
2528 dir->d_u_name = dir->d_m_name;
2531 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2532 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2535 dir->d_u_name = buf;
2536 strcpy( dir->d_u_name, dst );
2539 if (dir->d_m_name_ucs2)
2540 free(dir->d_m_name_ucs2);
2542 dir->d_m_name_ucs2 = NULL;
2543 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))
2544 dir->d_m_name_ucs2 = NULL;
2546 if (( parent = dir->d_parent ) == NULL ) {
2549 if ( parent == newparent ) {
2550 hash_alloc_insert(vol->v_hash, dir, dir);
2554 /* detach from old parent and add to new one. */
2555 dirchildremove(parent, dir);
2556 dir->d_parent = newparent;
2557 dirchildadd(vol, newparent, dir);
2561 /* delete an empty directory */
2562 int deletecurdir(struct vol *vol)
2572 if ( curdir->d_parent == NULL ) {
2573 return( AFPERR_ACCESS );
2578 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2579 /* we never want to create a resource fork here, we are going to delete it */
2580 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2582 ad_getattr(&ad, &ashort);
2583 ad_close( &ad, ADFLAGS_HF );
2584 if ((ashort & htons(ATTRBIT_NODELETE))) {
2585 return AFPERR_OLOCK;
2588 err = vol->vfs->vfs_deletecurdir(vol);
2593 /* now get rid of dangling symlinks */
2594 if ((dp = opendir("."))) {
2595 while ((de = readdir(dp))) {
2596 /* skip this and previous directory */
2597 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2600 /* bail if it's not a symlink */
2601 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2603 return AFPERR_DIRNEMPT;
2606 if ((err = netatalk_unlink(de->d_name))) {
2613 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2618 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2619 dirchildremove(curdir, fdir);
2620 cnid_delete(vol->v_cdb, fdir->d_did);
2621 dir_remove( vol, fdir );
2626 /* inode is used as key for cnid.
2627 * Close the descriptor only after cnid_delete
2635 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2645 sfunc = (unsigned char) *ibuf++;
2649 if (sfunc >= 3 && sfunc <= 6) {
2650 if (afp_version < 30) {
2651 return( AFPERR_PARAM );
2658 case 3 :/* unicode */
2659 memcpy( &id, ibuf, sizeof( id ));
2662 if (( pw = getpwuid( id )) == NULL ) {
2663 return( AFPERR_NOITEM );
2665 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2666 pw->pw_name, -1, &name);
2673 case 4 : /* unicode */
2674 memcpy( &id, ibuf, sizeof( id ));
2677 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2678 return( AFPERR_NOITEM );
2680 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2681 gr->gr_name, -1, &name);
2687 #ifdef HAVE_NFSv4_ACLS
2688 case 5 : /* UUID -> username */
2689 case 6 : /* UUID -> groupname */
2690 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2691 return AFPERR_PARAM;
2692 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2694 len = getnamefromuuid( ibuf, &name, &type);
2695 if (len != 0) /* its a error code, not len */
2696 return AFPERR_NOITEM;
2697 if (type == UUID_USER) {
2698 if (( pw = getpwnam( name )) == NULL )
2699 return( AFPERR_NOITEM );
2700 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2701 id = htonl(UUID_USER);
2702 memcpy( rbuf, &id, sizeof( id ));
2703 id = htonl( pw->pw_uid);
2704 rbuf += sizeof( id );
2705 memcpy( rbuf, &id, sizeof( id ));
2706 rbuf += sizeof( id );
2707 *rbuflen = 2 * sizeof( id );
2708 } else { /* type == UUID_GROUP */
2709 if (( gr = getgrnam( name )) == NULL )
2710 return( AFPERR_NOITEM );
2711 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2712 id = htonl(UUID_GROUP);
2713 memcpy( rbuf, &id, sizeof( id ));
2714 rbuf += sizeof( id );
2715 id = htonl( gr->gr_gid);
2716 memcpy( rbuf, &id, sizeof( id ));
2717 rbuf += sizeof( id );
2718 *rbuflen = 2 * sizeof( id );
2723 return( AFPERR_PARAM );
2727 len = strlen( name );
2730 u_int16_t tp = htons(len);
2731 memcpy(rbuf, &tp, sizeof(tp));
2740 memcpy( rbuf, name, len );
2748 int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2757 sfunc = (unsigned char) *ibuf++;
2759 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2762 case 2 : /* unicode */
2763 if (afp_version < 30) {
2764 return( AFPERR_PARAM );
2766 memcpy(&ulen, ibuf, sizeof(ulen));
2769 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2773 len = (unsigned char) *ibuf++;
2775 #ifdef HAVE_NFSv4_ACLS
2776 case 5 : /* username -> UUID */
2777 case 6 : /* groupname -> UUID */
2778 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2779 return AFPERR_PARAM;
2780 memcpy(&ulen, ibuf, sizeof(ulen));
2786 return( AFPERR_PARAM );
2792 return AFPERR_PARAM;
2795 case 1 : /* unicode */
2797 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2798 return( AFPERR_NOITEM );
2802 memcpy( rbuf, &id, sizeof( id ));
2803 *rbuflen = sizeof( id );
2806 case 2 : /* unicode */
2808 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2809 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2810 return( AFPERR_NOITEM );
2813 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2815 memcpy( rbuf, &id, sizeof( id ));
2816 *rbuflen = sizeof( id );
2818 #ifdef HAVE_NFSv4_ACLS
2819 case 5 : /* username -> UUID */
2820 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2821 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2822 return AFPERR_NOITEM;
2823 *rbuflen = UUID_BINSIZE;
2825 case 6 : /* groupname -> UUID */
2826 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2827 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2828 return AFPERR_NOITEM;
2829 *rbuflen = UUID_BINSIZE;
2837 /* ------------------------------------
2838 variable DID support
2840 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2851 /* do nothing as dids are static for the life of the process. */
2855 memcpy(&vid, ibuf, sizeof( vid ));
2856 ibuf += sizeof( vid );
2857 if (( vol = getvolbyvid( vid )) == NULL ) {
2858 return( AFPERR_PARAM );
2861 memcpy( &did, ibuf, sizeof( did ));
2862 ibuf += sizeof( did );
2863 if (( dir = dirlookup( vol, did )) == NULL ) {
2864 return( AFPERR_PARAM );
2867 /* dir_remove -- deletedid */
2873 /* did creation gets done automatically
2874 * there's a pb again with case but move it to cname
2876 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2879 struct dir *parentdir;
2887 memcpy(&vid, ibuf, sizeof(vid));
2888 ibuf += sizeof( vid );
2890 if (NULL == ( vol = getvolbyvid( vid )) ) {
2891 return( AFPERR_PARAM );
2894 memcpy(&did, ibuf, sizeof(did));
2895 ibuf += sizeof(did);
2897 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2901 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2902 return get_afp_errno(AFPERR_PARAM);
2905 if ( *path->m_name != '\0' ) {
2906 return path_error(path, AFPERR_NOOBJ);
2909 if ( !path->st_valid && of_stat(path ) < 0 ) {
2910 return( AFPERR_NOOBJ );
2912 if ( path->st_errno ) {
2913 return( AFPERR_NOOBJ );
2916 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2917 *rbuflen = sizeof(curdir->d_did);