2 * $Id: directory.c,v 1.98 2009-06-19 13:38:32 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;
707 if (of_stat( path ) != 0 ) {
708 if (!(vol->v_flags & AFPVOL_CASEINSEN))
710 else if(caseenumerate(vol, path, dir) != 0)
714 if (!S_ISDIR(path->st.st_mode)) {
718 /* mac name is always with the right encoding (from cname()) */
719 if (( dir = adddir( vol, dir, path)) == NULL ) {
724 if ( movecwd( vol, dir ) < 0 ) {
731 /* -------------------
732 system rmdir with afp error code.
733 ENOENT is not an error.
735 int netatalk_rmdir(const char *name)
737 if (rmdir(name) < 0) {
742 return AFPERR_DIRNEMPT;
745 return AFPERR_ACCESS;
755 /* -------------------------
756 appledouble mkdir afp error code.
758 static int netatalk_mkdir(const char *name)
760 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
763 return( AFPERR_NOOBJ );
765 return( AFPERR_VLOCK );
768 return( AFPERR_ACCESS );
770 return( AFPERR_EXIST );
773 return( AFPERR_DFULL );
775 return( AFPERR_PARAM );
781 /* -------------------
782 system unlink with afp error code.
783 ENOENT is not an error.
785 int netatalk_unlink(const char *name)
787 if (unlink(name) < 0) {
795 return AFPERR_ACCESS;
803 /* ------------------- */
804 static int deletedir(char *dir)
806 char path[MAXPATHLEN + 1];
814 if ((len = strlen(dir)) +2 > sizeof(path))
818 if ((dp = opendir(dir)) == NULL)
824 remain = sizeof(path) -len -1;
825 while ((de = readdir(dp)) && err == AFP_OK) {
826 /* skip this and previous directory */
827 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
830 if (strlen(de->d_name) > remain) {
834 strcpy(path + len, de->d_name);
835 if (stat(path, &st)) {
838 if (S_ISDIR(st.st_mode)) {
839 err = deletedir(path);
841 err = netatalk_unlink(path);
846 /* okay. the directory is empty. delete it. note: we already got rid
849 err = netatalk_rmdir(dir);
854 /* do a recursive copy. */
855 static int copydir(const struct vol *vol, char *src, char *dst)
857 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
866 /* doesn't exist or the path is too long. */
867 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
868 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
869 ((dp = opendir(src)) == NULL))
872 /* try to create the destination directory */
873 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
878 /* set things up to copy */
882 srem = sizeof(spath) - slen -1;
887 drem = sizeof(dpath) - dlen -1;
890 while ((de = readdir(dp))) {
891 /* skip this and previous directory */
892 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
895 if (strlen(de->d_name) > srem) {
899 strcpy(spath + slen, de->d_name);
901 if (stat(spath, &st) == 0) {
902 if (strlen(de->d_name) > drem) {
906 strcpy(dpath + dlen, de->d_name);
908 if (S_ISDIR(st.st_mode)) {
909 if (AFP_OK != (err = copydir(vol, spath, dpath)))
911 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
915 /* keep the same time stamp. */
916 ut.actime = ut.modtime = st.st_mtime;
922 /* keep the same time stamp. */
923 if (stat(src, &st) == 0) {
924 ut.actime = ut.modtime = st.st_mtime;
934 /* --- public functions follow --- */
936 /* NOTE: we start off with at least one node (the root directory). */
937 static struct dir *dirinsert( vol, dir )
943 if ((node = dir_insert(vol, dir)))
946 /* recolor the tree. the current node is red. */
947 dir->d_color = DIRTREE_COLOR_RED;
949 /* parent of this node has to be black. if the parent node
950 * is red, then we have a grandparent. */
951 while ((dir != vol->v_root) &&
952 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
953 /* are we on the left tree? */
954 if (dir->d_back == dir->d_back->d_back->d_left) {
955 node = dir->d_back->d_back->d_right; /* get the right node */
956 if (node->d_color == DIRTREE_COLOR_RED) {
957 /* we're red. we need to change to black. */
958 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
959 node->d_color = DIRTREE_COLOR_BLACK;
960 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
961 dir = dir->d_back->d_back; /* finished. go up. */
963 if (dir == dir->d_back->d_right) {
965 dir_leftrotate(vol, dir);
967 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
968 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
969 dir_rightrotate(vol, dir->d_back->d_back);
972 node = dir->d_back->d_back->d_left;
973 if (node->d_color == DIRTREE_COLOR_RED) {
974 /* we're red. we need to change to black. */
975 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
976 node->d_color = DIRTREE_COLOR_BLACK;
977 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
978 dir = dir->d_back->d_back; /* finished. ascend */
980 if (dir == dir->d_back->d_left) {
982 dir_rightrotate(vol, dir);
984 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
985 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
986 dir_leftrotate(vol, dir->d_back->d_back);
991 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
995 /* ---------------------------- */
997 adddir( vol, dir, path)
1002 struct dir *cdir, *edir;
1010 upath = path->u_name;
1012 upathlen = strlen(upath);
1014 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
1018 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
1021 name = path->m_name;
1022 if ((cdir = dirnew(name, upath)) == NULL) {
1023 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
1026 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)) {
1027 LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
1028 cdir->d_m_name_ucs2 = NULL;
1033 if ((edir = dirinsert( vol, cdir ))) {
1034 /* it's not possible with LASTDID
1036 - someone else have moved the directory.
1037 - it's a symlink inside the share.
1038 - it's an ID reused, the old directory was deleted but not
1039 the cnid record and the server've reused the inode for
1041 for HASH (we should get ride of HASH)
1042 - someone else have moved the directory.
1043 - it's an ID reused as above
1044 - it's a hash duplicate and we are in big trouble
1046 deleted = (edir->d_m_name == NULL);
1048 dir_hash_del(vol, edir);
1050 edir->d_m_name = cdir->d_m_name;
1051 edir->d_u_name = cdir->d_u_name;
1052 edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
1055 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
1056 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
1057 hash_alloc_insert(vol->v_hash, cdir, cdir);
1060 /* the old was not in the same folder */
1062 dirchildremove(cdir->d_parent, cdir);
1065 /* parent/child directories */
1066 cdir->d_parent = dir;
1067 dirchildadd(vol, dir, cdir);
1071 /* --- public functions follow --- */
1072 /* free everything down. we don't bother to recolor as this is only
1073 * called to free the entire tree */
1074 void dirfreename(struct dir *dir)
1076 if (dir->d_u_name != dir->d_m_name) {
1077 free(dir->d_u_name);
1079 if (dir->d_m_name_ucs2)
1080 free(dir->d_m_name_ucs2);
1081 free(dir->d_m_name);
1087 if (!dir || (dir == SENTINEL))
1090 if ( dir->d_left != SENTINEL ) {
1091 dirfree( dir->d_left );
1093 if ( dir->d_right != SENTINEL ) {
1094 dirfree( dir->d_right );
1097 if (dir != SENTINEL) {
1103 /* --------------------------------------------
1104 * most of the time mac name and unix name are the same
1106 struct dir *dirnew(const char *m_name, const char *u_name)
1110 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1114 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1119 if (m_name == u_name || !strcmp(m_name, u_name)) {
1120 dir->d_u_name = dir->d_m_name;
1122 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1123 free(dir->d_m_name);
1128 dir->d_m_name_ucs2 = NULL;
1129 dir->d_left = dir->d_right = SENTINEL;
1130 dir->d_next = dir->d_prev = dir;
1134 /* ------------------ */
1135 static hash_val_t hash_fun_dir(const void *key)
1137 const struct dir *k = key;
1139 static unsigned long randbox[] = {
1140 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1141 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1142 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1143 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1146 const unsigned char *str = k->d_u_name;
1150 acc ^= randbox[(*str + acc) & 0xf];
1151 acc = (acc << 1) | (acc >> 31);
1153 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1154 acc = (acc << 2) | (acc >> 30);
1160 /* ---------------- */
1161 static int hash_comp_dir(const void *key1, const void *key2)
1163 const struct dir *k1 = key1;
1164 const struct dir *k2 = key2;
1166 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1169 /* ---------------- */
1173 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1176 /* ------------------ */
1177 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1180 movecwd failed some of dir path are not there anymore.
1181 FIXME Is it true with other errors?
1182 so we remove dir from the cache
1184 if (dir->d_did == DIRDID_ROOT_PARENT)
1186 if (afp_errno == AFPERR_ACCESS) {
1187 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1190 /* FIXME should we set these?, don't need to call stat() after:
1192 ret->st_errno = EACCES;
1194 ret->m_name = dir->d_m_name;
1195 ret->u_name = dir->d_u_name;
1198 } else if (afp_errno == AFPERR_NOOBJ) {
1199 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1202 strcpy(ret->m_name, dir->d_m_name);
1203 if (dir->d_m_name == dir->d_u_name) {
1204 ret->u_name = ret->m_name;
1207 size_t tp = strlen(ret->m_name)+1;
1209 ret->u_name = ret->m_name +tp;
1210 strcpy(ret->u_name, dir->d_u_name);
1212 /* FIXME should we set :
1214 ret->st_errno = ENOENT;
1216 dir_invalidate(vol, dir);
1219 dir_invalidate(vol, dir);
1223 /* -------------------------------------------------- */
1229 stat the file or errno
1232 curdir: filename parent directory
1238 stat the dir or errno
1242 curdir: dir parent directory
1250 curdir: dir parent directory
1257 cname( vol, dir, cpath )
1258 const struct vol *vol;
1262 struct dir *cdir, *scdir=NULL;
1263 static char path[ MAXPATHLEN + 1];
1264 static struct path ret;
1276 afp_errno = AFPERR_NOOBJ;
1277 memset(&ret, 0, sizeof(ret));
1278 switch (ret.m_type = *data) { /* path type */
1281 len = (unsigned char) *data++;
1284 if (afp_version >= 30) {
1290 if (afp_version >= 30) {
1292 memcpy(&hint, data, sizeof(hint));
1294 data += sizeof(hint);
1296 memcpy(&len16, data, sizeof(len16));
1303 /* else it's an error */
1305 afp_errno = AFPERR_PARAM;
1308 *cpath += len + size;
1313 if (movecwd( vol, dir ) < 0 ) {
1314 return invalidate(vol, dir, &ret );
1316 if (*path == '\0') {
1323 if (*data == sep ) {
1327 while (*data == sep && len > 0 ) {
1328 if ( dir->d_parent == NULL ) {
1331 dir = dir->d_parent;
1336 /* would this be faster with strlen + strncpy? */
1338 while ( *data != sep && len > 0 ) {
1340 if (p > &path[ MAXPATHLEN]) {
1341 afp_errno = AFPERR_PARAM;
1347 /* short cut bits by chopping off a trailing \0. this also
1348 makes the traversal happy w/ filenames at the end of the
1355 if ( p == path ) { /* end of the name parameter */
1359 if (afp_version >= 30) {
1364 static char temp[ MAXPATHLEN + 1];
1366 if (dir->d_did == DIRDID_ROOT_PARENT) {
1368 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1369 So we compare it with the longname from the current volume and if they match
1370 we overwrite the requested path with the utf8 volume name so that the following
1373 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1374 if (strcasecmp( path, temp) == 0)
1375 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1378 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1379 afp_errno = AFPERR_PARAM;
1385 /* check for OS X mangled filename :( */
1387 t = demangle_osx(vol, path, dir->d_did, &fileid);
1390 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1391 * flags weren't the same
1393 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1394 /* at last got our view of mac name */
1399 if (ret.u_name == NULL) {
1400 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1401 afp_errno = AFPERR_PARAM;
1407 cdir = dir->d_child;
1409 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1410 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1411 CH_UCS2, path, strlen(path), (char **)&tmpname) )
1414 if (!cdir->d_m_name_ucs2) {
1415 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1416 /* this shouldn't happen !!!! */
1420 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1423 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1426 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1432 if (dir->d_did == DIRDID_ROOT_PARENT) {
1434 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1435 must check against the volume name.
1437 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1443 cdir = dirsearch_byname(vol, dir, ret.u_name);
1447 if (cdir == NULL && scdir != NULL) {
1449 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1452 if ( cdir == NULL ) {
1454 /* if dir == curdir it always succeed,
1455 even if curdir is deleted.
1456 it's not a pb because it will fail in extenddir
1458 if ( movecwd( vol, dir ) < 0 ) {
1459 /* dir is not valid anymore
1460 we delete dir from the cache and abort.
1462 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1463 afp_errno = AFPERR_NOOBJ;
1466 if (afp_errno == AFPERR_ACCESS)
1468 dir_invalidate(vol, dir);
1471 cdir = extenddir( vol, dir, &ret );
1475 cdir = extenddir( vol, dir, &ret );
1476 } /* if (!extend) */
1478 if ( cdir == NULL ) {
1492 * Move curdir to dir, with a possible chdir()
1494 int movecwd( vol, dir)
1495 const struct vol *vol;
1498 char path[MAXPATHLEN + 1];
1503 if ( dir == curdir ) {
1506 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1507 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1511 p = path + sizeof(path) - 1;
1514 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1517 /* parent directory is deleted */
1518 afp_errno = AFPERR_NOOBJ;
1522 if (p -n -1 < path) {
1523 afp_errno = AFPERR_PARAM;
1530 if ( d != curdir ) {
1531 n = strlen( vol->v_path );
1532 if (p -n -1 < path) {
1533 afp_errno = AFPERR_PARAM;
1538 memcpy( p, vol->v_path, n );
1540 if ( chdir( p ) < 0 ) {
1544 afp_errno = AFPERR_ACCESS;
1547 afp_errno = AFPERR_NOOBJ;
1557 * We can't use unix file's perm to support Apple's inherited protection modes.
1558 * If we aren't the file's owner we can't change its perms when moving it and smb
1559 * nfs,... don't even try.
1561 #define AFP_CHECK_ACCESS
1563 int check_access(char *path, int mode)
1565 #ifdef AFP_CHECK_ACCESS
1573 accessmode(p, &ma, curdir, NULL);
1574 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1576 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1582 /* --------------------- */
1583 int file_access(struct path *path, int mode)
1587 accessmode(path->u_name, &ma, curdir, &path->st);
1588 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1590 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1596 /* --------------------- */
1597 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1599 dir->offcnt = count;
1600 dir->ctime = st->st_ctime;
1601 dir->d_flags &= ~DIRF_CNID;
1604 /* ---------------------
1605 * is our cached offspring count valid?
1608 int diroffcnt(struct dir *dir, struct stat *st)
1610 return st->st_ctime == dir->ctime;
1613 /* ---------------------
1614 * is our cached also for reenumerate id?
1617 int dirreenumerate(struct dir *dir, struct stat *st)
1619 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1622 /* --------------------- */
1623 static int invisible_dots(const struct vol *vol, const char *name)
1625 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1628 /* ------------------------------
1630 (name, dir) with curdir:name == dir, from afp_enumerate
1633 int getdirparams(const struct vol *vol,
1634 u_int16_t bitmap, struct path *s_path,
1636 char *buf, int *buflen )
1640 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1641 int bit = 0, isad = 0;
1647 struct stat *st = &s_path->st;
1648 char *upath = s_path->u_name;
1650 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1651 (1 << DIRPBIT_CDATE) |
1652 (1 << DIRPBIT_MDATE) |
1653 (1 << DIRPBIT_BDATE) |
1654 (1 << DIRPBIT_FINFO)))) {
1656 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1657 if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1662 if ( dir->d_did == DIRDID_ROOT) {
1663 pdid = DIRDID_ROOT_PARENT;
1664 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1667 pdid = dir->d_parent->d_did;
1671 while ( bitmap != 0 ) {
1672 while (( bitmap & 1 ) == 0 ) {
1680 ad_getattr(&ad, &ashort);
1681 } else if (invisible_dots(vol, dir->d_u_name)) {
1682 ashort = htons(ATTRBIT_INVISIBLE);
1685 ashort |= htons(ATTRBIT_SHARED);
1686 memcpy( data, &ashort, sizeof( ashort ));
1687 data += sizeof( ashort );
1691 memcpy( data, &pdid, sizeof( pdid ));
1692 data += sizeof( pdid );
1695 case DIRPBIT_CDATE :
1696 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1697 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1698 memcpy( data, &aint, sizeof( aint ));
1699 data += sizeof( aint );
1702 case DIRPBIT_MDATE :
1703 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1704 memcpy( data, &aint, sizeof( aint ));
1705 data += sizeof( aint );
1708 case DIRPBIT_BDATE :
1709 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1710 aint = AD_DATE_START;
1711 memcpy( data, &aint, sizeof( aint ));
1712 data += sizeof( aint );
1715 case DIRPBIT_FINFO :
1717 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1718 } else { /* no appledouble */
1719 memset( data, 0, 32 );
1720 /* set default view -- this also gets done in ad_open() */
1721 ashort = htons(FINDERINFO_CLOSEDVIEW);
1722 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1724 /* dot files are by default visible */
1725 if (invisible_dots(vol, dir->d_u_name)) {
1726 ashort = htons(FINDERINFO_INVISIBLE);
1727 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1733 case DIRPBIT_LNAME :
1734 if (dir->d_m_name) /* root of parent can have a null name */
1737 memset(data, 0, sizeof(u_int16_t));
1738 data += sizeof( u_int16_t );
1741 case DIRPBIT_SNAME :
1742 memset(data, 0, sizeof(u_int16_t));
1743 data += sizeof( u_int16_t );
1747 memcpy( data, &dir->d_did, sizeof( aint ));
1748 data += sizeof( aint );
1751 case DIRPBIT_OFFCNT :
1753 /* this needs to handle current directory access rights */
1754 if (diroffcnt(dir, st)) {
1755 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1757 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1758 setdiroffcnt(dir, st, ret);
1759 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1761 ashort = htons( ashort );
1762 memcpy( data, &ashort, sizeof( ashort ));
1763 data += sizeof( ashort );
1767 aint = htonl(st->st_uid);
1768 memcpy( data, &aint, sizeof( aint ));
1769 data += sizeof( aint );
1773 aint = htonl(st->st_gid);
1774 memcpy( data, &aint, sizeof( aint ));
1775 data += sizeof( aint );
1778 case DIRPBIT_ACCESS :
1779 accessmode( upath, &ma, dir , st);
1781 *data++ = ma.ma_user;
1782 *data++ = ma.ma_world;
1783 *data++ = ma.ma_group;
1784 *data++ = ma.ma_owner;
1787 /* Client has requested the ProDOS information block.
1788 Just pass back the same basic block for all
1789 directories. <shirsch@ibm.net> */
1790 case DIRPBIT_PDINFO :
1791 if (afp_version >= 30) { /* UTF8 name */
1792 utf8 = kTextEncodingUTF8;
1793 if (dir->d_m_name) /* root of parent can have a null name */
1796 memset(data, 0, sizeof(u_int16_t));
1797 data += sizeof( u_int16_t );
1799 memcpy(data, &aint, sizeof( aint ));
1800 data += sizeof( aint );
1802 else { /* ProDOS Info Block */
1805 ashort = htons( 0x0200 );
1806 memcpy( data, &ashort, sizeof( ashort ));
1807 data += sizeof( ashort );
1808 memset( data, 0, sizeof( ashort ));
1809 data += sizeof( ashort );
1813 case DIRPBIT_UNIXPR :
1814 aint = htonl(st->st_uid);
1815 memcpy( data, &aint, sizeof( aint ));
1816 data += sizeof( aint );
1817 aint = htonl(st->st_gid);
1818 memcpy( data, &aint, sizeof( aint ));
1819 data += sizeof( aint );
1822 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1823 memcpy( data, &aint, sizeof( aint ));
1824 data += sizeof( aint );
1826 accessmode( upath, &ma, dir , st);
1828 *data++ = ma.ma_user;
1829 *data++ = ma.ma_world;
1830 *data++ = ma.ma_group;
1831 *data++ = ma.ma_owner;
1836 ad_close_metadata( &ad );
1838 return( AFPERR_BITMAP );
1844 ashort = htons( data - buf );
1845 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1846 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1848 if ( utf_nameoff ) {
1849 ashort = htons( data - buf );
1850 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1851 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1854 ad_close_metadata( &ad );
1856 *buflen = data - buf;
1860 /* ----------------------------- */
1861 int path_error(struct path *path, int error)
1863 /* - a dir with access error
1864 * - no error it's a file
1867 if (path_isadir(path))
1869 if (path->st_valid && path->st_errno)
1871 return AFPERR_BADTYPE ;
1874 /* ----------------------------- */
1875 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1877 char *ibuf, *rbuf _U_;
1878 int ibuflen _U_, *rbuflen;
1883 u_int16_t vid, bitmap;
1889 memcpy( &vid, ibuf, sizeof( vid ));
1890 ibuf += sizeof( vid );
1892 if (NULL == ( vol = getvolbyvid( vid )) ) {
1893 return( AFPERR_PARAM );
1896 if (vol->v_flags & AFPVOL_RO)
1897 return AFPERR_VLOCK;
1899 memcpy( &did, ibuf, sizeof( did ));
1900 ibuf += sizeof( int );
1902 if (NULL == ( dir = dirlookup( vol, did )) ) {
1906 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1907 bitmap = ntohs( bitmap );
1908 ibuf += sizeof( bitmap );
1910 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1911 return get_afp_errno(AFPERR_NOOBJ);
1914 if ( *path->m_name != '\0' ) {
1915 rc = path_error(path, AFPERR_NOOBJ);
1916 /* maybe we are trying to set perms back */
1917 if (rc != AFPERR_ACCESS)
1922 * If ibuf is odd, make it even.
1924 if ((u_long)ibuf & 1 ) {
1928 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1929 setvoltime(obj, vol );
1935 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1937 * assume path == '\0' eg. it's a directory in canonical form
1940 struct path Cur_Path = {
1943 ".", /* unix name */
1945 NULL,/* struct dir */
1946 0, /* stat is not set */
1949 /* ------------------ */
1950 static int set_dir_errors(struct path *path, const char *where, int err)
1955 return AFPERR_ACCESS;
1957 return AFPERR_VLOCK;
1959 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1960 return AFPERR_PARAM;
1963 /* ------------------ */
1964 int setdirparams(const struct vol *vol,
1965 struct path *path, u_int16_t d_bitmap, char *buf )
1977 u_int16_t ashort, bshort;
1979 int change_mdate = 0;
1980 int change_parent_mdate = 0;
1982 u_int16_t bitmap = d_bitmap;
1983 u_char finder_buf[32];
1986 u_int16_t upriv_bit = 0;
1989 upath = path->u_name;
1991 while ( bitmap != 0 ) {
1992 while (( bitmap & 1 ) == 0 ) {
2000 memcpy( &ashort, buf, sizeof( ashort ));
2001 buf += sizeof( ashort );
2003 case DIRPBIT_CDATE :
2005 memcpy(&cdate, buf, sizeof(cdate));
2006 buf += sizeof( cdate );
2008 case DIRPBIT_MDATE :
2009 memcpy(&newdate, buf, sizeof(newdate));
2010 buf += sizeof( newdate );
2012 case DIRPBIT_BDATE :
2014 memcpy(&bdate, buf, sizeof(bdate));
2015 buf += sizeof( bdate );
2017 case DIRPBIT_FINFO :
2019 memcpy( finder_buf, buf, 32 );
2022 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2023 change_parent_mdate = 1;
2024 memcpy( &owner, buf, sizeof(owner));
2025 buf += sizeof( owner );
2028 change_parent_mdate = 1;
2029 memcpy( &group, buf, sizeof( group ));
2030 buf += sizeof( group );
2032 case DIRPBIT_ACCESS :
2034 change_parent_mdate = 1;
2035 ma.ma_user = *buf++;
2036 ma.ma_world = *buf++;
2037 ma.ma_group = *buf++;
2038 ma.ma_owner = *buf++;
2039 mpriv = mtoumode( &ma ) | vol->v_dperm;
2040 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2041 err = set_dir_errors(path, "setdirmode", errno);
2045 /* Ignore what the client thinks we should do to the
2046 ProDOS information block. Skip over the data and
2047 report nothing amiss. <shirsch@ibm.net> */
2048 case DIRPBIT_PDINFO :
2049 if (afp_version < 30) {
2053 err = AFPERR_BITMAP;
2057 case DIRPBIT_UNIXPR :
2058 if (vol_unix_priv(vol)) {
2059 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2060 buf += sizeof( owner );
2061 memcpy( &group, buf, sizeof( group ));
2062 buf += sizeof( group );
2065 change_parent_mdate = 1;
2066 memcpy( &upriv, buf, sizeof( upriv ));
2067 buf += sizeof( upriv );
2068 upriv = ntohl (upriv) | vol->v_dperm;
2069 if (dir_rx_set(upriv)) {
2070 /* maybe we are trying to set perms back */
2071 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2073 err = set_dir_errors(path, "setdirunixmode", errno);
2084 err = AFPERR_BITMAP;
2092 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2094 if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2096 * Check to see what we're trying to set. If it's anything
2097 * but ACCESS, UID, or GID, give an error. If it's any of those
2098 * three, we don't need the ad to be open, so just continue.
2100 * note: we also don't need to worry about mdate. also, be quiet
2101 * if we're using the noadouble option.
2103 if (!vol_noadouble(vol) && (d_bitmap &
2104 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2105 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2106 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2107 return AFPERR_ACCESS;
2113 * Check to see if a create was necessary. If it was, we'll want
2114 * to set our name, etc.
2116 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2117 ad_setname(&ad, curdir->d_m_name);
2123 while ( bitmap != 0 ) {
2124 while (( bitmap & 1 ) == 0 ) {
2132 ad_getattr(&ad, &bshort);
2133 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2134 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2135 change_parent_mdate = 1;
2136 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2137 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2141 ad_setattr(&ad, bshort);
2144 case DIRPBIT_CDATE :
2146 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2149 case DIRPBIT_MDATE :
2151 case DIRPBIT_BDATE :
2153 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2156 case DIRPBIT_FINFO :
2158 /* Fixes #2802236 */
2159 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2160 *fflags &= htons(~FINDERINFO_ISHARED);
2162 if ( dir->d_did == DIRDID_ROOT ) {
2164 * Alright, we admit it, this is *really* sick!
2165 * The 4 bytes that we don't copy, when we're dealing
2166 * with the root of a volume, are the directory's
2167 * location information. This eliminates that annoying
2168 * behavior one sees when mounting above another mount
2171 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2172 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2174 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2178 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2179 if ( (dir->d_did == DIRDID_ROOT) &&
2180 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2181 err = set_dir_errors(path, "setdeskowner", errno);
2182 if (isad && err == AFPERR_PARAM) {
2183 err = AFP_OK; /* ???*/
2186 goto setdirparam_done;
2189 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2190 err = set_dir_errors(path, "setdirowner", errno);
2191 goto setdirparam_done;
2195 if (dir->d_did == DIRDID_ROOT)
2196 setdeskowner( -1, ntohl(group) );
2197 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2198 err = set_dir_errors(path, "setdirowner", errno);
2199 goto setdirparam_done;
2202 case DIRPBIT_ACCESS :
2203 if (dir->d_did == DIRDID_ROOT) {
2205 if (!dir_rx_set(mpriv)) {
2206 /* we can't remove read and search for owner on volume root */
2207 err = AFPERR_ACCESS;
2208 goto setdirparam_done;
2212 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2213 err = set_dir_errors(path, "setdirmode", errno);
2214 goto setdirparam_done;
2217 case DIRPBIT_PDINFO :
2218 if (afp_version >= 30) {
2219 err = AFPERR_BITMAP;
2220 goto setdirparam_done;
2223 case DIRPBIT_UNIXPR :
2224 if (vol_unix_priv(vol)) {
2225 if (dir->d_did == DIRDID_ROOT) {
2226 if (!dir_rx_set(upriv)) {
2227 /* we can't remove read and search for owner on volume root */
2228 err = AFPERR_ACCESS;
2229 goto setdirparam_done;
2231 setdeskowner( -1, ntohl(group) );
2232 setdeskmode( upriv );
2234 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2235 err = set_dir_errors(path, "setdirowner", errno);
2236 goto setdirparam_done;
2239 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2240 err = set_dir_errors(path, "setdirunixmode", errno);
2241 goto setdirparam_done;
2245 err = AFPERR_BITMAP;
2246 goto setdirparam_done;
2250 err = AFPERR_BITMAP;
2251 goto setdirparam_done;
2260 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2261 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2265 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2266 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2271 if (path->st_valid && !path->st_errno) {
2272 struct stat *st = &path->st;
2274 if (dir && dir->d_parent) {
2275 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2279 ad_close_metadata( &ad);
2282 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2283 && gettimeofday(&tv, NULL) == 0) {
2284 if (!movecwd(vol, dir->d_parent)) {
2285 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2286 /* be careful with bitmap because now dir is null */
2287 bitmap = 1<<DIRPBIT_MDATE;
2288 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2289 /* should we reset curdir ?*/
2297 int afp_syncdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2299 char *ibuf, *rbuf _U_;
2300 int ibuflen _U_, *rbuflen;
2314 memcpy( &vid, ibuf, sizeof( vid ));
2315 ibuf += sizeof( vid );
2316 if (NULL == (vol = getvolbyvid( vid )) ) {
2317 return( AFPERR_PARAM );
2320 memcpy( &did, ibuf, sizeof( did ));
2321 ibuf += sizeof( did );
2322 if (NULL == ( dir = dirlookup( vol, did )) ) {
2323 return afp_errno; /* was AFPERR_NOOBJ */
2326 if (movecwd( vol, dir ) < 0 )
2327 return ( AFPERR_NOOBJ );
2330 Assuming only OSens that have dirfd also may require fsyncing directories
2331 in order to flush metadata e.g. Linux.
2335 if (NULL == ( dp = opendir( "." )) ) {
2338 return( AFPERR_NOOBJ );
2340 return( AFPERR_ACCESS );
2342 return( AFPERR_PARAM );
2346 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2349 if ( fsync ( dfd ) < 0 )
2350 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2351 dir->d_u_name, strerror(errno) );
2352 closedir(dp); /* closes dfd too */
2355 if ( -1 == (dfd = open(vol->vfs->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2358 return( AFPERR_NOOBJ );
2360 return( AFPERR_ACCESS );
2362 return( AFPERR_PARAM );
2366 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2367 vol->vfs->ad_path(".", ADFLAGS_DIR) );
2369 if ( fsync(dfd) < 0 )
2370 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2371 vol->vfs->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2377 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2380 int ibuflen _U_, *rbuflen;
2386 struct path *s_path;
2394 memcpy( &vid, ibuf, sizeof( vid ));
2395 ibuf += sizeof( vid );
2396 if (NULL == ( vol = getvolbyvid( vid )) ) {
2397 return( AFPERR_PARAM );
2400 if (vol->v_flags & AFPVOL_RO)
2401 return AFPERR_VLOCK;
2403 memcpy( &did, ibuf, sizeof( did ));
2404 ibuf += sizeof( did );
2405 if (NULL == ( dir = dirlookup( vol, did )) ) {
2406 return afp_errno; /* was AFPERR_NOOBJ */
2408 /* for concurrent access we need to be sure we are not in the
2409 * folder we want to create...
2413 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2414 return get_afp_errno(AFPERR_PARAM);
2416 /* cname was able to move curdir to it! */
2417 if (*s_path->m_name == '\0')
2418 return AFPERR_EXIST;
2420 upath = s_path->u_name;
2421 if (0 != (err = check_name(vol, upath))) {
2425 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2429 if (of_stat(s_path) < 0) {
2433 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2437 if ( movecwd( vol, dir ) < 0 ) {
2438 return( AFPERR_PARAM );
2441 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2442 if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2443 if (vol_noadouble(vol))
2444 goto createdir_done;
2445 return( AFPERR_ACCESS );
2447 ad_setname(&ad, s_path->m_name);
2448 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2451 ad_close_metadata( &ad);
2454 #ifdef HAVE_NFSv4_ACLS
2455 /* FIXME: are we really inside the created dir? */
2456 addir_inherit_acl(vol);
2459 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2460 *rbuflen = sizeof( u_int32_t );
2461 setvoltime(obj, vol );
2466 * dst new unix filename (not a pathname)
2467 * newname new mac name
2471 int renamedir(vol, src, dst, dir, newparent, newname)
2472 const struct vol *vol;
2473 char *src, *dst, *newname;
2474 struct dir *dir, *newparent;
2481 /* existence check moved to afp_moveandrename */
2482 if ( unix_rename( src, dst ) < 0 ) {
2485 return( AFPERR_NOOBJ );
2487 return( AFPERR_ACCESS );
2489 return AFPERR_VLOCK;
2491 /* tried to move directory into a subdirectory of itself */
2492 return AFPERR_CANTMOVE;
2494 /* this needs to copy and delete. bleah. that means we have
2495 * to deal with entire directory hierarchies. */
2496 if ((err = copydir(vol, src, dst)) < 0) {
2500 if ((err = deletedir(src)) < 0)
2504 return( AFPERR_PARAM );
2508 vol->vfs->rf_renamedir(vol, src, dst);
2510 len = strlen( newname );
2511 /* rename() succeeded so we need to update our tree even if we can't open
2515 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2517 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2518 ad_setname(&ad, newname);
2520 ad_close_metadata( &ad);
2523 dir_hash_del(vol, dir);
2524 if (dir->d_m_name == dir->d_u_name)
2525 dir->d_u_name = NULL;
2527 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2528 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2529 /* FIXME : fatal ? */
2532 dir->d_m_name = buf;
2533 strcpy( dir->d_m_name, newname );
2535 if (newname == dst) {
2536 free(dir->d_u_name);
2537 dir->d_u_name = dir->d_m_name;
2540 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2541 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2544 dir->d_u_name = buf;
2545 strcpy( dir->d_u_name, dst );
2548 if (dir->d_m_name_ucs2)
2549 free(dir->d_m_name_ucs2);
2551 dir->d_m_name_ucs2 = NULL;
2552 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))
2553 dir->d_m_name_ucs2 = NULL;
2555 if (( parent = dir->d_parent ) == NULL ) {
2558 if ( parent == newparent ) {
2559 hash_alloc_insert(vol->v_hash, dir, dir);
2563 /* detach from old parent and add to new one. */
2564 dirchildremove(parent, dir);
2565 dir->d_parent = newparent;
2566 dirchildadd(vol, newparent, dir);
2570 /* delete an empty directory */
2571 int deletecurdir( vol)
2572 const struct vol *vol;
2582 if ( curdir->d_parent == NULL ) {
2583 return( AFPERR_ACCESS );
2588 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2589 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2591 ad_getattr(&ad, &ashort);
2592 ad_close( &ad, ADFLAGS_HF );
2593 if ((ashort & htons(ATTRBIT_NODELETE))) {
2594 return AFPERR_OLOCK;
2597 err = vol->vfs->rf_deletecurdir(vol);
2602 /* now get rid of dangling symlinks */
2603 if ((dp = opendir("."))) {
2604 while ((de = readdir(dp))) {
2605 /* skip this and previous directory */
2606 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2609 /* bail if it's not a symlink */
2610 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2612 return AFPERR_DIRNEMPT;
2615 if ((err = netatalk_unlink(de->d_name))) {
2622 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2627 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2628 dirchildremove(curdir, fdir);
2629 cnid_delete(vol->v_cdb, fdir->d_did);
2630 dir_remove( vol, fdir );
2635 /* inode is used as key for cnid.
2636 * Close the descriptor only after cnid_delete
2644 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2647 int ibuflen _U_, *rbuflen;
2658 sfunc = (unsigned char) *ibuf++;
2662 if (sfunc >= 3 && sfunc <= 6) {
2663 if (afp_version < 30) {
2664 return( AFPERR_PARAM );
2671 case 3 :/* unicode */
2672 memcpy( &id, ibuf, sizeof( id ));
2675 if (( pw = getpwuid( id )) == NULL ) {
2676 return( AFPERR_NOITEM );
2678 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2679 pw->pw_name, strlen(pw->pw_name), &name);
2686 case 4 : /* unicode */
2687 memcpy( &id, ibuf, sizeof( id ));
2690 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2691 return( AFPERR_NOITEM );
2693 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2694 gr->gr_name, strlen(gr->gr_name), &name);
2700 #ifdef HAVE_NFSv4_ACLS
2701 case 5 : /* UUID -> username */
2702 case 6 : /* UUID -> groupname */
2703 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2704 return AFPERR_PARAM;
2705 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2706 len = getnamefromuuid( ibuf, &name, &type);
2707 if (len != 0) /* its a error code, not len */
2708 return AFPERR_NOITEM;
2709 if (type == UUID_USER) {
2710 if (( pw = getpwnam( name )) == NULL )
2711 return( AFPERR_NOITEM );
2712 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2713 id = htonl(UUID_USER);
2714 memcpy( rbuf, &id, sizeof( id ));
2715 id = htonl( pw->pw_uid);
2716 rbuf += sizeof( id );
2717 memcpy( rbuf, &id, sizeof( id ));
2718 rbuf += sizeof( id );
2719 *rbuflen = 2 * sizeof( id );
2720 } else { /* type == UUID_GROUP */
2721 if (( gr = getgrnam( name )) == NULL )
2722 return( AFPERR_NOITEM );
2723 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2724 id = htonl(UUID_GROUP);
2725 memcpy( rbuf, &id, sizeof( id ));
2726 rbuf += sizeof( id );
2727 id = htonl( gr->gr_gid);
2728 memcpy( rbuf, &id, sizeof( id ));
2729 rbuf += sizeof( id );
2730 *rbuflen = 2 * sizeof( id );
2735 return( AFPERR_PARAM );
2739 len = strlen( name );
2742 u_int16_t tp = htons(len);
2743 memcpy(rbuf, &tp, sizeof(tp));
2752 memcpy( rbuf, name, len );
2760 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2763 int ibuflen _U_, *rbuflen;
2772 sfunc = (unsigned char) *ibuf++;
2774 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2777 case 2 : /* unicode */
2778 if (afp_version < 30) {
2779 return( AFPERR_PARAM );
2781 memcpy(&ulen, ibuf, sizeof(ulen));
2784 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2788 len = (unsigned char) *ibuf++;
2790 #ifdef HAVE_NFSv4_ACLS
2791 case 5 : /* username -> UUID */
2792 case 6 : /* groupname -> UUID */
2793 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2794 return AFPERR_PARAM;
2795 memcpy(&ulen, ibuf, sizeof(ulen));
2801 return( AFPERR_PARAM );
2807 return AFPERR_PARAM;
2810 case 1 : /* unicode */
2812 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2813 return( AFPERR_NOITEM );
2817 memcpy( rbuf, &id, sizeof( id ));
2818 *rbuflen = sizeof( id );
2821 case 2 : /* unicode */
2823 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2824 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2825 return( AFPERR_NOITEM );
2828 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2830 memcpy( rbuf, &id, sizeof( id ));
2831 *rbuflen = sizeof( id );
2833 #ifdef HAVE_NFSv4_ACLS
2834 case 5 : /* username -> UUID */
2835 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2836 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2837 return AFPERR_NOITEM;
2838 *rbuflen = UUID_BINSIZE;
2840 case 6 : /* groupname -> UUID */
2841 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2842 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2843 return AFPERR_NOITEM;
2844 *rbuflen = UUID_BINSIZE;
2852 /* ------------------------------------
2853 variable DID support
2855 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2857 char *ibuf _U_, *rbuf _U_;
2858 int ibuflen _U_, *rbuflen;
2869 /* do nothing as dids are static for the life of the process. */
2873 memcpy(&vid, ibuf, sizeof( vid ));
2874 ibuf += sizeof( vid );
2875 if (( vol = getvolbyvid( vid )) == NULL ) {
2876 return( AFPERR_PARAM );
2879 memcpy( &did, ibuf, sizeof( did ));
2880 ibuf += sizeof( did );
2881 if (( dir = dirlookup( vol, did )) == NULL ) {
2882 return( AFPERR_PARAM );
2885 /* dir_remove -- deletedid */
2891 /* did creation gets done automatically
2892 * there's a pb again with case but move it to cname
2894 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2897 int ibuflen _U_, *rbuflen;
2900 struct dir *parentdir;
2908 memcpy(&vid, ibuf, sizeof(vid));
2909 ibuf += sizeof( vid );
2911 if (NULL == ( vol = getvolbyvid( vid )) ) {
2912 return( AFPERR_PARAM );
2915 memcpy(&did, ibuf, sizeof(did));
2916 ibuf += sizeof(did);
2918 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2922 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2923 return get_afp_errno(AFPERR_PARAM);
2926 if ( *path->m_name != '\0' ) {
2927 return path_error(path, AFPERR_NOOBJ);
2930 if ( !path->st_valid && of_stat(path ) < 0 ) {
2931 return( AFPERR_NOOBJ );
2933 if ( path->st_errno ) {
2934 return( AFPERR_NOOBJ );
2937 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2938 *rbuflen = sizeof(curdir->d_did);