2 * $Id: directory.c,v 1.99 2009-07-20 18:31:04 didg Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
7 * 19 jan 2000 implemented red-black trees for directory lookups
13 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
38 #include <sys/param.h>
41 #include <atalk/adouble.h>
43 #include <atalk/afp.h>
44 #include <atalk/util.h>
45 #include <atalk/cnid.h>
46 #include <atalk/logger.h>
47 #include <atalk/uuid.h>
49 #include "directory.h"
59 #ifdef HAVE_NFSv4_ACLS
60 extern void addir_inherit_acl(const struct vol *vol);
66 #define SENTINEL (&sentinel)
67 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
68 NULL, NULL, NULL, NULL, NULL, 0, 0,
69 0, 0, NULL, NULL, NULL};
70 static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
71 NULL, NULL, NULL, NULL, NULL, 0, 0,
72 0, 0, NULL, NULL, NULL};
74 /* (from IM: Toolbox Essentials)
75 * dirFinderInfo (DInfo) fields:
77 * frRect 8 folder's window rectangle
79 * frLocation 4 folder's location in window
80 * frView 2 folder's view (default == closedView (256))
82 * extended dirFinderInfo (DXInfo) fields:
83 * frScroll 4 scroll position
84 * frOpenChain: 4 directory ID chain of open folders
85 * frScript: 1 script flag and code
86 * frXFlags: 1 reserved
87 * frComment: 2 comment ID
88 * frPutAway: 4 home directory ID
92 * redid did assignment for directories. now we use red-black trees.
97 const struct vol *vol;
103 /* check for 0 did */
105 afp_errno = AFPERR_PARAM;
108 if ( did == DIRDID_ROOT_PARENT ) {
110 rootpar.d_did = DIRDID_ROOT_PARENT;
111 rootpar.d_child = vol->v_dir;
115 /* XXX would be nice to check against curdir but we need to keep its volume */
116 if (vol->curdir && curdir->d_did == did) {
125 afp_errno = AFPERR_NOOBJ;
126 while ( dir != SENTINEL ) {
127 if (dir->d_did == did)
128 return dir->d_m_name ? dir : NULL;
129 dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
134 /* ------------------- */
136 int path_isadir(struct path *o_path)
138 return o_path->d_dir != NULL;
140 return o_path->m_name == '\0' || /* we are in a it */
141 !o_path->st_valid || /* in cache but we can't chdir in it */
142 (!o_path->st_errno && S_ISDIR(o_path->st.st_mode)); /* not in cache an can't chdir */
147 int get_afp_errno(const int param)
149 if (afp_errno != AFPERR_DID1)
154 /* ------------------- */
156 dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
158 struct dir *dir = NULL;
160 if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
166 hn = hash_lookup(vol->v_hash, &key);
174 /* -----------------------------------------
175 * if did is not in the cache resolve it with cnid
178 * OSX call it with bogus id, ie file ID not folder ID,
179 * and we are really bad in this case.
182 dirlookup( vol, did )
183 const struct vol *vol;
189 static char path[MAXPATHLEN + 1];
192 static char buffer[12 + MAXPATHLEN + 1];
193 int buflen = 12 + MAXPATHLEN + 1;
198 ret = dirsearch(vol, did);
199 if (ret != NULL || afp_errno == AFPERR_PARAM)
202 utf8 = utf8_encoding();
203 maxpath = (utf8)?MAXPATHLEN -7:255;
205 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
206 afp_errno = AFPERR_NOOBJ;
209 ptr = path + MAXPATHLEN;
210 if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
211 afp_errno = AFPERR_NOOBJ;
215 pathlen = len; /* no 0 in the last part */
217 strcpy(ptr - len, mpath);
220 ret = dirsearch(vol,id);
225 if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
227 NULL == (mpath = utompath(vol, upath, cnid, utf8))
229 afp_errno = AFPERR_NOOBJ;
233 len = strlen(mpath) + 1;
235 if (pathlen > maxpath) {
236 afp_errno = AFPERR_PARAM;
239 strcpy(ptr - len, mpath);
243 /* fill the cache, another place where we know about the path type */
249 temp16 = htons(pathlen);
250 memcpy(ptr, &temp16, sizeof(temp16));
252 temp = htonl(kTextEncodingUTF8);
254 memcpy(ptr, &temp, sizeof(temp));
260 *ptr = (unsigned char)pathlen;
264 /* cname is not efficient */
265 if (cname( vol, ret, &ptr ) == NULL )
268 return dirsearch(vol, did);
271 /* child addition/removal */
272 static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
277 b->d_next = a->d_child;
278 b->d_prev = b->d_next->d_prev;
279 b->d_next->d_prev = b;
280 b->d_prev->d_next = b;
282 if (!hash_alloc_insert(vol->v_hash, b, b)) {
283 LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
287 static void dirchildremove(struct dir *a,struct dir *b)
290 a->d_child = (b == b->d_next) ? NULL : b->d_next;
291 b->d_next->d_prev = b->d_prev;
292 b->d_prev->d_next = b->d_next;
293 b->d_next = b->d_prev = b;
296 /* --------------------------- */
297 /* rotate the tree to the left */
298 static void dir_leftrotate(vol, dir)
302 struct dir *right = dir->d_right;
304 /* whee. move the right's left tree into dir's right tree */
305 dir->d_right = right->d_left;
306 if (right->d_left != SENTINEL)
307 right->d_left->d_back = dir;
309 if (right != SENTINEL) {
310 right->d_back = dir->d_back;
314 if (!dir->d_back) /* no parent. move the right tree to the top. */
316 else if (dir == dir->d_back->d_left) /* we were on the left */
317 dir->d_back->d_left = right;
319 dir->d_back->d_right = right; /* we were on the right */
321 /* re-insert dir on the left tree */
328 /* rotate the tree to the right */
329 static void dir_rightrotate(vol, dir)
333 struct dir *left = dir->d_left;
335 /* whee. move the left's right tree into dir's left tree */
336 dir->d_left = left->d_right;
337 if (left->d_right != SENTINEL)
338 left->d_right->d_back = dir;
340 if (left != SENTINEL) {
341 left->d_back = dir->d_back;
345 if (!dir->d_back) /* no parent. move the left tree to the top. */
347 else if (dir == dir->d_back->d_right) /* we were on the right */
348 dir->d_back->d_right = left;
350 dir->d_back->d_left = left; /* we were on the left */
352 /* re-insert dir on the right tree */
358 /* recolor after a removal */
359 static struct dir *dir_rmrecolor(vol, dir)
365 while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
366 /* are we on the left tree? */
367 if (dir == dir->d_back->d_left) {
368 leaf = dir->d_back->d_right; /* get right side */
369 if (leaf->d_color == DIRTREE_COLOR_RED) {
370 /* we're red. we need to change to black. */
371 leaf->d_color = DIRTREE_COLOR_BLACK;
372 dir->d_back->d_color = DIRTREE_COLOR_RED;
373 dir_leftrotate(vol, dir->d_back);
374 leaf = dir->d_back->d_right;
377 /* right leaf has black end nodes */
378 if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
379 (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
380 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
381 dir = dir->d_back; /* ascend */
383 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
384 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
385 leaf->d_color = DIRTREE_COLOR_RED;
386 dir_rightrotate(vol, leaf);
387 leaf = dir->d_back->d_right;
389 leaf->d_color = dir->d_back->d_color;
390 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
391 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
392 dir_leftrotate(vol, dir->d_back);
395 } else { /* right tree */
396 leaf = dir->d_back->d_left; /* left tree */
397 if (leaf->d_color == DIRTREE_COLOR_RED) {
398 leaf->d_color = DIRTREE_COLOR_BLACK;
399 dir->d_back->d_color = DIRTREE_COLOR_RED;
400 dir_rightrotate(vol, dir->d_back);
401 leaf = dir->d_back->d_left;
404 /* left leaf has black end nodes */
405 if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
406 (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
407 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
408 dir = dir->d_back; /* ascend */
410 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
411 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
412 leaf->d_color = DIRTREE_COLOR_RED;
413 dir_leftrotate(vol, leaf);
414 leaf = dir->d_back->d_left;
416 leaf->d_color = dir->d_back->d_color;
417 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
418 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
419 dir_rightrotate(vol, dir->d_back);
424 dir->d_color = DIRTREE_COLOR_BLACK;
430 /* --------------------- */
431 static void dir_hash_del(const struct vol *vol, struct dir *dir)
435 hn = hash_lookup(vol->v_hash, dir);
437 LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
440 hash_delete(vol->v_hash, hn);
444 /* remove the node from the tree. this is just like insertion, but
445 * different. actually, it has to worry about a bunch of things that
446 * insertion doesn't care about. */
448 static void dir_remove( const struct vol *vol _U_, struct dir *dir)
451 struct ofork *of, *last;
452 struct dir *node, *leaf;
453 #endif /* REMOVE_NODES */
455 if (!dir || (dir == SENTINEL))
458 /* i'm not sure if it really helps to delete stuff. */
459 dir_hash_del(vol, dir);
462 dir->d_m_name = NULL;
463 dir->d_u_name = NULL;
464 dir->d_m_name_ucs2 = NULL;
465 #else /* ! REMOVE_NODES */
467 /* go searching for a node with at most one child */
468 if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
472 while (node->d_left != SENTINEL)
477 leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
480 leaf->d_back = node->d_back;
483 } else if (node == node->d_back->d_left) { /* left tree */
484 node->d_back->d_left = leaf;
486 node->d_back->d_right = leaf;
489 /* we want to free node, but we also want to free the data in dir.
490 * currently, that's d_name and the directory traversal bits.
491 * we just copy the necessary bits and then fix up all the
492 * various pointers to the directory. needless to say, there are
493 * a bunch of places that store the directory struct. */
495 struct dir save, *tmp;
497 memcpy(&save, dir, sizeof(save));
498 memcpy(dir, node, sizeof(struct dir));
500 /* restore the red-black bits */
501 dir->d_left = save.d_left;
502 dir->d_right = save.d_right;
503 dir->d_back = save.d_back;
504 dir->d_color = save.d_color;
506 if (node == vol->v_dir) {/* we may need to fix up this pointer */
508 rootpar.d_child = vol->v_dir;
510 /* if we aren't the root directory, we have parents and
511 * siblings to worry about */
512 if (dir->d_parent->d_child == node)
513 dir->d_parent->d_child = dir;
514 dir->d_next->d_prev = dir;
515 dir->d_prev->d_next = dir;
518 /* fix up children. */
522 tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
525 if (node == curdir) /* another pointer to fixup */
528 /* we also need to fix up oforks. bleah */
529 if ((of = dir->d_ofork)) {
530 last = of->of_d_prev;
533 of = (last == of) ? NULL : of->of_d_next;
537 /* set the node's d_name */
538 node->d_m_name = save.d_m_name;
539 node->d_u_name = save.d_u_name;
540 node->d_m_name_ucs2 = save.d_m_name_ucs2;
543 if (node->d_color == DIRTREE_COLOR_BLACK)
544 dir_rmrecolor(vol, leaf);
546 if (node->d_m_name_ucs2)
547 free(node->d_u_name_ucs2);
548 if (node->d_u_name != node->d_m_name) {
549 free(node->d_u_name);
551 free(node->d_m_name);
553 #endif /* ! REMOVE_NODES */
556 /* ---------------------------------------
557 * remove the node and its childs from the tree
559 * FIXME what about opened forks with refs to it?
560 * it's an afp specs violation because you can't delete
561 * an opened forks. Now afpd doesn't care about forks opened by other
562 * process. It's fixable within afpd if fnctl_lock, doable with smb and
563 * next to impossible for nfs and local filesystem access.
565 static void dir_invalidate( vol, dir )
566 const struct vol *vol;
570 /* v_root can't be deleted */
571 if (movecwd(vol, vol->v_root) < 0) {
572 LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
576 dirchildremove(dir->d_parent, dir);
577 dir_remove( vol, dir );
580 /* ------------------------------------ */
581 static struct dir *dir_insert(vol, dir)
582 const struct vol *vol;
588 while (pdir->d_did != dir->d_did ) {
589 if ( pdir->d_did > dir->d_did ) {
590 if ( pdir->d_left == SENTINEL ) {
597 if ( pdir->d_right == SENTINEL ) {
602 pdir = pdir->d_right;
608 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
611 caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
616 static u_int32_t did = 0;
617 static char cname[MAXPATHLEN];
618 static char lname[MAXPATHLEN];
619 ucs2_t u2_path[MAXPATHLEN];
620 ucs2_t u2_dename[MAXPATHLEN];
621 char *tmp, *savepath;
623 if (!(vol->v_flags & AFPVOL_CASEINSEN))
626 if (veto_file(ENUMVETO, path->u_name))
629 savepath = path->u_name;
631 /* very simple cache */
632 if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
633 path->u_name = cname;
635 if (of_stat( path ) == 0 ) {
638 /* something changed, we cannot stat ... */
642 if (NULL == ( dp = opendir( "." )) ) {
643 LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
648 /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
649 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, strlen(path->u_name), u2_path, sizeof(u2_path)) )
650 LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
652 /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
654 for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
655 if (NULL == check_dirent(vol, de->d_name))
658 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, strlen(de->d_name), u2_dename, sizeof(u2_dename)) )
661 if (strcasecmp_w( u2_path, u2_dename) == 0) {
663 strlcpy(cname, de->d_name, sizeof(cname));
664 path->u_name = cname;
666 if (of_stat( path ) == 0 ) {
667 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
668 strlcpy(lname, tmp, sizeof(lname));
681 /* invalidate cache */
684 path->u_name = savepath;
686 /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
692 * attempt to extend the current dir. tree to include path
693 * as a side-effect, movecwd to that point and return the new dir
696 extenddir( vol, dir, path )
703 if ( path->u_name == NULL) {
704 afp_errno = AFPERR_PARAM;
708 if (check_name(vol, path->u_name)) {
709 /* the name is illegal */
710 afp_errno = AFPERR_PARAM;
714 if (of_stat( path ) != 0 ) {
715 if (!(vol->v_flags & AFPVOL_CASEINSEN))
717 else if(caseenumerate(vol, path, dir) != 0)
721 if (!S_ISDIR(path->st.st_mode)) {
725 /* mac name is always with the right encoding (from cname()) */
726 if (( dir = adddir( vol, dir, path)) == NULL ) {
731 if ( movecwd( vol, dir ) < 0 ) {
738 /* -------------------
739 system rmdir with afp error code.
740 ENOENT is not an error.
742 int netatalk_rmdir(const char *name)
744 if (rmdir(name) < 0) {
749 return AFPERR_DIRNEMPT;
752 return AFPERR_ACCESS;
762 /* -------------------------
763 appledouble mkdir afp error code.
765 static int netatalk_mkdir(const char *name)
767 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
770 return( AFPERR_NOOBJ );
772 return( AFPERR_VLOCK );
775 return( AFPERR_ACCESS );
777 return( AFPERR_EXIST );
780 return( AFPERR_DFULL );
782 return( AFPERR_PARAM );
788 /* -------------------
789 system unlink with afp error code.
790 ENOENT is not an error.
792 int netatalk_unlink(const char *name)
794 if (unlink(name) < 0) {
802 return AFPERR_ACCESS;
810 /* ------------------- */
811 static int deletedir(char *dir)
813 char path[MAXPATHLEN + 1];
821 if ((len = strlen(dir)) +2 > sizeof(path))
825 if ((dp = opendir(dir)) == NULL)
831 remain = sizeof(path) -len -1;
832 while ((de = readdir(dp)) && err == AFP_OK) {
833 /* skip this and previous directory */
834 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
837 if (strlen(de->d_name) > remain) {
841 strcpy(path + len, de->d_name);
842 if (stat(path, &st)) {
845 if (S_ISDIR(st.st_mode)) {
846 err = deletedir(path);
848 err = netatalk_unlink(path);
853 /* okay. the directory is empty. delete it. note: we already got rid
856 err = netatalk_rmdir(dir);
861 /* do a recursive copy. */
862 static int copydir(const struct vol *vol, char *src, char *dst)
864 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
873 /* doesn't exist or the path is too long. */
874 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
875 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
876 ((dp = opendir(src)) == NULL))
879 /* try to create the destination directory */
880 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
885 /* set things up to copy */
889 srem = sizeof(spath) - slen -1;
894 drem = sizeof(dpath) - dlen -1;
897 while ((de = readdir(dp))) {
898 /* skip this and previous directory */
899 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
902 if (strlen(de->d_name) > srem) {
906 strcpy(spath + slen, de->d_name);
908 if (stat(spath, &st) == 0) {
909 if (strlen(de->d_name) > drem) {
913 strcpy(dpath + dlen, de->d_name);
915 if (S_ISDIR(st.st_mode)) {
916 if (AFP_OK != (err = copydir(vol, spath, dpath)))
918 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
922 /* keep the same time stamp. */
923 ut.actime = ut.modtime = st.st_mtime;
929 /* keep the same time stamp. */
930 if (stat(src, &st) == 0) {
931 ut.actime = ut.modtime = st.st_mtime;
941 /* --- public functions follow --- */
943 /* NOTE: we start off with at least one node (the root directory). */
944 static struct dir *dirinsert( vol, dir )
950 if ((node = dir_insert(vol, dir)))
953 /* recolor the tree. the current node is red. */
954 dir->d_color = DIRTREE_COLOR_RED;
956 /* parent of this node has to be black. if the parent node
957 * is red, then we have a grandparent. */
958 while ((dir != vol->v_root) &&
959 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
960 /* are we on the left tree? */
961 if (dir->d_back == dir->d_back->d_back->d_left) {
962 node = dir->d_back->d_back->d_right; /* get the right node */
963 if (node->d_color == DIRTREE_COLOR_RED) {
964 /* we're red. we need to change to black. */
965 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
966 node->d_color = DIRTREE_COLOR_BLACK;
967 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
968 dir = dir->d_back->d_back; /* finished. go up. */
970 if (dir == dir->d_back->d_right) {
972 dir_leftrotate(vol, dir);
974 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
975 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
976 dir_rightrotate(vol, dir->d_back->d_back);
979 node = dir->d_back->d_back->d_left;
980 if (node->d_color == DIRTREE_COLOR_RED) {
981 /* we're red. we need to change to black. */
982 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
983 node->d_color = DIRTREE_COLOR_BLACK;
984 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
985 dir = dir->d_back->d_back; /* finished. ascend */
987 if (dir == dir->d_back->d_left) {
989 dir_rightrotate(vol, dir);
991 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
992 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
993 dir_leftrotate(vol, dir->d_back->d_back);
998 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
1002 /* ---------------------------- */
1004 adddir( vol, dir, path)
1009 struct dir *cdir, *edir;
1017 upath = path->u_name;
1019 upathlen = strlen(upath);
1021 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
1025 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
1028 name = path->m_name;
1029 if ((cdir = dirnew(name, upath)) == NULL) {
1030 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
1033 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)) {
1034 LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
1035 cdir->d_m_name_ucs2 = NULL;
1040 if ((edir = dirinsert( vol, cdir ))) {
1041 /* it's not possible with LASTDID
1043 - someone else have moved the directory.
1044 - it's a symlink inside the share.
1045 - it's an ID reused, the old directory was deleted but not
1046 the cnid record and the server've reused the inode for
1048 for HASH (we should get ride of HASH)
1049 - someone else have moved the directory.
1050 - it's an ID reused as above
1051 - it's a hash duplicate and we are in big trouble
1053 deleted = (edir->d_m_name == NULL);
1055 dir_hash_del(vol, edir);
1057 edir->d_m_name = cdir->d_m_name;
1058 edir->d_u_name = cdir->d_u_name;
1059 edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
1062 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
1063 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
1064 hash_alloc_insert(vol->v_hash, cdir, cdir);
1067 /* the old was not in the same folder */
1069 dirchildremove(cdir->d_parent, cdir);
1072 /* parent/child directories */
1073 cdir->d_parent = dir;
1074 dirchildadd(vol, dir, cdir);
1078 /* --- public functions follow --- */
1079 /* free everything down. we don't bother to recolor as this is only
1080 * called to free the entire tree */
1081 void dirfreename(struct dir *dir)
1083 if (dir->d_u_name != dir->d_m_name) {
1084 free(dir->d_u_name);
1086 if (dir->d_m_name_ucs2)
1087 free(dir->d_m_name_ucs2);
1088 free(dir->d_m_name);
1094 if (!dir || (dir == SENTINEL))
1097 if ( dir->d_left != SENTINEL ) {
1098 dirfree( dir->d_left );
1100 if ( dir->d_right != SENTINEL ) {
1101 dirfree( dir->d_right );
1104 if (dir != SENTINEL) {
1110 /* --------------------------------------------
1111 * most of the time mac name and unix name are the same
1113 struct dir *dirnew(const char *m_name, const char *u_name)
1117 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1121 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1126 if (m_name == u_name || !strcmp(m_name, u_name)) {
1127 dir->d_u_name = dir->d_m_name;
1129 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1130 free(dir->d_m_name);
1135 dir->d_m_name_ucs2 = NULL;
1136 dir->d_left = dir->d_right = SENTINEL;
1137 dir->d_next = dir->d_prev = dir;
1141 /* ------------------ */
1142 static hash_val_t hash_fun_dir(const void *key)
1144 const struct dir *k = key;
1146 static unsigned long randbox[] = {
1147 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1148 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1149 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1150 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1153 const unsigned char *str = k->d_u_name;
1157 acc ^= randbox[(*str + acc) & 0xf];
1158 acc = (acc << 1) | (acc >> 31);
1160 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1161 acc = (acc << 2) | (acc >> 30);
1167 /* ---------------- */
1168 static int hash_comp_dir(const void *key1, const void *key2)
1170 const struct dir *k1 = key1;
1171 const struct dir *k2 = key2;
1173 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1176 /* ---------------- */
1180 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1183 /* ------------------ */
1184 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1187 movecwd failed some of dir path are not there anymore.
1188 FIXME Is it true with other errors?
1189 so we remove dir from the cache
1191 if (dir->d_did == DIRDID_ROOT_PARENT)
1193 if (afp_errno == AFPERR_ACCESS) {
1194 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1197 /* FIXME should we set these?, don't need to call stat() after:
1199 ret->st_errno = EACCES;
1201 ret->m_name = dir->d_m_name;
1202 ret->u_name = dir->d_u_name;
1205 } else if (afp_errno == AFPERR_NOOBJ) {
1206 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1209 strcpy(ret->m_name, dir->d_m_name);
1210 if (dir->d_m_name == dir->d_u_name) {
1211 ret->u_name = ret->m_name;
1214 size_t tp = strlen(ret->m_name)+1;
1216 ret->u_name = ret->m_name +tp;
1217 strcpy(ret->u_name, dir->d_u_name);
1219 /* FIXME should we set :
1221 ret->st_errno = ENOENT;
1223 dir_invalidate(vol, dir);
1226 dir_invalidate(vol, dir);
1230 /* -------------------------------------------------- */
1236 stat the file or errno
1239 curdir: filename parent directory
1245 stat the dir or errno
1249 curdir: dir parent directory
1257 curdir: dir parent directory
1264 cname( vol, dir, cpath )
1265 const struct vol *vol;
1269 struct dir *cdir, *scdir=NULL;
1270 static char path[ MAXPATHLEN + 1];
1271 static struct path ret;
1283 afp_errno = AFPERR_NOOBJ;
1284 memset(&ret, 0, sizeof(ret));
1285 switch (ret.m_type = *data) { /* path type */
1288 len = (unsigned char) *data++;
1291 if (afp_version >= 30) {
1297 if (afp_version >= 30) {
1299 memcpy(&hint, data, sizeof(hint));
1301 data += sizeof(hint);
1303 memcpy(&len16, data, sizeof(len16));
1310 /* else it's an error */
1312 afp_errno = AFPERR_PARAM;
1315 *cpath += len + size;
1320 if (movecwd( vol, dir ) < 0 ) {
1321 return invalidate(vol, dir, &ret );
1323 if (*path == '\0') {
1330 if (*data == sep ) {
1334 while (*data == sep && len > 0 ) {
1335 if ( dir->d_parent == NULL ) {
1338 dir = dir->d_parent;
1343 /* would this be faster with strlen + strncpy? */
1345 while ( *data != sep && len > 0 ) {
1347 if (p > &path[ MAXPATHLEN]) {
1348 afp_errno = AFPERR_PARAM;
1354 /* short cut bits by chopping off a trailing \0. this also
1355 makes the traversal happy w/ filenames at the end of the
1362 if ( p == path ) { /* end of the name parameter */
1366 if (afp_version >= 30) {
1371 static char temp[ MAXPATHLEN + 1];
1373 if (dir->d_did == DIRDID_ROOT_PARENT) {
1375 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1376 So we compare it with the longname from the current volume and if they match
1377 we overwrite the requested path with the utf8 volume name so that the following
1380 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1381 if (strcasecmp( path, temp) == 0)
1382 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1385 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1386 afp_errno = AFPERR_PARAM;
1392 /* check for OS X mangled filename :( */
1394 t = demangle_osx(vol, path, dir->d_did, &fileid);
1397 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1398 * flags weren't the same
1400 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1401 /* at last got our view of mac name */
1406 if (ret.u_name == NULL) {
1407 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1408 afp_errno = AFPERR_PARAM;
1414 cdir = dir->d_child;
1416 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1417 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1418 CH_UCS2, path, strlen(path), (char **)&tmpname) )
1421 if (!cdir->d_m_name_ucs2) {
1422 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1423 /* this shouldn't happen !!!! */
1427 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1430 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1433 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1439 if (dir->d_did == DIRDID_ROOT_PARENT) {
1441 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1442 must check against the volume name.
1444 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1450 cdir = dirsearch_byname(vol, dir, ret.u_name);
1454 if (cdir == NULL && scdir != NULL) {
1456 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1459 if ( cdir == NULL ) {
1461 /* if dir == curdir it always succeed,
1462 even if curdir is deleted.
1463 it's not a pb because it will fail in extenddir
1465 if ( movecwd( vol, dir ) < 0 ) {
1466 /* dir is not valid anymore
1467 we delete dir from the cache and abort.
1469 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1470 afp_errno = AFPERR_NOOBJ;
1473 if (afp_errno == AFPERR_ACCESS)
1475 dir_invalidate(vol, dir);
1478 cdir = extenddir( vol, dir, &ret );
1482 cdir = extenddir( vol, dir, &ret );
1483 } /* if (!extend) */
1485 if ( cdir == NULL ) {
1499 * Move curdir to dir, with a possible chdir()
1501 int movecwd( vol, dir)
1502 const struct vol *vol;
1505 char path[MAXPATHLEN + 1];
1510 if ( dir == curdir ) {
1513 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1514 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1518 p = path + sizeof(path) - 1;
1521 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1524 /* parent directory is deleted */
1525 afp_errno = AFPERR_NOOBJ;
1529 if (p -n -1 < path) {
1530 afp_errno = AFPERR_PARAM;
1537 if ( d != curdir ) {
1538 n = strlen( vol->v_path );
1539 if (p -n -1 < path) {
1540 afp_errno = AFPERR_PARAM;
1545 memcpy( p, vol->v_path, n );
1547 if ( chdir( p ) < 0 ) {
1551 afp_errno = AFPERR_ACCESS;
1554 afp_errno = AFPERR_NOOBJ;
1564 * We can't use unix file's perm to support Apple's inherited protection modes.
1565 * If we aren't the file's owner we can't change its perms when moving it and smb
1566 * nfs,... don't even try.
1568 #define AFP_CHECK_ACCESS
1570 int check_access(char *path, int mode)
1572 #ifdef AFP_CHECK_ACCESS
1580 accessmode(p, &ma, curdir, NULL);
1581 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1583 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1589 /* --------------------- */
1590 int file_access(struct path *path, int mode)
1594 accessmode(path->u_name, &ma, curdir, &path->st);
1595 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1597 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1603 /* --------------------- */
1604 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1606 dir->offcnt = count;
1607 dir->ctime = st->st_ctime;
1608 dir->d_flags &= ~DIRF_CNID;
1611 /* ---------------------
1612 * is our cached offspring count valid?
1615 int diroffcnt(struct dir *dir, struct stat *st)
1617 return st->st_ctime == dir->ctime;
1620 /* ---------------------
1621 * is our cached also for reenumerate id?
1624 int dirreenumerate(struct dir *dir, struct stat *st)
1626 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1629 /* --------------------- */
1630 static int invisible_dots(const struct vol *vol, const char *name)
1632 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1635 /* ------------------------------
1637 (name, dir) with curdir:name == dir, from afp_enumerate
1640 int getdirparams(const struct vol *vol,
1641 u_int16_t bitmap, struct path *s_path,
1643 char *buf, int *buflen )
1647 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1648 int bit = 0, isad = 0;
1654 struct stat *st = &s_path->st;
1655 char *upath = s_path->u_name;
1657 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1658 (1 << DIRPBIT_CDATE) |
1659 (1 << DIRPBIT_MDATE) |
1660 (1 << DIRPBIT_BDATE) |
1661 (1 << DIRPBIT_FINFO)))) {
1663 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1664 if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1669 if ( dir->d_did == DIRDID_ROOT) {
1670 pdid = DIRDID_ROOT_PARENT;
1671 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1674 pdid = dir->d_parent->d_did;
1678 while ( bitmap != 0 ) {
1679 while (( bitmap & 1 ) == 0 ) {
1687 ad_getattr(&ad, &ashort);
1688 } else if (invisible_dots(vol, dir->d_u_name)) {
1689 ashort = htons(ATTRBIT_INVISIBLE);
1692 ashort |= htons(ATTRBIT_SHARED);
1693 memcpy( data, &ashort, sizeof( ashort ));
1694 data += sizeof( ashort );
1698 memcpy( data, &pdid, sizeof( pdid ));
1699 data += sizeof( pdid );
1702 case DIRPBIT_CDATE :
1703 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1704 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1705 memcpy( data, &aint, sizeof( aint ));
1706 data += sizeof( aint );
1709 case DIRPBIT_MDATE :
1710 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1711 memcpy( data, &aint, sizeof( aint ));
1712 data += sizeof( aint );
1715 case DIRPBIT_BDATE :
1716 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1717 aint = AD_DATE_START;
1718 memcpy( data, &aint, sizeof( aint ));
1719 data += sizeof( aint );
1722 case DIRPBIT_FINFO :
1724 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1725 } else { /* no appledouble */
1726 memset( data, 0, 32 );
1727 /* set default view -- this also gets done in ad_open() */
1728 ashort = htons(FINDERINFO_CLOSEDVIEW);
1729 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1731 /* dot files are by default visible */
1732 if (invisible_dots(vol, dir->d_u_name)) {
1733 ashort = htons(FINDERINFO_INVISIBLE);
1734 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1740 case DIRPBIT_LNAME :
1741 if (dir->d_m_name) /* root of parent can have a null name */
1744 memset(data, 0, sizeof(u_int16_t));
1745 data += sizeof( u_int16_t );
1748 case DIRPBIT_SNAME :
1749 memset(data, 0, sizeof(u_int16_t));
1750 data += sizeof( u_int16_t );
1754 memcpy( data, &dir->d_did, sizeof( aint ));
1755 data += sizeof( aint );
1758 case DIRPBIT_OFFCNT :
1760 /* this needs to handle current directory access rights */
1761 if (diroffcnt(dir, st)) {
1762 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1764 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1765 setdiroffcnt(dir, st, ret);
1766 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1768 ashort = htons( ashort );
1769 memcpy( data, &ashort, sizeof( ashort ));
1770 data += sizeof( ashort );
1774 aint = htonl(st->st_uid);
1775 memcpy( data, &aint, sizeof( aint ));
1776 data += sizeof( aint );
1780 aint = htonl(st->st_gid);
1781 memcpy( data, &aint, sizeof( aint ));
1782 data += sizeof( aint );
1785 case DIRPBIT_ACCESS :
1786 accessmode( upath, &ma, dir , st);
1788 *data++ = ma.ma_user;
1789 *data++ = ma.ma_world;
1790 *data++ = ma.ma_group;
1791 *data++ = ma.ma_owner;
1794 /* Client has requested the ProDOS information block.
1795 Just pass back the same basic block for all
1796 directories. <shirsch@ibm.net> */
1797 case DIRPBIT_PDINFO :
1798 if (afp_version >= 30) { /* UTF8 name */
1799 utf8 = kTextEncodingUTF8;
1800 if (dir->d_m_name) /* root of parent can have a null name */
1803 memset(data, 0, sizeof(u_int16_t));
1804 data += sizeof( u_int16_t );
1806 memcpy(data, &aint, sizeof( aint ));
1807 data += sizeof( aint );
1809 else { /* ProDOS Info Block */
1812 ashort = htons( 0x0200 );
1813 memcpy( data, &ashort, sizeof( ashort ));
1814 data += sizeof( ashort );
1815 memset( data, 0, sizeof( ashort ));
1816 data += sizeof( ashort );
1820 case DIRPBIT_UNIXPR :
1821 aint = htonl(st->st_uid);
1822 memcpy( data, &aint, sizeof( aint ));
1823 data += sizeof( aint );
1824 aint = htonl(st->st_gid);
1825 memcpy( data, &aint, sizeof( aint ));
1826 data += sizeof( aint );
1829 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1830 memcpy( data, &aint, sizeof( aint ));
1831 data += sizeof( aint );
1833 accessmode( upath, &ma, dir , st);
1835 *data++ = ma.ma_user;
1836 *data++ = ma.ma_world;
1837 *data++ = ma.ma_group;
1838 *data++ = ma.ma_owner;
1843 ad_close_metadata( &ad );
1845 return( AFPERR_BITMAP );
1851 ashort = htons( data - buf );
1852 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1853 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1855 if ( utf_nameoff ) {
1856 ashort = htons( data - buf );
1857 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1858 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1861 ad_close_metadata( &ad );
1863 *buflen = data - buf;
1867 /* ----------------------------- */
1868 int path_error(struct path *path, int error)
1870 /* - a dir with access error
1871 * - no error it's a file
1874 if (path_isadir(path))
1876 if (path->st_valid && path->st_errno)
1878 return AFPERR_BADTYPE ;
1881 /* ----------------------------- */
1882 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1884 char *ibuf, *rbuf _U_;
1885 int ibuflen _U_, *rbuflen;
1890 u_int16_t vid, bitmap;
1896 memcpy( &vid, ibuf, sizeof( vid ));
1897 ibuf += sizeof( vid );
1899 if (NULL == ( vol = getvolbyvid( vid )) ) {
1900 return( AFPERR_PARAM );
1903 if (vol->v_flags & AFPVOL_RO)
1904 return AFPERR_VLOCK;
1906 memcpy( &did, ibuf, sizeof( did ));
1907 ibuf += sizeof( int );
1909 if (NULL == ( dir = dirlookup( vol, did )) ) {
1913 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1914 bitmap = ntohs( bitmap );
1915 ibuf += sizeof( bitmap );
1917 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1918 return get_afp_errno(AFPERR_NOOBJ);
1921 if ( *path->m_name != '\0' ) {
1922 rc = path_error(path, AFPERR_NOOBJ);
1923 /* maybe we are trying to set perms back */
1924 if (rc != AFPERR_ACCESS)
1929 * If ibuf is odd, make it even.
1931 if ((u_long)ibuf & 1 ) {
1935 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1936 setvoltime(obj, vol );
1942 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1944 * assume path == '\0' eg. it's a directory in canonical form
1947 struct path Cur_Path = {
1950 ".", /* unix name */
1952 NULL,/* struct dir */
1953 0, /* stat is not set */
1956 /* ------------------ */
1957 static int set_dir_errors(struct path *path, const char *where, int err)
1962 return AFPERR_ACCESS;
1964 return AFPERR_VLOCK;
1966 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1967 return AFPERR_PARAM;
1970 /* ------------------ */
1971 int setdirparams(const struct vol *vol,
1972 struct path *path, u_int16_t d_bitmap, char *buf )
1984 u_int16_t ashort, bshort;
1986 int change_mdate = 0;
1987 int change_parent_mdate = 0;
1989 u_int16_t bitmap = d_bitmap;
1990 u_char finder_buf[32];
1993 u_int16_t upriv_bit = 0;
1996 upath = path->u_name;
1998 while ( bitmap != 0 ) {
1999 while (( bitmap & 1 ) == 0 ) {
2007 memcpy( &ashort, buf, sizeof( ashort ));
2008 buf += sizeof( ashort );
2010 case DIRPBIT_CDATE :
2012 memcpy(&cdate, buf, sizeof(cdate));
2013 buf += sizeof( cdate );
2015 case DIRPBIT_MDATE :
2016 memcpy(&newdate, buf, sizeof(newdate));
2017 buf += sizeof( newdate );
2019 case DIRPBIT_BDATE :
2021 memcpy(&bdate, buf, sizeof(bdate));
2022 buf += sizeof( bdate );
2024 case DIRPBIT_FINFO :
2026 memcpy( finder_buf, buf, 32 );
2029 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2030 change_parent_mdate = 1;
2031 memcpy( &owner, buf, sizeof(owner));
2032 buf += sizeof( owner );
2035 change_parent_mdate = 1;
2036 memcpy( &group, buf, sizeof( group ));
2037 buf += sizeof( group );
2039 case DIRPBIT_ACCESS :
2041 change_parent_mdate = 1;
2042 ma.ma_user = *buf++;
2043 ma.ma_world = *buf++;
2044 ma.ma_group = *buf++;
2045 ma.ma_owner = *buf++;
2046 mpriv = mtoumode( &ma ) | vol->v_dperm;
2047 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2048 err = set_dir_errors(path, "setdirmode", errno);
2052 /* Ignore what the client thinks we should do to the
2053 ProDOS information block. Skip over the data and
2054 report nothing amiss. <shirsch@ibm.net> */
2055 case DIRPBIT_PDINFO :
2056 if (afp_version < 30) {
2060 err = AFPERR_BITMAP;
2064 case DIRPBIT_UNIXPR :
2065 if (vol_unix_priv(vol)) {
2066 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2067 buf += sizeof( owner );
2068 memcpy( &group, buf, sizeof( group ));
2069 buf += sizeof( group );
2072 change_parent_mdate = 1;
2073 memcpy( &upriv, buf, sizeof( upriv ));
2074 buf += sizeof( upriv );
2075 upriv = ntohl (upriv) | vol->v_dperm;
2076 if (dir_rx_set(upriv)) {
2077 /* maybe we are trying to set perms back */
2078 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2080 err = set_dir_errors(path, "setdirunixmode", errno);
2091 err = AFPERR_BITMAP;
2099 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2101 if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2103 * Check to see what we're trying to set. If it's anything
2104 * but ACCESS, UID, or GID, give an error. If it's any of those
2105 * three, we don't need the ad to be open, so just continue.
2107 * note: we also don't need to worry about mdate. also, be quiet
2108 * if we're using the noadouble option.
2110 if (!vol_noadouble(vol) && (d_bitmap &
2111 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2112 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2113 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2114 return AFPERR_ACCESS;
2120 * Check to see if a create was necessary. If it was, we'll want
2121 * to set our name, etc.
2123 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2124 ad_setname(&ad, curdir->d_m_name);
2130 while ( bitmap != 0 ) {
2131 while (( bitmap & 1 ) == 0 ) {
2139 ad_getattr(&ad, &bshort);
2140 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2141 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2142 change_parent_mdate = 1;
2143 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2144 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2148 ad_setattr(&ad, bshort);
2151 case DIRPBIT_CDATE :
2153 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2156 case DIRPBIT_MDATE :
2158 case DIRPBIT_BDATE :
2160 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2163 case DIRPBIT_FINFO :
2165 /* Fixes #2802236 */
2166 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2167 *fflags &= htons(~FINDERINFO_ISHARED);
2169 if ( dir->d_did == DIRDID_ROOT ) {
2171 * Alright, we admit it, this is *really* sick!
2172 * The 4 bytes that we don't copy, when we're dealing
2173 * with the root of a volume, are the directory's
2174 * location information. This eliminates that annoying
2175 * behavior one sees when mounting above another mount
2178 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2179 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2181 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2185 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2186 if ( (dir->d_did == DIRDID_ROOT) &&
2187 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2188 err = set_dir_errors(path, "setdeskowner", errno);
2189 if (isad && err == AFPERR_PARAM) {
2190 err = AFP_OK; /* ???*/
2193 goto setdirparam_done;
2196 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2197 err = set_dir_errors(path, "setdirowner", errno);
2198 goto setdirparam_done;
2202 if (dir->d_did == DIRDID_ROOT)
2203 setdeskowner( -1, ntohl(group) );
2204 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2205 err = set_dir_errors(path, "setdirowner", errno);
2206 goto setdirparam_done;
2209 case DIRPBIT_ACCESS :
2210 if (dir->d_did == DIRDID_ROOT) {
2212 if (!dir_rx_set(mpriv)) {
2213 /* we can't remove read and search for owner on volume root */
2214 err = AFPERR_ACCESS;
2215 goto setdirparam_done;
2219 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2220 err = set_dir_errors(path, "setdirmode", errno);
2221 goto setdirparam_done;
2224 case DIRPBIT_PDINFO :
2225 if (afp_version >= 30) {
2226 err = AFPERR_BITMAP;
2227 goto setdirparam_done;
2230 case DIRPBIT_UNIXPR :
2231 if (vol_unix_priv(vol)) {
2232 if (dir->d_did == DIRDID_ROOT) {
2233 if (!dir_rx_set(upriv)) {
2234 /* we can't remove read and search for owner on volume root */
2235 err = AFPERR_ACCESS;
2236 goto setdirparam_done;
2238 setdeskowner( -1, ntohl(group) );
2239 setdeskmode( upriv );
2241 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2242 err = set_dir_errors(path, "setdirowner", errno);
2243 goto setdirparam_done;
2246 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2247 err = set_dir_errors(path, "setdirunixmode", errno);
2248 goto setdirparam_done;
2252 err = AFPERR_BITMAP;
2253 goto setdirparam_done;
2257 err = AFPERR_BITMAP;
2258 goto setdirparam_done;
2267 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2268 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2272 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2273 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2278 if (path->st_valid && !path->st_errno) {
2279 struct stat *st = &path->st;
2281 if (dir && dir->d_parent) {
2282 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2286 ad_close_metadata( &ad);
2289 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2290 && gettimeofday(&tv, NULL) == 0) {
2291 if (!movecwd(vol, dir->d_parent)) {
2292 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2293 /* be careful with bitmap because now dir is null */
2294 bitmap = 1<<DIRPBIT_MDATE;
2295 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2296 /* should we reset curdir ?*/
2304 int afp_syncdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2306 char *ibuf, *rbuf _U_;
2307 int ibuflen _U_, *rbuflen;
2321 memcpy( &vid, ibuf, sizeof( vid ));
2322 ibuf += sizeof( vid );
2323 if (NULL == (vol = getvolbyvid( vid )) ) {
2324 return( AFPERR_PARAM );
2327 memcpy( &did, ibuf, sizeof( did ));
2328 ibuf += sizeof( did );
2329 if (NULL == ( dir = dirlookup( vol, did )) ) {
2330 return afp_errno; /* was AFPERR_NOOBJ */
2333 if (movecwd( vol, dir ) < 0 )
2334 return ( AFPERR_NOOBJ );
2337 Assuming only OSens that have dirfd also may require fsyncing directories
2338 in order to flush metadata e.g. Linux.
2342 if (NULL == ( dp = opendir( "." )) ) {
2345 return( AFPERR_NOOBJ );
2347 return( AFPERR_ACCESS );
2349 return( AFPERR_PARAM );
2353 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2356 if ( fsync ( dfd ) < 0 )
2357 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2358 dir->d_u_name, strerror(errno) );
2359 closedir(dp); /* closes dfd too */
2362 if ( -1 == (dfd = open(vol->vfs->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2365 return( AFPERR_NOOBJ );
2367 return( AFPERR_ACCESS );
2369 return( AFPERR_PARAM );
2373 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2374 vol->vfs->ad_path(".", ADFLAGS_DIR) );
2376 if ( fsync(dfd) < 0 )
2377 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2378 vol->vfs->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2384 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2387 int ibuflen _U_, *rbuflen;
2393 struct path *s_path;
2401 memcpy( &vid, ibuf, sizeof( vid ));
2402 ibuf += sizeof( vid );
2403 if (NULL == ( vol = getvolbyvid( vid )) ) {
2404 return( AFPERR_PARAM );
2407 if (vol->v_flags & AFPVOL_RO)
2408 return AFPERR_VLOCK;
2410 memcpy( &did, ibuf, sizeof( did ));
2411 ibuf += sizeof( did );
2412 if (NULL == ( dir = dirlookup( vol, did )) ) {
2413 return afp_errno; /* was AFPERR_NOOBJ */
2415 /* for concurrent access we need to be sure we are not in the
2416 * folder we want to create...
2420 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2421 return get_afp_errno(AFPERR_PARAM);
2423 /* cname was able to move curdir to it! */
2424 if (*s_path->m_name == '\0')
2425 return AFPERR_EXIST;
2427 upath = s_path->u_name;
2429 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2433 if (of_stat(s_path) < 0) {
2437 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2441 if ( movecwd( vol, dir ) < 0 ) {
2442 return( AFPERR_PARAM );
2445 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2446 if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2447 if (vol_noadouble(vol))
2448 goto createdir_done;
2449 return( AFPERR_ACCESS );
2451 ad_setname(&ad, s_path->m_name);
2452 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2455 ad_close_metadata( &ad);
2458 #ifdef HAVE_NFSv4_ACLS
2459 /* FIXME: are we really inside the created dir? */
2460 addir_inherit_acl(vol);
2463 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2464 *rbuflen = sizeof( u_int32_t );
2465 setvoltime(obj, vol );
2470 * dst new unix filename (not a pathname)
2471 * newname new mac name
2475 int renamedir(vol, src, dst, dir, newparent, newname)
2476 const struct vol *vol;
2477 char *src, *dst, *newname;
2478 struct dir *dir, *newparent;
2485 /* existence check moved to afp_moveandrename */
2486 if ( unix_rename( src, dst ) < 0 ) {
2489 return( AFPERR_NOOBJ );
2491 return( AFPERR_ACCESS );
2493 return AFPERR_VLOCK;
2495 /* tried to move directory into a subdirectory of itself */
2496 return AFPERR_CANTMOVE;
2498 /* this needs to copy and delete. bleah. that means we have
2499 * to deal with entire directory hierarchies. */
2500 if ((err = copydir(vol, src, dst)) < 0) {
2504 if ((err = deletedir(src)) < 0)
2508 return( AFPERR_PARAM );
2512 vol->vfs->rf_renamedir(vol, src, dst);
2514 len = strlen( newname );
2515 /* rename() succeeded so we need to update our tree even if we can't open
2519 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2521 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2522 ad_setname(&ad, newname);
2524 ad_close_metadata( &ad);
2527 dir_hash_del(vol, dir);
2528 if (dir->d_m_name == dir->d_u_name)
2529 dir->d_u_name = NULL;
2531 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2532 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2533 /* FIXME : fatal ? */
2536 dir->d_m_name = buf;
2537 strcpy( dir->d_m_name, newname );
2539 if (newname == dst) {
2540 free(dir->d_u_name);
2541 dir->d_u_name = dir->d_m_name;
2544 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2545 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2548 dir->d_u_name = buf;
2549 strcpy( dir->d_u_name, dst );
2552 if (dir->d_m_name_ucs2)
2553 free(dir->d_m_name_ucs2);
2555 dir->d_m_name_ucs2 = NULL;
2556 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))
2557 dir->d_m_name_ucs2 = NULL;
2559 if (( parent = dir->d_parent ) == NULL ) {
2562 if ( parent == newparent ) {
2563 hash_alloc_insert(vol->v_hash, dir, dir);
2567 /* detach from old parent and add to new one. */
2568 dirchildremove(parent, dir);
2569 dir->d_parent = newparent;
2570 dirchildadd(vol, newparent, dir);
2574 /* delete an empty directory */
2575 int deletecurdir( vol)
2576 const struct vol *vol;
2586 if ( curdir->d_parent == NULL ) {
2587 return( AFPERR_ACCESS );
2592 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2593 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2595 ad_getattr(&ad, &ashort);
2596 ad_close( &ad, ADFLAGS_HF );
2597 if ((ashort & htons(ATTRBIT_NODELETE))) {
2598 return AFPERR_OLOCK;
2601 err = vol->vfs->rf_deletecurdir(vol);
2606 /* now get rid of dangling symlinks */
2607 if ((dp = opendir("."))) {
2608 while ((de = readdir(dp))) {
2609 /* skip this and previous directory */
2610 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2613 /* bail if it's not a symlink */
2614 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2616 return AFPERR_DIRNEMPT;
2619 if ((err = netatalk_unlink(de->d_name))) {
2626 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2631 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2632 dirchildremove(curdir, fdir);
2633 cnid_delete(vol->v_cdb, fdir->d_did);
2634 dir_remove( vol, fdir );
2639 /* inode is used as key for cnid.
2640 * Close the descriptor only after cnid_delete
2648 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2651 int ibuflen _U_, *rbuflen;
2662 sfunc = (unsigned char) *ibuf++;
2666 if (sfunc >= 3 && sfunc <= 6) {
2667 if (afp_version < 30) {
2668 return( AFPERR_PARAM );
2675 case 3 :/* unicode */
2676 memcpy( &id, ibuf, sizeof( id ));
2679 if (( pw = getpwuid( id )) == NULL ) {
2680 return( AFPERR_NOITEM );
2682 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2683 pw->pw_name, strlen(pw->pw_name), &name);
2690 case 4 : /* unicode */
2691 memcpy( &id, ibuf, sizeof( id ));
2694 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2695 return( AFPERR_NOITEM );
2697 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2698 gr->gr_name, strlen(gr->gr_name), &name);
2704 #ifdef HAVE_NFSv4_ACLS
2705 case 5 : /* UUID -> username */
2706 case 6 : /* UUID -> groupname */
2707 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2708 return AFPERR_PARAM;
2709 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2710 len = getnamefromuuid( ibuf, &name, &type);
2711 if (len != 0) /* its a error code, not len */
2712 return AFPERR_NOITEM;
2713 if (type == UUID_USER) {
2714 if (( pw = getpwnam( name )) == NULL )
2715 return( AFPERR_NOITEM );
2716 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2717 id = htonl(UUID_USER);
2718 memcpy( rbuf, &id, sizeof( id ));
2719 id = htonl( pw->pw_uid);
2720 rbuf += sizeof( id );
2721 memcpy( rbuf, &id, sizeof( id ));
2722 rbuf += sizeof( id );
2723 *rbuflen = 2 * sizeof( id );
2724 } else { /* type == UUID_GROUP */
2725 if (( gr = getgrnam( name )) == NULL )
2726 return( AFPERR_NOITEM );
2727 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2728 id = htonl(UUID_GROUP);
2729 memcpy( rbuf, &id, sizeof( id ));
2730 rbuf += sizeof( id );
2731 id = htonl( gr->gr_gid);
2732 memcpy( rbuf, &id, sizeof( id ));
2733 rbuf += sizeof( id );
2734 *rbuflen = 2 * sizeof( id );
2739 return( AFPERR_PARAM );
2743 len = strlen( name );
2746 u_int16_t tp = htons(len);
2747 memcpy(rbuf, &tp, sizeof(tp));
2756 memcpy( rbuf, name, len );
2764 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2767 int ibuflen _U_, *rbuflen;
2776 sfunc = (unsigned char) *ibuf++;
2778 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2781 case 2 : /* unicode */
2782 if (afp_version < 30) {
2783 return( AFPERR_PARAM );
2785 memcpy(&ulen, ibuf, sizeof(ulen));
2788 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2792 len = (unsigned char) *ibuf++;
2794 #ifdef HAVE_NFSv4_ACLS
2795 case 5 : /* username -> UUID */
2796 case 6 : /* groupname -> UUID */
2797 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2798 return AFPERR_PARAM;
2799 memcpy(&ulen, ibuf, sizeof(ulen));
2805 return( AFPERR_PARAM );
2811 return AFPERR_PARAM;
2814 case 1 : /* unicode */
2816 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2817 return( AFPERR_NOITEM );
2821 memcpy( rbuf, &id, sizeof( id ));
2822 *rbuflen = sizeof( id );
2825 case 2 : /* unicode */
2827 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2828 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2829 return( AFPERR_NOITEM );
2832 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2834 memcpy( rbuf, &id, sizeof( id ));
2835 *rbuflen = sizeof( id );
2837 #ifdef HAVE_NFSv4_ACLS
2838 case 5 : /* username -> UUID */
2839 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2840 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2841 return AFPERR_NOITEM;
2842 *rbuflen = UUID_BINSIZE;
2844 case 6 : /* groupname -> UUID */
2845 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2846 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2847 return AFPERR_NOITEM;
2848 *rbuflen = UUID_BINSIZE;
2856 /* ------------------------------------
2857 variable DID support
2859 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2861 char *ibuf _U_, *rbuf _U_;
2862 int ibuflen _U_, *rbuflen;
2873 /* do nothing as dids are static for the life of the process. */
2877 memcpy(&vid, ibuf, sizeof( vid ));
2878 ibuf += sizeof( vid );
2879 if (( vol = getvolbyvid( vid )) == NULL ) {
2880 return( AFPERR_PARAM );
2883 memcpy( &did, ibuf, sizeof( did ));
2884 ibuf += sizeof( did );
2885 if (( dir = dirlookup( vol, did )) == NULL ) {
2886 return( AFPERR_PARAM );
2889 /* dir_remove -- deletedid */
2895 /* did creation gets done automatically
2896 * there's a pb again with case but move it to cname
2898 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2901 int ibuflen _U_, *rbuflen;
2904 struct dir *parentdir;
2912 memcpy(&vid, ibuf, sizeof(vid));
2913 ibuf += sizeof( vid );
2915 if (NULL == ( vol = getvolbyvid( vid )) ) {
2916 return( AFPERR_PARAM );
2919 memcpy(&did, ibuf, sizeof(did));
2920 ibuf += sizeof(did);
2922 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2926 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2927 return get_afp_errno(AFPERR_PARAM);
2930 if ( *path->m_name != '\0' ) {
2931 return path_error(path, AFPERR_NOOBJ);
2934 if ( !path->st_valid && of_stat(path ) < 0 ) {
2935 return( AFPERR_NOOBJ );
2937 if ( path->st_errno ) {
2938 return( AFPERR_NOOBJ );
2941 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2942 *rbuflen = sizeof(curdir->d_did);