2 * $Id: directory.c,v 1.103 2009-09-11 09:14:16 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>
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 /* ------------------- */
136 int path_isadir(struct path *o_path)
138 return o_path->d_dir != NULL;
140 return o_path->m_name == '\0' || /* we are in a it */
141 !o_path->st_valid || /* in cache but we can't chdir in it */
142 (!o_path->st_errno && S_ISDIR(o_path->st.st_mode)); /* not in cache an can't chdir */
147 int get_afp_errno(const int param)
149 if (afp_errno != AFPERR_DID1)
154 /* ------------------- */
156 dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
158 struct dir *dir = NULL;
160 if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
166 hn = hash_lookup(vol->v_hash, &key);
174 /* -----------------------------------------
175 * if did is not in the cache resolve it with cnid
178 * OSX call it with bogus id, ie file ID not folder ID,
179 * and we are really bad in this case.
182 dirlookup( vol, did )
183 const struct vol *vol;
189 static char path[MAXPATHLEN + 1];
192 static char buffer[12 + MAXPATHLEN + 1];
193 int buflen = 12 + MAXPATHLEN + 1;
198 ret = dirsearch(vol, did);
199 if (ret != NULL || afp_errno == AFPERR_PARAM)
202 utf8 = utf8_encoding();
203 maxpath = (utf8)?MAXPATHLEN -7:255;
205 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
206 afp_errno = AFPERR_NOOBJ;
209 ptr = path + MAXPATHLEN;
210 if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
211 afp_errno = AFPERR_NOOBJ;
215 pathlen = len; /* no 0 in the last part */
217 strcpy(ptr - len, mpath);
220 ret = dirsearch(vol,id);
225 if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
227 NULL == (mpath = utompath(vol, upath, cnid, utf8))
229 afp_errno = AFPERR_NOOBJ;
233 len = strlen(mpath) + 1;
235 if (pathlen > maxpath) {
236 afp_errno = AFPERR_PARAM;
239 strcpy(ptr - len, mpath);
243 /* fill the cache, another place where we know about the path type */
249 temp16 = htons(pathlen);
250 memcpy(ptr, &temp16, sizeof(temp16));
252 temp = htonl(kTextEncodingUTF8);
254 memcpy(ptr, &temp, sizeof(temp));
260 *ptr = (unsigned char)pathlen;
264 /* cname is not efficient */
265 if (cname( vol, ret, &ptr ) == NULL )
268 return dirsearch(vol, did);
271 /* child addition/removal */
272 static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
277 b->d_next = a->d_child;
278 b->d_prev = b->d_next->d_prev;
279 b->d_next->d_prev = b;
280 b->d_prev->d_next = b;
282 if (!hash_alloc_insert(vol->v_hash, b, b)) {
283 LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
287 static void dirchildremove(struct dir *a,struct dir *b)
290 a->d_child = (b == b->d_next) ? NULL : b->d_next;
291 b->d_next->d_prev = b->d_prev;
292 b->d_prev->d_next = b->d_next;
293 b->d_next = b->d_prev = b;
296 /* --------------------------- */
297 /* rotate the tree to the left */
298 static void dir_leftrotate(vol, dir)
302 struct dir *right = dir->d_right;
304 /* whee. move the right's left tree into dir's right tree */
305 dir->d_right = right->d_left;
306 if (right->d_left != SENTINEL)
307 right->d_left->d_back = dir;
309 if (right != SENTINEL) {
310 right->d_back = dir->d_back;
314 if (!dir->d_back) /* no parent. move the right tree to the top. */
316 else if (dir == dir->d_back->d_left) /* we were on the left */
317 dir->d_back->d_left = right;
319 dir->d_back->d_right = right; /* we were on the right */
321 /* re-insert dir on the left tree */
328 /* rotate the tree to the right */
329 static void dir_rightrotate(vol, dir)
333 struct dir *left = dir->d_left;
335 /* whee. move the left's right tree into dir's left tree */
336 dir->d_left = left->d_right;
337 if (left->d_right != SENTINEL)
338 left->d_right->d_back = dir;
340 if (left != SENTINEL) {
341 left->d_back = dir->d_back;
345 if (!dir->d_back) /* no parent. move the left tree to the top. */
347 else if (dir == dir->d_back->d_right) /* we were on the right */
348 dir->d_back->d_right = left;
350 dir->d_back->d_left = left; /* we were on the left */
352 /* re-insert dir on the right tree */
358 /* recolor after a removal */
359 static struct dir *dir_rmrecolor(vol, dir)
365 while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
366 /* are we on the left tree? */
367 if (dir == dir->d_back->d_left) {
368 leaf = dir->d_back->d_right; /* get right side */
369 if (leaf->d_color == DIRTREE_COLOR_RED) {
370 /* we're red. we need to change to black. */
371 leaf->d_color = DIRTREE_COLOR_BLACK;
372 dir->d_back->d_color = DIRTREE_COLOR_RED;
373 dir_leftrotate(vol, dir->d_back);
374 leaf = dir->d_back->d_right;
377 /* right leaf has black end nodes */
378 if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
379 (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
380 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
381 dir = dir->d_back; /* ascend */
383 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
384 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
385 leaf->d_color = DIRTREE_COLOR_RED;
386 dir_rightrotate(vol, leaf);
387 leaf = dir->d_back->d_right;
389 leaf->d_color = dir->d_back->d_color;
390 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
391 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
392 dir_leftrotate(vol, dir->d_back);
395 } else { /* right tree */
396 leaf = dir->d_back->d_left; /* left tree */
397 if (leaf->d_color == DIRTREE_COLOR_RED) {
398 leaf->d_color = DIRTREE_COLOR_BLACK;
399 dir->d_back->d_color = DIRTREE_COLOR_RED;
400 dir_rightrotate(vol, dir->d_back);
401 leaf = dir->d_back->d_left;
404 /* left leaf has black end nodes */
405 if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
406 (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
407 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
408 dir = dir->d_back; /* ascend */
410 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
411 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
412 leaf->d_color = DIRTREE_COLOR_RED;
413 dir_leftrotate(vol, leaf);
414 leaf = dir->d_back->d_left;
416 leaf->d_color = dir->d_back->d_color;
417 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
418 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
419 dir_rightrotate(vol, dir->d_back);
424 dir->d_color = DIRTREE_COLOR_BLACK;
430 /* --------------------- */
431 static void dir_hash_del(const struct vol *vol, struct dir *dir)
435 hn = hash_lookup(vol->v_hash, dir);
437 LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
440 hash_delete(vol->v_hash, hn);
444 /* remove the node from the tree. this is just like insertion, but
445 * different. actually, it has to worry about a bunch of things that
446 * insertion doesn't care about. */
448 static void dir_remove( const struct vol *vol _U_, struct dir *dir)
451 struct ofork *of, *last;
452 struct dir *node, *leaf;
453 #endif /* REMOVE_NODES */
455 if (!dir || (dir == SENTINEL))
458 /* i'm not sure if it really helps to delete stuff. */
459 dir_hash_del(vol, dir);
462 dir->d_m_name = NULL;
463 dir->d_u_name = NULL;
464 dir->d_m_name_ucs2 = NULL;
465 #else /* ! REMOVE_NODES */
467 /* go searching for a node with at most one child */
468 if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
472 while (node->d_left != SENTINEL)
477 leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
480 leaf->d_back = node->d_back;
483 } else if (node == node->d_back->d_left) { /* left tree */
484 node->d_back->d_left = leaf;
486 node->d_back->d_right = leaf;
489 /* we want to free node, but we also want to free the data in dir.
490 * currently, that's d_name and the directory traversal bits.
491 * we just copy the necessary bits and then fix up all the
492 * various pointers to the directory. needless to say, there are
493 * a bunch of places that store the directory struct. */
495 struct dir save, *tmp;
497 memcpy(&save, dir, sizeof(save));
498 memcpy(dir, node, sizeof(struct dir));
500 /* restore the red-black bits */
501 dir->d_left = save.d_left;
502 dir->d_right = save.d_right;
503 dir->d_back = save.d_back;
504 dir->d_color = save.d_color;
506 if (node == vol->v_dir) {/* we may need to fix up this pointer */
508 rootpar.d_child = vol->v_dir;
510 /* if we aren't the root directory, we have parents and
511 * siblings to worry about */
512 if (dir->d_parent->d_child == node)
513 dir->d_parent->d_child = dir;
514 dir->d_next->d_prev = dir;
515 dir->d_prev->d_next = dir;
518 /* fix up children. */
522 tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
525 if (node == curdir) /* another pointer to fixup */
528 /* we also need to fix up oforks. bleah */
529 if ((of = dir->d_ofork)) {
530 last = of->of_d_prev;
533 of = (last == of) ? NULL : of->of_d_next;
537 /* set the node's d_name */
538 node->d_m_name = save.d_m_name;
539 node->d_u_name = save.d_u_name;
540 node->d_m_name_ucs2 = save.d_m_name_ucs2;
543 if (node->d_color == DIRTREE_COLOR_BLACK)
544 dir_rmrecolor(vol, leaf);
546 if (node->d_m_name_ucs2)
547 free(node->d_u_name_ucs2);
548 if (node->d_u_name != node->d_m_name) {
549 free(node->d_u_name);
551 free(node->d_m_name);
553 #endif /* ! REMOVE_NODES */
556 /* ---------------------------------------
557 * remove the node and its childs from the tree
559 * FIXME what about opened forks with refs to it?
560 * it's an afp specs violation because you can't delete
561 * an opened forks. Now afpd doesn't care about forks opened by other
562 * process. It's fixable within afpd if fnctl_lock, doable with smb and
563 * next to impossible for nfs and local filesystem access.
565 static void dir_invalidate( vol, dir )
566 const struct vol *vol;
570 /* v_root can't be deleted */
571 if (movecwd(vol, vol->v_root) < 0) {
572 LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
576 dirchildremove(dir->d_parent, dir);
577 dir_remove( vol, dir );
580 /* ------------------------------------ */
581 static struct dir *dir_insert(vol, dir)
582 const struct vol *vol;
588 while (pdir->d_did != dir->d_did ) {
589 if ( pdir->d_did > dir->d_did ) {
590 if ( pdir->d_left == SENTINEL ) {
597 if ( pdir->d_right == SENTINEL ) {
602 pdir = pdir->d_right;
608 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
611 caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
616 static u_int32_t did = 0;
617 static char cname[MAXPATHLEN];
618 static char lname[MAXPATHLEN];
619 ucs2_t u2_path[MAXPATHLEN];
620 ucs2_t u2_dename[MAXPATHLEN];
621 char *tmp, *savepath;
623 if (!(vol->v_flags & AFPVOL_CASEINSEN))
626 if (veto_file(ENUMVETO, path->u_name))
629 savepath = path->u_name;
631 /* very simple cache */
632 if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
633 path->u_name = cname;
635 if (of_stat( path ) == 0 ) {
638 /* something changed, we cannot stat ... */
642 if (NULL == ( dp = opendir( "." )) ) {
643 LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
648 /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
649 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, strlen(path->u_name), u2_path, sizeof(u2_path)) )
650 LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
652 /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
654 for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
655 if (NULL == check_dirent(vol, de->d_name))
658 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, strlen(de->d_name), u2_dename, sizeof(u2_dename)) )
661 if (strcasecmp_w( u2_path, u2_dename) == 0) {
663 strlcpy(cname, de->d_name, sizeof(cname));
664 path->u_name = cname;
666 if (of_stat( path ) == 0 ) {
667 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
668 strlcpy(lname, tmp, sizeof(lname));
681 /* invalidate cache */
684 path->u_name = savepath;
686 /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
692 * attempt to extend the current dir. tree to include path
693 * as a side-effect, movecwd to that point and return the new dir
696 extenddir( vol, dir, path )
703 if ( path->u_name == NULL) {
704 afp_errno = AFPERR_PARAM;
708 if (check_name(vol, path->u_name)) {
709 /* the name is illegal */
710 LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
712 afp_errno = AFPERR_PARAM;
716 if (of_stat( path ) != 0 ) {
717 if (!(vol->v_flags & AFPVOL_CASEINSEN))
719 else if(caseenumerate(vol, path, dir) != 0)
723 if (!S_ISDIR(path->st.st_mode)) {
727 /* mac name is always with the right encoding (from cname()) */
728 if (( dir = adddir( vol, dir, path)) == NULL ) {
733 if ( movecwd( vol, dir ) < 0 ) {
740 /* -------------------
741 system rmdir with afp error code.
742 ENOENT is not an error.
744 int netatalk_rmdir(const char *name)
746 if (rmdir(name) < 0) {
751 return AFPERR_DIRNEMPT;
754 return AFPERR_ACCESS;
764 /* -------------------------
765 appledouble mkdir afp error code.
767 static int netatalk_mkdir(const char *name)
769 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
772 return( AFPERR_NOOBJ );
774 return( AFPERR_VLOCK );
777 return( AFPERR_ACCESS );
779 return( AFPERR_EXIST );
782 return( AFPERR_DFULL );
784 return( AFPERR_PARAM );
790 /* -------------------
791 system unlink with afp error code.
792 ENOENT is not an error.
794 int netatalk_unlink(const char *name)
796 if (unlink(name) < 0) {
804 return AFPERR_ACCESS;
812 /* ------------------- */
813 static int deletedir(char *dir)
815 char path[MAXPATHLEN + 1];
823 if ((len = strlen(dir)) +2 > sizeof(path))
827 if ((dp = opendir(dir)) == NULL)
833 remain = sizeof(path) -len -1;
834 while ((de = readdir(dp)) && err == AFP_OK) {
835 /* skip this and previous directory */
836 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
839 if (strlen(de->d_name) > remain) {
843 strcpy(path + len, de->d_name);
844 if (stat(path, &st)) {
847 if (S_ISDIR(st.st_mode)) {
848 err = deletedir(path);
850 err = netatalk_unlink(path);
855 /* okay. the directory is empty. delete it. note: we already got rid
858 err = netatalk_rmdir(dir);
863 /* do a recursive copy. */
864 static int copydir(const struct vol *vol, char *src, char *dst)
866 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
875 /* doesn't exist or the path is too long. */
876 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
877 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
878 ((dp = opendir(src)) == NULL))
881 /* try to create the destination directory */
882 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
887 /* set things up to copy */
891 srem = sizeof(spath) - slen -1;
896 drem = sizeof(dpath) - dlen -1;
899 while ((de = readdir(dp))) {
900 /* skip this and previous directory */
901 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
904 if (strlen(de->d_name) > srem) {
908 strcpy(spath + slen, de->d_name);
910 if (stat(spath, &st) == 0) {
911 if (strlen(de->d_name) > drem) {
915 strcpy(dpath + dlen, de->d_name);
917 if (S_ISDIR(st.st_mode)) {
918 if (AFP_OK != (err = copydir(vol, spath, dpath)))
920 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
924 /* keep the same time stamp. */
925 ut.actime = ut.modtime = st.st_mtime;
931 /* keep the same time stamp. */
932 if (stat(src, &st) == 0) {
933 ut.actime = ut.modtime = st.st_mtime;
943 /* --- public functions follow --- */
945 /* NOTE: we start off with at least one node (the root directory). */
946 static struct dir *dirinsert( vol, dir )
952 if ((node = dir_insert(vol, dir)))
955 /* recolor the tree. the current node is red. */
956 dir->d_color = DIRTREE_COLOR_RED;
958 /* parent of this node has to be black. if the parent node
959 * is red, then we have a grandparent. */
960 while ((dir != vol->v_root) &&
961 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
962 /* are we on the left tree? */
963 if (dir->d_back == dir->d_back->d_back->d_left) {
964 node = dir->d_back->d_back->d_right; /* get the right node */
965 if (node->d_color == DIRTREE_COLOR_RED) {
966 /* we're red. we need to change to black. */
967 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
968 node->d_color = DIRTREE_COLOR_BLACK;
969 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
970 dir = dir->d_back->d_back; /* finished. go up. */
972 if (dir == dir->d_back->d_right) {
974 dir_leftrotate(vol, dir);
976 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
977 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
978 dir_rightrotate(vol, dir->d_back->d_back);
981 node = dir->d_back->d_back->d_left;
982 if (node->d_color == DIRTREE_COLOR_RED) {
983 /* we're red. we need to change to black. */
984 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
985 node->d_color = DIRTREE_COLOR_BLACK;
986 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
987 dir = dir->d_back->d_back; /* finished. ascend */
989 if (dir == dir->d_back->d_left) {
991 dir_rightrotate(vol, dir);
993 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
994 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
995 dir_leftrotate(vol, dir->d_back->d_back);
1000 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
1004 /* ---------------------------- */
1006 adddir( vol, dir, path)
1011 struct dir *cdir, *edir;
1019 upath = path->u_name;
1021 upathlen = strlen(upath);
1023 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
1027 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
1030 name = path->m_name;
1031 if ((cdir = dirnew(name, upath)) == NULL) {
1032 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
1035 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)) {
1036 LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
1037 cdir->d_m_name_ucs2 = NULL;
1042 if ((edir = dirinsert( vol, cdir ))) {
1043 /* it's not possible with LASTDID
1045 - someone else have moved the directory.
1046 - it's a symlink inside the share.
1047 - it's an ID reused, the old directory was deleted but not
1048 the cnid record and the server've reused the inode for
1050 for HASH (we should get ride of HASH)
1051 - someone else have moved the directory.
1052 - it's an ID reused as above
1053 - it's a hash duplicate and we are in big trouble
1055 deleted = (edir->d_m_name == NULL);
1057 dir_hash_del(vol, edir);
1059 edir->d_m_name = cdir->d_m_name;
1060 edir->d_u_name = cdir->d_u_name;
1061 edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
1064 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
1065 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
1066 hash_alloc_insert(vol->v_hash, cdir, cdir);
1069 /* the old was not in the same folder */
1071 dirchildremove(cdir->d_parent, cdir);
1074 /* parent/child directories */
1075 cdir->d_parent = dir;
1076 dirchildadd(vol, dir, cdir);
1080 /* --- public functions follow --- */
1081 /* free everything down. we don't bother to recolor as this is only
1082 * called to free the entire tree */
1083 void dirfreename(struct dir *dir)
1085 if (dir->d_u_name != dir->d_m_name) {
1086 free(dir->d_u_name);
1088 if (dir->d_m_name_ucs2)
1089 free(dir->d_m_name_ucs2);
1090 free(dir->d_m_name);
1096 if (!dir || (dir == SENTINEL))
1099 if ( dir->d_left != SENTINEL ) {
1100 dirfree( dir->d_left );
1102 if ( dir->d_right != SENTINEL ) {
1103 dirfree( dir->d_right );
1106 if (dir != SENTINEL) {
1112 /* --------------------------------------------
1113 * most of the time mac name and unix name are the same
1115 struct dir *dirnew(const char *m_name, const char *u_name)
1119 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1123 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1128 if (m_name == u_name || !strcmp(m_name, u_name)) {
1129 dir->d_u_name = dir->d_m_name;
1131 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1132 free(dir->d_m_name);
1137 dir->d_m_name_ucs2 = NULL;
1138 dir->d_left = dir->d_right = SENTINEL;
1139 dir->d_next = dir->d_prev = dir;
1143 /* ------------------ */
1144 static hash_val_t hash_fun_dir(const void *key)
1146 const struct dir *k = key;
1148 static unsigned long randbox[] = {
1149 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1150 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1151 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1152 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1155 const unsigned char *str = k->d_u_name;
1159 acc ^= randbox[(*str + acc) & 0xf];
1160 acc = (acc << 1) | (acc >> 31);
1162 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1163 acc = (acc << 2) | (acc >> 30);
1169 /* ---------------- */
1170 static int hash_comp_dir(const void *key1, const void *key2)
1172 const struct dir *k1 = key1;
1173 const struct dir *k2 = key2;
1175 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1178 /* ---------------- */
1182 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1185 /* ------------------ */
1186 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1189 movecwd failed some of dir path are not there anymore.
1190 FIXME Is it true with other errors?
1191 so we remove dir from the cache
1193 if (dir->d_did == DIRDID_ROOT_PARENT)
1195 if (afp_errno == AFPERR_ACCESS) {
1196 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1199 /* FIXME should we set these?, don't need to call stat() after:
1201 ret->st_errno = EACCES;
1203 ret->m_name = dir->d_m_name;
1204 ret->u_name = dir->d_u_name;
1207 } else if (afp_errno == AFPERR_NOOBJ) {
1208 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1211 strcpy(ret->m_name, dir->d_m_name);
1212 if (dir->d_m_name == dir->d_u_name) {
1213 ret->u_name = ret->m_name;
1216 size_t tp = strlen(ret->m_name)+1;
1218 ret->u_name = ret->m_name +tp;
1219 strcpy(ret->u_name, dir->d_u_name);
1221 /* FIXME should we set :
1223 ret->st_errno = ENOENT;
1225 dir_invalidate(vol, dir);
1228 dir_invalidate(vol, dir);
1232 /* -------------------------------------------------- */
1238 stat the file or errno
1241 curdir: filename parent directory
1247 stat the dir or errno
1251 curdir: dir parent directory
1259 curdir: dir parent directory
1266 cname( vol, dir, cpath )
1267 const struct vol *vol;
1271 struct dir *cdir, *scdir=NULL;
1272 static char path[ MAXPATHLEN + 1];
1273 static struct path ret;
1285 afp_errno = AFPERR_NOOBJ;
1286 memset(&ret, 0, sizeof(ret));
1287 switch (ret.m_type = *data) { /* path type */
1290 len = (unsigned char) *data++;
1293 if (afp_version >= 30) {
1299 if (afp_version >= 30) {
1301 memcpy(&hint, data, sizeof(hint));
1303 data += sizeof(hint);
1305 memcpy(&len16, data, sizeof(len16));
1312 /* else it's an error */
1314 afp_errno = AFPERR_PARAM;
1317 *cpath += len + size;
1322 if (movecwd( vol, dir ) < 0 ) {
1323 return invalidate(vol, dir, &ret );
1325 if (*path == '\0') {
1332 if (*data == sep ) {
1336 while (*data == sep && len > 0 ) {
1337 if ( dir->d_parent == NULL ) {
1340 dir = dir->d_parent;
1345 /* would this be faster with strlen + strncpy? */
1347 while ( *data != sep && len > 0 ) {
1349 if (p > &path[ MAXPATHLEN]) {
1350 afp_errno = AFPERR_PARAM;
1356 /* short cut bits by chopping off a trailing \0. this also
1357 makes the traversal happy w/ filenames at the end of the
1364 if ( p == path ) { /* end of the name parameter */
1368 if (afp_version >= 30) {
1373 static char temp[ MAXPATHLEN + 1];
1375 if (dir->d_did == DIRDID_ROOT_PARENT) {
1377 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1378 So we compare it with the longname from the current volume and if they match
1379 we overwrite the requested path with the utf8 volume name so that the following
1382 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1383 if (strcasecmp( path, temp) == 0)
1384 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1387 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1388 afp_errno = AFPERR_PARAM;
1394 /* check for OS X mangled filename :( */
1396 t = demangle_osx(vol, path, dir->d_did, &fileid);
1399 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1400 * flags weren't the same
1402 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1403 /* at last got our view of mac name */
1408 if (ret.u_name == NULL) {
1409 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1410 afp_errno = AFPERR_PARAM;
1416 cdir = dir->d_child;
1418 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1419 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1420 CH_UCS2, path, strlen(path), (char **)&tmpname) )
1423 if (!cdir->d_m_name_ucs2) {
1424 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1425 /* this shouldn't happen !!!! */
1429 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1432 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1435 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1441 if (dir->d_did == DIRDID_ROOT_PARENT) {
1443 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1444 must check against the volume name.
1446 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1452 cdir = dirsearch_byname(vol, dir, ret.u_name);
1456 if (cdir == NULL && scdir != NULL) {
1458 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1461 if ( cdir == NULL ) {
1463 /* if dir == curdir it always succeed,
1464 even if curdir is deleted.
1465 it's not a pb because it will fail in extenddir
1467 if ( movecwd( vol, dir ) < 0 ) {
1468 /* dir is not valid anymore
1469 we delete dir from the cache and abort.
1471 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1472 afp_errno = AFPERR_NOOBJ;
1475 if (afp_errno == AFPERR_ACCESS)
1477 dir_invalidate(vol, dir);
1480 cdir = extenddir( vol, dir, &ret );
1484 cdir = extenddir( vol, dir, &ret );
1485 } /* if (!extend) */
1487 if ( cdir == NULL ) {
1489 if ( len > 0 || !ret.u_name ) {
1501 * Move curdir to dir, with a possible chdir()
1503 int movecwd( vol, dir)
1504 const struct vol *vol;
1507 char path[MAXPATHLEN + 1];
1512 if ( dir == curdir ) {
1515 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1516 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1520 p = path + sizeof(path) - 1;
1523 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1526 /* parent directory is deleted */
1527 afp_errno = AFPERR_NOOBJ;
1531 if (p -n -1 < path) {
1532 afp_errno = AFPERR_PARAM;
1539 if ( d != curdir ) {
1540 n = strlen( vol->v_path );
1541 if (p -n -1 < path) {
1542 afp_errno = AFPERR_PARAM;
1547 memcpy( p, vol->v_path, n );
1549 if ( chdir( p ) < 0 ) {
1553 afp_errno = AFPERR_ACCESS;
1556 afp_errno = AFPERR_NOOBJ;
1566 * We can't use unix file's perm to support Apple's inherited protection modes.
1567 * If we aren't the file's owner we can't change its perms when moving it and smb
1568 * nfs,... don't even try.
1570 #define AFP_CHECK_ACCESS
1572 int check_access(char *path, int mode)
1574 #ifdef AFP_CHECK_ACCESS
1582 accessmode(p, &ma, curdir, NULL);
1583 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1585 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1591 /* --------------------- */
1592 int file_access(struct path *path, int mode)
1596 accessmode(path->u_name, &ma, curdir, &path->st);
1597 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1599 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1605 /* --------------------- */
1606 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1608 dir->offcnt = count;
1609 dir->ctime = st->st_ctime;
1610 dir->d_flags &= ~DIRF_CNID;
1613 /* ---------------------
1614 * is our cached offspring count valid?
1617 int diroffcnt(struct dir *dir, struct stat *st)
1619 return st->st_ctime == dir->ctime;
1622 /* ---------------------
1623 * is our cached also for reenumerate id?
1626 int dirreenumerate(struct dir *dir, struct stat *st)
1628 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1631 /* --------------------- */
1632 static int invisible_dots(const struct vol *vol, const char *name)
1634 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1637 /* ------------------------------
1639 (name, dir) with curdir:name == dir, from afp_enumerate
1642 int getdirparams(const struct vol *vol,
1643 u_int16_t bitmap, struct path *s_path,
1645 char *buf, int *buflen )
1649 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1650 int bit = 0, isad = 0;
1656 struct stat *st = &s_path->st;
1657 char *upath = s_path->u_name;
1659 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1660 (1 << DIRPBIT_CDATE) |
1661 (1 << DIRPBIT_MDATE) |
1662 (1 << DIRPBIT_BDATE) |
1663 (1 << DIRPBIT_FINFO)))) {
1665 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1666 if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1671 if ( dir->d_did == DIRDID_ROOT) {
1672 pdid = DIRDID_ROOT_PARENT;
1673 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1676 pdid = dir->d_parent->d_did;
1680 while ( bitmap != 0 ) {
1681 while (( bitmap & 1 ) == 0 ) {
1689 ad_getattr(&ad, &ashort);
1690 } else if (invisible_dots(vol, dir->d_u_name)) {
1691 ashort = htons(ATTRBIT_INVISIBLE);
1694 ashort |= htons(ATTRBIT_SHARED);
1695 memcpy( data, &ashort, sizeof( ashort ));
1696 data += sizeof( ashort );
1700 memcpy( data, &pdid, sizeof( pdid ));
1701 data += sizeof( pdid );
1704 case DIRPBIT_CDATE :
1705 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1706 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1707 memcpy( data, &aint, sizeof( aint ));
1708 data += sizeof( aint );
1711 case DIRPBIT_MDATE :
1712 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1713 memcpy( data, &aint, sizeof( aint ));
1714 data += sizeof( aint );
1717 case DIRPBIT_BDATE :
1718 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1719 aint = AD_DATE_START;
1720 memcpy( data, &aint, sizeof( aint ));
1721 data += sizeof( aint );
1724 case DIRPBIT_FINFO :
1726 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1727 } else { /* no appledouble */
1728 memset( data, 0, 32 );
1729 /* set default view -- this also gets done in ad_open() */
1730 ashort = htons(FINDERINFO_CLOSEDVIEW);
1731 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1733 /* dot files are by default visible */
1734 if (invisible_dots(vol, dir->d_u_name)) {
1735 ashort = htons(FINDERINFO_INVISIBLE);
1736 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1742 case DIRPBIT_LNAME :
1743 if (dir->d_m_name) /* root of parent can have a null name */
1746 memset(data, 0, sizeof(u_int16_t));
1747 data += sizeof( u_int16_t );
1750 case DIRPBIT_SNAME :
1751 memset(data, 0, sizeof(u_int16_t));
1752 data += sizeof( u_int16_t );
1756 memcpy( data, &dir->d_did, sizeof( aint ));
1757 data += sizeof( aint );
1760 case DIRPBIT_OFFCNT :
1762 /* this needs to handle current directory access rights */
1763 if (diroffcnt(dir, st)) {
1764 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1766 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1767 setdiroffcnt(dir, st, ret);
1768 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1770 ashort = htons( ashort );
1771 memcpy( data, &ashort, sizeof( ashort ));
1772 data += sizeof( ashort );
1776 aint = htonl(st->st_uid);
1777 memcpy( data, &aint, sizeof( aint ));
1778 data += sizeof( aint );
1782 aint = htonl(st->st_gid);
1783 memcpy( data, &aint, sizeof( aint ));
1784 data += sizeof( aint );
1787 case DIRPBIT_ACCESS :
1788 accessmode( upath, &ma, dir , st);
1790 *data++ = ma.ma_user;
1791 *data++ = ma.ma_world;
1792 *data++ = ma.ma_group;
1793 *data++ = ma.ma_owner;
1796 /* Client has requested the ProDOS information block.
1797 Just pass back the same basic block for all
1798 directories. <shirsch@ibm.net> */
1799 case DIRPBIT_PDINFO :
1800 if (afp_version >= 30) { /* UTF8 name */
1801 utf8 = kTextEncodingUTF8;
1802 if (dir->d_m_name) /* root of parent can have a null name */
1805 memset(data, 0, sizeof(u_int16_t));
1806 data += sizeof( u_int16_t );
1808 memcpy(data, &aint, sizeof( aint ));
1809 data += sizeof( aint );
1811 else { /* ProDOS Info Block */
1814 ashort = htons( 0x0200 );
1815 memcpy( data, &ashort, sizeof( ashort ));
1816 data += sizeof( ashort );
1817 memset( data, 0, sizeof( ashort ));
1818 data += sizeof( ashort );
1822 case DIRPBIT_UNIXPR :
1823 aint = htonl(st->st_uid);
1824 memcpy( data, &aint, sizeof( aint ));
1825 data += sizeof( aint );
1826 aint = htonl(st->st_gid);
1827 memcpy( data, &aint, sizeof( aint ));
1828 data += sizeof( aint );
1831 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1832 memcpy( data, &aint, sizeof( aint ));
1833 data += sizeof( aint );
1835 accessmode( upath, &ma, dir , st);
1837 *data++ = ma.ma_user;
1838 *data++ = ma.ma_world;
1839 *data++ = ma.ma_group;
1840 *data++ = ma.ma_owner;
1845 ad_close_metadata( &ad );
1847 return( AFPERR_BITMAP );
1853 ashort = htons( data - buf );
1854 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1855 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1857 if ( utf_nameoff ) {
1858 ashort = htons( data - buf );
1859 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1860 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1863 ad_close_metadata( &ad );
1865 *buflen = data - buf;
1869 /* ----------------------------- */
1870 int path_error(struct path *path, int error)
1872 /* - a dir with access error
1873 * - no error it's a file
1876 if (path_isadir(path))
1878 if (path->st_valid && path->st_errno)
1880 return AFPERR_BADTYPE ;
1883 /* ----------------------------- */
1884 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1886 char *ibuf, *rbuf _U_;
1887 int ibuflen _U_, *rbuflen;
1892 u_int16_t vid, bitmap;
1898 memcpy( &vid, ibuf, sizeof( vid ));
1899 ibuf += sizeof( vid );
1901 if (NULL == ( vol = getvolbyvid( vid )) ) {
1902 return( AFPERR_PARAM );
1905 if (vol->v_flags & AFPVOL_RO)
1906 return AFPERR_VLOCK;
1908 memcpy( &did, ibuf, sizeof( did ));
1909 ibuf += sizeof( int );
1911 if (NULL == ( dir = dirlookup( vol, did )) ) {
1915 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1916 bitmap = ntohs( bitmap );
1917 ibuf += sizeof( bitmap );
1919 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1920 return get_afp_errno(AFPERR_NOOBJ);
1923 if ( *path->m_name != '\0' ) {
1924 rc = path_error(path, AFPERR_NOOBJ);
1925 /* maybe we are trying to set perms back */
1926 if (rc != AFPERR_ACCESS)
1931 * If ibuf is odd, make it even.
1933 if ((u_long)ibuf & 1 ) {
1937 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1938 setvoltime(obj, vol );
1944 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1946 * assume path == '\0' eg. it's a directory in canonical form
1949 struct path Cur_Path = {
1952 ".", /* unix name */
1954 NULL,/* struct dir */
1955 0, /* stat is not set */
1958 /* ------------------ */
1959 static int set_dir_errors(struct path *path, const char *where, int err)
1964 return AFPERR_ACCESS;
1966 return AFPERR_VLOCK;
1968 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1969 return AFPERR_PARAM;
1972 /* ------------------ */
1973 int setdirparams(const struct vol *vol,
1974 struct path *path, u_int16_t d_bitmap, char *buf )
1986 u_int16_t ashort, bshort;
1988 int change_mdate = 0;
1989 int change_parent_mdate = 0;
1991 u_int16_t bitmap = d_bitmap;
1992 u_char finder_buf[32];
1995 u_int16_t upriv_bit = 0;
1998 upath = path->u_name;
2000 while ( bitmap != 0 ) {
2001 while (( bitmap & 1 ) == 0 ) {
2009 memcpy( &ashort, buf, sizeof( ashort ));
2010 buf += sizeof( ashort );
2012 case DIRPBIT_CDATE :
2014 memcpy(&cdate, buf, sizeof(cdate));
2015 buf += sizeof( cdate );
2017 case DIRPBIT_MDATE :
2018 memcpy(&newdate, buf, sizeof(newdate));
2019 buf += sizeof( newdate );
2021 case DIRPBIT_BDATE :
2023 memcpy(&bdate, buf, sizeof(bdate));
2024 buf += sizeof( bdate );
2026 case DIRPBIT_FINFO :
2028 memcpy( finder_buf, buf, 32 );
2031 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2032 change_parent_mdate = 1;
2033 memcpy( &owner, buf, sizeof(owner));
2034 buf += sizeof( owner );
2037 change_parent_mdate = 1;
2038 memcpy( &group, buf, sizeof( group ));
2039 buf += sizeof( group );
2041 case DIRPBIT_ACCESS :
2043 change_parent_mdate = 1;
2044 ma.ma_user = *buf++;
2045 ma.ma_world = *buf++;
2046 ma.ma_group = *buf++;
2047 ma.ma_owner = *buf++;
2048 mpriv = mtoumode( &ma ) | vol->v_dperm;
2049 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2050 err = set_dir_errors(path, "setdirmode", errno);
2054 /* Ignore what the client thinks we should do to the
2055 ProDOS information block. Skip over the data and
2056 report nothing amiss. <shirsch@ibm.net> */
2057 case DIRPBIT_PDINFO :
2058 if (afp_version < 30) {
2062 err = AFPERR_BITMAP;
2066 case DIRPBIT_UNIXPR :
2067 if (vol_unix_priv(vol)) {
2068 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2069 buf += sizeof( owner );
2070 memcpy( &group, buf, sizeof( group ));
2071 buf += sizeof( group );
2074 change_parent_mdate = 1;
2075 memcpy( &upriv, buf, sizeof( upriv ));
2076 buf += sizeof( upriv );
2077 upriv = ntohl (upriv) | vol->v_dperm;
2078 if (dir_rx_set(upriv)) {
2079 /* maybe we are trying to set perms back */
2080 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2082 err = set_dir_errors(path, "setdirunixmode", errno);
2093 err = AFPERR_BITMAP;
2101 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2103 if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2105 * Check to see what we're trying to set. If it's anything
2106 * but ACCESS, UID, or GID, give an error. If it's any of those
2107 * three, we don't need the ad to be open, so just continue.
2109 * note: we also don't need to worry about mdate. also, be quiet
2110 * if we're using the noadouble option.
2112 if (!vol_noadouble(vol) && (d_bitmap &
2113 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2114 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2115 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2116 return AFPERR_ACCESS;
2122 * Check to see if a create was necessary. If it was, we'll want
2123 * to set our name, etc.
2125 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2126 ad_setname(&ad, curdir->d_m_name);
2132 while ( bitmap != 0 ) {
2133 while (( bitmap & 1 ) == 0 ) {
2141 ad_getattr(&ad, &bshort);
2142 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2143 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2144 change_parent_mdate = 1;
2145 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2146 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2150 ad_setattr(&ad, bshort);
2153 case DIRPBIT_CDATE :
2155 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2158 case DIRPBIT_MDATE :
2160 case DIRPBIT_BDATE :
2162 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2165 case DIRPBIT_FINFO :
2167 /* Fixes #2802236 */
2168 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2169 *fflags &= htons(~FINDERINFO_ISHARED);
2171 if ( dir->d_did == DIRDID_ROOT ) {
2173 * Alright, we admit it, this is *really* sick!
2174 * The 4 bytes that we don't copy, when we're dealing
2175 * with the root of a volume, are the directory's
2176 * location information. This eliminates that annoying
2177 * behavior one sees when mounting above another mount
2180 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2181 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2183 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2187 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2188 if ( (dir->d_did == DIRDID_ROOT) &&
2189 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2190 err = set_dir_errors(path, "setdeskowner", errno);
2191 if (isad && err == AFPERR_PARAM) {
2192 err = AFP_OK; /* ???*/
2195 goto setdirparam_done;
2198 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2199 err = set_dir_errors(path, "setdirowner", errno);
2200 goto setdirparam_done;
2204 if (dir->d_did == DIRDID_ROOT)
2205 setdeskowner( -1, ntohl(group) );
2206 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2207 err = set_dir_errors(path, "setdirowner", errno);
2208 goto setdirparam_done;
2211 case DIRPBIT_ACCESS :
2212 if (dir->d_did == DIRDID_ROOT) {
2214 if (!dir_rx_set(mpriv)) {
2215 /* we can't remove read and search for owner on volume root */
2216 err = AFPERR_ACCESS;
2217 goto setdirparam_done;
2221 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2222 err = set_dir_errors(path, "setdirmode", errno);
2223 goto setdirparam_done;
2226 case DIRPBIT_PDINFO :
2227 if (afp_version >= 30) {
2228 err = AFPERR_BITMAP;
2229 goto setdirparam_done;
2232 case DIRPBIT_UNIXPR :
2233 if (vol_unix_priv(vol)) {
2234 if (dir->d_did == DIRDID_ROOT) {
2235 if (!dir_rx_set(upriv)) {
2236 /* we can't remove read and search for owner on volume root */
2237 err = AFPERR_ACCESS;
2238 goto setdirparam_done;
2240 setdeskowner( -1, ntohl(group) );
2241 setdeskmode( upriv );
2243 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2244 err = set_dir_errors(path, "setdirowner", errno);
2245 goto setdirparam_done;
2248 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2249 err = set_dir_errors(path, "setdirunixmode", errno);
2250 goto setdirparam_done;
2254 err = AFPERR_BITMAP;
2255 goto setdirparam_done;
2259 err = AFPERR_BITMAP;
2260 goto setdirparam_done;
2269 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2270 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2274 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2275 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2280 if (path->st_valid && !path->st_errno) {
2281 struct stat *st = &path->st;
2283 if (dir && dir->d_parent) {
2284 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2288 ad_close_metadata( &ad);
2291 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2292 && gettimeofday(&tv, NULL) == 0) {
2293 if (!movecwd(vol, dir->d_parent)) {
2294 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2295 /* be careful with bitmap because now dir is null */
2296 bitmap = 1<<DIRPBIT_MDATE;
2297 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2298 /* should we reset curdir ?*/
2305 int afp_syncdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2307 char *ibuf, *rbuf _U_;
2308 int ibuflen _U_, *rbuflen;
2322 memcpy( &vid, ibuf, sizeof( vid ));
2323 ibuf += sizeof( vid );
2324 if (NULL == (vol = getvolbyvid( vid )) ) {
2325 return( AFPERR_PARAM );
2328 memcpy( &did, ibuf, sizeof( did ));
2329 ibuf += sizeof( did );
2333 * if it's CNID 2 our only choice to meet the specs is call sync.
2334 * For any other CNID just sync that dir. To my knowledge the
2335 * intended use of FPSyncDir is to sync the volume so all we're
2336 * ever going to see here is probably CNID 2. Anyway, we' prepared.
2339 if ( ntohl(did) == 2 ) {
2342 if (NULL == ( dir = dirlookup( vol, did )) ) {
2343 return afp_errno; /* was AFPERR_NOOBJ */
2346 if (movecwd( vol, dir ) < 0 )
2347 return ( AFPERR_NOOBJ );
2350 * Assuming only OSens that have dirfd also may require fsyncing directories
2351 * in order to flush metadata e.g. Linux.
2355 if (NULL == ( dp = opendir( "." )) ) {
2358 return( AFPERR_NOOBJ );
2360 return( AFPERR_ACCESS );
2362 return( AFPERR_PARAM );
2366 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2369 if ( fsync ( dfd ) < 0 )
2370 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2371 dir->d_u_name, strerror(errno) );
2372 closedir(dp); /* closes dfd too */
2375 if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2378 return( AFPERR_NOOBJ );
2380 return( AFPERR_ACCESS );
2382 return( AFPERR_PARAM );
2386 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2387 vol->ad_path(".", ADFLAGS_DIR) );
2389 if ( fsync(dfd) < 0 )
2390 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2391 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2398 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2401 int ibuflen _U_, *rbuflen;
2407 struct path *s_path;
2415 memcpy( &vid, ibuf, sizeof( vid ));
2416 ibuf += sizeof( vid );
2417 if (NULL == ( vol = getvolbyvid( vid )) ) {
2418 return( AFPERR_PARAM );
2421 if (vol->v_flags & AFPVOL_RO)
2422 return AFPERR_VLOCK;
2424 memcpy( &did, ibuf, sizeof( did ));
2425 ibuf += sizeof( did );
2426 if (NULL == ( dir = dirlookup( vol, did )) ) {
2427 return afp_errno; /* was AFPERR_NOOBJ */
2429 /* for concurrent access we need to be sure we are not in the
2430 * folder we want to create...
2434 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2435 return get_afp_errno(AFPERR_PARAM);
2437 /* cname was able to move curdir to it! */
2438 if (*s_path->m_name == '\0')
2439 return AFPERR_EXIST;
2441 upath = s_path->u_name;
2443 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2447 if (of_stat(s_path) < 0) {
2451 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2455 if ( movecwd( vol, dir ) < 0 ) {
2456 return( AFPERR_PARAM );
2459 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2460 if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2461 if (vol_noadouble(vol))
2462 goto createdir_done;
2463 return( AFPERR_ACCESS );
2465 ad_setname(&ad, s_path->m_name);
2466 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2469 ad_close_metadata( &ad);
2472 #ifdef HAVE_NFSv4_ACLS
2473 /* FIXME: are we really inside the created dir? */
2474 addir_inherit_acl(vol);
2477 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2478 *rbuflen = sizeof( u_int32_t );
2479 setvoltime(obj, vol );
2484 * dst new unix filename (not a pathname)
2485 * newname new mac name
2489 int renamedir(vol, src, dst, dir, newparent, newname)
2490 const struct vol *vol;
2491 char *src, *dst, *newname;
2492 struct dir *dir, *newparent;
2499 /* existence check moved to afp_moveandrename */
2500 if ( unix_rename( src, dst ) < 0 ) {
2503 return( AFPERR_NOOBJ );
2505 return( AFPERR_ACCESS );
2507 return AFPERR_VLOCK;
2509 /* tried to move directory into a subdirectory of itself */
2510 return AFPERR_CANTMOVE;
2512 /* this needs to copy and delete. bleah. that means we have
2513 * to deal with entire directory hierarchies. */
2514 if ((err = copydir(vol, src, dst)) < 0) {
2518 if ((err = deletedir(src)) < 0)
2522 return( AFPERR_PARAM );
2526 vol->vfs->rf_renamedir(vol, src, dst);
2528 len = strlen( newname );
2529 /* rename() succeeded so we need to update our tree even if we can't open
2533 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2535 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2536 ad_setname(&ad, newname);
2538 ad_close_metadata( &ad);
2541 dir_hash_del(vol, dir);
2542 if (dir->d_m_name == dir->d_u_name)
2543 dir->d_u_name = NULL;
2545 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2546 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2547 /* FIXME : fatal ? */
2550 dir->d_m_name = buf;
2551 strcpy( dir->d_m_name, newname );
2553 if (newname == dst) {
2554 free(dir->d_u_name);
2555 dir->d_u_name = dir->d_m_name;
2558 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2559 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2562 dir->d_u_name = buf;
2563 strcpy( dir->d_u_name, dst );
2566 if (dir->d_m_name_ucs2)
2567 free(dir->d_m_name_ucs2);
2569 dir->d_m_name_ucs2 = NULL;
2570 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))
2571 dir->d_m_name_ucs2 = NULL;
2573 if (( parent = dir->d_parent ) == NULL ) {
2576 if ( parent == newparent ) {
2577 hash_alloc_insert(vol->v_hash, dir, dir);
2581 /* detach from old parent and add to new one. */
2582 dirchildremove(parent, dir);
2583 dir->d_parent = newparent;
2584 dirchildadd(vol, newparent, dir);
2588 /* delete an empty directory */
2589 int deletecurdir( vol)
2590 const struct vol *vol;
2600 if ( curdir->d_parent == NULL ) {
2601 return( AFPERR_ACCESS );
2606 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2607 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2609 ad_getattr(&ad, &ashort);
2610 ad_close( &ad, ADFLAGS_HF );
2611 if ((ashort & htons(ATTRBIT_NODELETE))) {
2612 return AFPERR_OLOCK;
2615 err = vol->vfs->rf_deletecurdir(vol);
2620 /* now get rid of dangling symlinks */
2621 if ((dp = opendir("."))) {
2622 while ((de = readdir(dp))) {
2623 /* skip this and previous directory */
2624 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2627 /* bail if it's not a symlink */
2628 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2630 return AFPERR_DIRNEMPT;
2633 if ((err = netatalk_unlink(de->d_name))) {
2640 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2645 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2646 dirchildremove(curdir, fdir);
2647 cnid_delete(vol->v_cdb, fdir->d_did);
2648 dir_remove( vol, fdir );
2653 /* inode is used as key for cnid.
2654 * Close the descriptor only after cnid_delete
2662 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2665 int ibuflen _U_, *rbuflen;
2676 sfunc = (unsigned char) *ibuf++;
2680 if (sfunc >= 3 && sfunc <= 6) {
2681 if (afp_version < 30) {
2682 return( AFPERR_PARAM );
2689 case 3 :/* unicode */
2690 memcpy( &id, ibuf, sizeof( id ));
2693 if (( pw = getpwuid( id )) == NULL ) {
2694 return( AFPERR_NOITEM );
2696 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2697 pw->pw_name, strlen(pw->pw_name), &name);
2704 case 4 : /* unicode */
2705 memcpy( &id, ibuf, sizeof( id ));
2708 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2709 return( AFPERR_NOITEM );
2711 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2712 gr->gr_name, strlen(gr->gr_name), &name);
2718 #ifdef HAVE_NFSv4_ACLS
2719 case 5 : /* UUID -> username */
2720 case 6 : /* UUID -> groupname */
2721 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2722 return AFPERR_PARAM;
2723 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2724 len = getnamefromuuid( ibuf, &name, &type);
2725 if (len != 0) /* its a error code, not len */
2726 return AFPERR_NOITEM;
2727 if (type == UUID_USER) {
2728 if (( pw = getpwnam( name )) == NULL )
2729 return( AFPERR_NOITEM );
2730 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2731 id = htonl(UUID_USER);
2732 memcpy( rbuf, &id, sizeof( id ));
2733 id = htonl( pw->pw_uid);
2734 rbuf += sizeof( id );
2735 memcpy( rbuf, &id, sizeof( id ));
2736 rbuf += sizeof( id );
2737 *rbuflen = 2 * sizeof( id );
2738 } else { /* type == UUID_GROUP */
2739 if (( gr = getgrnam( name )) == NULL )
2740 return( AFPERR_NOITEM );
2741 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2742 id = htonl(UUID_GROUP);
2743 memcpy( rbuf, &id, sizeof( id ));
2744 rbuf += sizeof( id );
2745 id = htonl( gr->gr_gid);
2746 memcpy( rbuf, &id, sizeof( id ));
2747 rbuf += sizeof( id );
2748 *rbuflen = 2 * sizeof( id );
2753 return( AFPERR_PARAM );
2757 len = strlen( name );
2760 u_int16_t tp = htons(len);
2761 memcpy(rbuf, &tp, sizeof(tp));
2770 memcpy( rbuf, name, len );
2778 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2781 int ibuflen _U_, *rbuflen;
2790 sfunc = (unsigned char) *ibuf++;
2792 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2795 case 2 : /* unicode */
2796 if (afp_version < 30) {
2797 return( AFPERR_PARAM );
2799 memcpy(&ulen, ibuf, sizeof(ulen));
2802 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2806 len = (unsigned char) *ibuf++;
2808 #ifdef HAVE_NFSv4_ACLS
2809 case 5 : /* username -> UUID */
2810 case 6 : /* groupname -> UUID */
2811 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2812 return AFPERR_PARAM;
2813 memcpy(&ulen, ibuf, sizeof(ulen));
2819 return( AFPERR_PARAM );
2825 return AFPERR_PARAM;
2828 case 1 : /* unicode */
2830 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2831 return( AFPERR_NOITEM );
2835 memcpy( rbuf, &id, sizeof( id ));
2836 *rbuflen = sizeof( id );
2839 case 2 : /* unicode */
2841 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2842 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2843 return( AFPERR_NOITEM );
2846 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2848 memcpy( rbuf, &id, sizeof( id ));
2849 *rbuflen = sizeof( id );
2851 #ifdef HAVE_NFSv4_ACLS
2852 case 5 : /* username -> UUID */
2853 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2854 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2855 return AFPERR_NOITEM;
2856 *rbuflen = UUID_BINSIZE;
2858 case 6 : /* groupname -> UUID */
2859 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2860 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2861 return AFPERR_NOITEM;
2862 *rbuflen = UUID_BINSIZE;
2870 /* ------------------------------------
2871 variable DID support
2873 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2875 char *ibuf _U_, *rbuf _U_;
2876 int ibuflen _U_, *rbuflen;
2887 /* do nothing as dids are static for the life of the process. */
2891 memcpy(&vid, ibuf, sizeof( vid ));
2892 ibuf += sizeof( vid );
2893 if (( vol = getvolbyvid( vid )) == NULL ) {
2894 return( AFPERR_PARAM );
2897 memcpy( &did, ibuf, sizeof( did ));
2898 ibuf += sizeof( did );
2899 if (( dir = dirlookup( vol, did )) == NULL ) {
2900 return( AFPERR_PARAM );
2903 /* dir_remove -- deletedid */
2909 /* did creation gets done automatically
2910 * there's a pb again with case but move it to cname
2912 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2915 int ibuflen _U_, *rbuflen;
2918 struct dir *parentdir;
2926 memcpy(&vid, ibuf, sizeof(vid));
2927 ibuf += sizeof( vid );
2929 if (NULL == ( vol = getvolbyvid( vid )) ) {
2930 return( AFPERR_PARAM );
2933 memcpy(&did, ibuf, sizeof(did));
2934 ibuf += sizeof(did);
2936 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2940 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2941 return get_afp_errno(AFPERR_PARAM);
2944 if ( *path->m_name != '\0' ) {
2945 return path_error(path, AFPERR_NOOBJ);
2948 if ( !path->st_valid && of_stat(path ) < 0 ) {
2949 return( AFPERR_NOOBJ );
2951 if ( path->st_errno ) {
2952 return( AFPERR_NOOBJ );
2955 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2956 *rbuflen = sizeof(curdir->d_did);