2 * $Id: directory.c,v 1.100 2009-09-01 13:15:13 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 ) {
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 ?*/
2306 int afp_syncdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2308 char *ibuf, *rbuf _U_;
2309 int ibuflen _U_, *rbuflen;
2323 memcpy( &vid, ibuf, sizeof( vid ));
2324 ibuf += sizeof( vid );
2325 if (NULL == (vol = getvolbyvid( vid )) ) {
2326 return( AFPERR_PARAM );
2329 memcpy( &did, ibuf, sizeof( did ));
2330 ibuf += sizeof( did );
2331 if (NULL == ( dir = dirlookup( vol, did )) ) {
2332 return afp_errno; /* was AFPERR_NOOBJ */
2335 if (movecwd( vol, dir ) < 0 )
2336 return ( AFPERR_NOOBJ );
2339 Assuming only OSens that have dirfd also may require fsyncing directories
2340 in order to flush metadata e.g. Linux.
2344 if (NULL == ( dp = opendir( "." )) ) {
2347 return( AFPERR_NOOBJ );
2349 return( AFPERR_ACCESS );
2351 return( AFPERR_PARAM );
2355 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2358 if ( fsync ( dfd ) < 0 )
2359 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2360 dir->d_u_name, strerror(errno) );
2361 closedir(dp); /* closes dfd too */
2364 if ( -1 == (dfd = open(vol->vfs->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2367 return( AFPERR_NOOBJ );
2369 return( AFPERR_ACCESS );
2371 return( AFPERR_PARAM );
2375 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2376 vol->vfs->ad_path(".", ADFLAGS_DIR) );
2378 if ( fsync(dfd) < 0 )
2379 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2380 vol->vfs->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2386 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2389 int ibuflen _U_, *rbuflen;
2395 struct path *s_path;
2403 memcpy( &vid, ibuf, sizeof( vid ));
2404 ibuf += sizeof( vid );
2405 if (NULL == ( vol = getvolbyvid( vid )) ) {
2406 return( AFPERR_PARAM );
2409 if (vol->v_flags & AFPVOL_RO)
2410 return AFPERR_VLOCK;
2412 memcpy( &did, ibuf, sizeof( did ));
2413 ibuf += sizeof( did );
2414 if (NULL == ( dir = dirlookup( vol, did )) ) {
2415 return afp_errno; /* was AFPERR_NOOBJ */
2417 /* for concurrent access we need to be sure we are not in the
2418 * folder we want to create...
2422 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2423 return get_afp_errno(AFPERR_PARAM);
2425 /* cname was able to move curdir to it! */
2426 if (*s_path->m_name == '\0')
2427 return AFPERR_EXIST;
2429 upath = s_path->u_name;
2431 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2435 if (of_stat(s_path) < 0) {
2439 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2443 if ( movecwd( vol, dir ) < 0 ) {
2444 return( AFPERR_PARAM );
2447 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2448 if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2449 if (vol_noadouble(vol))
2450 goto createdir_done;
2451 return( AFPERR_ACCESS );
2453 ad_setname(&ad, s_path->m_name);
2454 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2457 ad_close_metadata( &ad);
2460 #ifdef HAVE_NFSv4_ACLS
2461 /* FIXME: are we really inside the created dir? */
2462 addir_inherit_acl(vol);
2465 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2466 *rbuflen = sizeof( u_int32_t );
2467 setvoltime(obj, vol );
2472 * dst new unix filename (not a pathname)
2473 * newname new mac name
2477 int renamedir(vol, src, dst, dir, newparent, newname)
2478 const struct vol *vol;
2479 char *src, *dst, *newname;
2480 struct dir *dir, *newparent;
2487 /* existence check moved to afp_moveandrename */
2488 if ( unix_rename( src, dst ) < 0 ) {
2491 return( AFPERR_NOOBJ );
2493 return( AFPERR_ACCESS );
2495 return AFPERR_VLOCK;
2497 /* tried to move directory into a subdirectory of itself */
2498 return AFPERR_CANTMOVE;
2500 /* this needs to copy and delete. bleah. that means we have
2501 * to deal with entire directory hierarchies. */
2502 if ((err = copydir(vol, src, dst)) < 0) {
2506 if ((err = deletedir(src)) < 0)
2510 return( AFPERR_PARAM );
2514 vol->vfs->rf_renamedir(vol, src, dst);
2516 len = strlen( newname );
2517 /* rename() succeeded so we need to update our tree even if we can't open
2521 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2523 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2524 ad_setname(&ad, newname);
2526 ad_close_metadata( &ad);
2529 dir_hash_del(vol, dir);
2530 if (dir->d_m_name == dir->d_u_name)
2531 dir->d_u_name = NULL;
2533 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2534 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2535 /* FIXME : fatal ? */
2538 dir->d_m_name = buf;
2539 strcpy( dir->d_m_name, newname );
2541 if (newname == dst) {
2542 free(dir->d_u_name);
2543 dir->d_u_name = dir->d_m_name;
2546 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2547 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2550 dir->d_u_name = buf;
2551 strcpy( dir->d_u_name, dst );
2554 if (dir->d_m_name_ucs2)
2555 free(dir->d_m_name_ucs2);
2557 dir->d_m_name_ucs2 = NULL;
2558 if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, strlen(dir->d_m_name), (char**)&dir->d_m_name_ucs2))
2559 dir->d_m_name_ucs2 = NULL;
2561 if (( parent = dir->d_parent ) == NULL ) {
2564 if ( parent == newparent ) {
2565 hash_alloc_insert(vol->v_hash, dir, dir);
2569 /* detach from old parent and add to new one. */
2570 dirchildremove(parent, dir);
2571 dir->d_parent = newparent;
2572 dirchildadd(vol, newparent, dir);
2576 /* delete an empty directory */
2577 int deletecurdir( vol)
2578 const struct vol *vol;
2588 if ( curdir->d_parent == NULL ) {
2589 return( AFPERR_ACCESS );
2594 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2595 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2597 ad_getattr(&ad, &ashort);
2598 ad_close( &ad, ADFLAGS_HF );
2599 if ((ashort & htons(ATTRBIT_NODELETE))) {
2600 return AFPERR_OLOCK;
2603 err = vol->vfs->rf_deletecurdir(vol);
2608 /* now get rid of dangling symlinks */
2609 if ((dp = opendir("."))) {
2610 while ((de = readdir(dp))) {
2611 /* skip this and previous directory */
2612 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2615 /* bail if it's not a symlink */
2616 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2618 return AFPERR_DIRNEMPT;
2621 if ((err = netatalk_unlink(de->d_name))) {
2628 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2633 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2634 dirchildremove(curdir, fdir);
2635 cnid_delete(vol->v_cdb, fdir->d_did);
2636 dir_remove( vol, fdir );
2641 /* inode is used as key for cnid.
2642 * Close the descriptor only after cnid_delete
2650 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2653 int ibuflen _U_, *rbuflen;
2664 sfunc = (unsigned char) *ibuf++;
2668 if (sfunc >= 3 && sfunc <= 6) {
2669 if (afp_version < 30) {
2670 return( AFPERR_PARAM );
2677 case 3 :/* unicode */
2678 memcpy( &id, ibuf, sizeof( id ));
2681 if (( pw = getpwuid( id )) == NULL ) {
2682 return( AFPERR_NOITEM );
2684 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2685 pw->pw_name, strlen(pw->pw_name), &name);
2692 case 4 : /* unicode */
2693 memcpy( &id, ibuf, sizeof( id ));
2696 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2697 return( AFPERR_NOITEM );
2699 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2700 gr->gr_name, strlen(gr->gr_name), &name);
2706 #ifdef HAVE_NFSv4_ACLS
2707 case 5 : /* UUID -> username */
2708 case 6 : /* UUID -> groupname */
2709 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2710 return AFPERR_PARAM;
2711 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2712 len = getnamefromuuid( ibuf, &name, &type);
2713 if (len != 0) /* its a error code, not len */
2714 return AFPERR_NOITEM;
2715 if (type == UUID_USER) {
2716 if (( pw = getpwnam( name )) == NULL )
2717 return( AFPERR_NOITEM );
2718 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2719 id = htonl(UUID_USER);
2720 memcpy( rbuf, &id, sizeof( id ));
2721 id = htonl( pw->pw_uid);
2722 rbuf += sizeof( id );
2723 memcpy( rbuf, &id, sizeof( id ));
2724 rbuf += sizeof( id );
2725 *rbuflen = 2 * sizeof( id );
2726 } else { /* type == UUID_GROUP */
2727 if (( gr = getgrnam( name )) == NULL )
2728 return( AFPERR_NOITEM );
2729 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2730 id = htonl(UUID_GROUP);
2731 memcpy( rbuf, &id, sizeof( id ));
2732 rbuf += sizeof( id );
2733 id = htonl( gr->gr_gid);
2734 memcpy( rbuf, &id, sizeof( id ));
2735 rbuf += sizeof( id );
2736 *rbuflen = 2 * sizeof( id );
2741 return( AFPERR_PARAM );
2745 len = strlen( name );
2748 u_int16_t tp = htons(len);
2749 memcpy(rbuf, &tp, sizeof(tp));
2758 memcpy( rbuf, name, len );
2766 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2769 int ibuflen _U_, *rbuflen;
2778 sfunc = (unsigned char) *ibuf++;
2780 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2783 case 2 : /* unicode */
2784 if (afp_version < 30) {
2785 return( AFPERR_PARAM );
2787 memcpy(&ulen, ibuf, sizeof(ulen));
2790 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2794 len = (unsigned char) *ibuf++;
2796 #ifdef HAVE_NFSv4_ACLS
2797 case 5 : /* username -> UUID */
2798 case 6 : /* groupname -> UUID */
2799 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2800 return AFPERR_PARAM;
2801 memcpy(&ulen, ibuf, sizeof(ulen));
2807 return( AFPERR_PARAM );
2813 return AFPERR_PARAM;
2816 case 1 : /* unicode */
2818 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2819 return( AFPERR_NOITEM );
2823 memcpy( rbuf, &id, sizeof( id ));
2824 *rbuflen = sizeof( id );
2827 case 2 : /* unicode */
2829 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2830 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2831 return( AFPERR_NOITEM );
2834 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2836 memcpy( rbuf, &id, sizeof( id ));
2837 *rbuflen = sizeof( id );
2839 #ifdef HAVE_NFSv4_ACLS
2840 case 5 : /* username -> UUID */
2841 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2842 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2843 return AFPERR_NOITEM;
2844 *rbuflen = UUID_BINSIZE;
2846 case 6 : /* groupname -> UUID */
2847 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2848 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2849 return AFPERR_NOITEM;
2850 *rbuflen = UUID_BINSIZE;
2858 /* ------------------------------------
2859 variable DID support
2861 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2863 char *ibuf _U_, *rbuf _U_;
2864 int ibuflen _U_, *rbuflen;
2875 /* do nothing as dids are static for the life of the process. */
2879 memcpy(&vid, ibuf, sizeof( vid ));
2880 ibuf += sizeof( vid );
2881 if (( vol = getvolbyvid( vid )) == NULL ) {
2882 return( AFPERR_PARAM );
2885 memcpy( &did, ibuf, sizeof( did ));
2886 ibuf += sizeof( did );
2887 if (( dir = dirlookup( vol, did )) == NULL ) {
2888 return( AFPERR_PARAM );
2891 /* dir_remove -- deletedid */
2897 /* did creation gets done automatically
2898 * there's a pb again with case but move it to cname
2900 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2903 int ibuflen _U_, *rbuflen;
2906 struct dir *parentdir;
2914 memcpy(&vid, ibuf, sizeof(vid));
2915 ibuf += sizeof( vid );
2917 if (NULL == ( vol = getvolbyvid( vid )) ) {
2918 return( AFPERR_PARAM );
2921 memcpy(&did, ibuf, sizeof(did));
2922 ibuf += sizeof(did);
2924 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2928 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2929 return get_afp_errno(AFPERR_PARAM);
2932 if ( *path->m_name != '\0' ) {
2933 return path_error(path, AFPERR_NOOBJ);
2936 if ( !path->st_valid && of_stat(path ) < 0 ) {
2937 return( AFPERR_NOOBJ );
2939 if ( path->st_errno ) {
2940 return( AFPERR_NOOBJ );
2943 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2944 *rbuflen = sizeof(curdir->d_did);