2 * $Id: directory.c,v 1.105 2009-10-02 09:32:40 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 */
38 #include <sys/param.h>
42 #include <atalk/adouble.h>
43 #include <atalk/vfs.h>
44 #include <atalk/afp.h>
45 #include <atalk/util.h>
46 #include <atalk/cnid.h>
47 #include <atalk/logger.h>
48 #include <atalk/uuid.h>
50 #include "directory.h"
61 #ifdef HAVE_NFSv4_ACLS
62 extern void addir_inherit_acl(const struct vol *vol);
68 #define SENTINEL (&sentinel)
69 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
70 NULL, NULL, NULL, NULL, NULL, 0, 0,
71 0, 0, NULL, NULL, NULL};
72 static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
73 NULL, NULL, NULL, NULL, NULL, 0, 0,
74 0, 0, NULL, NULL, NULL};
76 /* (from IM: Toolbox Essentials)
77 * dirFinderInfo (DInfo) fields:
79 * frRect 8 folder's window rectangle
81 * frLocation 4 folder's location in window
82 * frView 2 folder's view (default == closedView (256))
84 * extended dirFinderInfo (DXInfo) fields:
85 * frScroll 4 scroll position
86 * frOpenChain: 4 directory ID chain of open folders
87 * frScript: 1 script flag and code
88 * frXFlags: 1 reserved
89 * frComment: 2 comment ID
90 * frPutAway: 4 home directory ID
94 * redid did assignment for directories. now we use red-black trees.
99 const struct vol *vol;
105 /* check for 0 did */
107 afp_errno = AFPERR_PARAM;
110 if ( did == DIRDID_ROOT_PARENT ) {
112 rootpar.d_did = DIRDID_ROOT_PARENT;
113 rootpar.d_child = vol->v_dir;
117 /* XXX would be nice to check against curdir but we need to keep its volume */
118 if (vol->curdir && curdir->d_did == did) {
127 afp_errno = AFPERR_NOOBJ;
128 while ( dir != SENTINEL ) {
129 if (dir->d_did == did)
130 return dir->d_m_name ? dir : NULL;
131 dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
136 /* ------------------- */
137 int get_afp_errno(const int param)
139 if (afp_errno != AFPERR_DID1)
144 /* ------------------- */
146 dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
148 struct dir *dir = NULL;
150 if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
156 hn = hash_lookup(vol->v_hash, &key);
164 /* -----------------------------------------
165 * if did is not in the cache resolve it with cnid
168 * OSX call it with bogus id, ie file ID not folder ID,
169 * and we are really bad in this case.
172 dirlookup( vol, did )
173 const struct vol *vol;
179 static char path[MAXPATHLEN + 1];
182 static char buffer[12 + MAXPATHLEN + 1];
183 int buflen = 12 + MAXPATHLEN + 1;
188 ret = dirsearch(vol, did);
189 if (ret != NULL || afp_errno == AFPERR_PARAM)
192 utf8 = utf8_encoding();
193 maxpath = (utf8)?MAXPATHLEN -7:255;
195 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
196 afp_errno = AFPERR_NOOBJ;
199 ptr = path + MAXPATHLEN;
200 if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
201 afp_errno = AFPERR_NOOBJ;
205 pathlen = len; /* no 0 in the last part */
207 strcpy(ptr - len, mpath);
210 ret = dirsearch(vol,id);
215 if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
217 NULL == (mpath = utompath(vol, upath, cnid, utf8))
219 afp_errno = AFPERR_NOOBJ;
223 len = strlen(mpath) + 1;
225 if (pathlen > maxpath) {
226 afp_errno = AFPERR_PARAM;
229 strcpy(ptr - len, mpath);
233 /* fill the cache, another place where we know about the path type */
239 temp16 = htons(pathlen);
240 memcpy(ptr, &temp16, sizeof(temp16));
242 temp = htonl(kTextEncodingUTF8);
244 memcpy(ptr, &temp, sizeof(temp));
250 *ptr = (unsigned char)pathlen;
254 /* cname is not efficient */
255 if (cname( vol, ret, &ptr ) == NULL )
258 return dirsearch(vol, did);
261 /* child addition/removal */
262 static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
267 b->d_next = a->d_child;
268 b->d_prev = b->d_next->d_prev;
269 b->d_next->d_prev = b;
270 b->d_prev->d_next = b;
272 if (!hash_alloc_insert(vol->v_hash, b, b)) {
273 LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
277 static void dirchildremove(struct dir *a,struct dir *b)
280 a->d_child = (b == b->d_next) ? NULL : b->d_next;
281 b->d_next->d_prev = b->d_prev;
282 b->d_prev->d_next = b->d_next;
283 b->d_next = b->d_prev = b;
286 /* --------------------------- */
287 /* rotate the tree to the left */
288 static void dir_leftrotate(vol, dir)
292 struct dir *right = dir->d_right;
294 /* whee. move the right's left tree into dir's right tree */
295 dir->d_right = right->d_left;
296 if (right->d_left != SENTINEL)
297 right->d_left->d_back = dir;
299 if (right != SENTINEL) {
300 right->d_back = dir->d_back;
304 if (!dir->d_back) /* no parent. move the right tree to the top. */
306 else if (dir == dir->d_back->d_left) /* we were on the left */
307 dir->d_back->d_left = right;
309 dir->d_back->d_right = right; /* we were on the right */
311 /* re-insert dir on the left tree */
318 /* rotate the tree to the right */
319 static void dir_rightrotate(vol, dir)
323 struct dir *left = dir->d_left;
325 /* whee. move the left's right tree into dir's left tree */
326 dir->d_left = left->d_right;
327 if (left->d_right != SENTINEL)
328 left->d_right->d_back = dir;
330 if (left != SENTINEL) {
331 left->d_back = dir->d_back;
335 if (!dir->d_back) /* no parent. move the left tree to the top. */
337 else if (dir == dir->d_back->d_right) /* we were on the right */
338 dir->d_back->d_right = left;
340 dir->d_back->d_left = left; /* we were on the left */
342 /* re-insert dir on the right tree */
348 /* recolor after a removal */
349 static struct dir *dir_rmrecolor(vol, dir)
355 while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
356 /* are we on the left tree? */
357 if (dir == dir->d_back->d_left) {
358 leaf = dir->d_back->d_right; /* get right side */
359 if (leaf->d_color == DIRTREE_COLOR_RED) {
360 /* we're red. we need to change to black. */
361 leaf->d_color = DIRTREE_COLOR_BLACK;
362 dir->d_back->d_color = DIRTREE_COLOR_RED;
363 dir_leftrotate(vol, dir->d_back);
364 leaf = dir->d_back->d_right;
367 /* right leaf has black end nodes */
368 if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
369 (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
370 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
371 dir = dir->d_back; /* ascend */
373 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
374 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
375 leaf->d_color = DIRTREE_COLOR_RED;
376 dir_rightrotate(vol, leaf);
377 leaf = dir->d_back->d_right;
379 leaf->d_color = dir->d_back->d_color;
380 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
381 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
382 dir_leftrotate(vol, dir->d_back);
385 } else { /* right tree */
386 leaf = dir->d_back->d_left; /* left tree */
387 if (leaf->d_color == DIRTREE_COLOR_RED) {
388 leaf->d_color = DIRTREE_COLOR_BLACK;
389 dir->d_back->d_color = DIRTREE_COLOR_RED;
390 dir_rightrotate(vol, dir->d_back);
391 leaf = dir->d_back->d_left;
394 /* left leaf has black end nodes */
395 if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
396 (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
397 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
398 dir = dir->d_back; /* ascend */
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;
403 dir_leftrotate(vol, leaf);
404 leaf = dir->d_back->d_left;
406 leaf->d_color = dir->d_back->d_color;
407 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
408 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
409 dir_rightrotate(vol, dir->d_back);
414 dir->d_color = DIRTREE_COLOR_BLACK;
420 /* --------------------- */
421 static void dir_hash_del(const struct vol *vol, struct dir *dir)
425 hn = hash_lookup(vol->v_hash, dir);
427 LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
430 hash_delete(vol->v_hash, hn);
434 /* remove the node from the tree. this is just like insertion, but
435 * different. actually, it has to worry about a bunch of things that
436 * insertion doesn't care about. */
438 static void dir_remove( const struct vol *vol _U_, struct dir *dir)
441 struct ofork *of, *last;
442 struct dir *node, *leaf;
443 #endif /* REMOVE_NODES */
445 if (!dir || (dir == SENTINEL))
448 /* i'm not sure if it really helps to delete stuff. */
449 dir_hash_del(vol, dir);
452 dir->d_m_name = NULL;
453 dir->d_u_name = NULL;
454 dir->d_m_name_ucs2 = NULL;
455 #else /* ! REMOVE_NODES */
457 /* go searching for a node with at most one child */
458 if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
462 while (node->d_left != SENTINEL)
467 leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
470 leaf->d_back = node->d_back;
473 } else if (node == node->d_back->d_left) { /* left tree */
474 node->d_back->d_left = leaf;
476 node->d_back->d_right = leaf;
479 /* we want to free node, but we also want to free the data in dir.
480 * currently, that's d_name and the directory traversal bits.
481 * we just copy the necessary bits and then fix up all the
482 * various pointers to the directory. needless to say, there are
483 * a bunch of places that store the directory struct. */
485 struct dir save, *tmp;
487 memcpy(&save, dir, sizeof(save));
488 memcpy(dir, node, sizeof(struct dir));
490 /* restore the red-black bits */
491 dir->d_left = save.d_left;
492 dir->d_right = save.d_right;
493 dir->d_back = save.d_back;
494 dir->d_color = save.d_color;
496 if (node == vol->v_dir) {/* we may need to fix up this pointer */
498 rootpar.d_child = vol->v_dir;
500 /* if we aren't the root directory, we have parents and
501 * siblings to worry about */
502 if (dir->d_parent->d_child == node)
503 dir->d_parent->d_child = dir;
504 dir->d_next->d_prev = dir;
505 dir->d_prev->d_next = dir;
508 /* fix up children. */
512 tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
515 if (node == curdir) /* another pointer to fixup */
518 /* we also need to fix up oforks. bleah */
519 if ((of = dir->d_ofork)) {
520 last = of->of_d_prev;
523 of = (last == of) ? NULL : of->of_d_next;
527 /* set the node's d_name */
528 node->d_m_name = save.d_m_name;
529 node->d_u_name = save.d_u_name;
530 node->d_m_name_ucs2 = save.d_m_name_ucs2;
533 if (node->d_color == DIRTREE_COLOR_BLACK)
534 dir_rmrecolor(vol, leaf);
536 if (node->d_m_name_ucs2)
537 free(node->d_u_name_ucs2);
538 if (node->d_u_name != node->d_m_name) {
539 free(node->d_u_name);
541 free(node->d_m_name);
543 #endif /* ! REMOVE_NODES */
546 /* ---------------------------------------
547 * remove the node and its childs from the tree
549 * FIXME what about opened forks with refs to it?
550 * it's an afp specs violation because you can't delete
551 * an opened forks. Now afpd doesn't care about forks opened by other
552 * process. It's fixable within afpd if fnctl_lock, doable with smb and
553 * next to impossible for nfs and local filesystem access.
555 static void dir_invalidate( vol, dir )
556 const struct vol *vol;
560 /* v_root can't be deleted */
561 if (movecwd(vol, vol->v_root) < 0) {
562 LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
566 dirchildremove(dir->d_parent, dir);
567 dir_remove( vol, dir );
570 /* ------------------------------------ */
571 static struct dir *dir_insert(vol, dir)
572 const struct vol *vol;
578 while (pdir->d_did != dir->d_did ) {
579 if ( pdir->d_did > dir->d_did ) {
580 if ( pdir->d_left == SENTINEL ) {
587 if ( pdir->d_right == SENTINEL ) {
592 pdir = pdir->d_right;
598 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
601 caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
606 static u_int32_t did = 0;
607 static char cname[MAXPATHLEN];
608 static char lname[MAXPATHLEN];
609 ucs2_t u2_path[MAXPATHLEN];
610 ucs2_t u2_dename[MAXPATHLEN];
611 char *tmp, *savepath;
613 if (!(vol->v_flags & AFPVOL_CASEINSEN))
616 if (veto_file(ENUMVETO, path->u_name))
619 savepath = path->u_name;
621 /* very simple cache */
622 if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
623 path->u_name = cname;
625 if (of_stat( path ) == 0 ) {
628 /* something changed, we cannot stat ... */
632 if (NULL == ( dp = opendir( "." )) ) {
633 LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
638 /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
639 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, strlen(path->u_name), u2_path, sizeof(u2_path)) )
640 LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
642 /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
644 for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
645 if (NULL == check_dirent(vol, de->d_name))
648 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, strlen(de->d_name), u2_dename, sizeof(u2_dename)) )
651 if (strcasecmp_w( u2_path, u2_dename) == 0) {
653 strlcpy(cname, de->d_name, sizeof(cname));
654 path->u_name = cname;
656 if (of_stat( path ) == 0 ) {
657 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
658 strlcpy(lname, tmp, sizeof(lname));
671 /* invalidate cache */
674 path->u_name = savepath;
676 /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
682 * attempt to extend the current dir. tree to include path
683 * as a side-effect, movecwd to that point and return the new dir
686 extenddir( vol, dir, path )
693 if ( path->u_name == NULL) {
694 afp_errno = AFPERR_PARAM;
698 if (check_name(vol, path->u_name)) {
699 /* the name is illegal */
700 LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
702 afp_errno = AFPERR_PARAM;
706 if (of_stat( path ) != 0 ) {
707 if (!(vol->v_flags & AFPVOL_CASEINSEN))
709 else if(caseenumerate(vol, path, dir) != 0)
713 if (!S_ISDIR(path->st.st_mode)) {
717 /* mac name is always with the right encoding (from cname()) */
718 if (( dir = adddir( vol, dir, path)) == NULL ) {
723 if ( movecwd( vol, dir ) < 0 ) {
730 /* -------------------------
731 appledouble mkdir afp error code.
733 static int netatalk_mkdir(const char *name)
735 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
738 return( AFPERR_NOOBJ );
740 return( AFPERR_VLOCK );
743 return( AFPERR_ACCESS );
745 return( AFPERR_EXIST );
748 return( AFPERR_DFULL );
750 return( AFPERR_PARAM );
756 /* ------------------- */
757 static int deletedir(char *dir)
759 char path[MAXPATHLEN + 1];
767 if ((len = strlen(dir)) +2 > sizeof(path))
771 if ((dp = opendir(dir)) == NULL)
777 remain = sizeof(path) -len -1;
778 while ((de = readdir(dp)) && err == AFP_OK) {
779 /* skip this and previous directory */
780 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
783 if (strlen(de->d_name) > remain) {
787 strcpy(path + len, de->d_name);
788 if (stat(path, &st)) {
791 if (S_ISDIR(st.st_mode)) {
792 err = deletedir(path);
794 err = netatalk_unlink(path);
799 /* okay. the directory is empty. delete it. note: we already got rid
802 err = netatalk_rmdir(dir);
807 /* do a recursive copy. */
808 static int copydir(const struct vol *vol, char *src, char *dst)
810 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
819 /* doesn't exist or the path is too long. */
820 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
821 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
822 ((dp = opendir(src)) == NULL))
825 /* try to create the destination directory */
826 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
831 /* set things up to copy */
835 srem = sizeof(spath) - slen -1;
840 drem = sizeof(dpath) - dlen -1;
843 while ((de = readdir(dp))) {
844 /* skip this and previous directory */
845 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
848 if (strlen(de->d_name) > srem) {
852 strcpy(spath + slen, de->d_name);
854 if (stat(spath, &st) == 0) {
855 if (strlen(de->d_name) > drem) {
859 strcpy(dpath + dlen, de->d_name);
861 if (S_ISDIR(st.st_mode)) {
862 if (AFP_OK != (err = copydir(vol, spath, dpath)))
864 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
868 /* keep the same time stamp. */
869 ut.actime = ut.modtime = st.st_mtime;
875 /* keep the same time stamp. */
876 if (stat(src, &st) == 0) {
877 ut.actime = ut.modtime = st.st_mtime;
887 /* --- public functions follow --- */
889 /* NOTE: we start off with at least one node (the root directory). */
890 static struct dir *dirinsert( vol, dir )
896 if ((node = dir_insert(vol, dir)))
899 /* recolor the tree. the current node is red. */
900 dir->d_color = DIRTREE_COLOR_RED;
902 /* parent of this node has to be black. if the parent node
903 * is red, then we have a grandparent. */
904 while ((dir != vol->v_root) &&
905 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
906 /* are we on the left tree? */
907 if (dir->d_back == dir->d_back->d_back->d_left) {
908 node = dir->d_back->d_back->d_right; /* get the right node */
909 if (node->d_color == DIRTREE_COLOR_RED) {
910 /* we're red. we need to change to black. */
911 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
912 node->d_color = DIRTREE_COLOR_BLACK;
913 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
914 dir = dir->d_back->d_back; /* finished. go up. */
916 if (dir == dir->d_back->d_right) {
918 dir_leftrotate(vol, dir);
920 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
921 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
922 dir_rightrotate(vol, dir->d_back->d_back);
925 node = dir->d_back->d_back->d_left;
926 if (node->d_color == DIRTREE_COLOR_RED) {
927 /* we're red. we need to change to black. */
928 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
929 node->d_color = DIRTREE_COLOR_BLACK;
930 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
931 dir = dir->d_back->d_back; /* finished. ascend */
933 if (dir == dir->d_back->d_left) {
935 dir_rightrotate(vol, dir);
937 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
938 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
939 dir_leftrotate(vol, dir->d_back->d_back);
944 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
948 /* ---------------------------- */
950 adddir( vol, dir, path)
955 struct dir *cdir, *edir;
963 upath = path->u_name;
965 upathlen = strlen(upath);
967 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
971 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
975 if ((cdir = dirnew(name, upath)) == NULL) {
976 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
979 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)) {
980 LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
981 cdir->d_m_name_ucs2 = NULL;
986 if ((edir = dirinsert( vol, cdir ))) {
987 /* it's not possible with LASTDID
989 - someone else have moved the directory.
990 - it's a symlink inside the share.
991 - it's an ID reused, the old directory was deleted but not
992 the cnid record and the server've reused the inode for
994 for HASH (we should get ride of HASH)
995 - someone else have moved the directory.
996 - it's an ID reused as above
997 - it's a hash duplicate and we are in big trouble
999 deleted = (edir->d_m_name == NULL);
1001 dir_hash_del(vol, edir);
1003 edir->d_m_name = cdir->d_m_name;
1004 edir->d_u_name = cdir->d_u_name;
1005 edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
1008 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
1009 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
1010 hash_alloc_insert(vol->v_hash, cdir, cdir);
1013 /* the old was not in the same folder */
1015 dirchildremove(cdir->d_parent, cdir);
1018 /* parent/child directories */
1019 cdir->d_parent = dir;
1020 dirchildadd(vol, dir, cdir);
1024 /* --- public functions follow --- */
1025 /* free everything down. we don't bother to recolor as this is only
1026 * called to free the entire tree */
1027 void dirfreename(struct dir *dir)
1029 if (dir->d_u_name != dir->d_m_name) {
1030 free(dir->d_u_name);
1032 if (dir->d_m_name_ucs2)
1033 free(dir->d_m_name_ucs2);
1034 free(dir->d_m_name);
1040 if (!dir || (dir == SENTINEL))
1043 if ( dir->d_left != SENTINEL ) {
1044 dirfree( dir->d_left );
1046 if ( dir->d_right != SENTINEL ) {
1047 dirfree( dir->d_right );
1050 if (dir != SENTINEL) {
1056 /* --------------------------------------------
1057 * most of the time mac name and unix name are the same
1059 struct dir *dirnew(const char *m_name, const char *u_name)
1063 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1067 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1072 if (m_name == u_name || !strcmp(m_name, u_name)) {
1073 dir->d_u_name = dir->d_m_name;
1075 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1076 free(dir->d_m_name);
1081 dir->d_m_name_ucs2 = NULL;
1082 dir->d_left = dir->d_right = SENTINEL;
1083 dir->d_next = dir->d_prev = dir;
1087 /* ------------------ */
1088 static hash_val_t hash_fun_dir(const void *key)
1090 const struct dir *k = key;
1092 static unsigned long randbox[] = {
1093 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1094 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1095 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1096 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1099 const unsigned char *str = k->d_u_name;
1103 acc ^= randbox[(*str + acc) & 0xf];
1104 acc = (acc << 1) | (acc >> 31);
1106 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1107 acc = (acc << 2) | (acc >> 30);
1113 /* ---------------- */
1114 static int hash_comp_dir(const void *key1, const void *key2)
1116 const struct dir *k1 = key1;
1117 const struct dir *k2 = key2;
1119 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1122 /* ---------------- */
1126 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1129 /* ------------------ */
1130 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1133 movecwd failed some of dir path are not there anymore.
1134 FIXME Is it true with other errors?
1135 so we remove dir from the cache
1137 if (dir->d_did == DIRDID_ROOT_PARENT)
1139 if (afp_errno == AFPERR_ACCESS) {
1140 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1143 /* FIXME should we set these?, don't need to call stat() after:
1145 ret->st_errno = EACCES;
1147 ret->m_name = dir->d_m_name;
1148 ret->u_name = dir->d_u_name;
1151 } else if (afp_errno == AFPERR_NOOBJ) {
1152 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1155 strcpy(ret->m_name, dir->d_m_name);
1156 if (dir->d_m_name == dir->d_u_name) {
1157 ret->u_name = ret->m_name;
1160 size_t tp = strlen(ret->m_name)+1;
1162 ret->u_name = ret->m_name +tp;
1163 strcpy(ret->u_name, dir->d_u_name);
1165 /* FIXME should we set :
1167 ret->st_errno = ENOENT;
1169 dir_invalidate(vol, dir);
1172 dir_invalidate(vol, dir);
1176 /* -------------------------------------------------- */
1182 stat the file or errno
1185 curdir: filename parent directory
1191 stat the dir or errno
1195 curdir: dir parent directory
1203 curdir: dir parent directory
1210 cname( vol, dir, cpath )
1211 const struct vol *vol;
1215 struct dir *cdir, *scdir=NULL;
1216 static char path[ MAXPATHLEN + 1];
1217 static struct path ret;
1229 afp_errno = AFPERR_NOOBJ;
1230 memset(&ret, 0, sizeof(ret));
1231 switch (ret.m_type = *data) { /* path type */
1234 len = (unsigned char) *data++;
1237 if (afp_version >= 30) {
1243 if (afp_version >= 30) {
1245 memcpy(&hint, data, sizeof(hint));
1247 data += sizeof(hint);
1249 memcpy(&len16, data, sizeof(len16));
1256 /* else it's an error */
1258 afp_errno = AFPERR_PARAM;
1261 *cpath += len + size;
1266 if (movecwd( vol, dir ) < 0 ) {
1267 return invalidate(vol, dir, &ret );
1269 if (*path == '\0') {
1276 if (*data == sep ) {
1280 while (*data == sep && len > 0 ) {
1281 if ( dir->d_parent == NULL ) {
1284 dir = dir->d_parent;
1289 /* would this be faster with strlen + strncpy? */
1291 while ( *data != sep && len > 0 ) {
1293 if (p > &path[ MAXPATHLEN]) {
1294 afp_errno = AFPERR_PARAM;
1300 /* short cut bits by chopping off a trailing \0. this also
1301 makes the traversal happy w/ filenames at the end of the
1308 if ( p == path ) { /* end of the name parameter */
1312 if (afp_version >= 30) {
1317 static char temp[ MAXPATHLEN + 1];
1319 if (dir->d_did == DIRDID_ROOT_PARENT) {
1321 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1322 So we compare it with the longname from the current volume and if they match
1323 we overwrite the requested path with the utf8 volume name so that the following
1326 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1327 if (strcasecmp( path, temp) == 0)
1328 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1331 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1332 afp_errno = AFPERR_PARAM;
1338 /* check for OS X mangled filename :( */
1340 t = demangle_osx(vol, path, dir->d_did, &fileid);
1343 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1344 * flags weren't the same
1346 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1347 /* at last got our view of mac name */
1352 if (ret.u_name == NULL) {
1353 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1354 afp_errno = AFPERR_PARAM;
1360 cdir = dir->d_child;
1362 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1363 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1364 CH_UCS2, path, strlen(path), (char **)&tmpname) )
1367 if (!cdir->d_m_name_ucs2) {
1368 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1369 /* this shouldn't happen !!!! */
1373 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1376 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1379 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1385 if (dir->d_did == DIRDID_ROOT_PARENT) {
1387 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1388 must check against the volume name.
1390 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1396 cdir = dirsearch_byname(vol, dir, ret.u_name);
1400 if (cdir == NULL && scdir != NULL) {
1402 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1405 if ( cdir == NULL ) {
1407 /* if dir == curdir it always succeed,
1408 even if curdir is deleted.
1409 it's not a pb because it will fail in extenddir
1411 if ( movecwd( vol, dir ) < 0 ) {
1412 /* dir is not valid anymore
1413 we delete dir from the cache and abort.
1415 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1416 afp_errno = AFPERR_NOOBJ;
1419 if (afp_errno == AFPERR_ACCESS)
1421 dir_invalidate(vol, dir);
1424 cdir = extenddir( vol, dir, &ret );
1428 cdir = extenddir( vol, dir, &ret );
1429 } /* if (!extend) */
1431 if ( cdir == NULL ) {
1433 if ( len > 0 || !ret.u_name ) {
1445 * Move curdir to dir, with a possible chdir()
1447 int movecwd( vol, dir)
1448 const struct vol *vol;
1451 char path[MAXPATHLEN + 1];
1456 if ( dir == curdir ) {
1459 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1460 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1464 p = path + sizeof(path) - 1;
1467 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1470 /* parent directory is deleted */
1471 afp_errno = AFPERR_NOOBJ;
1475 if (p -n -1 < path) {
1476 afp_errno = AFPERR_PARAM;
1483 if ( d != curdir ) {
1484 n = strlen( vol->v_path );
1485 if (p -n -1 < path) {
1486 afp_errno = AFPERR_PARAM;
1491 memcpy( p, vol->v_path, n );
1493 if ( chdir( p ) < 0 ) {
1497 afp_errno = AFPERR_ACCESS;
1500 afp_errno = AFPERR_NOOBJ;
1510 * We can't use unix file's perm to support Apple's inherited protection modes.
1511 * If we aren't the file's owner we can't change its perms when moving it and smb
1512 * nfs,... don't even try.
1514 #define AFP_CHECK_ACCESS
1516 int check_access(char *path, int mode)
1518 #ifdef AFP_CHECK_ACCESS
1526 accessmode(p, &ma, curdir, NULL);
1527 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1529 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1535 /* --------------------- */
1536 int file_access(struct path *path, int mode)
1540 accessmode(path->u_name, &ma, curdir, &path->st);
1541 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1543 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1549 /* --------------------- */
1550 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1552 dir->offcnt = count;
1553 dir->ctime = st->st_ctime;
1554 dir->d_flags &= ~DIRF_CNID;
1557 /* ---------------------
1558 * is our cached offspring count valid?
1561 int diroffcnt(struct dir *dir, struct stat *st)
1563 return st->st_ctime == dir->ctime;
1566 /* ---------------------
1567 * is our cached also for reenumerate id?
1570 int dirreenumerate(struct dir *dir, struct stat *st)
1572 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1575 /* --------------------- */
1576 static int invisible_dots(const struct vol *vol, const char *name)
1578 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1581 /* ------------------------------
1583 (name, dir) with curdir:name == dir, from afp_enumerate
1586 int getdirparams(const struct vol *vol,
1587 u_int16_t bitmap, struct path *s_path,
1589 char *buf, int *buflen )
1593 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1594 int bit = 0, isad = 0;
1600 struct stat *st = &s_path->st;
1601 char *upath = s_path->u_name;
1603 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1604 (1 << DIRPBIT_CDATE) |
1605 (1 << DIRPBIT_MDATE) |
1606 (1 << DIRPBIT_BDATE) |
1607 (1 << DIRPBIT_FINFO)))) {
1609 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1610 if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1615 if ( dir->d_did == DIRDID_ROOT) {
1616 pdid = DIRDID_ROOT_PARENT;
1617 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1620 pdid = dir->d_parent->d_did;
1624 while ( bitmap != 0 ) {
1625 while (( bitmap & 1 ) == 0 ) {
1633 ad_getattr(&ad, &ashort);
1634 } else if (invisible_dots(vol, dir->d_u_name)) {
1635 ashort = htons(ATTRBIT_INVISIBLE);
1638 ashort |= htons(ATTRBIT_SHARED);
1639 memcpy( data, &ashort, sizeof( ashort ));
1640 data += sizeof( ashort );
1644 memcpy( data, &pdid, sizeof( pdid ));
1645 data += sizeof( pdid );
1648 case DIRPBIT_CDATE :
1649 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1650 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1651 memcpy( data, &aint, sizeof( aint ));
1652 data += sizeof( aint );
1655 case DIRPBIT_MDATE :
1656 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1657 memcpy( data, &aint, sizeof( aint ));
1658 data += sizeof( aint );
1661 case DIRPBIT_BDATE :
1662 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1663 aint = AD_DATE_START;
1664 memcpy( data, &aint, sizeof( aint ));
1665 data += sizeof( aint );
1668 case DIRPBIT_FINFO :
1670 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1671 } else { /* no appledouble */
1672 memset( data, 0, 32 );
1673 /* set default view -- this also gets done in ad_open() */
1674 ashort = htons(FINDERINFO_CLOSEDVIEW);
1675 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1677 /* dot files are by default visible */
1678 if (invisible_dots(vol, dir->d_u_name)) {
1679 ashort = htons(FINDERINFO_INVISIBLE);
1680 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1686 case DIRPBIT_LNAME :
1687 if (dir->d_m_name) /* root of parent can have a null name */
1690 memset(data, 0, sizeof(u_int16_t));
1691 data += sizeof( u_int16_t );
1694 case DIRPBIT_SNAME :
1695 memset(data, 0, sizeof(u_int16_t));
1696 data += sizeof( u_int16_t );
1700 memcpy( data, &dir->d_did, sizeof( aint ));
1701 data += sizeof( aint );
1704 case DIRPBIT_OFFCNT :
1706 /* this needs to handle current directory access rights */
1707 if (diroffcnt(dir, st)) {
1708 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1710 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1711 setdiroffcnt(dir, st, ret);
1712 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1714 ashort = htons( ashort );
1715 memcpy( data, &ashort, sizeof( ashort ));
1716 data += sizeof( ashort );
1720 aint = htonl(st->st_uid);
1721 memcpy( data, &aint, sizeof( aint ));
1722 data += sizeof( aint );
1726 aint = htonl(st->st_gid);
1727 memcpy( data, &aint, sizeof( aint ));
1728 data += sizeof( aint );
1731 case DIRPBIT_ACCESS :
1732 accessmode( upath, &ma, dir , st);
1734 *data++ = ma.ma_user;
1735 *data++ = ma.ma_world;
1736 *data++ = ma.ma_group;
1737 *data++ = ma.ma_owner;
1740 /* Client has requested the ProDOS information block.
1741 Just pass back the same basic block for all
1742 directories. <shirsch@ibm.net> */
1743 case DIRPBIT_PDINFO :
1744 if (afp_version >= 30) { /* UTF8 name */
1745 utf8 = kTextEncodingUTF8;
1746 if (dir->d_m_name) /* root of parent can have a null name */
1749 memset(data, 0, sizeof(u_int16_t));
1750 data += sizeof( u_int16_t );
1752 memcpy(data, &aint, sizeof( aint ));
1753 data += sizeof( aint );
1755 else { /* ProDOS Info Block */
1758 ashort = htons( 0x0200 );
1759 memcpy( data, &ashort, sizeof( ashort ));
1760 data += sizeof( ashort );
1761 memset( data, 0, sizeof( ashort ));
1762 data += sizeof( ashort );
1766 case DIRPBIT_UNIXPR :
1767 aint = htonl(st->st_uid);
1768 memcpy( data, &aint, sizeof( aint ));
1769 data += sizeof( aint );
1770 aint = htonl(st->st_gid);
1771 memcpy( data, &aint, sizeof( aint ));
1772 data += sizeof( aint );
1775 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1776 memcpy( data, &aint, sizeof( aint ));
1777 data += sizeof( aint );
1779 accessmode( upath, &ma, dir , st);
1781 *data++ = ma.ma_user;
1782 *data++ = ma.ma_world;
1783 *data++ = ma.ma_group;
1784 *data++ = ma.ma_owner;
1789 ad_close_metadata( &ad );
1791 return( AFPERR_BITMAP );
1797 ashort = htons( data - buf );
1798 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1799 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1801 if ( utf_nameoff ) {
1802 ashort = htons( data - buf );
1803 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1804 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1807 ad_close_metadata( &ad );
1809 *buflen = data - buf;
1813 /* ----------------------------- */
1814 int path_error(struct path *path, int error)
1816 /* - a dir with access error
1817 * - no error it's a file
1820 if (path_isadir(path))
1822 if (path->st_valid && path->st_errno)
1824 return AFPERR_BADTYPE ;
1827 /* ----------------------------- */
1828 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1830 char *ibuf, *rbuf _U_;
1831 int ibuflen _U_, *rbuflen;
1836 u_int16_t vid, bitmap;
1842 memcpy( &vid, ibuf, sizeof( vid ));
1843 ibuf += sizeof( vid );
1845 if (NULL == ( vol = getvolbyvid( vid )) ) {
1846 return( AFPERR_PARAM );
1849 if (vol->v_flags & AFPVOL_RO)
1850 return AFPERR_VLOCK;
1852 memcpy( &did, ibuf, sizeof( did ));
1853 ibuf += sizeof( int );
1855 if (NULL == ( dir = dirlookup( vol, did )) ) {
1859 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1860 bitmap = ntohs( bitmap );
1861 ibuf += sizeof( bitmap );
1863 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1864 return get_afp_errno(AFPERR_NOOBJ);
1867 if ( *path->m_name != '\0' ) {
1868 rc = path_error(path, AFPERR_NOOBJ);
1869 /* maybe we are trying to set perms back */
1870 if (rc != AFPERR_ACCESS)
1875 * If ibuf is odd, make it even.
1877 if ((u_long)ibuf & 1 ) {
1881 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1882 setvoltime(obj, vol );
1888 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1890 * assume path == '\0' eg. it's a directory in canonical form
1893 struct path Cur_Path = {
1896 ".", /* unix name */
1898 NULL,/* struct dir */
1899 0, /* stat is not set */
1902 /* ------------------ */
1903 static int set_dir_errors(struct path *path, const char *where, int err)
1908 return AFPERR_ACCESS;
1910 return AFPERR_VLOCK;
1912 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1913 return AFPERR_PARAM;
1916 /* ------------------ */
1917 int setdirparams(const struct vol *vol,
1918 struct path *path, u_int16_t d_bitmap, char *buf )
1930 u_int16_t ashort, bshort;
1932 int change_mdate = 0;
1933 int change_parent_mdate = 0;
1935 u_int16_t bitmap = d_bitmap;
1936 u_char finder_buf[32];
1939 u_int16_t upriv_bit = 0;
1942 upath = path->u_name;
1944 while ( bitmap != 0 ) {
1945 while (( bitmap & 1 ) == 0 ) {
1953 memcpy( &ashort, buf, sizeof( ashort ));
1954 buf += sizeof( ashort );
1956 case DIRPBIT_CDATE :
1958 memcpy(&cdate, buf, sizeof(cdate));
1959 buf += sizeof( cdate );
1961 case DIRPBIT_MDATE :
1962 memcpy(&newdate, buf, sizeof(newdate));
1963 buf += sizeof( newdate );
1965 case DIRPBIT_BDATE :
1967 memcpy(&bdate, buf, sizeof(bdate));
1968 buf += sizeof( bdate );
1970 case DIRPBIT_FINFO :
1972 memcpy( finder_buf, buf, 32 );
1975 case DIRPBIT_UID : /* What kind of loser mounts as root? */
1976 change_parent_mdate = 1;
1977 memcpy( &owner, buf, sizeof(owner));
1978 buf += sizeof( owner );
1981 change_parent_mdate = 1;
1982 memcpy( &group, buf, sizeof( group ));
1983 buf += sizeof( group );
1985 case DIRPBIT_ACCESS :
1987 change_parent_mdate = 1;
1988 ma.ma_user = *buf++;
1989 ma.ma_world = *buf++;
1990 ma.ma_group = *buf++;
1991 ma.ma_owner = *buf++;
1992 mpriv = mtoumode( &ma ) | vol->v_dperm;
1993 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
1994 err = set_dir_errors(path, "setdirmode", errno);
1998 /* Ignore what the client thinks we should do to the
1999 ProDOS information block. Skip over the data and
2000 report nothing amiss. <shirsch@ibm.net> */
2001 case DIRPBIT_PDINFO :
2002 if (afp_version < 30) {
2006 err = AFPERR_BITMAP;
2010 case DIRPBIT_UNIXPR :
2011 if (vol_unix_priv(vol)) {
2012 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2013 buf += sizeof( owner );
2014 memcpy( &group, buf, sizeof( group ));
2015 buf += sizeof( group );
2018 change_parent_mdate = 1;
2019 memcpy( &upriv, buf, sizeof( upriv ));
2020 buf += sizeof( upriv );
2021 upriv = ntohl (upriv) | vol->v_dperm;
2022 if (dir_rx_set(upriv)) {
2023 /* maybe we are trying to set perms back */
2024 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2026 err = set_dir_errors(path, "setdirunixmode", errno);
2037 err = AFPERR_BITMAP;
2045 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2047 if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2049 * Check to see what we're trying to set. If it's anything
2050 * but ACCESS, UID, or GID, give an error. If it's any of those
2051 * three, we don't need the ad to be open, so just continue.
2053 * note: we also don't need to worry about mdate. also, be quiet
2054 * if we're using the noadouble option.
2056 if (!vol_noadouble(vol) && (d_bitmap &
2057 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2058 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2059 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2060 return AFPERR_ACCESS;
2066 * Check to see if a create was necessary. If it was, we'll want
2067 * to set our name, etc.
2069 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2070 ad_setname(&ad, curdir->d_m_name);
2076 while ( bitmap != 0 ) {
2077 while (( bitmap & 1 ) == 0 ) {
2085 ad_getattr(&ad, &bshort);
2086 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2087 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2088 change_parent_mdate = 1;
2089 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2090 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2094 ad_setattr(&ad, bshort);
2097 case DIRPBIT_CDATE :
2099 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2102 case DIRPBIT_MDATE :
2104 case DIRPBIT_BDATE :
2106 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2109 case DIRPBIT_FINFO :
2111 /* Fixes #2802236 */
2112 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2113 *fflags &= htons(~FINDERINFO_ISHARED);
2115 if ( dir->d_did == DIRDID_ROOT ) {
2117 * Alright, we admit it, this is *really* sick!
2118 * The 4 bytes that we don't copy, when we're dealing
2119 * with the root of a volume, are the directory's
2120 * location information. This eliminates that annoying
2121 * behavior one sees when mounting above another mount
2124 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2125 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2127 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2131 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2132 if ( (dir->d_did == DIRDID_ROOT) &&
2133 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2134 err = set_dir_errors(path, "setdeskowner", errno);
2135 if (isad && err == AFPERR_PARAM) {
2136 err = AFP_OK; /* ???*/
2139 goto setdirparam_done;
2142 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2143 err = set_dir_errors(path, "setdirowner", errno);
2144 goto setdirparam_done;
2148 if (dir->d_did == DIRDID_ROOT)
2149 setdeskowner( -1, ntohl(group) );
2150 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2151 err = set_dir_errors(path, "setdirowner", errno);
2152 goto setdirparam_done;
2155 case DIRPBIT_ACCESS :
2156 if (dir->d_did == DIRDID_ROOT) {
2158 if (!dir_rx_set(mpriv)) {
2159 /* we can't remove read and search for owner on volume root */
2160 err = AFPERR_ACCESS;
2161 goto setdirparam_done;
2165 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2166 err = set_dir_errors(path, "setdirmode", errno);
2167 goto setdirparam_done;
2170 case DIRPBIT_PDINFO :
2171 if (afp_version >= 30) {
2172 err = AFPERR_BITMAP;
2173 goto setdirparam_done;
2176 case DIRPBIT_UNIXPR :
2177 if (vol_unix_priv(vol)) {
2178 if (dir->d_did == DIRDID_ROOT) {
2179 if (!dir_rx_set(upriv)) {
2180 /* we can't remove read and search for owner on volume root */
2181 err = AFPERR_ACCESS;
2182 goto setdirparam_done;
2184 setdeskowner( -1, ntohl(group) );
2185 setdeskmode( upriv );
2187 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2188 err = set_dir_errors(path, "setdirowner", errno);
2189 goto setdirparam_done;
2192 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2193 err = set_dir_errors(path, "setdirunixmode", errno);
2194 goto setdirparam_done;
2198 err = AFPERR_BITMAP;
2199 goto setdirparam_done;
2203 err = AFPERR_BITMAP;
2204 goto setdirparam_done;
2213 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2214 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2218 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2219 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2224 if (path->st_valid && !path->st_errno) {
2225 struct stat *st = &path->st;
2227 if (dir && dir->d_parent) {
2228 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2232 ad_close_metadata( &ad);
2235 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2236 && gettimeofday(&tv, NULL) == 0) {
2237 if (!movecwd(vol, dir->d_parent)) {
2238 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2239 /* be careful with bitmap because now dir is null */
2240 bitmap = 1<<DIRPBIT_MDATE;
2241 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2242 /* should we reset curdir ?*/
2249 int afp_syncdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2251 char *ibuf, *rbuf _U_;
2252 int ibuflen _U_, *rbuflen;
2266 memcpy( &vid, ibuf, sizeof( vid ));
2267 ibuf += sizeof( vid );
2268 if (NULL == (vol = getvolbyvid( vid )) ) {
2269 return( AFPERR_PARAM );
2272 memcpy( &did, ibuf, sizeof( did ));
2273 ibuf += sizeof( did );
2277 * if it's CNID 2 our only choice to meet the specs is call sync.
2278 * For any other CNID just sync that dir. To my knowledge the
2279 * intended use of FPSyncDir is to sync the volume so all we're
2280 * ever going to see here is probably CNID 2. Anyway, we' prepared.
2283 if ( ntohl(did) == 2 ) {
2286 if (NULL == ( dir = dirlookup( vol, did )) ) {
2287 return afp_errno; /* was AFPERR_NOOBJ */
2290 if (movecwd( vol, dir ) < 0 )
2291 return ( AFPERR_NOOBJ );
2294 * Assuming only OSens that have dirfd also may require fsyncing directories
2295 * in order to flush metadata e.g. Linux.
2299 if (NULL == ( dp = opendir( "." )) ) {
2302 return( AFPERR_NOOBJ );
2304 return( AFPERR_ACCESS );
2306 return( AFPERR_PARAM );
2310 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2313 if ( fsync ( dfd ) < 0 )
2314 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2315 dir->d_u_name, strerror(errno) );
2316 closedir(dp); /* closes dfd too */
2319 if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2322 return( AFPERR_NOOBJ );
2324 return( AFPERR_ACCESS );
2326 return( AFPERR_PARAM );
2330 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2331 vol->ad_path(".", ADFLAGS_DIR) );
2333 if ( fsync(dfd) < 0 )
2334 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2335 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2342 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2345 int ibuflen _U_, *rbuflen;
2351 struct path *s_path;
2359 memcpy( &vid, ibuf, sizeof( vid ));
2360 ibuf += sizeof( vid );
2361 if (NULL == ( vol = getvolbyvid( vid )) ) {
2362 return( AFPERR_PARAM );
2365 if (vol->v_flags & AFPVOL_RO)
2366 return AFPERR_VLOCK;
2368 memcpy( &did, ibuf, sizeof( did ));
2369 ibuf += sizeof( did );
2370 if (NULL == ( dir = dirlookup( vol, did )) ) {
2371 return afp_errno; /* was AFPERR_NOOBJ */
2373 /* for concurrent access we need to be sure we are not in the
2374 * folder we want to create...
2378 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2379 return get_afp_errno(AFPERR_PARAM);
2381 /* cname was able to move curdir to it! */
2382 if (*s_path->m_name == '\0')
2383 return AFPERR_EXIST;
2385 upath = s_path->u_name;
2387 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2391 if (of_stat(s_path) < 0) {
2395 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2399 if ( movecwd( vol, dir ) < 0 ) {
2400 return( AFPERR_PARAM );
2403 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2404 if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2405 if (vol_noadouble(vol))
2406 goto createdir_done;
2407 return( AFPERR_ACCESS );
2409 ad_setname(&ad, s_path->m_name);
2410 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2413 ad_close_metadata( &ad);
2416 #ifdef HAVE_NFSv4_ACLS
2417 /* FIXME: are we really inside the created dir? */
2418 addir_inherit_acl(vol);
2421 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2422 *rbuflen = sizeof( u_int32_t );
2423 setvoltime(obj, vol );
2428 * dst new unix filename (not a pathname)
2429 * newname new mac name
2433 int renamedir(vol, src, dst, dir, newparent, newname)
2434 const struct vol *vol;
2435 char *src, *dst, *newname;
2436 struct dir *dir, *newparent;
2443 /* existence check moved to afp_moveandrename */
2444 if ( unix_rename( src, dst ) < 0 ) {
2447 return( AFPERR_NOOBJ );
2449 return( AFPERR_ACCESS );
2451 return AFPERR_VLOCK;
2453 /* tried to move directory into a subdirectory of itself */
2454 return AFPERR_CANTMOVE;
2456 /* this needs to copy and delete. bleah. that means we have
2457 * to deal with entire directory hierarchies. */
2458 if ((err = copydir(vol, src, dst)) < 0) {
2462 if ((err = deletedir(src)) < 0)
2466 return( AFPERR_PARAM );
2470 vol->vfs->rf_renamedir(vol, src, dst);
2472 len = strlen( newname );
2473 /* rename() succeeded so we need to update our tree even if we can't open
2477 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2479 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2480 ad_setname(&ad, newname);
2482 ad_close_metadata( &ad);
2485 dir_hash_del(vol, dir);
2486 if (dir->d_m_name == dir->d_u_name)
2487 dir->d_u_name = NULL;
2489 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2490 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2491 /* FIXME : fatal ? */
2494 dir->d_m_name = buf;
2495 strcpy( dir->d_m_name, newname );
2497 if (newname == dst) {
2498 free(dir->d_u_name);
2499 dir->d_u_name = dir->d_m_name;
2502 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2503 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2506 dir->d_u_name = buf;
2507 strcpy( dir->d_u_name, dst );
2510 if (dir->d_m_name_ucs2)
2511 free(dir->d_m_name_ucs2);
2513 dir->d_m_name_ucs2 = NULL;
2514 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))
2515 dir->d_m_name_ucs2 = NULL;
2517 if (( parent = dir->d_parent ) == NULL ) {
2520 if ( parent == newparent ) {
2521 hash_alloc_insert(vol->v_hash, dir, dir);
2525 /* detach from old parent and add to new one. */
2526 dirchildremove(parent, dir);
2527 dir->d_parent = newparent;
2528 dirchildadd(vol, newparent, dir);
2532 /* delete an empty directory */
2533 int deletecurdir( vol)
2534 const struct vol *vol;
2544 if ( curdir->d_parent == NULL ) {
2545 return( AFPERR_ACCESS );
2550 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2551 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2553 ad_getattr(&ad, &ashort);
2554 ad_close( &ad, ADFLAGS_HF );
2555 if ((ashort & htons(ATTRBIT_NODELETE))) {
2556 return AFPERR_OLOCK;
2559 err = vol->vfs->rf_deletecurdir(vol);
2564 /* now get rid of dangling symlinks */
2565 if ((dp = opendir("."))) {
2566 while ((de = readdir(dp))) {
2567 /* skip this and previous directory */
2568 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2571 /* bail if it's not a symlink */
2572 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2574 return AFPERR_DIRNEMPT;
2577 if ((err = netatalk_unlink(de->d_name))) {
2584 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2589 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2590 dirchildremove(curdir, fdir);
2591 cnid_delete(vol->v_cdb, fdir->d_did);
2592 dir_remove( vol, fdir );
2597 /* inode is used as key for cnid.
2598 * Close the descriptor only after cnid_delete
2606 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2609 int ibuflen _U_, *rbuflen;
2620 sfunc = (unsigned char) *ibuf++;
2624 if (sfunc >= 3 && sfunc <= 6) {
2625 if (afp_version < 30) {
2626 return( AFPERR_PARAM );
2633 case 3 :/* unicode */
2634 memcpy( &id, ibuf, sizeof( id ));
2637 if (( pw = getpwuid( id )) == NULL ) {
2638 return( AFPERR_NOITEM );
2640 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2641 pw->pw_name, strlen(pw->pw_name), &name);
2648 case 4 : /* unicode */
2649 memcpy( &id, ibuf, sizeof( id ));
2652 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2653 return( AFPERR_NOITEM );
2655 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2656 gr->gr_name, strlen(gr->gr_name), &name);
2662 #ifdef HAVE_NFSv4_ACLS
2663 case 5 : /* UUID -> username */
2664 case 6 : /* UUID -> groupname */
2665 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2666 return AFPERR_PARAM;
2667 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2668 len = getnamefromuuid( ibuf, &name, &type);
2669 if (len != 0) /* its a error code, not len */
2670 return AFPERR_NOITEM;
2671 if (type == UUID_USER) {
2672 if (( pw = getpwnam( name )) == NULL )
2673 return( AFPERR_NOITEM );
2674 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2675 id = htonl(UUID_USER);
2676 memcpy( rbuf, &id, sizeof( id ));
2677 id = htonl( pw->pw_uid);
2678 rbuf += sizeof( id );
2679 memcpy( rbuf, &id, sizeof( id ));
2680 rbuf += sizeof( id );
2681 *rbuflen = 2 * sizeof( id );
2682 } else { /* type == UUID_GROUP */
2683 if (( gr = getgrnam( name )) == NULL )
2684 return( AFPERR_NOITEM );
2685 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2686 id = htonl(UUID_GROUP);
2687 memcpy( rbuf, &id, sizeof( id ));
2688 rbuf += sizeof( id );
2689 id = htonl( gr->gr_gid);
2690 memcpy( rbuf, &id, sizeof( id ));
2691 rbuf += sizeof( id );
2692 *rbuflen = 2 * sizeof( id );
2697 return( AFPERR_PARAM );
2701 len = strlen( name );
2704 u_int16_t tp = htons(len);
2705 memcpy(rbuf, &tp, sizeof(tp));
2714 memcpy( rbuf, name, len );
2722 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2725 int ibuflen _U_, *rbuflen;
2734 sfunc = (unsigned char) *ibuf++;
2736 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2739 case 2 : /* unicode */
2740 if (afp_version < 30) {
2741 return( AFPERR_PARAM );
2743 memcpy(&ulen, ibuf, sizeof(ulen));
2746 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2750 len = (unsigned char) *ibuf++;
2752 #ifdef HAVE_NFSv4_ACLS
2753 case 5 : /* username -> UUID */
2754 case 6 : /* groupname -> UUID */
2755 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2756 return AFPERR_PARAM;
2757 memcpy(&ulen, ibuf, sizeof(ulen));
2763 return( AFPERR_PARAM );
2769 return AFPERR_PARAM;
2772 case 1 : /* unicode */
2774 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2775 return( AFPERR_NOITEM );
2779 memcpy( rbuf, &id, sizeof( id ));
2780 *rbuflen = sizeof( id );
2783 case 2 : /* unicode */
2785 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2786 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2787 return( AFPERR_NOITEM );
2790 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2792 memcpy( rbuf, &id, sizeof( id ));
2793 *rbuflen = sizeof( id );
2795 #ifdef HAVE_NFSv4_ACLS
2796 case 5 : /* username -> UUID */
2797 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2798 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2799 return AFPERR_NOITEM;
2800 *rbuflen = UUID_BINSIZE;
2802 case 6 : /* groupname -> UUID */
2803 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2804 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2805 return AFPERR_NOITEM;
2806 *rbuflen = UUID_BINSIZE;
2814 /* ------------------------------------
2815 variable DID support
2817 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2819 char *ibuf _U_, *rbuf _U_;
2820 int ibuflen _U_, *rbuflen;
2831 /* do nothing as dids are static for the life of the process. */
2835 memcpy(&vid, ibuf, sizeof( vid ));
2836 ibuf += sizeof( vid );
2837 if (( vol = getvolbyvid( vid )) == NULL ) {
2838 return( AFPERR_PARAM );
2841 memcpy( &did, ibuf, sizeof( did ));
2842 ibuf += sizeof( did );
2843 if (( dir = dirlookup( vol, did )) == NULL ) {
2844 return( AFPERR_PARAM );
2847 /* dir_remove -- deletedid */
2853 /* did creation gets done automatically
2854 * there's a pb again with case but move it to cname
2856 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2859 int ibuflen _U_, *rbuflen;
2862 struct dir *parentdir;
2870 memcpy(&vid, ibuf, sizeof(vid));
2871 ibuf += sizeof( vid );
2873 if (NULL == ( vol = getvolbyvid( vid )) ) {
2874 return( AFPERR_PARAM );
2877 memcpy(&did, ibuf, sizeof(did));
2878 ibuf += sizeof(did);
2880 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2884 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2885 return get_afp_errno(AFPERR_PARAM);
2888 if ( *path->m_name != '\0' ) {
2889 return path_error(path, AFPERR_NOOBJ);
2892 if ( !path->st_valid && of_stat(path ) < 0 ) {
2893 return( AFPERR_NOOBJ );
2895 if ( path->st_errno ) {
2896 return( AFPERR_NOOBJ );
2899 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2900 *rbuflen = sizeof(curdir->d_did);