2 * $Id: directory.c,v 1.104 2009-09-14 00:02:21 didg 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 */
38 #include <sys/param.h>
41 #include <atalk/adouble.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>
49 #include "directory.h"
59 #ifdef HAVE_NFSv4_ACLS
60 extern void addir_inherit_acl(const struct vol *vol);
66 #define SENTINEL (&sentinel)
67 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
68 NULL, NULL, NULL, NULL, NULL, 0, 0,
69 0, 0, NULL, NULL, NULL};
70 static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
71 NULL, NULL, NULL, NULL, NULL, 0, 0,
72 0, 0, NULL, NULL, NULL};
74 /* (from IM: Toolbox Essentials)
75 * dirFinderInfo (DInfo) fields:
77 * frRect 8 folder's window rectangle
79 * frLocation 4 folder's location in window
80 * frView 2 folder's view (default == closedView (256))
82 * extended dirFinderInfo (DXInfo) fields:
83 * frScroll 4 scroll position
84 * frOpenChain: 4 directory ID chain of open folders
85 * frScript: 1 script flag and code
86 * frXFlags: 1 reserved
87 * frComment: 2 comment ID
88 * frPutAway: 4 home directory ID
92 * redid did assignment for directories. now we use red-black trees.
97 const struct vol *vol;
103 /* check for 0 did */
105 afp_errno = AFPERR_PARAM;
108 if ( did == DIRDID_ROOT_PARENT ) {
110 rootpar.d_did = DIRDID_ROOT_PARENT;
111 rootpar.d_child = vol->v_dir;
115 /* XXX would be nice to check against curdir but we need to keep its volume */
116 if (vol->curdir && curdir->d_did == did) {
125 afp_errno = AFPERR_NOOBJ;
126 while ( dir != SENTINEL ) {
127 if (dir->d_did == did)
128 return dir->d_m_name ? dir : NULL;
129 dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
134 /* ------------------- */
135 int get_afp_errno(const int param)
137 if (afp_errno != AFPERR_DID1)
142 /* ------------------- */
144 dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
146 struct dir *dir = NULL;
148 if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
154 hn = hash_lookup(vol->v_hash, &key);
162 /* -----------------------------------------
163 * if did is not in the cache resolve it with cnid
166 * OSX call it with bogus id, ie file ID not folder ID,
167 * and we are really bad in this case.
170 dirlookup( vol, did )
171 const struct vol *vol;
177 static char path[MAXPATHLEN + 1];
180 static char buffer[12 + MAXPATHLEN + 1];
181 int buflen = 12 + MAXPATHLEN + 1;
186 ret = dirsearch(vol, did);
187 if (ret != NULL || afp_errno == AFPERR_PARAM)
190 utf8 = utf8_encoding();
191 maxpath = (utf8)?MAXPATHLEN -7:255;
193 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
194 afp_errno = AFPERR_NOOBJ;
197 ptr = path + MAXPATHLEN;
198 if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
199 afp_errno = AFPERR_NOOBJ;
203 pathlen = len; /* no 0 in the last part */
205 strcpy(ptr - len, mpath);
208 ret = dirsearch(vol,id);
213 if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
215 NULL == (mpath = utompath(vol, upath, cnid, utf8))
217 afp_errno = AFPERR_NOOBJ;
221 len = strlen(mpath) + 1;
223 if (pathlen > maxpath) {
224 afp_errno = AFPERR_PARAM;
227 strcpy(ptr - len, mpath);
231 /* fill the cache, another place where we know about the path type */
237 temp16 = htons(pathlen);
238 memcpy(ptr, &temp16, sizeof(temp16));
240 temp = htonl(kTextEncodingUTF8);
242 memcpy(ptr, &temp, sizeof(temp));
248 *ptr = (unsigned char)pathlen;
252 /* cname is not efficient */
253 if (cname( vol, ret, &ptr ) == NULL )
256 return dirsearch(vol, did);
259 /* child addition/removal */
260 static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
265 b->d_next = a->d_child;
266 b->d_prev = b->d_next->d_prev;
267 b->d_next->d_prev = b;
268 b->d_prev->d_next = b;
270 if (!hash_alloc_insert(vol->v_hash, b, b)) {
271 LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
275 static void dirchildremove(struct dir *a,struct dir *b)
278 a->d_child = (b == b->d_next) ? NULL : b->d_next;
279 b->d_next->d_prev = b->d_prev;
280 b->d_prev->d_next = b->d_next;
281 b->d_next = b->d_prev = b;
284 /* --------------------------- */
285 /* rotate the tree to the left */
286 static void dir_leftrotate(vol, dir)
290 struct dir *right = dir->d_right;
292 /* whee. move the right's left tree into dir's right tree */
293 dir->d_right = right->d_left;
294 if (right->d_left != SENTINEL)
295 right->d_left->d_back = dir;
297 if (right != SENTINEL) {
298 right->d_back = dir->d_back;
302 if (!dir->d_back) /* no parent. move the right tree to the top. */
304 else if (dir == dir->d_back->d_left) /* we were on the left */
305 dir->d_back->d_left = right;
307 dir->d_back->d_right = right; /* we were on the right */
309 /* re-insert dir on the left tree */
316 /* rotate the tree to the right */
317 static void dir_rightrotate(vol, dir)
321 struct dir *left = dir->d_left;
323 /* whee. move the left's right tree into dir's left tree */
324 dir->d_left = left->d_right;
325 if (left->d_right != SENTINEL)
326 left->d_right->d_back = dir;
328 if (left != SENTINEL) {
329 left->d_back = dir->d_back;
333 if (!dir->d_back) /* no parent. move the left tree to the top. */
335 else if (dir == dir->d_back->d_right) /* we were on the right */
336 dir->d_back->d_right = left;
338 dir->d_back->d_left = left; /* we were on the left */
340 /* re-insert dir on the right tree */
346 /* recolor after a removal */
347 static struct dir *dir_rmrecolor(vol, dir)
353 while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
354 /* are we on the left tree? */
355 if (dir == dir->d_back->d_left) {
356 leaf = dir->d_back->d_right; /* get right side */
357 if (leaf->d_color == DIRTREE_COLOR_RED) {
358 /* we're red. we need to change to black. */
359 leaf->d_color = DIRTREE_COLOR_BLACK;
360 dir->d_back->d_color = DIRTREE_COLOR_RED;
361 dir_leftrotate(vol, dir->d_back);
362 leaf = dir->d_back->d_right;
365 /* right leaf has black end nodes */
366 if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
367 (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
368 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
369 dir = dir->d_back; /* ascend */
371 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
372 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
373 leaf->d_color = DIRTREE_COLOR_RED;
374 dir_rightrotate(vol, leaf);
375 leaf = dir->d_back->d_right;
377 leaf->d_color = dir->d_back->d_color;
378 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
379 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
380 dir_leftrotate(vol, dir->d_back);
383 } else { /* right tree */
384 leaf = dir->d_back->d_left; /* left tree */
385 if (leaf->d_color == DIRTREE_COLOR_RED) {
386 leaf->d_color = DIRTREE_COLOR_BLACK;
387 dir->d_back->d_color = DIRTREE_COLOR_RED;
388 dir_rightrotate(vol, dir->d_back);
389 leaf = dir->d_back->d_left;
392 /* left leaf has black end nodes */
393 if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
394 (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
395 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
396 dir = dir->d_back; /* ascend */
398 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
399 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
400 leaf->d_color = DIRTREE_COLOR_RED;
401 dir_leftrotate(vol, leaf);
402 leaf = dir->d_back->d_left;
404 leaf->d_color = dir->d_back->d_color;
405 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
406 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
407 dir_rightrotate(vol, dir->d_back);
412 dir->d_color = DIRTREE_COLOR_BLACK;
418 /* --------------------- */
419 static void dir_hash_del(const struct vol *vol, struct dir *dir)
423 hn = hash_lookup(vol->v_hash, dir);
425 LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
428 hash_delete(vol->v_hash, hn);
432 /* remove the node from the tree. this is just like insertion, but
433 * different. actually, it has to worry about a bunch of things that
434 * insertion doesn't care about. */
436 static void dir_remove( const struct vol *vol _U_, struct dir *dir)
439 struct ofork *of, *last;
440 struct dir *node, *leaf;
441 #endif /* REMOVE_NODES */
443 if (!dir || (dir == SENTINEL))
446 /* i'm not sure if it really helps to delete stuff. */
447 dir_hash_del(vol, dir);
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( vol, dir )
554 const struct vol *vol;
558 /* v_root can't be deleted */
559 if (movecwd(vol, vol->v_root) < 0) {
560 LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
564 dirchildremove(dir->d_parent, dir);
565 dir_remove( vol, dir );
568 /* ------------------------------------ */
569 static struct dir *dir_insert(vol, dir)
570 const struct vol *vol;
576 while (pdir->d_did != dir->d_did ) {
577 if ( pdir->d_did > dir->d_did ) {
578 if ( pdir->d_left == SENTINEL ) {
585 if ( pdir->d_right == SENTINEL ) {
590 pdir = pdir->d_right;
596 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
599 caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
604 static u_int32_t did = 0;
605 static char cname[MAXPATHLEN];
606 static char lname[MAXPATHLEN];
607 ucs2_t u2_path[MAXPATHLEN];
608 ucs2_t u2_dename[MAXPATHLEN];
609 char *tmp, *savepath;
611 if (!(vol->v_flags & AFPVOL_CASEINSEN))
614 if (veto_file(ENUMVETO, path->u_name))
617 savepath = path->u_name;
619 /* very simple cache */
620 if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
621 path->u_name = cname;
623 if (of_stat( path ) == 0 ) {
626 /* something changed, we cannot stat ... */
630 if (NULL == ( dp = opendir( "." )) ) {
631 LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
636 /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
637 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, strlen(path->u_name), u2_path, sizeof(u2_path)) )
638 LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
640 /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
642 for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
643 if (NULL == check_dirent(vol, de->d_name))
646 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, strlen(de->d_name), u2_dename, sizeof(u2_dename)) )
649 if (strcasecmp_w( u2_path, u2_dename) == 0) {
651 strlcpy(cname, de->d_name, sizeof(cname));
652 path->u_name = cname;
654 if (of_stat( path ) == 0 ) {
655 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
656 strlcpy(lname, tmp, sizeof(lname));
669 /* invalidate cache */
672 path->u_name = savepath;
674 /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
680 * attempt to extend the current dir. tree to include path
681 * as a side-effect, movecwd to that point and return the new dir
684 extenddir( vol, dir, path )
691 if ( path->u_name == NULL) {
692 afp_errno = AFPERR_PARAM;
696 if (check_name(vol, path->u_name)) {
697 /* the name is illegal */
698 LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
700 afp_errno = AFPERR_PARAM;
704 if (of_stat( path ) != 0 ) {
705 if (!(vol->v_flags & AFPVOL_CASEINSEN))
707 else if(caseenumerate(vol, path, dir) != 0)
711 if (!S_ISDIR(path->st.st_mode)) {
715 /* mac name is always with the right encoding (from cname()) */
716 if (( dir = adddir( vol, dir, path)) == NULL ) {
721 if ( movecwd( vol, dir ) < 0 ) {
728 /* -------------------
729 system rmdir with afp error code.
730 ENOENT is not an error.
732 int netatalk_rmdir(const char *name)
734 if (rmdir(name) < 0) {
739 return AFPERR_DIRNEMPT;
742 return AFPERR_ACCESS;
752 /* -------------------------
753 appledouble mkdir afp error code.
755 static int netatalk_mkdir(const char *name)
757 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
760 return( AFPERR_NOOBJ );
762 return( AFPERR_VLOCK );
765 return( AFPERR_ACCESS );
767 return( AFPERR_EXIST );
770 return( AFPERR_DFULL );
772 return( AFPERR_PARAM );
778 /* -------------------
779 system unlink with afp error code.
780 ENOENT is not an error.
782 int netatalk_unlink(const char *name)
784 if (unlink(name) < 0) {
792 return AFPERR_ACCESS;
800 /* ------------------- */
801 static int deletedir(char *dir)
803 char path[MAXPATHLEN + 1];
811 if ((len = strlen(dir)) +2 > sizeof(path))
815 if ((dp = opendir(dir)) == NULL)
821 remain = sizeof(path) -len -1;
822 while ((de = readdir(dp)) && err == AFP_OK) {
823 /* skip this and previous directory */
824 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
827 if (strlen(de->d_name) > remain) {
831 strcpy(path + len, de->d_name);
832 if (stat(path, &st)) {
835 if (S_ISDIR(st.st_mode)) {
836 err = deletedir(path);
838 err = netatalk_unlink(path);
843 /* okay. the directory is empty. delete it. note: we already got rid
846 err = netatalk_rmdir(dir);
851 /* do a recursive copy. */
852 static int copydir(const struct vol *vol, char *src, char *dst)
854 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
863 /* doesn't exist or the path is too long. */
864 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
865 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
866 ((dp = opendir(src)) == NULL))
869 /* try to create the destination directory */
870 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
875 /* set things up to copy */
879 srem = sizeof(spath) - slen -1;
884 drem = sizeof(dpath) - dlen -1;
887 while ((de = readdir(dp))) {
888 /* skip this and previous directory */
889 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
892 if (strlen(de->d_name) > srem) {
896 strcpy(spath + slen, de->d_name);
898 if (stat(spath, &st) == 0) {
899 if (strlen(de->d_name) > drem) {
903 strcpy(dpath + dlen, de->d_name);
905 if (S_ISDIR(st.st_mode)) {
906 if (AFP_OK != (err = copydir(vol, spath, dpath)))
908 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
912 /* keep the same time stamp. */
913 ut.actime = ut.modtime = st.st_mtime;
919 /* keep the same time stamp. */
920 if (stat(src, &st) == 0) {
921 ut.actime = ut.modtime = st.st_mtime;
931 /* --- public functions follow --- */
933 /* NOTE: we start off with at least one node (the root directory). */
934 static struct dir *dirinsert( vol, dir )
940 if ((node = dir_insert(vol, dir)))
943 /* recolor the tree. the current node is red. */
944 dir->d_color = DIRTREE_COLOR_RED;
946 /* parent of this node has to be black. if the parent node
947 * is red, then we have a grandparent. */
948 while ((dir != vol->v_root) &&
949 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
950 /* are we on the left tree? */
951 if (dir->d_back == dir->d_back->d_back->d_left) {
952 node = dir->d_back->d_back->d_right; /* get the right node */
953 if (node->d_color == DIRTREE_COLOR_RED) {
954 /* we're red. we need to change to black. */
955 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
956 node->d_color = DIRTREE_COLOR_BLACK;
957 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
958 dir = dir->d_back->d_back; /* finished. go up. */
960 if (dir == dir->d_back->d_right) {
962 dir_leftrotate(vol, dir);
964 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
965 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
966 dir_rightrotate(vol, dir->d_back->d_back);
969 node = dir->d_back->d_back->d_left;
970 if (node->d_color == DIRTREE_COLOR_RED) {
971 /* we're red. we need to change to black. */
972 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
973 node->d_color = DIRTREE_COLOR_BLACK;
974 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
975 dir = dir->d_back->d_back; /* finished. ascend */
977 if (dir == dir->d_back->d_left) {
979 dir_rightrotate(vol, dir);
981 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
982 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
983 dir_leftrotate(vol, dir->d_back->d_back);
988 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
992 /* ---------------------------- */
994 adddir( vol, dir, path)
999 struct dir *cdir, *edir;
1007 upath = path->u_name;
1009 upathlen = strlen(upath);
1011 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
1015 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
1018 name = path->m_name;
1019 if ((cdir = dirnew(name, upath)) == NULL) {
1020 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
1023 if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, path->m_name, strlen(path->m_name), &cdir->d_m_name_ucs2)) {
1024 LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
1025 cdir->d_m_name_ucs2 = NULL;
1030 if ((edir = dirinsert( vol, cdir ))) {
1031 /* it's not possible with LASTDID
1033 - someone else have moved the directory.
1034 - it's a symlink inside the share.
1035 - it's an ID reused, the old directory was deleted but not
1036 the cnid record and the server've reused the inode for
1038 for HASH (we should get ride of HASH)
1039 - someone else have moved the directory.
1040 - it's an ID reused as above
1041 - it's a hash duplicate and we are in big trouble
1043 deleted = (edir->d_m_name == NULL);
1045 dir_hash_del(vol, edir);
1047 edir->d_m_name = cdir->d_m_name;
1048 edir->d_u_name = cdir->d_u_name;
1049 edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
1052 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
1053 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
1054 hash_alloc_insert(vol->v_hash, cdir, cdir);
1057 /* the old was not in the same folder */
1059 dirchildremove(cdir->d_parent, cdir);
1062 /* parent/child directories */
1063 cdir->d_parent = dir;
1064 dirchildadd(vol, dir, cdir);
1068 /* --- public functions follow --- */
1069 /* free everything down. we don't bother to recolor as this is only
1070 * called to free the entire tree */
1071 void dirfreename(struct dir *dir)
1073 if (dir->d_u_name != dir->d_m_name) {
1074 free(dir->d_u_name);
1076 if (dir->d_m_name_ucs2)
1077 free(dir->d_m_name_ucs2);
1078 free(dir->d_m_name);
1084 if (!dir || (dir == SENTINEL))
1087 if ( dir->d_left != SENTINEL ) {
1088 dirfree( dir->d_left );
1090 if ( dir->d_right != SENTINEL ) {
1091 dirfree( dir->d_right );
1094 if (dir != SENTINEL) {
1100 /* --------------------------------------------
1101 * most of the time mac name and unix name are the same
1103 struct dir *dirnew(const char *m_name, const char *u_name)
1107 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1111 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1116 if (m_name == u_name || !strcmp(m_name, u_name)) {
1117 dir->d_u_name = dir->d_m_name;
1119 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1120 free(dir->d_m_name);
1125 dir->d_m_name_ucs2 = NULL;
1126 dir->d_left = dir->d_right = SENTINEL;
1127 dir->d_next = dir->d_prev = dir;
1131 /* ------------------ */
1132 static hash_val_t hash_fun_dir(const void *key)
1134 const struct dir *k = key;
1136 static unsigned long randbox[] = {
1137 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1138 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1139 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1140 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1143 const unsigned char *str = k->d_u_name;
1147 acc ^= randbox[(*str + acc) & 0xf];
1148 acc = (acc << 1) | (acc >> 31);
1150 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1151 acc = (acc << 2) | (acc >> 30);
1157 /* ---------------- */
1158 static int hash_comp_dir(const void *key1, const void *key2)
1160 const struct dir *k1 = key1;
1161 const struct dir *k2 = key2;
1163 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1166 /* ---------------- */
1170 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1173 /* ------------------ */
1174 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1177 movecwd failed some of dir path are not there anymore.
1178 FIXME Is it true with other errors?
1179 so we remove dir from the cache
1181 if (dir->d_did == DIRDID_ROOT_PARENT)
1183 if (afp_errno == AFPERR_ACCESS) {
1184 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1187 /* FIXME should we set these?, don't need to call stat() after:
1189 ret->st_errno = EACCES;
1191 ret->m_name = dir->d_m_name;
1192 ret->u_name = dir->d_u_name;
1195 } else if (afp_errno == AFPERR_NOOBJ) {
1196 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1199 strcpy(ret->m_name, dir->d_m_name);
1200 if (dir->d_m_name == dir->d_u_name) {
1201 ret->u_name = ret->m_name;
1204 size_t tp = strlen(ret->m_name)+1;
1206 ret->u_name = ret->m_name +tp;
1207 strcpy(ret->u_name, dir->d_u_name);
1209 /* FIXME should we set :
1211 ret->st_errno = ENOENT;
1213 dir_invalidate(vol, dir);
1216 dir_invalidate(vol, dir);
1220 /* -------------------------------------------------- */
1226 stat the file or errno
1229 curdir: filename parent directory
1235 stat the dir or errno
1239 curdir: dir parent directory
1247 curdir: dir parent directory
1254 cname( vol, dir, cpath )
1255 const struct vol *vol;
1259 struct dir *cdir, *scdir=NULL;
1260 static char path[ MAXPATHLEN + 1];
1261 static struct path ret;
1273 afp_errno = AFPERR_NOOBJ;
1274 memset(&ret, 0, sizeof(ret));
1275 switch (ret.m_type = *data) { /* path type */
1278 len = (unsigned char) *data++;
1281 if (afp_version >= 30) {
1287 if (afp_version >= 30) {
1289 memcpy(&hint, data, sizeof(hint));
1291 data += sizeof(hint);
1293 memcpy(&len16, data, sizeof(len16));
1300 /* else it's an error */
1302 afp_errno = AFPERR_PARAM;
1305 *cpath += len + size;
1310 if (movecwd( vol, dir ) < 0 ) {
1311 return invalidate(vol, dir, &ret );
1313 if (*path == '\0') {
1320 if (*data == sep ) {
1324 while (*data == sep && len > 0 ) {
1325 if ( dir->d_parent == NULL ) {
1328 dir = dir->d_parent;
1333 /* would this be faster with strlen + strncpy? */
1335 while ( *data != sep && len > 0 ) {
1337 if (p > &path[ MAXPATHLEN]) {
1338 afp_errno = AFPERR_PARAM;
1344 /* short cut bits by chopping off a trailing \0. this also
1345 makes the traversal happy w/ filenames at the end of the
1352 if ( p == path ) { /* end of the name parameter */
1356 if (afp_version >= 30) {
1361 static char temp[ MAXPATHLEN + 1];
1363 if (dir->d_did == DIRDID_ROOT_PARENT) {
1365 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1366 So we compare it with the longname from the current volume and if they match
1367 we overwrite the requested path with the utf8 volume name so that the following
1370 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1371 if (strcasecmp( path, temp) == 0)
1372 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1375 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1376 afp_errno = AFPERR_PARAM;
1382 /* check for OS X mangled filename :( */
1384 t = demangle_osx(vol, path, dir->d_did, &fileid);
1387 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1388 * flags weren't the same
1390 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1391 /* at last got our view of mac name */
1396 if (ret.u_name == NULL) {
1397 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1398 afp_errno = AFPERR_PARAM;
1404 cdir = dir->d_child;
1406 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1407 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1408 CH_UCS2, path, strlen(path), (char **)&tmpname) )
1411 if (!cdir->d_m_name_ucs2) {
1412 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1413 /* this shouldn't happen !!!! */
1417 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1420 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1423 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1429 if (dir->d_did == DIRDID_ROOT_PARENT) {
1431 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1432 must check against the volume name.
1434 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1440 cdir = dirsearch_byname(vol, dir, ret.u_name);
1444 if (cdir == NULL && scdir != NULL) {
1446 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1449 if ( cdir == NULL ) {
1451 /* if dir == curdir it always succeed,
1452 even if curdir is deleted.
1453 it's not a pb because it will fail in extenddir
1455 if ( movecwd( vol, dir ) < 0 ) {
1456 /* dir is not valid anymore
1457 we delete dir from the cache and abort.
1459 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1460 afp_errno = AFPERR_NOOBJ;
1463 if (afp_errno == AFPERR_ACCESS)
1465 dir_invalidate(vol, dir);
1468 cdir = extenddir( vol, dir, &ret );
1472 cdir = extenddir( vol, dir, &ret );
1473 } /* if (!extend) */
1475 if ( cdir == NULL ) {
1477 if ( len > 0 || !ret.u_name ) {
1489 * Move curdir to dir, with a possible chdir()
1491 int movecwd( vol, dir)
1492 const struct vol *vol;
1495 char path[MAXPATHLEN + 1];
1500 if ( dir == curdir ) {
1503 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1504 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1508 p = path + sizeof(path) - 1;
1511 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1514 /* parent directory is deleted */
1515 afp_errno = AFPERR_NOOBJ;
1519 if (p -n -1 < path) {
1520 afp_errno = AFPERR_PARAM;
1527 if ( d != curdir ) {
1528 n = strlen( vol->v_path );
1529 if (p -n -1 < path) {
1530 afp_errno = AFPERR_PARAM;
1535 memcpy( p, vol->v_path, n );
1537 if ( chdir( p ) < 0 ) {
1541 afp_errno = AFPERR_ACCESS;
1544 afp_errno = AFPERR_NOOBJ;
1554 * We can't use unix file's perm to support Apple's inherited protection modes.
1555 * If we aren't the file's owner we can't change its perms when moving it and smb
1556 * nfs,... don't even try.
1558 #define AFP_CHECK_ACCESS
1560 int check_access(char *path, int mode)
1562 #ifdef AFP_CHECK_ACCESS
1570 accessmode(p, &ma, curdir, NULL);
1571 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1573 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1579 /* --------------------- */
1580 int file_access(struct path *path, int mode)
1584 accessmode(path->u_name, &ma, curdir, &path->st);
1585 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1587 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1593 /* --------------------- */
1594 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1596 dir->offcnt = count;
1597 dir->ctime = st->st_ctime;
1598 dir->d_flags &= ~DIRF_CNID;
1601 /* ---------------------
1602 * is our cached offspring count valid?
1605 int diroffcnt(struct dir *dir, struct stat *st)
1607 return st->st_ctime == dir->ctime;
1610 /* ---------------------
1611 * is our cached also for reenumerate id?
1614 int dirreenumerate(struct dir *dir, struct stat *st)
1616 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1619 /* --------------------- */
1620 static int invisible_dots(const struct vol *vol, const char *name)
1622 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1625 /* ------------------------------
1627 (name, dir) with curdir:name == dir, from afp_enumerate
1630 int getdirparams(const struct vol *vol,
1631 u_int16_t bitmap, struct path *s_path,
1633 char *buf, int *buflen )
1637 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1638 int bit = 0, isad = 0;
1644 struct stat *st = &s_path->st;
1645 char *upath = s_path->u_name;
1647 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1648 (1 << DIRPBIT_CDATE) |
1649 (1 << DIRPBIT_MDATE) |
1650 (1 << DIRPBIT_BDATE) |
1651 (1 << DIRPBIT_FINFO)))) {
1653 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1654 if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1659 if ( dir->d_did == DIRDID_ROOT) {
1660 pdid = DIRDID_ROOT_PARENT;
1661 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1664 pdid = dir->d_parent->d_did;
1668 while ( bitmap != 0 ) {
1669 while (( bitmap & 1 ) == 0 ) {
1677 ad_getattr(&ad, &ashort);
1678 } else if (invisible_dots(vol, dir->d_u_name)) {
1679 ashort = htons(ATTRBIT_INVISIBLE);
1682 ashort |= htons(ATTRBIT_SHARED);
1683 memcpy( data, &ashort, sizeof( ashort ));
1684 data += sizeof( ashort );
1688 memcpy( data, &pdid, sizeof( pdid ));
1689 data += sizeof( pdid );
1692 case DIRPBIT_CDATE :
1693 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1694 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1695 memcpy( data, &aint, sizeof( aint ));
1696 data += sizeof( aint );
1699 case DIRPBIT_MDATE :
1700 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1701 memcpy( data, &aint, sizeof( aint ));
1702 data += sizeof( aint );
1705 case DIRPBIT_BDATE :
1706 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1707 aint = AD_DATE_START;
1708 memcpy( data, &aint, sizeof( aint ));
1709 data += sizeof( aint );
1712 case DIRPBIT_FINFO :
1714 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1715 } else { /* no appledouble */
1716 memset( data, 0, 32 );
1717 /* set default view -- this also gets done in ad_open() */
1718 ashort = htons(FINDERINFO_CLOSEDVIEW);
1719 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1721 /* dot files are by default visible */
1722 if (invisible_dots(vol, dir->d_u_name)) {
1723 ashort = htons(FINDERINFO_INVISIBLE);
1724 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1730 case DIRPBIT_LNAME :
1731 if (dir->d_m_name) /* root of parent can have a null name */
1734 memset(data, 0, sizeof(u_int16_t));
1735 data += sizeof( u_int16_t );
1738 case DIRPBIT_SNAME :
1739 memset(data, 0, sizeof(u_int16_t));
1740 data += sizeof( u_int16_t );
1744 memcpy( data, &dir->d_did, sizeof( aint ));
1745 data += sizeof( aint );
1748 case DIRPBIT_OFFCNT :
1750 /* this needs to handle current directory access rights */
1751 if (diroffcnt(dir, st)) {
1752 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1754 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1755 setdiroffcnt(dir, st, ret);
1756 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1758 ashort = htons( ashort );
1759 memcpy( data, &ashort, sizeof( ashort ));
1760 data += sizeof( ashort );
1764 aint = htonl(st->st_uid);
1765 memcpy( data, &aint, sizeof( aint ));
1766 data += sizeof( aint );
1770 aint = htonl(st->st_gid);
1771 memcpy( data, &aint, sizeof( aint ));
1772 data += sizeof( aint );
1775 case DIRPBIT_ACCESS :
1776 accessmode( upath, &ma, dir , st);
1778 *data++ = ma.ma_user;
1779 *data++ = ma.ma_world;
1780 *data++ = ma.ma_group;
1781 *data++ = ma.ma_owner;
1784 /* Client has requested the ProDOS information block.
1785 Just pass back the same basic block for all
1786 directories. <shirsch@ibm.net> */
1787 case DIRPBIT_PDINFO :
1788 if (afp_version >= 30) { /* UTF8 name */
1789 utf8 = kTextEncodingUTF8;
1790 if (dir->d_m_name) /* root of parent can have a null name */
1793 memset(data, 0, sizeof(u_int16_t));
1794 data += sizeof( u_int16_t );
1796 memcpy(data, &aint, sizeof( aint ));
1797 data += sizeof( aint );
1799 else { /* ProDOS Info Block */
1802 ashort = htons( 0x0200 );
1803 memcpy( data, &ashort, sizeof( ashort ));
1804 data += sizeof( ashort );
1805 memset( data, 0, sizeof( ashort ));
1806 data += sizeof( ashort );
1810 case DIRPBIT_UNIXPR :
1811 aint = htonl(st->st_uid);
1812 memcpy( data, &aint, sizeof( aint ));
1813 data += sizeof( aint );
1814 aint = htonl(st->st_gid);
1815 memcpy( data, &aint, sizeof( aint ));
1816 data += sizeof( aint );
1819 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1820 memcpy( data, &aint, sizeof( aint ));
1821 data += sizeof( aint );
1823 accessmode( upath, &ma, dir , st);
1825 *data++ = ma.ma_user;
1826 *data++ = ma.ma_world;
1827 *data++ = ma.ma_group;
1828 *data++ = ma.ma_owner;
1833 ad_close_metadata( &ad );
1835 return( AFPERR_BITMAP );
1841 ashort = htons( data - buf );
1842 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1843 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1845 if ( utf_nameoff ) {
1846 ashort = htons( data - buf );
1847 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1848 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1851 ad_close_metadata( &ad );
1853 *buflen = data - buf;
1857 /* ----------------------------- */
1858 int path_error(struct path *path, int error)
1860 /* - a dir with access error
1861 * - no error it's a file
1864 if (path_isadir(path))
1866 if (path->st_valid && path->st_errno)
1868 return AFPERR_BADTYPE ;
1871 /* ----------------------------- */
1872 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1874 char *ibuf, *rbuf _U_;
1875 int ibuflen _U_, *rbuflen;
1880 u_int16_t vid, bitmap;
1886 memcpy( &vid, ibuf, sizeof( vid ));
1887 ibuf += sizeof( vid );
1889 if (NULL == ( vol = getvolbyvid( vid )) ) {
1890 return( AFPERR_PARAM );
1893 if (vol->v_flags & AFPVOL_RO)
1894 return AFPERR_VLOCK;
1896 memcpy( &did, ibuf, sizeof( did ));
1897 ibuf += sizeof( int );
1899 if (NULL == ( dir = dirlookup( vol, did )) ) {
1903 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1904 bitmap = ntohs( bitmap );
1905 ibuf += sizeof( bitmap );
1907 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1908 return get_afp_errno(AFPERR_NOOBJ);
1911 if ( *path->m_name != '\0' ) {
1912 rc = path_error(path, AFPERR_NOOBJ);
1913 /* maybe we are trying to set perms back */
1914 if (rc != AFPERR_ACCESS)
1919 * If ibuf is odd, make it even.
1921 if ((u_long)ibuf & 1 ) {
1925 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1926 setvoltime(obj, vol );
1932 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1934 * assume path == '\0' eg. it's a directory in canonical form
1937 struct path Cur_Path = {
1940 ".", /* unix name */
1942 NULL,/* struct dir */
1943 0, /* stat is not set */
1946 /* ------------------ */
1947 static int set_dir_errors(struct path *path, const char *where, int err)
1952 return AFPERR_ACCESS;
1954 return AFPERR_VLOCK;
1956 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1957 return AFPERR_PARAM;
1960 /* ------------------ */
1961 int setdirparams(const struct vol *vol,
1962 struct path *path, u_int16_t d_bitmap, char *buf )
1974 u_int16_t ashort, bshort;
1976 int change_mdate = 0;
1977 int change_parent_mdate = 0;
1979 u_int16_t bitmap = d_bitmap;
1980 u_char finder_buf[32];
1983 u_int16_t upriv_bit = 0;
1986 upath = path->u_name;
1988 while ( bitmap != 0 ) {
1989 while (( bitmap & 1 ) == 0 ) {
1997 memcpy( &ashort, buf, sizeof( ashort ));
1998 buf += sizeof( ashort );
2000 case DIRPBIT_CDATE :
2002 memcpy(&cdate, buf, sizeof(cdate));
2003 buf += sizeof( cdate );
2005 case DIRPBIT_MDATE :
2006 memcpy(&newdate, buf, sizeof(newdate));
2007 buf += sizeof( newdate );
2009 case DIRPBIT_BDATE :
2011 memcpy(&bdate, buf, sizeof(bdate));
2012 buf += sizeof( bdate );
2014 case DIRPBIT_FINFO :
2016 memcpy( finder_buf, buf, 32 );
2019 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2020 change_parent_mdate = 1;
2021 memcpy( &owner, buf, sizeof(owner));
2022 buf += sizeof( owner );
2025 change_parent_mdate = 1;
2026 memcpy( &group, buf, sizeof( group ));
2027 buf += sizeof( group );
2029 case DIRPBIT_ACCESS :
2031 change_parent_mdate = 1;
2032 ma.ma_user = *buf++;
2033 ma.ma_world = *buf++;
2034 ma.ma_group = *buf++;
2035 ma.ma_owner = *buf++;
2036 mpriv = mtoumode( &ma ) | vol->v_dperm;
2037 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2038 err = set_dir_errors(path, "setdirmode", errno);
2042 /* Ignore what the client thinks we should do to the
2043 ProDOS information block. Skip over the data and
2044 report nothing amiss. <shirsch@ibm.net> */
2045 case DIRPBIT_PDINFO :
2046 if (afp_version < 30) {
2050 err = AFPERR_BITMAP;
2054 case DIRPBIT_UNIXPR :
2055 if (vol_unix_priv(vol)) {
2056 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2057 buf += sizeof( owner );
2058 memcpy( &group, buf, sizeof( group ));
2059 buf += sizeof( group );
2062 change_parent_mdate = 1;
2063 memcpy( &upriv, buf, sizeof( upriv ));
2064 buf += sizeof( upriv );
2065 upriv = ntohl (upriv) | vol->v_dperm;
2066 if (dir_rx_set(upriv)) {
2067 /* maybe we are trying to set perms back */
2068 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2070 err = set_dir_errors(path, "setdirunixmode", errno);
2081 err = AFPERR_BITMAP;
2089 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2091 if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2093 * Check to see what we're trying to set. If it's anything
2094 * but ACCESS, UID, or GID, give an error. If it's any of those
2095 * three, we don't need the ad to be open, so just continue.
2097 * note: we also don't need to worry about mdate. also, be quiet
2098 * if we're using the noadouble option.
2100 if (!vol_noadouble(vol) && (d_bitmap &
2101 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2102 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2103 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2104 return AFPERR_ACCESS;
2110 * Check to see if a create was necessary. If it was, we'll want
2111 * to set our name, etc.
2113 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2114 ad_setname(&ad, curdir->d_m_name);
2120 while ( bitmap != 0 ) {
2121 while (( bitmap & 1 ) == 0 ) {
2129 ad_getattr(&ad, &bshort);
2130 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2131 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2132 change_parent_mdate = 1;
2133 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2134 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2138 ad_setattr(&ad, bshort);
2141 case DIRPBIT_CDATE :
2143 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2146 case DIRPBIT_MDATE :
2148 case DIRPBIT_BDATE :
2150 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2153 case DIRPBIT_FINFO :
2155 /* Fixes #2802236 */
2156 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2157 *fflags &= htons(~FINDERINFO_ISHARED);
2159 if ( dir->d_did == DIRDID_ROOT ) {
2161 * Alright, we admit it, this is *really* sick!
2162 * The 4 bytes that we don't copy, when we're dealing
2163 * with the root of a volume, are the directory's
2164 * location information. This eliminates that annoying
2165 * behavior one sees when mounting above another mount
2168 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2169 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2171 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2175 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2176 if ( (dir->d_did == DIRDID_ROOT) &&
2177 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2178 err = set_dir_errors(path, "setdeskowner", errno);
2179 if (isad && err == AFPERR_PARAM) {
2180 err = AFP_OK; /* ???*/
2183 goto setdirparam_done;
2186 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2187 err = set_dir_errors(path, "setdirowner", errno);
2188 goto setdirparam_done;
2192 if (dir->d_did == DIRDID_ROOT)
2193 setdeskowner( -1, ntohl(group) );
2194 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2195 err = set_dir_errors(path, "setdirowner", errno);
2196 goto setdirparam_done;
2199 case DIRPBIT_ACCESS :
2200 if (dir->d_did == DIRDID_ROOT) {
2202 if (!dir_rx_set(mpriv)) {
2203 /* we can't remove read and search for owner on volume root */
2204 err = AFPERR_ACCESS;
2205 goto setdirparam_done;
2209 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2210 err = set_dir_errors(path, "setdirmode", errno);
2211 goto setdirparam_done;
2214 case DIRPBIT_PDINFO :
2215 if (afp_version >= 30) {
2216 err = AFPERR_BITMAP;
2217 goto setdirparam_done;
2220 case DIRPBIT_UNIXPR :
2221 if (vol_unix_priv(vol)) {
2222 if (dir->d_did == DIRDID_ROOT) {
2223 if (!dir_rx_set(upriv)) {
2224 /* we can't remove read and search for owner on volume root */
2225 err = AFPERR_ACCESS;
2226 goto setdirparam_done;
2228 setdeskowner( -1, ntohl(group) );
2229 setdeskmode( upriv );
2231 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2232 err = set_dir_errors(path, "setdirowner", errno);
2233 goto setdirparam_done;
2236 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2237 err = set_dir_errors(path, "setdirunixmode", errno);
2238 goto setdirparam_done;
2242 err = AFPERR_BITMAP;
2243 goto setdirparam_done;
2247 err = AFPERR_BITMAP;
2248 goto setdirparam_done;
2257 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2258 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2262 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2263 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2268 if (path->st_valid && !path->st_errno) {
2269 struct stat *st = &path->st;
2271 if (dir && dir->d_parent) {
2272 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2276 ad_close_metadata( &ad);
2279 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2280 && gettimeofday(&tv, NULL) == 0) {
2281 if (!movecwd(vol, dir->d_parent)) {
2282 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2283 /* be careful with bitmap because now dir is null */
2284 bitmap = 1<<DIRPBIT_MDATE;
2285 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2286 /* should we reset curdir ?*/
2293 int afp_syncdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2295 char *ibuf, *rbuf _U_;
2296 int ibuflen _U_, *rbuflen;
2310 memcpy( &vid, ibuf, sizeof( vid ));
2311 ibuf += sizeof( vid );
2312 if (NULL == (vol = getvolbyvid( vid )) ) {
2313 return( AFPERR_PARAM );
2316 memcpy( &did, ibuf, sizeof( did ));
2317 ibuf += sizeof( did );
2321 * if it's CNID 2 our only choice to meet the specs is call sync.
2322 * For any other CNID just sync that dir. To my knowledge the
2323 * intended use of FPSyncDir is to sync the volume so all we're
2324 * ever going to see here is probably CNID 2. Anyway, we' prepared.
2327 if ( ntohl(did) == 2 ) {
2330 if (NULL == ( dir = dirlookup( vol, did )) ) {
2331 return afp_errno; /* was AFPERR_NOOBJ */
2334 if (movecwd( vol, dir ) < 0 )
2335 return ( AFPERR_NOOBJ );
2338 * Assuming only OSens that have dirfd also may require fsyncing directories
2339 * in order to flush metadata e.g. Linux.
2343 if (NULL == ( dp = opendir( "." )) ) {
2346 return( AFPERR_NOOBJ );
2348 return( AFPERR_ACCESS );
2350 return( AFPERR_PARAM );
2354 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2357 if ( fsync ( dfd ) < 0 )
2358 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2359 dir->d_u_name, strerror(errno) );
2360 closedir(dp); /* closes dfd too */
2363 if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2366 return( AFPERR_NOOBJ );
2368 return( AFPERR_ACCESS );
2370 return( AFPERR_PARAM );
2374 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2375 vol->ad_path(".", ADFLAGS_DIR) );
2377 if ( fsync(dfd) < 0 )
2378 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2379 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2386 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2389 int ibuflen _U_, *rbuflen;
2395 struct path *s_path;
2403 memcpy( &vid, ibuf, sizeof( vid ));
2404 ibuf += sizeof( vid );
2405 if (NULL == ( vol = getvolbyvid( vid )) ) {
2406 return( AFPERR_PARAM );
2409 if (vol->v_flags & AFPVOL_RO)
2410 return AFPERR_VLOCK;
2412 memcpy( &did, ibuf, sizeof( did ));
2413 ibuf += sizeof( did );
2414 if (NULL == ( dir = dirlookup( vol, did )) ) {
2415 return afp_errno; /* was AFPERR_NOOBJ */
2417 /* for concurrent access we need to be sure we are not in the
2418 * folder we want to create...
2422 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2423 return get_afp_errno(AFPERR_PARAM);
2425 /* cname was able to move curdir to it! */
2426 if (*s_path->m_name == '\0')
2427 return AFPERR_EXIST;
2429 upath = s_path->u_name;
2431 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2435 if (of_stat(s_path) < 0) {
2439 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2443 if ( movecwd( vol, dir ) < 0 ) {
2444 return( AFPERR_PARAM );
2447 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2448 if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2449 if (vol_noadouble(vol))
2450 goto createdir_done;
2451 return( AFPERR_ACCESS );
2453 ad_setname(&ad, s_path->m_name);
2454 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2457 ad_close_metadata( &ad);
2460 #ifdef HAVE_NFSv4_ACLS
2461 /* FIXME: are we really inside the created dir? */
2462 addir_inherit_acl(vol);
2465 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2466 *rbuflen = sizeof( u_int32_t );
2467 setvoltime(obj, vol );
2472 * dst new unix filename (not a pathname)
2473 * newname new mac name
2477 int renamedir(vol, src, dst, dir, newparent, newname)
2478 const struct vol *vol;
2479 char *src, *dst, *newname;
2480 struct dir *dir, *newparent;
2487 /* existence check moved to afp_moveandrename */
2488 if ( unix_rename( src, dst ) < 0 ) {
2491 return( AFPERR_NOOBJ );
2493 return( AFPERR_ACCESS );
2495 return AFPERR_VLOCK;
2497 /* tried to move directory into a subdirectory of itself */
2498 return AFPERR_CANTMOVE;
2500 /* this needs to copy and delete. bleah. that means we have
2501 * to deal with entire directory hierarchies. */
2502 if ((err = copydir(vol, src, dst)) < 0) {
2506 if ((err = deletedir(src)) < 0)
2510 return( AFPERR_PARAM );
2514 vol->vfs->rf_renamedir(vol, src, dst);
2516 len = strlen( newname );
2517 /* rename() succeeded so we need to update our tree even if we can't open
2521 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2523 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2524 ad_setname(&ad, newname);
2526 ad_close_metadata( &ad);
2529 dir_hash_del(vol, dir);
2530 if (dir->d_m_name == dir->d_u_name)
2531 dir->d_u_name = NULL;
2533 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2534 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2535 /* FIXME : fatal ? */
2538 dir->d_m_name = buf;
2539 strcpy( dir->d_m_name, newname );
2541 if (newname == dst) {
2542 free(dir->d_u_name);
2543 dir->d_u_name = dir->d_m_name;
2546 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2547 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2550 dir->d_u_name = buf;
2551 strcpy( dir->d_u_name, dst );
2554 if (dir->d_m_name_ucs2)
2555 free(dir->d_m_name_ucs2);
2557 dir->d_m_name_ucs2 = NULL;
2558 if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, strlen(dir->d_m_name), (char**)&dir->d_m_name_ucs2))
2559 dir->d_m_name_ucs2 = NULL;
2561 if (( parent = dir->d_parent ) == NULL ) {
2564 if ( parent == newparent ) {
2565 hash_alloc_insert(vol->v_hash, dir, dir);
2569 /* detach from old parent and add to new one. */
2570 dirchildremove(parent, dir);
2571 dir->d_parent = newparent;
2572 dirchildadd(vol, newparent, dir);
2576 /* delete an empty directory */
2577 int deletecurdir( vol)
2578 const struct vol *vol;
2588 if ( curdir->d_parent == NULL ) {
2589 return( AFPERR_ACCESS );
2594 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2595 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2597 ad_getattr(&ad, &ashort);
2598 ad_close( &ad, ADFLAGS_HF );
2599 if ((ashort & htons(ATTRBIT_NODELETE))) {
2600 return AFPERR_OLOCK;
2603 err = vol->vfs->rf_deletecurdir(vol);
2608 /* now get rid of dangling symlinks */
2609 if ((dp = opendir("."))) {
2610 while ((de = readdir(dp))) {
2611 /* skip this and previous directory */
2612 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2615 /* bail if it's not a symlink */
2616 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2618 return AFPERR_DIRNEMPT;
2621 if ((err = netatalk_unlink(de->d_name))) {
2628 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2633 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2634 dirchildremove(curdir, fdir);
2635 cnid_delete(vol->v_cdb, fdir->d_did);
2636 dir_remove( vol, fdir );
2641 /* inode is used as key for cnid.
2642 * Close the descriptor only after cnid_delete
2650 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2653 int ibuflen _U_, *rbuflen;
2664 sfunc = (unsigned char) *ibuf++;
2668 if (sfunc >= 3 && sfunc <= 6) {
2669 if (afp_version < 30) {
2670 return( AFPERR_PARAM );
2677 case 3 :/* unicode */
2678 memcpy( &id, ibuf, sizeof( id ));
2681 if (( pw = getpwuid( id )) == NULL ) {
2682 return( AFPERR_NOITEM );
2684 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2685 pw->pw_name, strlen(pw->pw_name), &name);
2692 case 4 : /* unicode */
2693 memcpy( &id, ibuf, sizeof( id ));
2696 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2697 return( AFPERR_NOITEM );
2699 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2700 gr->gr_name, strlen(gr->gr_name), &name);
2706 #ifdef HAVE_NFSv4_ACLS
2707 case 5 : /* UUID -> username */
2708 case 6 : /* UUID -> groupname */
2709 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2710 return AFPERR_PARAM;
2711 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2712 len = getnamefromuuid( ibuf, &name, &type);
2713 if (len != 0) /* its a error code, not len */
2714 return AFPERR_NOITEM;
2715 if (type == UUID_USER) {
2716 if (( pw = getpwnam( name )) == NULL )
2717 return( AFPERR_NOITEM );
2718 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2719 id = htonl(UUID_USER);
2720 memcpy( rbuf, &id, sizeof( id ));
2721 id = htonl( pw->pw_uid);
2722 rbuf += sizeof( id );
2723 memcpy( rbuf, &id, sizeof( id ));
2724 rbuf += sizeof( id );
2725 *rbuflen = 2 * sizeof( id );
2726 } else { /* type == UUID_GROUP */
2727 if (( gr = getgrnam( name )) == NULL )
2728 return( AFPERR_NOITEM );
2729 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2730 id = htonl(UUID_GROUP);
2731 memcpy( rbuf, &id, sizeof( id ));
2732 rbuf += sizeof( id );
2733 id = htonl( gr->gr_gid);
2734 memcpy( rbuf, &id, sizeof( id ));
2735 rbuf += sizeof( id );
2736 *rbuflen = 2 * sizeof( id );
2741 return( AFPERR_PARAM );
2745 len = strlen( name );
2748 u_int16_t tp = htons(len);
2749 memcpy(rbuf, &tp, sizeof(tp));
2758 memcpy( rbuf, name, len );
2766 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2769 int ibuflen _U_, *rbuflen;
2778 sfunc = (unsigned char) *ibuf++;
2780 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2783 case 2 : /* unicode */
2784 if (afp_version < 30) {
2785 return( AFPERR_PARAM );
2787 memcpy(&ulen, ibuf, sizeof(ulen));
2790 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2794 len = (unsigned char) *ibuf++;
2796 #ifdef HAVE_NFSv4_ACLS
2797 case 5 : /* username -> UUID */
2798 case 6 : /* groupname -> UUID */
2799 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2800 return AFPERR_PARAM;
2801 memcpy(&ulen, ibuf, sizeof(ulen));
2807 return( AFPERR_PARAM );
2813 return AFPERR_PARAM;
2816 case 1 : /* unicode */
2818 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2819 return( AFPERR_NOITEM );
2823 memcpy( rbuf, &id, sizeof( id ));
2824 *rbuflen = sizeof( id );
2827 case 2 : /* unicode */
2829 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2830 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2831 return( AFPERR_NOITEM );
2834 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2836 memcpy( rbuf, &id, sizeof( id ));
2837 *rbuflen = sizeof( id );
2839 #ifdef HAVE_NFSv4_ACLS
2840 case 5 : /* username -> UUID */
2841 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2842 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2843 return AFPERR_NOITEM;
2844 *rbuflen = UUID_BINSIZE;
2846 case 6 : /* groupname -> UUID */
2847 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2848 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2849 return AFPERR_NOITEM;
2850 *rbuflen = UUID_BINSIZE;
2858 /* ------------------------------------
2859 variable DID support
2861 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2863 char *ibuf _U_, *rbuf _U_;
2864 int ibuflen _U_, *rbuflen;
2875 /* do nothing as dids are static for the life of the process. */
2879 memcpy(&vid, ibuf, sizeof( vid ));
2880 ibuf += sizeof( vid );
2881 if (( vol = getvolbyvid( vid )) == NULL ) {
2882 return( AFPERR_PARAM );
2885 memcpy( &did, ibuf, sizeof( did ));
2886 ibuf += sizeof( did );
2887 if (( dir = dirlookup( vol, did )) == NULL ) {
2888 return( AFPERR_PARAM );
2891 /* dir_remove -- deletedid */
2897 /* did creation gets done automatically
2898 * there's a pb again with case but move it to cname
2900 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2903 int ibuflen _U_, *rbuflen;
2906 struct dir *parentdir;
2914 memcpy(&vid, ibuf, sizeof(vid));
2915 ibuf += sizeof( vid );
2917 if (NULL == ( vol = getvolbyvid( vid )) ) {
2918 return( AFPERR_PARAM );
2921 memcpy(&did, ibuf, sizeof(did));
2922 ibuf += sizeof(did);
2924 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2928 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2929 return get_afp_errno(AFPERR_PARAM);
2932 if ( *path->m_name != '\0' ) {
2933 return path_error(path, AFPERR_NOOBJ);
2936 if ( !path->st_valid && of_stat(path ) < 0 ) {
2937 return( AFPERR_NOOBJ );
2939 if ( path->st_errno ) {
2940 return( AFPERR_NOOBJ );
2943 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2944 *rbuflen = sizeof(curdir->d_did);