2 * $Id: directory.c,v 1.112 2009-10-22 12:35:38 franklahm Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
7 * 19 jan 2000 implemented red-black trees for directory lookups
13 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
38 #include <sys/param.h>
42 #include <atalk/adouble.h>
43 #include <atalk/vfs.h>
44 #include <atalk/afp.h>
45 #include <atalk/util.h>
46 #include <atalk/cnid.h>
47 #include <atalk/logger.h>
48 #include <atalk/uuid.h>
49 #include <atalk/unix.h>
51 #include "directory.h"
62 #ifdef HAVE_NFSv4_ACLS
63 extern void addir_inherit_acl(const struct vol *vol);
69 #define SENTINEL (&sentinel)
70 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
71 NULL, NULL, NULL, NULL, NULL, 0, 0,
72 0, 0, NULL, NULL, NULL};
73 static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
74 NULL, NULL, NULL, NULL, NULL, 0, 0,
75 0, 0, NULL, NULL, NULL};
77 /* (from IM: Toolbox Essentials)
78 * dirFinderInfo (DInfo) fields:
80 * frRect 8 folder's window rectangle
82 * frLocation 4 folder's location in window
83 * frView 2 folder's view (default == closedView (256))
85 * extended dirFinderInfo (DXInfo) fields:
86 * frScroll 4 scroll position
87 * frOpenChain: 4 directory ID chain of open folders
88 * frScript: 1 script flag and code
89 * frXFlags: 1 reserved
90 * frComment: 2 comment ID
91 * frPutAway: 4 home directory ID
95 * redid did assignment for directories. now we use red-black trees.
99 dirsearch(const struct vol *vol, u_int32_t did)
104 /* check for 0 did */
106 afp_errno = AFPERR_PARAM;
109 if ( did == DIRDID_ROOT_PARENT ) {
111 rootpar.d_did = DIRDID_ROOT_PARENT;
112 rootpar.d_child = vol->v_dir;
116 /* XXX would be nice to check against curdir but we need to keep its volume */
117 if (vol->curdir && curdir->d_did == did) {
126 afp_errno = AFPERR_NOOBJ;
127 while ( dir != SENTINEL ) {
128 if (dir->d_did == did)
129 return dir->d_m_name ? dir : NULL;
130 dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
135 /* ------------------- */
136 int get_afp_errno(const int param)
138 if (afp_errno != AFPERR_DID1)
143 /* ------------------- */
145 dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
147 struct dir *dir = NULL;
149 if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
155 hn = hash_lookup(vol->v_hash, &key);
163 /* -----------------------------------------
164 * if did is not in the cache resolve it with cnid
167 * OSX call it with bogus id, ie file ID not folder ID,
168 * and we are really bad in this case.
171 dirlookup( struct vol *vol, u_int32_t did)
176 static char path[MAXPATHLEN + 1];
179 static char buffer[12 + MAXPATHLEN + 1];
180 int buflen = 12 + MAXPATHLEN + 1;
185 ret = dirsearch(vol, did);
186 if (ret != NULL || afp_errno == AFPERR_PARAM)
189 utf8 = utf8_encoding();
190 maxpath = (utf8)?MAXPATHLEN -7:255;
192 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
193 afp_errno = AFPERR_NOOBJ;
196 ptr = path + MAXPATHLEN;
197 if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
198 afp_errno = AFPERR_NOOBJ;
202 pathlen = len; /* no 0 in the last part */
204 strcpy(ptr - len, mpath);
207 ret = dirsearch(vol,id);
212 if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
214 NULL == (mpath = utompath(vol, upath, cnid, utf8))
216 afp_errno = AFPERR_NOOBJ;
220 len = strlen(mpath) + 1;
222 if (pathlen > maxpath) {
223 afp_errno = AFPERR_PARAM;
226 strcpy(ptr - len, mpath);
230 /* fill the cache, another place where we know about the path type */
236 temp16 = htons(pathlen);
237 memcpy(ptr, &temp16, sizeof(temp16));
239 temp = htonl(kTextEncodingUTF8);
241 memcpy(ptr, &temp, sizeof(temp));
247 *ptr = (unsigned char)pathlen;
251 /* cname is not efficient */
252 if (cname( vol, ret, &ptr ) == NULL )
255 return dirsearch(vol, did);
258 /* child addition/removal */
259 static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
264 b->d_next = a->d_child;
265 b->d_prev = b->d_next->d_prev;
266 b->d_next->d_prev = b;
267 b->d_prev->d_next = b;
269 if (!hash_alloc_insert(vol->v_hash, b, b)) {
270 LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
274 static void dirchildremove(struct dir *a,struct dir *b)
277 a->d_child = (b == b->d_next) ? NULL : b->d_next;
278 b->d_next->d_prev = b->d_prev;
279 b->d_prev->d_next = b->d_next;
280 b->d_next = b->d_prev = b;
283 /* --------------------------- */
284 /* rotate the tree to the left */
285 static void dir_leftrotate(struct vol *vol, struct dir *dir)
287 struct dir *right = dir->d_right;
289 /* whee. move the right's left tree into dir's right tree */
290 dir->d_right = right->d_left;
291 if (right->d_left != SENTINEL)
292 right->d_left->d_back = dir;
294 if (right != SENTINEL) {
295 right->d_back = dir->d_back;
299 if (!dir->d_back) /* no parent. move the right tree to the top. */
301 else if (dir == dir->d_back->d_left) /* we were on the left */
302 dir->d_back->d_left = right;
304 dir->d_back->d_right = right; /* we were on the right */
306 /* re-insert dir on the left tree */
313 /* rotate the tree to the right */
314 static void dir_rightrotate(struct vol *vol, struct dir *dir)
316 struct dir *left = dir->d_left;
318 /* whee. move the left's right tree into dir's left tree */
319 dir->d_left = left->d_right;
320 if (left->d_right != SENTINEL)
321 left->d_right->d_back = dir;
323 if (left != SENTINEL) {
324 left->d_back = dir->d_back;
328 if (!dir->d_back) /* no parent. move the left tree to the top. */
330 else if (dir == dir->d_back->d_right) /* we were on the right */
331 dir->d_back->d_right = left;
333 dir->d_back->d_left = left; /* we were on the left */
335 /* re-insert dir on the right tree */
341 /* recolor after a removal */
342 static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
346 while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
347 /* are we on the left tree? */
348 if (dir == dir->d_back->d_left) {
349 leaf = dir->d_back->d_right; /* get right side */
350 if (leaf->d_color == DIRTREE_COLOR_RED) {
351 /* we're red. we need to change to black. */
352 leaf->d_color = DIRTREE_COLOR_BLACK;
353 dir->d_back->d_color = DIRTREE_COLOR_RED;
354 dir_leftrotate(vol, dir->d_back);
355 leaf = dir->d_back->d_right;
358 /* right leaf has black end nodes */
359 if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
360 (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
361 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
362 dir = dir->d_back; /* ascend */
364 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
365 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
366 leaf->d_color = DIRTREE_COLOR_RED;
367 dir_rightrotate(vol, leaf);
368 leaf = dir->d_back->d_right;
370 leaf->d_color = dir->d_back->d_color;
371 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
372 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
373 dir_leftrotate(vol, dir->d_back);
376 } else { /* right tree */
377 leaf = dir->d_back->d_left; /* left tree */
378 if (leaf->d_color == DIRTREE_COLOR_RED) {
379 leaf->d_color = DIRTREE_COLOR_BLACK;
380 dir->d_back->d_color = DIRTREE_COLOR_RED;
381 dir_rightrotate(vol, dir->d_back);
382 leaf = dir->d_back->d_left;
385 /* left leaf has black end nodes */
386 if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
387 (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
388 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
389 dir = dir->d_back; /* ascend */
391 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
392 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
393 leaf->d_color = DIRTREE_COLOR_RED;
394 dir_leftrotate(vol, leaf);
395 leaf = dir->d_back->d_left;
397 leaf->d_color = dir->d_back->d_color;
398 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
399 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
400 dir_rightrotate(vol, dir->d_back);
405 dir->d_color = DIRTREE_COLOR_BLACK;
411 /* --------------------- */
412 static void dir_hash_del(const struct vol *vol, struct dir *dir)
416 hn = hash_lookup(vol->v_hash, dir);
418 LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
421 hash_delete(vol->v_hash, hn);
425 /* remove the node from the tree. this is just like insertion, but
426 * different. actually, it has to worry about a bunch of things that
427 * insertion doesn't care about. */
429 static void dir_remove( const struct vol *vol _U_, struct dir *dir)
432 struct ofork *of, *last;
433 struct dir *node, *leaf;
434 #endif /* REMOVE_NODES */
436 if (!dir || (dir == SENTINEL))
439 /* i'm not sure if it really helps to delete stuff. */
440 dir_hash_del(vol, dir);
443 dir->d_m_name = NULL;
444 dir->d_u_name = NULL;
445 dir->d_m_name_ucs2 = NULL;
446 #else /* ! REMOVE_NODES */
448 /* go searching for a node with at most one child */
449 if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
453 while (node->d_left != SENTINEL)
458 leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
461 leaf->d_back = node->d_back;
464 } else if (node == node->d_back->d_left) { /* left tree */
465 node->d_back->d_left = leaf;
467 node->d_back->d_right = leaf;
470 /* we want to free node, but we also want to free the data in dir.
471 * currently, that's d_name and the directory traversal bits.
472 * we just copy the necessary bits and then fix up all the
473 * various pointers to the directory. needless to say, there are
474 * a bunch of places that store the directory struct. */
476 struct dir save, *tmp;
478 memcpy(&save, dir, sizeof(save));
479 memcpy(dir, node, sizeof(struct dir));
481 /* restore the red-black bits */
482 dir->d_left = save.d_left;
483 dir->d_right = save.d_right;
484 dir->d_back = save.d_back;
485 dir->d_color = save.d_color;
487 if (node == vol->v_dir) {/* we may need to fix up this pointer */
489 rootpar.d_child = vol->v_dir;
491 /* if we aren't the root directory, we have parents and
492 * siblings to worry about */
493 if (dir->d_parent->d_child == node)
494 dir->d_parent->d_child = dir;
495 dir->d_next->d_prev = dir;
496 dir->d_prev->d_next = dir;
499 /* fix up children. */
503 tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
506 if (node == curdir) /* another pointer to fixup */
509 /* we also need to fix up oforks. bleah */
510 if ((of = dir->d_ofork)) {
511 last = of->of_d_prev;
514 of = (last == of) ? NULL : of->of_d_next;
518 /* set the node's d_name */
519 node->d_m_name = save.d_m_name;
520 node->d_u_name = save.d_u_name;
521 node->d_m_name_ucs2 = save.d_m_name_ucs2;
524 if (node->d_color == DIRTREE_COLOR_BLACK)
525 dir_rmrecolor(vol, leaf);
527 if (node->d_m_name_ucs2)
528 free(node->d_u_name_ucs2);
529 if (node->d_u_name != node->d_m_name) {
530 free(node->d_u_name);
532 free(node->d_m_name);
534 #endif /* ! REMOVE_NODES */
537 /* ---------------------------------------
538 * remove the node and its childs from the tree
540 * FIXME what about opened forks with refs to it?
541 * it's an afp specs violation because you can't delete
542 * an opened forks. Now afpd doesn't care about forks opened by other
543 * process. It's fixable within afpd if fnctl_lock, doable with smb and
544 * next to impossible for nfs and local filesystem access.
546 static void dir_invalidate( const struct vol *vol, struct dir *dir)
549 /* v_root can't be deleted */
550 if (movecwd(vol, vol->v_root) < 0) {
551 LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
555 dirchildremove(dir->d_parent, dir);
556 dir_remove( vol, dir );
559 /* ------------------------------------ */
560 static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
565 while (pdir->d_did != dir->d_did ) {
566 if ( pdir->d_did > dir->d_did ) {
567 if ( pdir->d_left == SENTINEL ) {
574 if ( pdir->d_right == SENTINEL ) {
579 pdir = pdir->d_right;
585 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
588 caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
593 static u_int32_t did = 0;
594 static char cname[MAXPATHLEN];
595 static char lname[MAXPATHLEN];
596 ucs2_t u2_path[MAXPATHLEN];
597 ucs2_t u2_dename[MAXPATHLEN];
598 char *tmp, *savepath;
600 if (!(vol->v_flags & AFPVOL_CASEINSEN))
603 if (veto_file(ENUMVETO, path->u_name))
606 savepath = path->u_name;
608 /* very simple cache */
609 if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
610 path->u_name = cname;
612 if (of_stat( path ) == 0 ) {
615 /* something changed, we cannot stat ... */
619 if (NULL == ( dp = opendir( "." )) ) {
620 LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
625 /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
626 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, strlen(path->u_name), u2_path, sizeof(u2_path)) )
627 LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
629 /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
631 for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
632 if (NULL == check_dirent(vol, de->d_name))
635 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, strlen(de->d_name), u2_dename, sizeof(u2_dename)) )
638 if (strcasecmp_w( u2_path, u2_dename) == 0) {
640 strlcpy(cname, de->d_name, sizeof(cname));
641 path->u_name = cname;
643 if (of_stat( path ) == 0 ) {
644 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
645 strlcpy(lname, tmp, sizeof(lname));
658 /* invalidate cache */
661 path->u_name = savepath;
663 /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
669 * attempt to extend the current dir. tree to include path
670 * as a side-effect, movecwd to that point and return the new dir
673 extenddir(struct vol *vol, struct dir *dir, struct path *path)
677 if ( path->u_name == NULL) {
678 afp_errno = AFPERR_PARAM;
682 if (check_name(vol, path->u_name)) {
683 /* the name is illegal */
684 LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
686 afp_errno = AFPERR_PARAM;
690 if (of_stat( path ) != 0 ) {
691 if (!(vol->v_flags & AFPVOL_CASEINSEN))
693 else if(caseenumerate(vol, path, dir) != 0)
697 if (!S_ISDIR(path->st.st_mode)) {
701 /* mac name is always with the right encoding (from cname()) */
702 if (( dir = adddir( vol, dir, path)) == NULL ) {
707 if ( movecwd( vol, dir ) < 0 ) {
714 /* -------------------------
715 appledouble mkdir afp error code.
717 static int netatalk_mkdir(const char *name)
719 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
722 return( AFPERR_NOOBJ );
724 return( AFPERR_VLOCK );
727 return( AFPERR_ACCESS );
729 return( AFPERR_EXIST );
732 return( AFPERR_DFULL );
734 return( AFPERR_PARAM );
740 /* ------------------- */
741 static int deletedir(char *dir)
743 char path[MAXPATHLEN + 1];
751 if ((len = strlen(dir)) +2 > sizeof(path))
755 if ((dp = opendir(dir)) == NULL)
761 remain = sizeof(path) -len -1;
762 while ((de = readdir(dp)) && err == AFP_OK) {
763 /* skip this and previous directory */
764 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
767 if (strlen(de->d_name) > remain) {
771 strcpy(path + len, de->d_name);
772 if (stat(path, &st)) {
775 if (S_ISDIR(st.st_mode)) {
776 err = deletedir(path);
778 err = netatalk_unlink(path);
783 /* okay. the directory is empty. delete it. note: we already got rid
786 err = netatalk_rmdir(dir);
791 /* do a recursive copy. */
792 static int copydir(const struct vol *vol, char *src, char *dst)
794 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
803 /* doesn't exist or the path is too long. */
804 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
805 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
806 ((dp = opendir(src)) == NULL))
809 /* try to create the destination directory */
810 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
815 /* set things up to copy */
819 srem = sizeof(spath) - slen -1;
824 drem = sizeof(dpath) - dlen -1;
827 while ((de = readdir(dp))) {
828 /* skip this and previous directory */
829 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
832 if (strlen(de->d_name) > srem) {
836 strcpy(spath + slen, de->d_name);
838 if (stat(spath, &st) == 0) {
839 if (strlen(de->d_name) > drem) {
843 strcpy(dpath + dlen, de->d_name);
845 if (S_ISDIR(st.st_mode)) {
846 if (AFP_OK != (err = copydir(vol, spath, dpath)))
848 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
852 /* keep the same time stamp. */
853 ut.actime = ut.modtime = st.st_mtime;
859 /* keep the same time stamp. */
860 if (stat(src, &st) == 0) {
861 ut.actime = ut.modtime = st.st_mtime;
871 /* --- public functions follow --- */
873 /* NOTE: we start off with at least one node (the root directory). */
874 static struct dir *dirinsert(struct vol *vol, struct dir *dir)
878 if ((node = dir_insert(vol, dir)))
881 /* recolor the tree. the current node is red. */
882 dir->d_color = DIRTREE_COLOR_RED;
884 /* parent of this node has to be black. if the parent node
885 * is red, then we have a grandparent. */
886 while ((dir != vol->v_root) &&
887 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
888 /* are we on the left tree? */
889 if (dir->d_back == dir->d_back->d_back->d_left) {
890 node = dir->d_back->d_back->d_right; /* get the right node */
891 if (node->d_color == DIRTREE_COLOR_RED) {
892 /* we're red. we need to change to black. */
893 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
894 node->d_color = DIRTREE_COLOR_BLACK;
895 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
896 dir = dir->d_back->d_back; /* finished. go up. */
898 if (dir == dir->d_back->d_right) {
900 dir_leftrotate(vol, dir);
902 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
903 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
904 dir_rightrotate(vol, dir->d_back->d_back);
907 node = dir->d_back->d_back->d_left;
908 if (node->d_color == DIRTREE_COLOR_RED) {
909 /* we're red. we need to change to black. */
910 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
911 node->d_color = DIRTREE_COLOR_BLACK;
912 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
913 dir = dir->d_back->d_back; /* finished. ascend */
915 if (dir == dir->d_back->d_left) {
917 dir_rightrotate(vol, dir);
919 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
920 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
921 dir_leftrotate(vol, dir->d_back->d_back);
926 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
930 /* ---------------------------- */
932 adddir(struct vol *vol, struct dir *dir, struct path *path)
934 struct dir *cdir, *edir;
942 upath = path->u_name;
944 upathlen = strlen(upath);
946 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
950 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
954 if ((cdir = dirnew(name, upath)) == NULL) {
955 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
958 if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, path->m_name, strlen(path->m_name), (char **)&cdir->d_m_name_ucs2)) {
959 LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
960 cdir->d_m_name_ucs2 = NULL;
965 if ((edir = dirinsert( vol, cdir ))) {
966 /* it's not possible with LASTDID
968 - someone else have moved the directory.
969 - it's a symlink inside the share.
970 - it's an ID reused, the old directory was deleted but not
971 the cnid record and the server've reused the inode for
973 for HASH (we should get ride of HASH)
974 - someone else have moved the directory.
975 - it's an ID reused as above
976 - it's a hash duplicate and we are in big trouble
978 deleted = (edir->d_m_name == NULL);
980 dir_hash_del(vol, edir);
982 edir->d_m_name = cdir->d_m_name;
983 edir->d_u_name = cdir->d_u_name;
984 edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
987 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
988 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
989 hash_alloc_insert(vol->v_hash, cdir, cdir);
992 /* the old was not in the same folder */
994 dirchildremove(cdir->d_parent, cdir);
997 /* parent/child directories */
998 cdir->d_parent = dir;
999 dirchildadd(vol, dir, cdir);
1003 /* --- public functions follow --- */
1004 /* free everything down. we don't bother to recolor as this is only
1005 * called to free the entire tree */
1006 void dirfreename(struct dir *dir)
1008 if (dir->d_u_name != dir->d_m_name) {
1009 free(dir->d_u_name);
1011 if (dir->d_m_name_ucs2)
1012 free(dir->d_m_name_ucs2);
1013 free(dir->d_m_name);
1016 void dirfree(struct dir *dir)
1018 if (!dir || (dir == SENTINEL))
1021 if ( dir->d_left != SENTINEL ) {
1022 dirfree( dir->d_left );
1024 if ( dir->d_right != SENTINEL ) {
1025 dirfree( dir->d_right );
1028 if (dir != SENTINEL) {
1034 /* --------------------------------------------
1035 * most of the time mac name and unix name are the same
1037 struct dir *dirnew(const char *m_name, const char *u_name)
1041 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1045 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1050 if (m_name == u_name || !strcmp(m_name, u_name)) {
1051 dir->d_u_name = dir->d_m_name;
1053 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1054 free(dir->d_m_name);
1059 dir->d_m_name_ucs2 = NULL;
1060 dir->d_left = dir->d_right = SENTINEL;
1061 dir->d_next = dir->d_prev = dir;
1065 /* ------------------ */
1066 static hash_val_t hash_fun_dir(const void *key)
1068 const struct dir *k = key;
1070 static unsigned long randbox[] = {
1071 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1072 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1073 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1074 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1077 const unsigned char *str = k->d_u_name;
1081 acc ^= randbox[(*str + acc) & 0xf];
1082 acc = (acc << 1) | (acc >> 31);
1084 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1085 acc = (acc << 2) | (acc >> 30);
1091 /* ---------------- */
1092 static int hash_comp_dir(const void *key1, const void *key2)
1094 const struct dir *k1 = key1;
1095 const struct dir *k2 = key2;
1097 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1100 /* ---------------- */
1104 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1107 /* ------------------ */
1108 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1111 movecwd failed some of dir path are not there anymore.
1112 FIXME Is it true with other errors?
1113 so we remove dir from the cache
1115 if (dir->d_did == DIRDID_ROOT_PARENT)
1117 if (afp_errno == AFPERR_ACCESS) {
1118 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1121 /* FIXME should we set these?, don't need to call stat() after:
1123 ret->st_errno = EACCES;
1125 ret->m_name = dir->d_m_name;
1126 ret->u_name = dir->d_u_name;
1129 } else if (afp_errno == AFPERR_NOOBJ) {
1130 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1133 strcpy(ret->m_name, dir->d_m_name);
1134 if (dir->d_m_name == dir->d_u_name) {
1135 ret->u_name = ret->m_name;
1138 size_t tp = strlen(ret->m_name)+1;
1140 ret->u_name = ret->m_name +tp;
1141 strcpy(ret->u_name, dir->d_u_name);
1143 /* FIXME should we set :
1145 ret->st_errno = ENOENT;
1147 dir_invalidate(vol, dir);
1150 dir_invalidate(vol, dir);
1154 /* -------------------------------------------------- */
1160 stat the file or errno
1163 curdir: filename parent directory
1169 stat the dir or errno
1173 curdir: dir parent directory
1181 curdir: dir parent directory
1188 cname(struct vol *vol, struct dir *dir, char **cpath)
1190 struct dir *cdir, *scdir=NULL;
1191 static char path[ MAXPATHLEN + 1];
1192 static struct path ret;
1204 afp_errno = AFPERR_NOOBJ;
1205 memset(&ret, 0, sizeof(ret));
1206 switch (ret.m_type = *data) { /* path type */
1209 len = (unsigned char) *data++;
1212 if (afp_version >= 30) {
1218 if (afp_version >= 30) {
1220 memcpy(&hint, data, sizeof(hint));
1222 data += sizeof(hint);
1224 memcpy(&len16, data, sizeof(len16));
1231 /* else it's an error */
1233 afp_errno = AFPERR_PARAM;
1236 *cpath += len + size;
1241 if (movecwd( vol, dir ) < 0 ) {
1242 return invalidate(vol, dir, &ret );
1244 if (*path == '\0') {
1251 if (*data == sep ) {
1255 while (*data == sep && len > 0 ) {
1256 if ( dir->d_parent == NULL ) {
1259 dir = dir->d_parent;
1264 /* would this be faster with strlen + strncpy? */
1266 while ( *data != sep && len > 0 ) {
1268 if (p > &path[ MAXPATHLEN]) {
1269 afp_errno = AFPERR_PARAM;
1275 /* short cut bits by chopping off a trailing \0. this also
1276 makes the traversal happy w/ filenames at the end of the
1283 if ( p == path ) { /* end of the name parameter */
1287 if (afp_version >= 30) {
1292 static char temp[ MAXPATHLEN + 1];
1294 if (dir->d_did == DIRDID_ROOT_PARENT) {
1296 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1297 So we compare it with the longname from the current volume and if they match
1298 we overwrite the requested path with the utf8 volume name so that the following
1301 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1302 if (strcasecmp( path, temp) == 0)
1303 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1306 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1307 afp_errno = AFPERR_PARAM;
1313 /* check for OS X mangled filename :( */
1315 t = demangle_osx(vol, path, dir->d_did, &fileid);
1318 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1319 * flags weren't the same
1321 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1322 /* at last got our view of mac name */
1327 if (ret.u_name == NULL) {
1328 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1329 afp_errno = AFPERR_PARAM;
1335 cdir = dir->d_child;
1337 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1338 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1339 CH_UCS2, path, strlen(path), (char **)&tmpname) )
1342 if (!cdir->d_m_name_ucs2) {
1343 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1344 /* this shouldn't happen !!!! */
1348 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1351 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1354 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1360 if (dir->d_did == DIRDID_ROOT_PARENT) {
1362 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1363 must check against the volume name.
1365 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1371 cdir = dirsearch_byname(vol, dir, ret.u_name);
1375 if (cdir == NULL && scdir != NULL) {
1377 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1380 if ( cdir == NULL ) {
1382 /* if dir == curdir it always succeed,
1383 even if curdir is deleted.
1384 it's not a pb because it will fail in extenddir
1386 if ( movecwd( vol, dir ) < 0 ) {
1387 /* dir is not valid anymore
1388 we delete dir from the cache and abort.
1390 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1391 afp_errno = AFPERR_NOOBJ;
1394 if (afp_errno == AFPERR_ACCESS)
1396 dir_invalidate(vol, dir);
1399 cdir = extenddir( vol, dir, &ret );
1403 cdir = extenddir( vol, dir, &ret );
1404 } /* if (!extend) */
1406 if ( cdir == NULL ) {
1408 if ( len > 0 || !ret.u_name ) {
1420 * Move curdir to dir, with a possible chdir()
1422 int movecwd(const struct vol *vol, struct dir *dir)
1424 char path[MAXPATHLEN + 1];
1429 if ( dir == curdir ) {
1432 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1433 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1437 p = path + sizeof(path) - 1;
1440 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1443 /* parent directory is deleted */
1444 afp_errno = AFPERR_NOOBJ;
1448 if (p -n -1 < path) {
1449 afp_errno = AFPERR_PARAM;
1456 if ( d != curdir ) {
1457 n = strlen( vol->v_path );
1458 if (p -n -1 < path) {
1459 afp_errno = AFPERR_PARAM;
1464 memcpy( p, vol->v_path, n );
1466 if ( chdir( p ) < 0 ) {
1470 afp_errno = AFPERR_ACCESS;
1473 afp_errno = AFPERR_NOOBJ;
1483 * We can't use unix file's perm to support Apple's inherited protection modes.
1484 * If we aren't the file's owner we can't change its perms when moving it and smb
1485 * nfs,... don't even try.
1487 #define AFP_CHECK_ACCESS
1489 int check_access(char *path, int mode)
1491 #ifdef AFP_CHECK_ACCESS
1499 accessmode(p, &ma, curdir, NULL);
1500 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1502 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1508 /* --------------------- */
1509 int file_access(struct path *path, int mode)
1513 accessmode(path->u_name, &ma, curdir, &path->st);
1514 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1516 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1522 /* --------------------- */
1523 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1525 dir->offcnt = count;
1526 dir->ctime = st->st_ctime;
1527 dir->d_flags &= ~DIRF_CNID;
1530 /* ---------------------
1531 * is our cached offspring count valid?
1534 static int diroffcnt(struct dir *dir, struct stat *st)
1536 return st->st_ctime == dir->ctime;
1539 /* ---------------------
1540 * is our cached also for reenumerate id?
1543 int dirreenumerate(struct dir *dir, struct stat *st)
1545 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1548 /* --------------------- */
1549 static int invisible_dots(const struct vol *vol, const char *name)
1551 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1554 /* ------------------------------
1556 (name, dir) with curdir:name == dir, from afp_enumerate
1559 int getdirparams(const struct vol *vol,
1560 u_int16_t bitmap, struct path *s_path,
1562 char *buf, size_t *buflen )
1566 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1567 int bit = 0, isad = 0;
1573 struct stat *st = &s_path->st;
1574 char *upath = s_path->u_name;
1576 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1577 (1 << DIRPBIT_CDATE) |
1578 (1 << DIRPBIT_MDATE) |
1579 (1 << DIRPBIT_BDATE) |
1580 (1 << DIRPBIT_FINFO)))) {
1582 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1583 if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1588 if ( dir->d_did == DIRDID_ROOT) {
1589 pdid = DIRDID_ROOT_PARENT;
1590 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1593 pdid = dir->d_parent->d_did;
1597 while ( bitmap != 0 ) {
1598 while (( bitmap & 1 ) == 0 ) {
1606 ad_getattr(&ad, &ashort);
1607 } else if (invisible_dots(vol, dir->d_u_name)) {
1608 ashort = htons(ATTRBIT_INVISIBLE);
1611 ashort |= htons(ATTRBIT_SHARED);
1612 memcpy( data, &ashort, sizeof( ashort ));
1613 data += sizeof( ashort );
1617 memcpy( data, &pdid, sizeof( pdid ));
1618 data += sizeof( pdid );
1621 case DIRPBIT_CDATE :
1622 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1623 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1624 memcpy( data, &aint, sizeof( aint ));
1625 data += sizeof( aint );
1628 case DIRPBIT_MDATE :
1629 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1630 memcpy( data, &aint, sizeof( aint ));
1631 data += sizeof( aint );
1634 case DIRPBIT_BDATE :
1635 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1636 aint = AD_DATE_START;
1637 memcpy( data, &aint, sizeof( aint ));
1638 data += sizeof( aint );
1641 case DIRPBIT_FINFO :
1643 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1644 } else { /* no appledouble */
1645 memset( data, 0, 32 );
1646 /* set default view -- this also gets done in ad_open() */
1647 ashort = htons(FINDERINFO_CLOSEDVIEW);
1648 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1650 /* dot files are by default visible */
1651 if (invisible_dots(vol, dir->d_u_name)) {
1652 ashort = htons(FINDERINFO_INVISIBLE);
1653 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1659 case DIRPBIT_LNAME :
1660 if (dir->d_m_name) /* root of parent can have a null name */
1663 memset(data, 0, sizeof(u_int16_t));
1664 data += sizeof( u_int16_t );
1667 case DIRPBIT_SNAME :
1668 memset(data, 0, sizeof(u_int16_t));
1669 data += sizeof( u_int16_t );
1673 memcpy( data, &dir->d_did, sizeof( aint ));
1674 data += sizeof( aint );
1677 case DIRPBIT_OFFCNT :
1679 /* this needs to handle current directory access rights */
1680 if (diroffcnt(dir, st)) {
1681 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1683 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1684 setdiroffcnt(dir, st, ret);
1685 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1687 ashort = htons( ashort );
1688 memcpy( data, &ashort, sizeof( ashort ));
1689 data += sizeof( ashort );
1693 aint = htonl(st->st_uid);
1694 memcpy( data, &aint, sizeof( aint ));
1695 data += sizeof( aint );
1699 aint = htonl(st->st_gid);
1700 memcpy( data, &aint, sizeof( aint ));
1701 data += sizeof( aint );
1704 case DIRPBIT_ACCESS :
1705 accessmode( upath, &ma, dir , st);
1707 *data++ = ma.ma_user;
1708 *data++ = ma.ma_world;
1709 *data++ = ma.ma_group;
1710 *data++ = ma.ma_owner;
1713 /* Client has requested the ProDOS information block.
1714 Just pass back the same basic block for all
1715 directories. <shirsch@ibm.net> */
1716 case DIRPBIT_PDINFO :
1717 if (afp_version >= 30) { /* UTF8 name */
1718 utf8 = kTextEncodingUTF8;
1719 if (dir->d_m_name) /* root of parent can have a null name */
1722 memset(data, 0, sizeof(u_int16_t));
1723 data += sizeof( u_int16_t );
1725 memcpy(data, &aint, sizeof( aint ));
1726 data += sizeof( aint );
1728 else { /* ProDOS Info Block */
1731 ashort = htons( 0x0200 );
1732 memcpy( data, &ashort, sizeof( ashort ));
1733 data += sizeof( ashort );
1734 memset( data, 0, sizeof( ashort ));
1735 data += sizeof( ashort );
1739 case DIRPBIT_UNIXPR :
1740 aint = htonl(st->st_uid);
1741 memcpy( data, &aint, sizeof( aint ));
1742 data += sizeof( aint );
1743 aint = htonl(st->st_gid);
1744 memcpy( data, &aint, sizeof( aint ));
1745 data += sizeof( aint );
1748 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1749 memcpy( data, &aint, sizeof( aint ));
1750 data += sizeof( aint );
1752 accessmode( upath, &ma, dir , st);
1754 *data++ = ma.ma_user;
1755 *data++ = ma.ma_world;
1756 *data++ = ma.ma_group;
1757 *data++ = ma.ma_owner;
1762 ad_close_metadata( &ad );
1764 return( AFPERR_BITMAP );
1770 ashort = htons( data - buf );
1771 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1772 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1774 if ( utf_nameoff ) {
1775 ashort = htons( data - buf );
1776 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1777 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1780 ad_close_metadata( &ad );
1782 *buflen = data - buf;
1786 /* ----------------------------- */
1787 int path_error(struct path *path, int error)
1789 /* - a dir with access error
1790 * - no error it's a file
1793 if (path_isadir(path))
1795 if (path->st_valid && path->st_errno)
1797 return AFPERR_BADTYPE ;
1800 /* ----------------------------- */
1801 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1806 u_int16_t vid, bitmap;
1812 memcpy( &vid, ibuf, sizeof( vid ));
1813 ibuf += sizeof( vid );
1815 if (NULL == ( vol = getvolbyvid( vid )) ) {
1816 return( AFPERR_PARAM );
1819 if (vol->v_flags & AFPVOL_RO)
1820 return AFPERR_VLOCK;
1822 memcpy( &did, ibuf, sizeof( did ));
1823 ibuf += sizeof( int );
1825 if (NULL == ( dir = dirlookup( vol, did )) ) {
1829 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1830 bitmap = ntohs( bitmap );
1831 ibuf += sizeof( bitmap );
1833 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1834 return get_afp_errno(AFPERR_NOOBJ);
1837 if ( *path->m_name != '\0' ) {
1838 rc = path_error(path, AFPERR_NOOBJ);
1839 /* maybe we are trying to set perms back */
1840 if (rc != AFPERR_ACCESS)
1845 * If ibuf is odd, make it even.
1847 if ((u_long)ibuf & 1 ) {
1851 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1852 setvoltime(obj, vol );
1858 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1860 * assume path == '\0' eg. it's a directory in canonical form
1863 struct path Cur_Path = {
1866 ".", /* unix name */
1868 NULL,/* struct dir */
1869 0, /* stat is not set */
1872 /* ------------------ */
1873 static int set_dir_errors(struct path *path, const char *where, int err)
1878 return AFPERR_ACCESS;
1880 return AFPERR_VLOCK;
1882 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1883 return AFPERR_PARAM;
1886 /* ------------------ */
1887 int setdirparams(const struct vol *vol,
1888 struct path *path, u_int16_t d_bitmap, char *buf )
1900 u_int16_t ashort, bshort;
1902 int change_mdate = 0;
1903 int change_parent_mdate = 0;
1905 u_int16_t bitmap = d_bitmap;
1906 u_char finder_buf[32];
1909 u_int16_t upriv_bit = 0;
1912 upath = path->u_name;
1914 while ( bitmap != 0 ) {
1915 while (( bitmap & 1 ) == 0 ) {
1923 memcpy( &ashort, buf, sizeof( ashort ));
1924 buf += sizeof( ashort );
1926 case DIRPBIT_CDATE :
1928 memcpy(&cdate, buf, sizeof(cdate));
1929 buf += sizeof( cdate );
1931 case DIRPBIT_MDATE :
1932 memcpy(&newdate, buf, sizeof(newdate));
1933 buf += sizeof( newdate );
1935 case DIRPBIT_BDATE :
1937 memcpy(&bdate, buf, sizeof(bdate));
1938 buf += sizeof( bdate );
1940 case DIRPBIT_FINFO :
1942 memcpy( finder_buf, buf, 32 );
1945 case DIRPBIT_UID : /* What kind of loser mounts as root? */
1946 change_parent_mdate = 1;
1947 memcpy( &owner, buf, sizeof(owner));
1948 buf += sizeof( owner );
1951 change_parent_mdate = 1;
1952 memcpy( &group, buf, sizeof( group ));
1953 buf += sizeof( group );
1955 case DIRPBIT_ACCESS :
1957 change_parent_mdate = 1;
1958 ma.ma_user = *buf++;
1959 ma.ma_world = *buf++;
1960 ma.ma_group = *buf++;
1961 ma.ma_owner = *buf++;
1962 mpriv = mtoumode( &ma ) | vol->v_dperm;
1963 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
1964 err = set_dir_errors(path, "setdirmode", errno);
1968 /* Ignore what the client thinks we should do to the
1969 ProDOS information block. Skip over the data and
1970 report nothing amiss. <shirsch@ibm.net> */
1971 case DIRPBIT_PDINFO :
1972 if (afp_version < 30) {
1976 err = AFPERR_BITMAP;
1980 case DIRPBIT_UNIXPR :
1981 if (vol_unix_priv(vol)) {
1982 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
1983 buf += sizeof( owner );
1984 memcpy( &group, buf, sizeof( group ));
1985 buf += sizeof( group );
1988 change_parent_mdate = 1;
1989 memcpy( &upriv, buf, sizeof( upriv ));
1990 buf += sizeof( upriv );
1991 upriv = ntohl (upriv) | vol->v_dperm;
1992 if (dir_rx_set(upriv)) {
1993 /* maybe we are trying to set perms back */
1994 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
1996 err = set_dir_errors(path, "setdirunixmode", errno);
2007 err = AFPERR_BITMAP;
2015 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2017 if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2019 * Check to see what we're trying to set. If it's anything
2020 * but ACCESS, UID, or GID, give an error. If it's any of those
2021 * three, we don't need the ad to be open, so just continue.
2023 * note: we also don't need to worry about mdate. also, be quiet
2024 * if we're using the noadouble option.
2026 if (!vol_noadouble(vol) && (d_bitmap &
2027 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2028 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2029 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2030 return AFPERR_ACCESS;
2036 * Check to see if a create was necessary. If it was, we'll want
2037 * to set our name, etc.
2039 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2040 ad_setname(&ad, curdir->d_m_name);
2046 while ( bitmap != 0 ) {
2047 while (( bitmap & 1 ) == 0 ) {
2055 ad_getattr(&ad, &bshort);
2056 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2057 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2058 change_parent_mdate = 1;
2059 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2060 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2064 ad_setattr(&ad, bshort);
2067 case DIRPBIT_CDATE :
2069 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2072 case DIRPBIT_MDATE :
2074 case DIRPBIT_BDATE :
2076 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2079 case DIRPBIT_FINFO :
2081 /* Fixes #2802236 */
2082 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2083 *fflags &= htons(~FINDERINFO_ISHARED);
2085 if ( dir->d_did == DIRDID_ROOT ) {
2087 * Alright, we admit it, this is *really* sick!
2088 * The 4 bytes that we don't copy, when we're dealing
2089 * with the root of a volume, are the directory's
2090 * location information. This eliminates that annoying
2091 * behavior one sees when mounting above another mount
2094 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2095 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2097 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2101 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2102 if ( (dir->d_did == DIRDID_ROOT) &&
2103 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2104 err = set_dir_errors(path, "setdeskowner", errno);
2105 if (isad && err == AFPERR_PARAM) {
2106 err = AFP_OK; /* ???*/
2109 goto setdirparam_done;
2112 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2113 err = set_dir_errors(path, "setdirowner", errno);
2114 goto setdirparam_done;
2118 if (dir->d_did == DIRDID_ROOT)
2119 setdeskowner( -1, ntohl(group) );
2120 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2121 err = set_dir_errors(path, "setdirowner", errno);
2122 goto setdirparam_done;
2125 case DIRPBIT_ACCESS :
2126 if (dir->d_did == DIRDID_ROOT) {
2128 if (!dir_rx_set(mpriv)) {
2129 /* we can't remove read and search for owner on volume root */
2130 err = AFPERR_ACCESS;
2131 goto setdirparam_done;
2135 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2136 err = set_dir_errors(path, "setdirmode", errno);
2137 goto setdirparam_done;
2140 case DIRPBIT_PDINFO :
2141 if (afp_version >= 30) {
2142 err = AFPERR_BITMAP;
2143 goto setdirparam_done;
2146 case DIRPBIT_UNIXPR :
2147 if (vol_unix_priv(vol)) {
2148 if (dir->d_did == DIRDID_ROOT) {
2149 if (!dir_rx_set(upriv)) {
2150 /* we can't remove read and search for owner on volume root */
2151 err = AFPERR_ACCESS;
2152 goto setdirparam_done;
2154 setdeskowner( -1, ntohl(group) );
2155 setdeskmode( upriv );
2157 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2158 err = set_dir_errors(path, "setdirowner", errno);
2159 goto setdirparam_done;
2162 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2163 err = set_dir_errors(path, "setdirunixmode", errno);
2164 goto setdirparam_done;
2168 err = AFPERR_BITMAP;
2169 goto setdirparam_done;
2173 err = AFPERR_BITMAP;
2174 goto setdirparam_done;
2183 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2184 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2188 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2189 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2194 if (path->st_valid && !path->st_errno) {
2195 struct stat *st = &path->st;
2197 if (dir && dir->d_parent) {
2198 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2202 ad_close_metadata( &ad);
2205 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2206 && gettimeofday(&tv, NULL) == 0) {
2207 if (!movecwd(vol, dir->d_parent)) {
2208 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2209 /* be careful with bitmap because now dir is null */
2210 bitmap = 1<<DIRPBIT_MDATE;
2211 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2212 /* should we reset curdir ?*/
2219 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2233 memcpy( &vid, ibuf, sizeof( vid ));
2234 ibuf += sizeof( vid );
2235 if (NULL == (vol = getvolbyvid( vid )) ) {
2236 return( AFPERR_PARAM );
2239 memcpy( &did, ibuf, sizeof( did ));
2240 ibuf += sizeof( did );
2244 * if it's CNID 2 our only choice to meet the specs is call sync.
2245 * For any other CNID just sync that dir. To my knowledge the
2246 * intended use of FPSyncDir is to sync the volume so all we're
2247 * ever going to see here is probably CNID 2. Anyway, we' prepared.
2250 if ( ntohl(did) == 2 ) {
2253 if (NULL == ( dir = dirlookup( vol, did )) ) {
2254 return afp_errno; /* was AFPERR_NOOBJ */
2257 if (movecwd( vol, dir ) < 0 )
2258 return ( AFPERR_NOOBJ );
2261 * Assuming only OSens that have dirfd also may require fsyncing directories
2262 * in order to flush metadata e.g. Linux.
2266 if (NULL == ( dp = opendir( "." )) ) {
2269 return( AFPERR_NOOBJ );
2271 return( AFPERR_ACCESS );
2273 return( AFPERR_PARAM );
2277 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2280 if ( fsync ( dfd ) < 0 )
2281 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2282 dir->d_u_name, strerror(errno) );
2283 closedir(dp); /* closes dfd too */
2286 if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2289 return( AFPERR_NOOBJ );
2291 return( AFPERR_ACCESS );
2293 return( AFPERR_PARAM );
2297 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2298 vol->ad_path(".", ADFLAGS_DIR) );
2300 if ( fsync(dfd) < 0 )
2301 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2302 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2309 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2315 struct path *s_path;
2323 memcpy( &vid, ibuf, sizeof( vid ));
2324 ibuf += sizeof( vid );
2325 if (NULL == ( vol = getvolbyvid( vid )) ) {
2326 return( AFPERR_PARAM );
2329 if (vol->v_flags & AFPVOL_RO)
2330 return AFPERR_VLOCK;
2332 memcpy( &did, ibuf, sizeof( did ));
2333 ibuf += sizeof( did );
2334 if (NULL == ( dir = dirlookup( vol, did )) ) {
2335 return afp_errno; /* was AFPERR_NOOBJ */
2337 /* for concurrent access we need to be sure we are not in the
2338 * folder we want to create...
2342 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2343 return get_afp_errno(AFPERR_PARAM);
2345 /* cname was able to move curdir to it! */
2346 if (*s_path->m_name == '\0')
2347 return AFPERR_EXIST;
2349 upath = s_path->u_name;
2351 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2355 if (of_stat(s_path) < 0) {
2359 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2363 if ( movecwd( vol, dir ) < 0 ) {
2364 return( AFPERR_PARAM );
2367 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2368 if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2369 if (vol_noadouble(vol))
2370 goto createdir_done;
2371 return( AFPERR_ACCESS );
2373 ad_setname(&ad, s_path->m_name);
2374 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2377 ad_close_metadata( &ad);
2380 #ifdef HAVE_NFSv4_ACLS
2381 /* FIXME: are we really inside the created dir? */
2382 addir_inherit_acl(vol);
2385 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2386 *rbuflen = sizeof( u_int32_t );
2387 setvoltime(obj, vol );
2392 * dst new unix filename (not a pathname)
2393 * newname new mac name
2397 int renamedir(const struct vol *vol, char *src, char *dst,
2399 struct dir *newparent,
2407 /* existence check moved to afp_moveandrename */
2408 if ( unix_rename( src, dst ) < 0 ) {
2411 return( AFPERR_NOOBJ );
2413 return( AFPERR_ACCESS );
2415 return AFPERR_VLOCK;
2417 /* tried to move directory into a subdirectory of itself */
2418 return AFPERR_CANTMOVE;
2420 /* this needs to copy and delete. bleah. that means we have
2421 * to deal with entire directory hierarchies. */
2422 if ((err = copydir(vol, src, dst)) < 0) {
2426 if ((err = deletedir(src)) < 0)
2430 return( AFPERR_PARAM );
2434 vol->vfs->vfs_renamedir(vol, src, dst);
2436 len = strlen( newname );
2437 /* rename() succeeded so we need to update our tree even if we can't open
2441 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2443 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2444 ad_setname(&ad, newname);
2446 ad_close_metadata( &ad);
2449 dir_hash_del(vol, dir);
2450 if (dir->d_m_name == dir->d_u_name)
2451 dir->d_u_name = NULL;
2453 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2454 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2455 /* FIXME : fatal ? */
2458 dir->d_m_name = buf;
2459 strcpy( dir->d_m_name, newname );
2461 if (newname == dst) {
2462 free(dir->d_u_name);
2463 dir->d_u_name = dir->d_m_name;
2466 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2467 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2470 dir->d_u_name = buf;
2471 strcpy( dir->d_u_name, dst );
2474 if (dir->d_m_name_ucs2)
2475 free(dir->d_m_name_ucs2);
2477 dir->d_m_name_ucs2 = NULL;
2478 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))
2479 dir->d_m_name_ucs2 = NULL;
2481 if (( parent = dir->d_parent ) == NULL ) {
2484 if ( parent == newparent ) {
2485 hash_alloc_insert(vol->v_hash, dir, dir);
2489 /* detach from old parent and add to new one. */
2490 dirchildremove(parent, dir);
2491 dir->d_parent = newparent;
2492 dirchildadd(vol, newparent, dir);
2496 /* delete an empty directory */
2497 int deletecurdir(const struct vol *vol)
2507 if ( curdir->d_parent == NULL ) {
2508 return( AFPERR_ACCESS );
2513 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2514 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2516 ad_getattr(&ad, &ashort);
2517 ad_close( &ad, ADFLAGS_HF );
2518 if ((ashort & htons(ATTRBIT_NODELETE))) {
2519 return AFPERR_OLOCK;
2522 err = vol->vfs->vfs_deletecurdir(vol);
2527 /* now get rid of dangling symlinks */
2528 if ((dp = opendir("."))) {
2529 while ((de = readdir(dp))) {
2530 /* skip this and previous directory */
2531 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2534 /* bail if it's not a symlink */
2535 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2537 return AFPERR_DIRNEMPT;
2540 if ((err = netatalk_unlink(de->d_name))) {
2547 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2552 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2553 dirchildremove(curdir, fdir);
2554 cnid_delete(vol->v_cdb, fdir->d_did);
2555 dir_remove( vol, fdir );
2560 /* inode is used as key for cnid.
2561 * Close the descriptor only after cnid_delete
2569 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2580 sfunc = (unsigned char) *ibuf++;
2584 if (sfunc >= 3 && sfunc <= 6) {
2585 if (afp_version < 30) {
2586 return( AFPERR_PARAM );
2593 case 3 :/* unicode */
2594 memcpy( &id, ibuf, sizeof( id ));
2597 if (( pw = getpwuid( id )) == NULL ) {
2598 return( AFPERR_NOITEM );
2600 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2601 pw->pw_name, strlen(pw->pw_name), &name);
2608 case 4 : /* unicode */
2609 memcpy( &id, ibuf, sizeof( id ));
2612 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2613 return( AFPERR_NOITEM );
2615 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2616 gr->gr_name, strlen(gr->gr_name), &name);
2622 #ifdef HAVE_NFSv4_ACLS
2623 case 5 : /* UUID -> username */
2624 case 6 : /* UUID -> groupname */
2625 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2626 return AFPERR_PARAM;
2627 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2628 len = getnamefromuuid( ibuf, &name, &type);
2629 if (len != 0) /* its a error code, not len */
2630 return AFPERR_NOITEM;
2631 if (type == UUID_USER) {
2632 if (( pw = getpwnam( name )) == NULL )
2633 return( AFPERR_NOITEM );
2634 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2635 id = htonl(UUID_USER);
2636 memcpy( rbuf, &id, sizeof( id ));
2637 id = htonl( pw->pw_uid);
2638 rbuf += sizeof( id );
2639 memcpy( rbuf, &id, sizeof( id ));
2640 rbuf += sizeof( id );
2641 *rbuflen = 2 * sizeof( id );
2642 } else { /* type == UUID_GROUP */
2643 if (( gr = getgrnam( name )) == NULL )
2644 return( AFPERR_NOITEM );
2645 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2646 id = htonl(UUID_GROUP);
2647 memcpy( rbuf, &id, sizeof( id ));
2648 rbuf += sizeof( id );
2649 id = htonl( gr->gr_gid);
2650 memcpy( rbuf, &id, sizeof( id ));
2651 rbuf += sizeof( id );
2652 *rbuflen = 2 * sizeof( id );
2657 return( AFPERR_PARAM );
2661 len = strlen( name );
2664 u_int16_t tp = htons(len);
2665 memcpy(rbuf, &tp, sizeof(tp));
2674 memcpy( rbuf, name, len );
2682 int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2691 sfunc = (unsigned char) *ibuf++;
2693 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2696 case 2 : /* unicode */
2697 if (afp_version < 30) {
2698 return( AFPERR_PARAM );
2700 memcpy(&ulen, ibuf, sizeof(ulen));
2703 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2707 len = (unsigned char) *ibuf++;
2709 #ifdef HAVE_NFSv4_ACLS
2710 case 5 : /* username -> UUID */
2711 case 6 : /* groupname -> UUID */
2712 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2713 return AFPERR_PARAM;
2714 memcpy(&ulen, ibuf, sizeof(ulen));
2720 return( AFPERR_PARAM );
2726 return AFPERR_PARAM;
2729 case 1 : /* unicode */
2731 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2732 return( AFPERR_NOITEM );
2736 memcpy( rbuf, &id, sizeof( id ));
2737 *rbuflen = sizeof( id );
2740 case 2 : /* unicode */
2742 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2743 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2744 return( AFPERR_NOITEM );
2747 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2749 memcpy( rbuf, &id, sizeof( id ));
2750 *rbuflen = sizeof( id );
2752 #ifdef HAVE_NFSv4_ACLS
2753 case 5 : /* username -> UUID */
2754 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2755 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2756 return AFPERR_NOITEM;
2757 *rbuflen = UUID_BINSIZE;
2759 case 6 : /* groupname -> UUID */
2760 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2761 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2762 return AFPERR_NOITEM;
2763 *rbuflen = UUID_BINSIZE;
2771 /* ------------------------------------
2772 variable DID support
2774 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2785 /* do nothing as dids are static for the life of the process. */
2789 memcpy(&vid, ibuf, sizeof( vid ));
2790 ibuf += sizeof( vid );
2791 if (( vol = getvolbyvid( vid )) == NULL ) {
2792 return( AFPERR_PARAM );
2795 memcpy( &did, ibuf, sizeof( did ));
2796 ibuf += sizeof( did );
2797 if (( dir = dirlookup( vol, did )) == NULL ) {
2798 return( AFPERR_PARAM );
2801 /* dir_remove -- deletedid */
2807 /* did creation gets done automatically
2808 * there's a pb again with case but move it to cname
2810 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2813 struct dir *parentdir;
2821 memcpy(&vid, ibuf, sizeof(vid));
2822 ibuf += sizeof( vid );
2824 if (NULL == ( vol = getvolbyvid( vid )) ) {
2825 return( AFPERR_PARAM );
2828 memcpy(&did, ibuf, sizeof(did));
2829 ibuf += sizeof(did);
2831 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2835 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2836 return get_afp_errno(AFPERR_PARAM);
2839 if ( *path->m_name != '\0' ) {
2840 return path_error(path, AFPERR_NOOBJ);
2843 if ( !path->st_valid && of_stat(path ) < 0 ) {
2844 return( AFPERR_NOOBJ );
2846 if ( path->st_errno ) {
2847 return( AFPERR_NOOBJ );
2850 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2851 *rbuflen = sizeof(curdir->d_did);