2 * $Id: directory.c,v 1.126 2010-01-06 15:37:01 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);
69 * There are currently two cache structures where afpd caches directory information
70 * a) a DID/dirname cache in a hashtable
71 * b) a (red-black) tree with CNIDs as key
73 * a) is for searching by DID/dirname
74 * b) is for searching by CNID
76 * Through additional parent, child, previous and next pointers, b) is also used to
77 * represent the on-disk layout of the filesystem. parent and child point to parent
78 * and child directory respectively, linking 2 or more subdirectories in one
79 * directory with previous and next pointers.
81 * Usage examples, highlighting the main functions:
83 * a) is eg used in enumerate():
85 * dir = dirsearch_byname() // search in cache
86 * if (dir == NULL) // not found
87 * dir = adddir() // add to cache
90 * b) is eg used in afp_getfildirparams()
91 * dirlookup() // wrapper for cache and db search
92 * => dir = dirsearch() // search in cache
96 * cnid_resolve() // resolve with CNID database
97 * cname() // add to cache
103 #define SENTINEL (&sentinel)
104 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
105 NULL, NULL, NULL, NULL, NULL, 0, 0,
106 0, 0, NULL, NULL, 0, NULL};
107 static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
108 NULL, NULL, NULL, NULL, NULL, 0, 0,
109 0, 0, NULL, NULL, 0, NULL};
111 /* (from IM: Toolbox Essentials)
112 * dirFinderInfo (DInfo) fields:
114 * frRect 8 folder's window rectangle
116 * frLocation 4 folder's location in window
117 * frView 2 folder's view (default == closedView (256))
119 * extended dirFinderInfo (DXInfo) fields:
120 * frScroll 4 scroll position
121 * frOpenChain: 4 directory ID chain of open folders
122 * frScript: 1 script flag and code
123 * frXFlags: 1 reserved
124 * frComment: 2 comment ID
125 * frPutAway: 4 home directory ID
129 vol_tree_root(const struct vol *vol, u_int32_t did)
133 if (vol->v_curdir && vol->v_curdir->d_did == did) {
143 * redid did assignment for directories. now we use red-black trees.
147 dirsearch(const struct vol *vol, u_int32_t did)
152 /* check for 0 did */
154 afp_errno = AFPERR_PARAM;
157 if ( did == DIRDID_ROOT_PARENT ) {
159 rootpar.d_did = DIRDID_ROOT_PARENT;
160 rootpar.d_child = vol->v_dir;
164 dir = vol_tree_root(vol, did);
166 afp_errno = AFPERR_NOOBJ;
167 while ( dir != SENTINEL ) {
168 if (dir->d_did == did)
169 return dir->d_m_name ? dir : NULL;
170 dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
175 /* ------------------- */
176 int get_afp_errno(const int param)
178 if (afp_errno != AFPERR_DID1)
183 /* ------------------- */
185 dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
187 struct dir *dir = NULL;
189 if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
195 key.d_u_name_len = strlen(name);
196 hn = hash_lookup(vol->v_hash, &key);
204 /* -----------------------------------------
205 * if did is not in the cache resolve it with cnid
208 * OSX call it with bogus id, ie file ID not folder ID,
209 * and we are really bad in this case.
212 dirlookup( struct vol *vol, u_int32_t did)
217 static char path[MAXPATHLEN + 1];
220 static char buffer[12 + MAXPATHLEN + 1];
221 int buflen = 12 + MAXPATHLEN + 1;
226 ret = dirsearch(vol, did);
227 if (ret != NULL || afp_errno == AFPERR_PARAM)
230 utf8 = utf8_encoding();
231 maxpath = (utf8)?MAXPATHLEN -7:255;
233 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
234 afp_errno = AFPERR_NOOBJ;
237 ptr = path + MAXPATHLEN;
238 if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
239 afp_errno = AFPERR_NOOBJ;
243 pathlen = len; /* no 0 in the last part */
245 strcpy(ptr - len, mpath);
248 ret = dirsearch(vol,id);
253 if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
255 NULL == (mpath = utompath(vol, upath, cnid, utf8))
257 afp_errno = AFPERR_NOOBJ;
261 len = strlen(mpath) + 1;
263 if (pathlen > maxpath) {
264 afp_errno = AFPERR_PARAM;
267 strcpy(ptr - len, mpath);
271 /* fill the cache, another place where we know about the path type */
277 temp16 = htons(pathlen);
278 memcpy(ptr, &temp16, sizeof(temp16));
280 temp = htonl(kTextEncodingUTF8);
282 memcpy(ptr, &temp, sizeof(temp));
288 *ptr = (unsigned char)pathlen;
292 /* cname is not efficient */
293 if (cname( vol, ret, &ptr ) == NULL )
296 return dirsearch(vol, did);
299 /* child addition/removal */
300 static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
305 b->d_next = a->d_child;
306 b->d_prev = b->d_next->d_prev;
307 b->d_next->d_prev = b;
308 b->d_prev->d_next = b;
310 if (!hash_alloc_insert(vol->v_hash, b, b)) {
311 LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
315 static void dirchildremove(struct dir *a,struct dir *b)
318 a->d_child = (b == b->d_next) ? NULL : b->d_next;
319 b->d_next->d_prev = b->d_prev;
320 b->d_prev->d_next = b->d_next;
321 b->d_next = b->d_prev = b;
324 /* --------------------------- */
325 /* rotate the tree to the left */
326 static void dir_leftrotate(struct vol *vol, struct dir *dir)
328 struct dir *right = dir->d_right;
330 /* whee. move the right's left tree into dir's right tree */
331 dir->d_right = right->d_left;
332 if (right->d_left != SENTINEL)
333 right->d_left->d_back = dir;
335 if (right != SENTINEL) {
336 right->d_back = dir->d_back;
340 if (!dir->d_back) /* no parent. move the right tree to the top. */
342 else if (dir == dir->d_back->d_left) /* we were on the left */
343 dir->d_back->d_left = right;
345 dir->d_back->d_right = right; /* we were on the right */
347 /* re-insert dir on the left tree */
354 /* rotate the tree to the right */
355 static void dir_rightrotate(struct vol *vol, struct dir *dir)
357 struct dir *left = dir->d_left;
359 /* whee. move the left's right tree into dir's left tree */
360 dir->d_left = left->d_right;
361 if (left->d_right != SENTINEL)
362 left->d_right->d_back = dir;
364 if (left != SENTINEL) {
365 left->d_back = dir->d_back;
369 if (!dir->d_back) /* no parent. move the left tree to the top. */
371 else if (dir == dir->d_back->d_right) /* we were on the right */
372 dir->d_back->d_right = left;
374 dir->d_back->d_left = left; /* we were on the left */
376 /* re-insert dir on the right tree */
382 /* recolor after a removal */
383 static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
387 while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
388 /* are we on the left tree? */
389 if (dir == dir->d_back->d_left) {
390 leaf = dir->d_back->d_right; /* get right side */
391 if (leaf->d_color == DIRTREE_COLOR_RED) {
392 /* we're red. we need to change to black. */
393 leaf->d_color = DIRTREE_COLOR_BLACK;
394 dir->d_back->d_color = DIRTREE_COLOR_RED;
395 dir_leftrotate(vol, dir->d_back);
396 leaf = dir->d_back->d_right;
399 /* right leaf has black end nodes */
400 if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
401 (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
402 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
403 dir = dir->d_back; /* ascend */
405 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
406 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
407 leaf->d_color = DIRTREE_COLOR_RED;
408 dir_rightrotate(vol, leaf);
409 leaf = dir->d_back->d_right;
411 leaf->d_color = dir->d_back->d_color;
412 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
413 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
414 dir_leftrotate(vol, dir->d_back);
417 } else { /* right tree */
418 leaf = dir->d_back->d_left; /* left tree */
419 if (leaf->d_color == DIRTREE_COLOR_RED) {
420 leaf->d_color = DIRTREE_COLOR_BLACK;
421 dir->d_back->d_color = DIRTREE_COLOR_RED;
422 dir_rightrotate(vol, dir->d_back);
423 leaf = dir->d_back->d_left;
426 /* left leaf has black end nodes */
427 if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
428 (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
429 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
430 dir = dir->d_back; /* ascend */
432 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
433 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
434 leaf->d_color = DIRTREE_COLOR_RED;
435 dir_leftrotate(vol, leaf);
436 leaf = dir->d_back->d_left;
438 leaf->d_color = dir->d_back->d_color;
439 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
440 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
441 dir_rightrotate(vol, dir->d_back);
446 dir->d_color = DIRTREE_COLOR_BLACK;
452 /* --------------------- */
453 static void dir_hash_del(const struct vol *vol, struct dir *dir)
457 hn = hash_lookup(vol->v_hash, dir);
459 LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
462 hash_delete(vol->v_hash, hn);
466 /* remove the node from the tree. this is just like insertion, but
467 * different. actually, it has to worry about a bunch of things that
468 * insertion doesn't care about. */
470 static void dir_remove( struct vol *vol, struct dir *dir)
473 struct ofork *of, *last;
474 struct dir *node, *leaf;
475 #endif /* REMOVE_NODES */
477 if (!dir || (dir == SENTINEL))
480 /* i'm not sure if it really helps to delete stuff. */
481 dir_hash_del(vol, dir);
482 vol->v_curdir = NULL;
485 dir->d_m_name = NULL;
486 dir->d_u_name = NULL;
487 dir->d_m_name_ucs2 = NULL;
488 #else /* ! REMOVE_NODES */
490 /* go searching for a node with at most one child */
491 if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
495 while (node->d_left != SENTINEL)
500 leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
503 leaf->d_back = node->d_back;
506 } else if (node == node->d_back->d_left) { /* left tree */
507 node->d_back->d_left = leaf;
509 node->d_back->d_right = leaf;
512 /* we want to free node, but we also want to free the data in dir.
513 * currently, that's d_name and the directory traversal bits.
514 * we just copy the necessary bits and then fix up all the
515 * various pointers to the directory. needless to say, there are
516 * a bunch of places that store the directory struct. */
518 struct dir save, *tmp;
520 memcpy(&save, dir, sizeof(save));
521 memcpy(dir, node, sizeof(struct dir));
523 /* restore the red-black bits */
524 dir->d_left = save.d_left;
525 dir->d_right = save.d_right;
526 dir->d_back = save.d_back;
527 dir->d_color = save.d_color;
529 if (node == vol->v_dir) {/* we may need to fix up this pointer */
531 rootpar.d_child = vol->v_dir;
533 /* if we aren't the root directory, we have parents and
534 * siblings to worry about */
535 if (dir->d_parent->d_child == node)
536 dir->d_parent->d_child = dir;
537 dir->d_next->d_prev = dir;
538 dir->d_prev->d_next = dir;
541 /* fix up children. */
545 tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
548 if (node == curdir) /* another pointer to fixup */
551 /* we also need to fix up oforks. bleah */
552 if ((of = dir->d_ofork)) {
553 last = of->of_d_prev;
556 of = (last == of) ? NULL : of->of_d_next;
560 /* set the node's d_name */
561 node->d_m_name = save.d_m_name;
562 node->d_u_name = save.d_u_name;
563 node->d_m_name_ucs2 = save.d_m_name_ucs2;
566 if (node->d_color == DIRTREE_COLOR_BLACK)
567 dir_rmrecolor(vol, leaf);
569 if (node->d_m_name_ucs2)
570 free(node->d_u_name_ucs2);
571 if (node->d_u_name != node->d_m_name) {
572 free(node->d_u_name);
574 free(node->d_m_name);
576 #endif /* ! REMOVE_NODES */
579 /* ---------------------------------------
580 * remove the node and its childs from the tree
582 * FIXME what about opened forks with refs to it?
583 * it's an afp specs violation because you can't delete
584 * an opened forks. Now afpd doesn't care about forks opened by other
585 * process. It's fixable within afpd if fnctl_lock, doable with smb and
586 * next to impossible for nfs and local filesystem access.
588 static void dir_invalidate( struct vol *vol, struct dir *dir)
591 /* v_root can't be deleted */
592 if (movecwd(vol, vol->v_root) < 0) {
593 LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
597 dirchildremove(dir->d_parent, dir);
598 dir_remove( vol, dir );
601 /* ------------------------------------ */
602 static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
606 pdir = vol_tree_root(vol, dir->d_did);
607 while (pdir->d_did != dir->d_did ) {
608 if ( pdir->d_did > dir->d_did ) {
609 if ( pdir->d_left == SENTINEL ) {
616 if ( pdir->d_right == SENTINEL ) {
621 pdir = pdir->d_right;
627 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
630 caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
635 static u_int32_t did = 0;
636 static char cname[MAXPATHLEN];
637 static char lname[MAXPATHLEN];
638 ucs2_t u2_path[MAXPATHLEN];
639 ucs2_t u2_dename[MAXPATHLEN];
640 char *tmp, *savepath;
642 if (!(vol->v_flags & AFPVOL_CASEINSEN))
645 if (veto_file(ENUMVETO, path->u_name))
648 savepath = path->u_name;
650 /* very simple cache */
651 if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
652 path->u_name = cname;
654 if (of_stat( path ) == 0 ) {
657 /* something changed, we cannot stat ... */
661 if (NULL == ( dp = opendir( "." )) ) {
662 LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
667 /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
668 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, -1, u2_path, sizeof(u2_path)) )
669 LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
671 /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
673 for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
674 if (NULL == check_dirent(vol, de->d_name))
677 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, -1, u2_dename, sizeof(u2_dename)) )
680 if (strcasecmp_w( u2_path, u2_dename) == 0) {
682 strlcpy(cname, de->d_name, sizeof(cname));
683 path->u_name = cname;
685 if (of_stat( path ) == 0 ) {
686 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
687 strlcpy(lname, tmp, sizeof(lname));
700 /* invalidate cache */
703 path->u_name = savepath;
705 /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
711 * attempt to extend the current dir. tree to include path
712 * as a side-effect, movecwd to that point and return the new dir
715 extenddir(struct vol *vol, struct dir *dir, struct path *path)
719 if ( path->u_name == NULL) {
720 afp_errno = AFPERR_PARAM;
724 if (check_name(vol, path->u_name)) {
725 /* the name is illegal */
726 LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
728 afp_errno = AFPERR_PARAM;
732 if (of_stat( path ) != 0 ) {
733 if (!(vol->v_flags & AFPVOL_CASEINSEN))
735 else if(caseenumerate(vol, path, dir) != 0)
739 if (!S_ISDIR(path->st.st_mode)) {
743 /* mac name is always with the right encoding (from cname()) */
744 if (( dir = adddir( vol, dir, path)) == NULL ) {
749 if ( movecwd( vol, dir ) < 0 ) {
756 /* -------------------------
757 appledouble mkdir afp error code.
759 static int netatalk_mkdir(const char *name)
761 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
764 return( AFPERR_NOOBJ );
766 return( AFPERR_VLOCK );
769 return( AFPERR_ACCESS );
771 return( AFPERR_EXIST );
774 return( AFPERR_DFULL );
776 return( AFPERR_PARAM );
782 /* ------------------- */
783 static int deletedir(char *dir)
785 char path[MAXPATHLEN + 1];
793 if ((len = strlen(dir)) +2 > sizeof(path))
797 if ((dp = opendir(dir)) == NULL)
803 remain = sizeof(path) -len -1;
804 while ((de = readdir(dp)) && err == AFP_OK) {
805 /* skip this and previous directory */
806 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
809 if (strlen(de->d_name) > remain) {
813 strcpy(path + len, de->d_name);
814 if (stat(path, &st)) {
817 if (S_ISDIR(st.st_mode)) {
818 err = deletedir(path);
820 err = netatalk_unlink(path);
825 /* okay. the directory is empty. delete it. note: we already got rid
828 err = netatalk_rmdir(dir);
833 /* do a recursive copy. */
834 static int copydir(const struct vol *vol, char *src, char *dst)
836 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
845 /* doesn't exist or the path is too long. */
846 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
847 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
848 ((dp = opendir(src)) == NULL))
851 /* try to create the destination directory */
852 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
857 /* set things up to copy */
861 srem = sizeof(spath) - slen -1;
866 drem = sizeof(dpath) - dlen -1;
869 while ((de = readdir(dp))) {
870 /* skip this and previous directory */
871 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
874 if (strlen(de->d_name) > srem) {
878 strcpy(spath + slen, de->d_name);
880 if (stat(spath, &st) == 0) {
881 if (strlen(de->d_name) > drem) {
885 strcpy(dpath + dlen, de->d_name);
887 if (S_ISDIR(st.st_mode)) {
888 if (AFP_OK != (err = copydir(vol, spath, dpath)))
890 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
894 /* keep the same time stamp. */
895 ut.actime = ut.modtime = st.st_mtime;
901 /* keep the same time stamp. */
902 if (stat(src, &st) == 0) {
903 ut.actime = ut.modtime = st.st_mtime;
913 /* --- public functions follow --- */
915 /* NOTE: we start off with at least one node (the root directory). */
916 static struct dir *dirinsert(struct vol *vol, struct dir *dir)
920 if ((node = dir_insert(vol, dir)))
923 /* recolor the tree. the current node is red. */
924 dir->d_color = DIRTREE_COLOR_RED;
926 /* parent of this node has to be black. if the parent node
927 * is red, then we have a grandparent. */
928 while ((dir != vol->v_root) &&
929 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
930 /* are we on the left tree? */
931 if (dir->d_back == dir->d_back->d_back->d_left) {
932 node = dir->d_back->d_back->d_right; /* get the right node */
933 if (node->d_color == DIRTREE_COLOR_RED) {
934 /* we're red. we need to change to black. */
935 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
936 node->d_color = DIRTREE_COLOR_BLACK;
937 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
938 dir = dir->d_back->d_back; /* finished. go up. */
940 if (dir == dir->d_back->d_right) {
942 dir_leftrotate(vol, dir);
944 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
945 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
946 dir_rightrotate(vol, dir->d_back->d_back);
949 node = dir->d_back->d_back->d_left;
950 if (node->d_color == DIRTREE_COLOR_RED) {
951 /* we're red. we need to change to black. */
952 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
953 node->d_color = DIRTREE_COLOR_BLACK;
954 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
955 dir = dir->d_back->d_back; /* finished. ascend */
957 if (dir == dir->d_back->d_left) {
959 dir_rightrotate(vol, dir);
961 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
962 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
963 dir_leftrotate(vol, dir->d_back->d_back);
968 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
972 /* ---------------------------- */
974 adddir(struct vol *vol, struct dir *dir, struct path *path)
976 struct dir *cdir, *edir;
984 upath = path->u_name;
986 upathlen = strlen(upath);
988 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
992 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
996 if ((cdir = dirnew(name, upath)) == NULL) {
997 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
1000 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)) {
1001 LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
1002 cdir->d_m_name_ucs2 = NULL;
1007 if ((edir = dirinsert( vol, cdir ))) {
1008 /* it's not possible with LASTDID
1010 - someone else have moved the directory.
1011 - it's a symlink inside the share.
1012 - it's an ID reused, the old directory was deleted but not
1013 the cnid record and the server've reused the inode for
1015 for HASH (we should get ride of HASH)
1016 - someone else have moved the directory.
1017 - it's an ID reused as above
1018 - it's a hash duplicate and we are in big trouble
1020 deleted = (edir->d_m_name == NULL);
1022 dir_hash_del(vol, edir);
1024 edir->d_m_name = cdir->d_m_name;
1025 edir->d_u_name = cdir->d_u_name;
1026 edir->d_u_name_len = cdir->d_u_name_len;
1027 edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
1030 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
1031 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
1032 hash_alloc_insert(vol->v_hash, cdir, cdir);
1035 /* the old was not in the same folder */
1037 dirchildremove(cdir->d_parent, cdir);
1040 /* parent/child directories */
1041 cdir->d_parent = dir;
1042 dirchildadd(vol, dir, cdir);
1046 /* --- public functions follow --- */
1047 /* free everything down. we don't bother to recolor as this is only
1048 * called to free the entire tree */
1049 void dirfreename(struct dir *dir)
1051 if (dir->d_u_name != dir->d_m_name) {
1052 free(dir->d_u_name);
1054 if (dir->d_m_name_ucs2)
1055 free(dir->d_m_name_ucs2);
1056 free(dir->d_m_name);
1059 void dirfree(struct dir *dir)
1061 if (!dir || (dir == SENTINEL))
1064 if ( dir->d_left != SENTINEL ) {
1065 dirfree( dir->d_left );
1067 if ( dir->d_right != SENTINEL ) {
1068 dirfree( dir->d_right );
1071 if (dir != SENTINEL) {
1077 /* --------------------------------------------
1078 * most of the time mac name and unix name are the same
1080 struct dir *dirnew(const char *m_name, const char *u_name)
1084 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1088 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1093 if (m_name == u_name || !strcmp(m_name, u_name)) {
1094 dir->d_u_name = dir->d_m_name;
1096 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1097 free(dir->d_m_name);
1102 dir->d_u_name_len = strlen(dir->d_u_name);
1103 dir->d_m_name_ucs2 = NULL;
1104 dir->d_left = dir->d_right = SENTINEL;
1105 dir->d_next = dir->d_prev = dir;
1109 /* ------------------ */
1110 static hash_val_t hash_fun_dir(const void *key)
1112 const struct dir *k = key;
1114 static unsigned long randbox[] = {
1115 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1116 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1117 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1118 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1121 const unsigned char *str = (unsigned char *)(k->d_u_name);
1122 hash_val_t acc = k->d_parent->d_did;
1125 acc ^= randbox[(*str + acc) & 0xf];
1126 acc = (acc << 1) | (acc >> 31);
1128 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1129 acc = (acc << 2) | (acc >> 30);
1136 #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
1137 || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
1138 #define get16bits(d) (*((const uint16_t *) (d)))
1141 #if !defined (get16bits)
1142 #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
1143 +(uint32_t)(((const uint8_t *)(d))[0]) )
1146 static hash_val_t hash_fun2_dir(const void *key)
1148 const struct dir *k = key;
1149 const char *data = k->d_u_name;
1150 int len = k->d_u_name_len;
1151 hash_val_t hash = k->d_parent->d_did, tmp;
1157 for (;len > 0; len--) {
1158 hash += get16bits (data);
1159 tmp = (get16bits (data+2) << 11) ^ hash;
1160 hash = (hash << 16) ^ tmp;
1161 data += 2*sizeof (uint16_t);
1165 /* Handle end cases */
1167 case 3: hash += get16bits (data);
1169 hash ^= data[sizeof (uint16_t)] << 18;
1172 case 2: hash += get16bits (data);
1176 case 1: hash += *data;
1181 /* Force "avalanching" of final 127 bits */
1192 /* ---------------- */
1193 static int hash_comp_dir(const void *key1, const void *key2)
1195 const struct dir *k1 = key1;
1196 const struct dir *k2 = key2;
1198 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1201 /* ---------------- */
1205 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun2_dir);
1208 /* ------------------ */
1209 static struct path *invalidate (struct vol *vol, struct dir *dir, struct path *ret)
1212 movecwd failed some of dir path are not there anymore.
1213 FIXME Is it true with other errors?
1214 so we remove dir from the cache
1216 if (dir->d_did == DIRDID_ROOT_PARENT)
1218 if (afp_errno == AFPERR_ACCESS) {
1219 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1222 /* FIXME should we set these?, don't need to call stat() after:
1224 ret->st_errno = EACCES;
1226 ret->m_name = dir->d_m_name;
1227 ret->u_name = dir->d_u_name;
1230 } else if (afp_errno == AFPERR_NOOBJ) {
1231 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1234 strcpy(ret->m_name, dir->d_m_name);
1235 if (dir->d_m_name == dir->d_u_name) {
1236 ret->u_name = ret->m_name;
1239 size_t tp = strlen(ret->m_name)+1;
1241 ret->u_name = ret->m_name +tp;
1242 strcpy(ret->u_name, dir->d_u_name);
1244 /* FIXME should we set :
1246 ret->st_errno = ENOENT;
1248 dir_invalidate(vol, dir);
1251 dir_invalidate(vol, dir);
1255 /* -------------------------------------------------- */
1261 stat the file or errno
1264 curdir: filename parent directory
1270 stat the dir or errno
1274 curdir: dir parent directory
1282 curdir: dir parent directory
1289 cname(struct vol *vol, struct dir *dir, char **cpath)
1291 struct dir *cdir, *scdir=NULL;
1292 static char path[ MAXPATHLEN + 1];
1293 static struct path ret;
1305 afp_errno = AFPERR_NOOBJ;
1306 memset(&ret, 0, sizeof(ret));
1307 switch (ret.m_type = *data) { /* path type */
1310 len = (unsigned char) *data++;
1313 if (afp_version >= 30) {
1319 if (afp_version >= 30) {
1321 memcpy(&hint, data, sizeof(hint));
1323 data += sizeof(hint);
1325 memcpy(&len16, data, sizeof(len16));
1332 /* else it's an error */
1334 afp_errno = AFPERR_PARAM;
1337 *cpath += len + size;
1342 if (movecwd( vol, dir ) < 0 ) {
1343 return invalidate(vol, dir, &ret );
1345 if (*path == '\0') {
1352 if (*data == sep ) {
1356 while (*data == sep && len > 0 ) {
1357 if ( dir->d_parent == NULL ) {
1360 dir = dir->d_parent;
1365 /* would this be faster with strlen + strncpy? */
1367 while ( *data != sep && len > 0 ) {
1369 if (p > &path[ MAXPATHLEN]) {
1370 afp_errno = AFPERR_PARAM;
1376 /* short cut bits by chopping off a trailing \0. this also
1377 makes the traversal happy w/ filenames at the end of the
1384 if ( p == path ) { /* end of the name parameter */
1388 if (afp_version >= 30) {
1393 static char temp[ MAXPATHLEN + 1];
1395 if (dir->d_did == DIRDID_ROOT_PARENT) {
1397 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1398 So we compare it with the longname from the current volume and if they match
1399 we overwrite the requested path with the utf8 volume name so that the following
1402 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1403 if (strcasecmp( path, temp) == 0)
1404 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1407 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1408 afp_errno = AFPERR_PARAM;
1414 /* check for OS X mangled filename :( */
1416 t = demangle_osx(vol, path, dir->d_did, &fileid);
1419 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1420 * flags weren't the same
1422 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1423 /* at last got our view of mac name */
1428 if (ret.u_name == NULL) {
1429 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1430 afp_errno = AFPERR_PARAM;
1436 cdir = dir->d_child;
1438 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1439 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1440 CH_UCS2, path, -1, (char **)&tmpname) )
1443 if (!cdir->d_m_name_ucs2) {
1444 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1445 /* this shouldn't happen !!!! */
1449 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1452 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1455 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1461 if (dir->d_did == DIRDID_ROOT_PARENT) {
1463 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1464 must check against the volume name.
1466 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1472 cdir = dirsearch_byname(vol, dir, ret.u_name);
1476 if (cdir == NULL && scdir != NULL) {
1478 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1481 if ( cdir == NULL ) {
1483 /* if dir == curdir it always succeed,
1484 even if curdir is deleted.
1485 it's not a pb because it will fail in extenddir
1487 if ( movecwd( vol, dir ) < 0 ) {
1488 /* dir is not valid anymore
1489 we delete dir from the cache and abort.
1491 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1492 afp_errno = AFPERR_NOOBJ;
1495 if (afp_errno == AFPERR_ACCESS)
1497 dir_invalidate(vol, dir);
1500 cdir = extenddir( vol, dir, &ret );
1504 cdir = extenddir( vol, dir, &ret );
1505 } /* if (!extend) */
1507 if ( cdir == NULL ) {
1509 if ( len > 0 || !ret.u_name ) {
1521 * Move curdir to dir, with a possible chdir()
1523 int movecwd(struct vol *vol, struct dir *dir)
1525 char path[MAXPATHLEN + 1];
1530 if ( dir == curdir ) {
1533 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1534 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1538 p = path + sizeof(path) - 1;
1541 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1544 /* parent directory is deleted */
1545 afp_errno = AFPERR_NOOBJ;
1549 if (p -n -1 < path) {
1550 afp_errno = AFPERR_PARAM;
1557 if ( d != curdir ) {
1558 n = strlen( vol->v_path );
1559 if (p -n -1 < path) {
1560 afp_errno = AFPERR_PARAM;
1565 memcpy( p, vol->v_path, n );
1567 if ( chdir( p ) < 0 ) {
1571 afp_errno = AFPERR_ACCESS;
1574 afp_errno = AFPERR_NOOBJ;
1579 vol->v_curdir = curdir = dir;
1584 * We can't use unix file's perm to support Apple's inherited protection modes.
1585 * If we aren't the file's owner we can't change its perms when moving it and smb
1586 * nfs,... don't even try.
1588 #define AFP_CHECK_ACCESS
1590 int check_access(char *path, int mode)
1592 #ifdef AFP_CHECK_ACCESS
1600 accessmode(p, &ma, curdir, NULL);
1601 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1603 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1609 /* --------------------- */
1610 int file_access(struct path *path, int mode)
1614 accessmode(path->u_name, &ma, curdir, &path->st);
1615 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1617 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1623 /* --------------------- */
1624 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1626 dir->offcnt = count;
1627 dir->ctime = st->st_ctime;
1628 dir->d_flags &= ~DIRF_CNID;
1631 /* ---------------------
1632 * is our cached offspring count valid?
1635 static int diroffcnt(struct dir *dir, struct stat *st)
1637 return st->st_ctime == dir->ctime;
1640 /* ---------------------
1641 * is our cached also for reenumerate id?
1644 int dirreenumerate(struct dir *dir, struct stat *st)
1646 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1649 /* --------------------- */
1650 static int invisible_dots(const struct vol *vol, const char *name)
1652 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1655 /* ------------------------------
1657 (name, dir) with curdir:name == dir, from afp_enumerate
1660 int getdirparams(const struct vol *vol,
1661 u_int16_t bitmap, struct path *s_path,
1663 char *buf, size_t *buflen )
1667 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1668 int bit = 0, isad = 0;
1674 struct stat *st = &s_path->st;
1675 char *upath = s_path->u_name;
1677 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1678 (1 << DIRPBIT_CDATE) |
1679 (1 << DIRPBIT_MDATE) |
1680 (1 << DIRPBIT_BDATE) |
1681 (1 << DIRPBIT_FINFO)))) {
1683 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1684 if ( !ad_metadata( upath, ADFLAGS_CREATE|ADFLAGS_DIR, &ad) ) {
1689 if ( dir->d_did == DIRDID_ROOT) {
1690 pdid = DIRDID_ROOT_PARENT;
1691 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1694 pdid = dir->d_parent->d_did;
1698 while ( bitmap != 0 ) {
1699 while (( bitmap & 1 ) == 0 ) {
1707 ad_getattr(&ad, &ashort);
1708 } else if (invisible_dots(vol, dir->d_u_name)) {
1709 ashort = htons(ATTRBIT_INVISIBLE);
1712 ashort |= htons(ATTRBIT_SHARED);
1713 memcpy( data, &ashort, sizeof( ashort ));
1714 data += sizeof( ashort );
1718 memcpy( data, &pdid, sizeof( pdid ));
1719 data += sizeof( pdid );
1722 case DIRPBIT_CDATE :
1723 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1724 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1725 memcpy( data, &aint, sizeof( aint ));
1726 data += sizeof( aint );
1729 case DIRPBIT_MDATE :
1730 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1731 memcpy( data, &aint, sizeof( aint ));
1732 data += sizeof( aint );
1735 case DIRPBIT_BDATE :
1736 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1737 aint = AD_DATE_START;
1738 memcpy( data, &aint, sizeof( aint ));
1739 data += sizeof( aint );
1742 case DIRPBIT_FINFO :
1744 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1745 } else { /* no appledouble */
1746 memset( data, 0, 32 );
1747 /* set default view -- this also gets done in ad_open() */
1748 ashort = htons(FINDERINFO_CLOSEDVIEW);
1749 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1751 /* dot files are by default visible */
1752 if (invisible_dots(vol, dir->d_u_name)) {
1753 ashort = htons(FINDERINFO_INVISIBLE);
1754 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1760 case DIRPBIT_LNAME :
1761 if (dir->d_m_name) /* root of parent can have a null name */
1764 memset(data, 0, sizeof(u_int16_t));
1765 data += sizeof( u_int16_t );
1768 case DIRPBIT_SNAME :
1769 memset(data, 0, sizeof(u_int16_t));
1770 data += sizeof( u_int16_t );
1774 memcpy( data, &dir->d_did, sizeof( aint ));
1775 data += sizeof( aint );
1778 case DIRPBIT_OFFCNT :
1780 /* this needs to handle current directory access rights */
1781 if (diroffcnt(dir, st)) {
1782 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1784 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1785 setdiroffcnt(dir, st, ret);
1786 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1788 ashort = htons( ashort );
1789 memcpy( data, &ashort, sizeof( ashort ));
1790 data += sizeof( ashort );
1794 aint = htonl(st->st_uid);
1795 memcpy( data, &aint, sizeof( aint ));
1796 data += sizeof( aint );
1800 aint = htonl(st->st_gid);
1801 memcpy( data, &aint, sizeof( aint ));
1802 data += sizeof( aint );
1805 case DIRPBIT_ACCESS :
1806 accessmode( upath, &ma, dir , st);
1808 *data++ = ma.ma_user;
1809 *data++ = ma.ma_world;
1810 *data++ = ma.ma_group;
1811 *data++ = ma.ma_owner;
1814 /* Client has requested the ProDOS information block.
1815 Just pass back the same basic block for all
1816 directories. <shirsch@ibm.net> */
1817 case DIRPBIT_PDINFO :
1818 if (afp_version >= 30) { /* UTF8 name */
1819 utf8 = kTextEncodingUTF8;
1820 if (dir->d_m_name) /* root of parent can have a null name */
1823 memset(data, 0, sizeof(u_int16_t));
1824 data += sizeof( u_int16_t );
1826 memcpy(data, &aint, sizeof( aint ));
1827 data += sizeof( aint );
1829 else { /* ProDOS Info Block */
1832 ashort = htons( 0x0200 );
1833 memcpy( data, &ashort, sizeof( ashort ));
1834 data += sizeof( ashort );
1835 memset( data, 0, sizeof( ashort ));
1836 data += sizeof( ashort );
1840 case DIRPBIT_UNIXPR :
1841 aint = htonl(st->st_uid);
1842 memcpy( data, &aint, sizeof( aint ));
1843 data += sizeof( aint );
1844 aint = htonl(st->st_gid);
1845 memcpy( data, &aint, sizeof( aint ));
1846 data += sizeof( aint );
1849 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1850 memcpy( data, &aint, sizeof( aint ));
1851 data += sizeof( aint );
1853 accessmode( upath, &ma, dir , st);
1855 *data++ = ma.ma_user;
1856 *data++ = ma.ma_world;
1857 *data++ = ma.ma_group;
1858 *data++ = ma.ma_owner;
1863 ad_close_metadata( &ad );
1865 return( AFPERR_BITMAP );
1871 ashort = htons( data - buf );
1872 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1873 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1875 if ( utf_nameoff ) {
1876 ashort = htons( data - buf );
1877 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1878 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1881 ad_close_metadata( &ad );
1883 *buflen = data - buf;
1887 /* ----------------------------- */
1888 int path_error(struct path *path, int error)
1890 /* - a dir with access error
1891 * - no error it's a file
1894 if (path_isadir(path))
1896 if (path->st_valid && path->st_errno)
1898 return AFPERR_BADTYPE ;
1901 /* ----------------------------- */
1902 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1907 u_int16_t vid, bitmap;
1913 memcpy( &vid, ibuf, sizeof( vid ));
1914 ibuf += sizeof( vid );
1916 if (NULL == ( vol = getvolbyvid( vid )) ) {
1917 return( AFPERR_PARAM );
1920 if (vol->v_flags & AFPVOL_RO)
1921 return AFPERR_VLOCK;
1923 memcpy( &did, ibuf, sizeof( did ));
1924 ibuf += sizeof( int );
1926 if (NULL == ( dir = dirlookup( vol, did )) ) {
1930 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1931 bitmap = ntohs( bitmap );
1932 ibuf += sizeof( bitmap );
1934 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1935 return get_afp_errno(AFPERR_NOOBJ);
1938 if ( *path->m_name != '\0' ) {
1939 rc = path_error(path, AFPERR_NOOBJ);
1940 /* maybe we are trying to set perms back */
1941 if (rc != AFPERR_ACCESS)
1946 * If ibuf is odd, make it even.
1948 if ((u_long)ibuf & 1 ) {
1952 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1953 setvoltime(obj, vol );
1959 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1961 * assume path == '\0' eg. it's a directory in canonical form
1964 struct path Cur_Path = {
1967 ".", /* unix name */
1969 NULL,/* struct dir */
1970 0, /* stat is not set */
1973 /* ------------------ */
1974 static int set_dir_errors(struct path *path, const char *where, int err)
1979 return AFPERR_ACCESS;
1981 return AFPERR_VLOCK;
1983 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1984 return AFPERR_PARAM;
1987 /* ------------------ */
1988 int setdirparams(struct vol *vol,
1989 struct path *path, u_int16_t d_bitmap, char *buf )
2001 u_int16_t ashort, bshort;
2003 int change_mdate = 0;
2004 int change_parent_mdate = 0;
2006 u_int16_t bitmap = d_bitmap;
2007 u_char finder_buf[32];
2010 u_int16_t upriv_bit = 0;
2013 upath = path->u_name;
2015 while ( bitmap != 0 ) {
2016 while (( bitmap & 1 ) == 0 ) {
2024 memcpy( &ashort, buf, sizeof( ashort ));
2025 buf += sizeof( ashort );
2027 case DIRPBIT_CDATE :
2029 memcpy(&cdate, buf, sizeof(cdate));
2030 buf += sizeof( cdate );
2032 case DIRPBIT_MDATE :
2033 memcpy(&newdate, buf, sizeof(newdate));
2034 buf += sizeof( newdate );
2036 case DIRPBIT_BDATE :
2038 memcpy(&bdate, buf, sizeof(bdate));
2039 buf += sizeof( bdate );
2041 case DIRPBIT_FINFO :
2043 memcpy( finder_buf, buf, 32 );
2046 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2047 change_parent_mdate = 1;
2048 memcpy( &owner, buf, sizeof(owner));
2049 buf += sizeof( owner );
2052 change_parent_mdate = 1;
2053 memcpy( &group, buf, sizeof( group ));
2054 buf += sizeof( group );
2056 case DIRPBIT_ACCESS :
2058 change_parent_mdate = 1;
2059 ma.ma_user = *buf++;
2060 ma.ma_world = *buf++;
2061 ma.ma_group = *buf++;
2062 ma.ma_owner = *buf++;
2063 mpriv = mtoumode( &ma ) | vol->v_dperm;
2064 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2065 err = set_dir_errors(path, "setdirmode", errno);
2069 /* Ignore what the client thinks we should do to the
2070 ProDOS information block. Skip over the data and
2071 report nothing amiss. <shirsch@ibm.net> */
2072 case DIRPBIT_PDINFO :
2073 if (afp_version < 30) {
2077 err = AFPERR_BITMAP;
2081 case DIRPBIT_UNIXPR :
2082 if (vol_unix_priv(vol)) {
2083 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2084 buf += sizeof( owner );
2085 memcpy( &group, buf, sizeof( group ));
2086 buf += sizeof( group );
2089 change_parent_mdate = 1;
2090 memcpy( &upriv, buf, sizeof( upriv ));
2091 buf += sizeof( upriv );
2092 upriv = ntohl (upriv) | vol->v_dperm;
2093 if (dir_rx_set(upriv)) {
2094 /* maybe we are trying to set perms back */
2095 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2097 err = set_dir_errors(path, "setdirunixmode", errno);
2108 err = AFPERR_BITMAP;
2116 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2118 if (ad_open_metadata( upath, ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2120 * Check to see what we're trying to set. If it's anything
2121 * but ACCESS, UID, or GID, give an error. If it's any of those
2122 * three, we don't need the ad to be open, so just continue.
2124 * note: we also don't need to worry about mdate. also, be quiet
2125 * if we're using the noadouble option.
2127 if (!vol_noadouble(vol) && (d_bitmap &
2128 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2129 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2130 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2131 return AFPERR_ACCESS;
2137 * Check to see if a create was necessary. If it was, we'll want
2138 * to set our name, etc.
2140 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2141 ad_setname(&ad, curdir->d_m_name);
2147 while ( bitmap != 0 ) {
2148 while (( bitmap & 1 ) == 0 ) {
2156 ad_getattr(&ad, &bshort);
2157 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2158 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2159 change_parent_mdate = 1;
2160 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2161 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2165 ad_setattr(&ad, bshort);
2168 case DIRPBIT_CDATE :
2170 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2173 case DIRPBIT_MDATE :
2175 case DIRPBIT_BDATE :
2177 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2180 case DIRPBIT_FINFO :
2182 /* Fixes #2802236 */
2183 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2184 *fflags &= htons(~FINDERINFO_ISHARED);
2186 if ( dir->d_did == DIRDID_ROOT ) {
2188 * Alright, we admit it, this is *really* sick!
2189 * The 4 bytes that we don't copy, when we're dealing
2190 * with the root of a volume, are the directory's
2191 * location information. This eliminates that annoying
2192 * behavior one sees when mounting above another mount
2195 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2196 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2198 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2202 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2203 if ( (dir->d_did == DIRDID_ROOT) &&
2204 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2205 err = set_dir_errors(path, "setdeskowner", errno);
2206 if (isad && err == AFPERR_PARAM) {
2207 err = AFP_OK; /* ???*/
2210 goto setdirparam_done;
2213 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2214 err = set_dir_errors(path, "setdirowner", errno);
2215 goto setdirparam_done;
2219 if (dir->d_did == DIRDID_ROOT)
2220 setdeskowner( -1, ntohl(group) );
2221 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2222 err = set_dir_errors(path, "setdirowner", errno);
2223 goto setdirparam_done;
2226 case DIRPBIT_ACCESS :
2227 if (dir->d_did == DIRDID_ROOT) {
2229 if (!dir_rx_set(mpriv)) {
2230 /* we can't remove read and search for owner on volume root */
2231 err = AFPERR_ACCESS;
2232 goto setdirparam_done;
2236 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2237 err = set_dir_errors(path, "setdirmode", errno);
2238 goto setdirparam_done;
2241 case DIRPBIT_PDINFO :
2242 if (afp_version >= 30) {
2243 err = AFPERR_BITMAP;
2244 goto setdirparam_done;
2247 case DIRPBIT_UNIXPR :
2248 if (vol_unix_priv(vol)) {
2249 if (dir->d_did == DIRDID_ROOT) {
2250 if (!dir_rx_set(upriv)) {
2251 /* we can't remove read and search for owner on volume root */
2252 err = AFPERR_ACCESS;
2253 goto setdirparam_done;
2255 setdeskowner( -1, ntohl(group) );
2256 setdeskmode( upriv );
2258 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2259 err = set_dir_errors(path, "setdirowner", errno);
2260 goto setdirparam_done;
2263 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2264 err = set_dir_errors(path, "setdirunixmode", errno);
2265 goto setdirparam_done;
2269 err = AFPERR_BITMAP;
2270 goto setdirparam_done;
2274 err = AFPERR_BITMAP;
2275 goto setdirparam_done;
2284 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2285 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2289 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2290 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2295 if (path->st_valid && !path->st_errno) {
2296 struct stat *st = &path->st;
2298 if (dir && dir->d_parent) {
2299 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2303 ad_close_metadata( &ad);
2306 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2307 && gettimeofday(&tv, NULL) == 0) {
2308 if (!movecwd(vol, dir->d_parent)) {
2309 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2310 /* be careful with bitmap because now dir is null */
2311 bitmap = 1<<DIRPBIT_MDATE;
2312 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2313 /* should we reset curdir ?*/
2320 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2334 memcpy( &vid, ibuf, sizeof( vid ));
2335 ibuf += sizeof( vid );
2336 if (NULL == (vol = getvolbyvid( vid )) ) {
2337 return( AFPERR_PARAM );
2340 memcpy( &did, ibuf, sizeof( did ));
2341 ibuf += sizeof( did );
2345 * if it's CNID 2 our only choice to meet the specs is call sync.
2346 * For any other CNID just sync that dir. To my knowledge the
2347 * intended use of FPSyncDir is to sync the volume so all we're
2348 * ever going to see here is probably CNID 2. Anyway, we' prepared.
2351 if ( ntohl(did) == 2 ) {
2354 if (NULL == ( dir = dirlookup( vol, did )) ) {
2355 return afp_errno; /* was AFPERR_NOOBJ */
2358 if (movecwd( vol, dir ) < 0 )
2359 return ( AFPERR_NOOBJ );
2362 * Assuming only OSens that have dirfd also may require fsyncing directories
2363 * in order to flush metadata e.g. Linux.
2367 if (NULL == ( dp = opendir( "." )) ) {
2370 return( AFPERR_NOOBJ );
2372 return( AFPERR_ACCESS );
2374 return( AFPERR_PARAM );
2378 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2381 if ( fsync ( dfd ) < 0 )
2382 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2383 dir->d_u_name, strerror(errno) );
2384 closedir(dp); /* closes dfd too */
2387 if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2390 return( AFPERR_NOOBJ );
2392 return( AFPERR_ACCESS );
2394 return( AFPERR_PARAM );
2398 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2399 vol->ad_path(".", ADFLAGS_DIR) );
2401 if ( fsync(dfd) < 0 )
2402 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2403 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2410 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2416 struct path *s_path;
2424 memcpy( &vid, ibuf, sizeof( vid ));
2425 ibuf += sizeof( vid );
2426 if (NULL == ( vol = getvolbyvid( vid )) ) {
2427 return( AFPERR_PARAM );
2430 if (vol->v_flags & AFPVOL_RO)
2431 return AFPERR_VLOCK;
2433 memcpy( &did, ibuf, sizeof( did ));
2434 ibuf += sizeof( did );
2435 if (NULL == ( dir = dirlookup( vol, did )) ) {
2436 return afp_errno; /* was AFPERR_NOOBJ */
2438 /* for concurrent access we need to be sure we are not in the
2439 * folder we want to create...
2443 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2444 return get_afp_errno(AFPERR_PARAM);
2446 /* cname was able to move curdir to it! */
2447 if (*s_path->m_name == '\0')
2448 return AFPERR_EXIST;
2450 upath = s_path->u_name;
2452 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2456 if (of_stat(s_path) < 0) {
2460 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2464 if ( movecwd( vol, dir ) < 0 ) {
2465 return( AFPERR_PARAM );
2468 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2469 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2470 if (vol_noadouble(vol))
2471 goto createdir_done;
2472 return( AFPERR_ACCESS );
2474 ad_setname(&ad, s_path->m_name);
2475 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2478 ad_close_metadata( &ad);
2481 #ifdef HAVE_NFSv4_ACLS
2482 /* FIXME: are we really inside the created dir? */
2483 addir_inherit_acl(vol);
2486 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2487 *rbuflen = sizeof( u_int32_t );
2488 setvoltime(obj, vol );
2493 * dst new unix filename (not a pathname)
2494 * newname new mac name
2498 int renamedir(const struct vol *vol, char *src, char *dst,
2500 struct dir *newparent,
2508 /* existence check moved to afp_moveandrename */
2509 if ( unix_rename( src, dst ) < 0 ) {
2512 return( AFPERR_NOOBJ );
2514 return( AFPERR_ACCESS );
2516 return AFPERR_VLOCK;
2518 /* tried to move directory into a subdirectory of itself */
2519 return AFPERR_CANTMOVE;
2521 /* this needs to copy and delete. bleah. that means we have
2522 * to deal with entire directory hierarchies. */
2523 if ((err = copydir(vol, src, dst)) < 0) {
2527 if ((err = deletedir(src)) < 0)
2531 return( AFPERR_PARAM );
2535 vol->vfs->vfs_renamedir(vol, src, dst);
2537 len = strlen( newname );
2538 /* rename() succeeded so we need to update our tree even if we can't open
2542 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2544 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2545 ad_setname(&ad, newname);
2547 ad_close_metadata( &ad);
2550 dir_hash_del(vol, dir);
2551 if (dir->d_m_name == dir->d_u_name)
2552 dir->d_u_name = NULL;
2554 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2555 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2556 /* FIXME : fatal ? */
2559 dir->d_m_name = buf;
2560 strcpy( dir->d_m_name, newname );
2562 if (newname == dst) {
2563 free(dir->d_u_name);
2564 dir->d_u_name = dir->d_m_name;
2567 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2568 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2571 dir->d_u_name = buf;
2572 strcpy( dir->d_u_name, dst );
2574 dir->d_u_name_len = strlen(dir->d_u_name);
2576 if (dir->d_m_name_ucs2)
2577 free(dir->d_m_name_ucs2);
2579 dir->d_m_name_ucs2 = NULL;
2580 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))
2581 dir->d_m_name_ucs2 = NULL;
2583 if (( parent = dir->d_parent ) == NULL ) {
2586 if ( parent == newparent ) {
2587 hash_alloc_insert(vol->v_hash, dir, dir);
2591 /* detach from old parent and add to new one. */
2592 dirchildremove(parent, dir);
2593 dir->d_parent = newparent;
2594 dirchildadd(vol, newparent, dir);
2598 /* delete an empty directory */
2599 int deletecurdir(struct vol *vol)
2609 if ( curdir->d_parent == NULL ) {
2610 return( AFPERR_ACCESS );
2615 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2616 /* we never want to create a resource fork here, we are going to delete it */
2617 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2619 ad_getattr(&ad, &ashort);
2620 ad_close( &ad, ADFLAGS_HF );
2621 if ((ashort & htons(ATTRBIT_NODELETE))) {
2622 return AFPERR_OLOCK;
2625 err = vol->vfs->vfs_deletecurdir(vol);
2630 /* now get rid of dangling symlinks */
2631 if ((dp = opendir("."))) {
2632 while ((de = readdir(dp))) {
2633 /* skip this and previous directory */
2634 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2637 /* bail if it's not a symlink */
2638 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2640 return AFPERR_DIRNEMPT;
2643 if ((err = netatalk_unlink(de->d_name))) {
2650 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2655 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2656 dirchildremove(curdir, fdir);
2657 cnid_delete(vol->v_cdb, fdir->d_did);
2658 dir_remove( vol, fdir );
2663 /* inode is used as key for cnid.
2664 * Close the descriptor only after cnid_delete
2672 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2682 sfunc = (unsigned char) *ibuf++;
2686 if (sfunc >= 3 && sfunc <= 6) {
2687 if (afp_version < 30) {
2688 return( AFPERR_PARAM );
2695 case 3 :/* unicode */
2696 memcpy( &id, ibuf, sizeof( id ));
2699 if (( pw = getpwuid( id )) == NULL ) {
2700 return( AFPERR_NOITEM );
2702 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2703 pw->pw_name, -1, &name);
2710 case 4 : /* unicode */
2711 memcpy( &id, ibuf, sizeof( id ));
2714 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2715 return( AFPERR_NOITEM );
2717 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2718 gr->gr_name, -1, &name);
2724 #ifdef HAVE_NFSv4_ACLS
2725 case 5 : /* UUID -> username */
2726 case 6 : /* UUID -> groupname */
2727 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2728 return AFPERR_PARAM;
2729 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2731 len = getnamefromuuid( ibuf, &name, &type);
2732 if (len != 0) /* its a error code, not len */
2733 return AFPERR_NOITEM;
2734 if (type == UUID_USER) {
2735 if (( pw = getpwnam( name )) == NULL )
2736 return( AFPERR_NOITEM );
2737 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2738 id = htonl(UUID_USER);
2739 memcpy( rbuf, &id, sizeof( id ));
2740 id = htonl( pw->pw_uid);
2741 rbuf += sizeof( id );
2742 memcpy( rbuf, &id, sizeof( id ));
2743 rbuf += sizeof( id );
2744 *rbuflen = 2 * sizeof( id );
2745 } else { /* type == UUID_GROUP */
2746 if (( gr = getgrnam( name )) == NULL )
2747 return( AFPERR_NOITEM );
2748 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2749 id = htonl(UUID_GROUP);
2750 memcpy( rbuf, &id, sizeof( id ));
2751 rbuf += sizeof( id );
2752 id = htonl( gr->gr_gid);
2753 memcpy( rbuf, &id, sizeof( id ));
2754 rbuf += sizeof( id );
2755 *rbuflen = 2 * sizeof( id );
2760 return( AFPERR_PARAM );
2764 len = strlen( name );
2767 u_int16_t tp = htons(len);
2768 memcpy(rbuf, &tp, sizeof(tp));
2777 memcpy( rbuf, name, len );
2785 int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2794 sfunc = (unsigned char) *ibuf++;
2796 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2799 case 2 : /* unicode */
2800 if (afp_version < 30) {
2801 return( AFPERR_PARAM );
2803 memcpy(&ulen, ibuf, sizeof(ulen));
2806 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2810 len = (unsigned char) *ibuf++;
2812 #ifdef HAVE_NFSv4_ACLS
2813 case 5 : /* username -> UUID */
2814 case 6 : /* groupname -> UUID */
2815 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2816 return AFPERR_PARAM;
2817 memcpy(&ulen, ibuf, sizeof(ulen));
2823 return( AFPERR_PARAM );
2829 return AFPERR_PARAM;
2832 case 1 : /* unicode */
2834 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2835 return( AFPERR_NOITEM );
2839 memcpy( rbuf, &id, sizeof( id ));
2840 *rbuflen = sizeof( id );
2843 case 2 : /* unicode */
2845 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2846 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2847 return( AFPERR_NOITEM );
2850 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2852 memcpy( rbuf, &id, sizeof( id ));
2853 *rbuflen = sizeof( id );
2855 #ifdef HAVE_NFSv4_ACLS
2856 case 5 : /* username -> UUID */
2857 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2858 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2859 return AFPERR_NOITEM;
2860 *rbuflen = UUID_BINSIZE;
2862 case 6 : /* groupname -> UUID */
2863 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2864 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2865 return AFPERR_NOITEM;
2866 *rbuflen = UUID_BINSIZE;
2874 /* ------------------------------------
2875 variable DID support
2877 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2888 /* do nothing as dids are static for the life of the process. */
2892 memcpy(&vid, ibuf, sizeof( vid ));
2893 ibuf += sizeof( vid );
2894 if (( vol = getvolbyvid( vid )) == NULL ) {
2895 return( AFPERR_PARAM );
2898 memcpy( &did, ibuf, sizeof( did ));
2899 ibuf += sizeof( did );
2900 if (( dir = dirlookup( vol, did )) == NULL ) {
2901 return( AFPERR_PARAM );
2904 /* dir_remove -- deletedid */
2910 /* did creation gets done automatically
2911 * there's a pb again with case but move it to cname
2913 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2916 struct dir *parentdir;
2924 memcpy(&vid, ibuf, sizeof(vid));
2925 ibuf += sizeof( vid );
2927 if (NULL == ( vol = getvolbyvid( vid )) ) {
2928 return( AFPERR_PARAM );
2931 memcpy(&did, ibuf, sizeof(did));
2932 ibuf += sizeof(did);
2934 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2938 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2939 return get_afp_errno(AFPERR_PARAM);
2942 if ( *path->m_name != '\0' ) {
2943 return path_error(path, AFPERR_NOOBJ);
2946 if ( !path->st_valid && of_stat(path ) < 0 ) {
2947 return( AFPERR_NOOBJ );
2949 if ( path->st_errno ) {
2950 return( AFPERR_NOOBJ );
2953 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2954 *rbuflen = sizeof(curdir->d_did);