2 * $Id: directory.c,v 1.108 2009-10-15 10:43:13 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>
42 #include <atalk/adouble.h>
43 #include <atalk/vfs.h>
44 #include <atalk/afp.h>
45 #include <atalk/util.h>
46 #include <atalk/cnid.h>
47 #include <atalk/logger.h>
48 #include <atalk/uuid.h>
50 #include "directory.h"
61 #ifdef HAVE_NFSv4_ACLS
62 extern void addir_inherit_acl(const struct vol *vol);
68 #define SENTINEL (&sentinel)
69 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
70 NULL, NULL, NULL, NULL, NULL, 0, 0,
71 0, 0, NULL, NULL, NULL};
72 static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
73 NULL, NULL, NULL, NULL, NULL, 0, 0,
74 0, 0, NULL, NULL, NULL};
76 /* (from IM: Toolbox Essentials)
77 * dirFinderInfo (DInfo) fields:
79 * frRect 8 folder's window rectangle
81 * frLocation 4 folder's location in window
82 * frView 2 folder's view (default == closedView (256))
84 * extended dirFinderInfo (DXInfo) fields:
85 * frScroll 4 scroll position
86 * frOpenChain: 4 directory ID chain of open folders
87 * frScript: 1 script flag and code
88 * frXFlags: 1 reserved
89 * frComment: 2 comment ID
90 * frPutAway: 4 home directory ID
94 * redid did assignment for directories. now we use red-black trees.
98 dirsearch(const struct vol *vol, u_int32_t did)
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 /* ------------------- */
135 int get_afp_errno(const int param)
137 if (afp_errno != AFPERR_DID1)
142 /* ------------------- */
144 dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
146 struct dir *dir = NULL;
148 if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
154 hn = hash_lookup(vol->v_hash, &key);
162 /* -----------------------------------------
163 * if did is not in the cache resolve it with cnid
166 * OSX call it with bogus id, ie file ID not folder ID,
167 * and we are really bad in this case.
170 dirlookup( const struct vol *vol, u_int32_t did)
175 static char path[MAXPATHLEN + 1];
178 static char buffer[12 + MAXPATHLEN + 1];
179 int buflen = 12 + MAXPATHLEN + 1;
184 ret = dirsearch(vol, did);
185 if (ret != NULL || afp_errno == AFPERR_PARAM)
188 utf8 = utf8_encoding();
189 maxpath = (utf8)?MAXPATHLEN -7:255;
191 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
192 afp_errno = AFPERR_NOOBJ;
195 ptr = path + MAXPATHLEN;
196 if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
197 afp_errno = AFPERR_NOOBJ;
201 pathlen = len; /* no 0 in the last part */
203 strcpy(ptr - len, mpath);
206 ret = dirsearch(vol,id);
211 if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
213 NULL == (mpath = utompath(vol, upath, cnid, utf8))
215 afp_errno = AFPERR_NOOBJ;
219 len = strlen(mpath) + 1;
221 if (pathlen > maxpath) {
222 afp_errno = AFPERR_PARAM;
225 strcpy(ptr - len, mpath);
229 /* fill the cache, another place where we know about the path type */
235 temp16 = htons(pathlen);
236 memcpy(ptr, &temp16, sizeof(temp16));
238 temp = htonl(kTextEncodingUTF8);
240 memcpy(ptr, &temp, sizeof(temp));
246 *ptr = (unsigned char)pathlen;
250 /* cname is not efficient */
251 if (cname( vol, ret, &ptr ) == NULL )
254 return dirsearch(vol, did);
257 /* child addition/removal */
258 static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
263 b->d_next = a->d_child;
264 b->d_prev = b->d_next->d_prev;
265 b->d_next->d_prev = b;
266 b->d_prev->d_next = b;
268 if (!hash_alloc_insert(vol->v_hash, b, b)) {
269 LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
273 static void dirchildremove(struct dir *a,struct dir *b)
276 a->d_child = (b == b->d_next) ? NULL : b->d_next;
277 b->d_next->d_prev = b->d_prev;
278 b->d_prev->d_next = b->d_next;
279 b->d_next = b->d_prev = b;
282 /* --------------------------- */
283 /* rotate the tree to the left */
284 static void dir_leftrotate(struct vol *vol, struct dir *dir)
286 struct dir *right = dir->d_right;
288 /* whee. move the right's left tree into dir's right tree */
289 dir->d_right = right->d_left;
290 if (right->d_left != SENTINEL)
291 right->d_left->d_back = dir;
293 if (right != SENTINEL) {
294 right->d_back = dir->d_back;
298 if (!dir->d_back) /* no parent. move the right tree to the top. */
300 else if (dir == dir->d_back->d_left) /* we were on the left */
301 dir->d_back->d_left = right;
303 dir->d_back->d_right = right; /* we were on the right */
305 /* re-insert dir on the left tree */
312 /* rotate the tree to the right */
313 static void dir_rightrotate(struct vol *vol, struct dir *dir)
315 struct dir *left = dir->d_left;
317 /* whee. move the left's right tree into dir's left tree */
318 dir->d_left = left->d_right;
319 if (left->d_right != SENTINEL)
320 left->d_right->d_back = dir;
322 if (left != SENTINEL) {
323 left->d_back = dir->d_back;
327 if (!dir->d_back) /* no parent. move the left tree to the top. */
329 else if (dir == dir->d_back->d_right) /* we were on the right */
330 dir->d_back->d_right = left;
332 dir->d_back->d_left = left; /* we were on the left */
334 /* re-insert dir on the right tree */
340 /* recolor after a removal */
341 static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
345 while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
346 /* are we on the left tree? */
347 if (dir == dir->d_back->d_left) {
348 leaf = dir->d_back->d_right; /* get right side */
349 if (leaf->d_color == DIRTREE_COLOR_RED) {
350 /* we're red. we need to change to black. */
351 leaf->d_color = DIRTREE_COLOR_BLACK;
352 dir->d_back->d_color = DIRTREE_COLOR_RED;
353 dir_leftrotate(vol, dir->d_back);
354 leaf = dir->d_back->d_right;
357 /* right leaf has black end nodes */
358 if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
359 (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
360 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
361 dir = dir->d_back; /* ascend */
363 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
364 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
365 leaf->d_color = DIRTREE_COLOR_RED;
366 dir_rightrotate(vol, leaf);
367 leaf = dir->d_back->d_right;
369 leaf->d_color = dir->d_back->d_color;
370 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
371 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
372 dir_leftrotate(vol, dir->d_back);
375 } else { /* right tree */
376 leaf = dir->d_back->d_left; /* left tree */
377 if (leaf->d_color == DIRTREE_COLOR_RED) {
378 leaf->d_color = DIRTREE_COLOR_BLACK;
379 dir->d_back->d_color = DIRTREE_COLOR_RED;
380 dir_rightrotate(vol, dir->d_back);
381 leaf = dir->d_back->d_left;
384 /* left leaf has black end nodes */
385 if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
386 (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
387 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
388 dir = dir->d_back; /* ascend */
390 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
391 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
392 leaf->d_color = DIRTREE_COLOR_RED;
393 dir_leftrotate(vol, leaf);
394 leaf = dir->d_back->d_left;
396 leaf->d_color = dir->d_back->d_color;
397 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
398 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
399 dir_rightrotate(vol, dir->d_back);
404 dir->d_color = DIRTREE_COLOR_BLACK;
410 /* --------------------- */
411 static void dir_hash_del(const struct vol *vol, struct dir *dir)
415 hn = hash_lookup(vol->v_hash, dir);
417 LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
420 hash_delete(vol->v_hash, hn);
424 /* remove the node from the tree. this is just like insertion, but
425 * different. actually, it has to worry about a bunch of things that
426 * insertion doesn't care about. */
428 static void dir_remove( const struct vol *vol _U_, struct dir *dir)
431 struct ofork *of, *last;
432 struct dir *node, *leaf;
433 #endif /* REMOVE_NODES */
435 if (!dir || (dir == SENTINEL))
438 /* i'm not sure if it really helps to delete stuff. */
439 dir_hash_del(vol, dir);
442 dir->d_m_name = NULL;
443 dir->d_u_name = NULL;
444 dir->d_m_name_ucs2 = NULL;
445 #else /* ! REMOVE_NODES */
447 /* go searching for a node with at most one child */
448 if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
452 while (node->d_left != SENTINEL)
457 leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
460 leaf->d_back = node->d_back;
463 } else if (node == node->d_back->d_left) { /* left tree */
464 node->d_back->d_left = leaf;
466 node->d_back->d_right = leaf;
469 /* we want to free node, but we also want to free the data in dir.
470 * currently, that's d_name and the directory traversal bits.
471 * we just copy the necessary bits and then fix up all the
472 * various pointers to the directory. needless to say, there are
473 * a bunch of places that store the directory struct. */
475 struct dir save, *tmp;
477 memcpy(&save, dir, sizeof(save));
478 memcpy(dir, node, sizeof(struct dir));
480 /* restore the red-black bits */
481 dir->d_left = save.d_left;
482 dir->d_right = save.d_right;
483 dir->d_back = save.d_back;
484 dir->d_color = save.d_color;
486 if (node == vol->v_dir) {/* we may need to fix up this pointer */
488 rootpar.d_child = vol->v_dir;
490 /* if we aren't the root directory, we have parents and
491 * siblings to worry about */
492 if (dir->d_parent->d_child == node)
493 dir->d_parent->d_child = dir;
494 dir->d_next->d_prev = dir;
495 dir->d_prev->d_next = dir;
498 /* fix up children. */
502 tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
505 if (node == curdir) /* another pointer to fixup */
508 /* we also need to fix up oforks. bleah */
509 if ((of = dir->d_ofork)) {
510 last = of->of_d_prev;
513 of = (last == of) ? NULL : of->of_d_next;
517 /* set the node's d_name */
518 node->d_m_name = save.d_m_name;
519 node->d_u_name = save.d_u_name;
520 node->d_m_name_ucs2 = save.d_m_name_ucs2;
523 if (node->d_color == DIRTREE_COLOR_BLACK)
524 dir_rmrecolor(vol, leaf);
526 if (node->d_m_name_ucs2)
527 free(node->d_u_name_ucs2);
528 if (node->d_u_name != node->d_m_name) {
529 free(node->d_u_name);
531 free(node->d_m_name);
533 #endif /* ! REMOVE_NODES */
536 /* ---------------------------------------
537 * remove the node and its childs from the tree
539 * FIXME what about opened forks with refs to it?
540 * it's an afp specs violation because you can't delete
541 * an opened forks. Now afpd doesn't care about forks opened by other
542 * process. It's fixable within afpd if fnctl_lock, doable with smb and
543 * next to impossible for nfs and local filesystem access.
545 static void dir_invalidate( const struct vol *vol, struct dir *dir)
548 /* v_root can't be deleted */
549 if (movecwd(vol, vol->v_root) < 0) {
550 LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
554 dirchildremove(dir->d_parent, dir);
555 dir_remove( vol, dir );
558 /* ------------------------------------ */
559 static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
564 while (pdir->d_did != dir->d_did ) {
565 if ( pdir->d_did > dir->d_did ) {
566 if ( pdir->d_left == SENTINEL ) {
573 if ( pdir->d_right == SENTINEL ) {
578 pdir = pdir->d_right;
584 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
587 caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
592 static u_int32_t did = 0;
593 static char cname[MAXPATHLEN];
594 static char lname[MAXPATHLEN];
595 ucs2_t u2_path[MAXPATHLEN];
596 ucs2_t u2_dename[MAXPATHLEN];
597 char *tmp, *savepath;
599 if (!(vol->v_flags & AFPVOL_CASEINSEN))
602 if (veto_file(ENUMVETO, path->u_name))
605 savepath = path->u_name;
607 /* very simple cache */
608 if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
609 path->u_name = cname;
611 if (of_stat( path ) == 0 ) {
614 /* something changed, we cannot stat ... */
618 if (NULL == ( dp = opendir( "." )) ) {
619 LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
624 /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
625 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, strlen(path->u_name), u2_path, sizeof(u2_path)) )
626 LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
628 /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
630 for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
631 if (NULL == check_dirent(vol, de->d_name))
634 if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, strlen(de->d_name), u2_dename, sizeof(u2_dename)) )
637 if (strcasecmp_w( u2_path, u2_dename) == 0) {
639 strlcpy(cname, de->d_name, sizeof(cname));
640 path->u_name = cname;
642 if (of_stat( path ) == 0 ) {
643 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
644 strlcpy(lname, tmp, sizeof(lname));
657 /* invalidate cache */
660 path->u_name = savepath;
662 /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
668 * attempt to extend the current dir. tree to include path
669 * as a side-effect, movecwd to that point and return the new dir
672 extenddir(struct vol *vol, struct dir *dir, struct path *path)
676 if ( path->u_name == NULL) {
677 afp_errno = AFPERR_PARAM;
681 if (check_name(vol, path->u_name)) {
682 /* the name is illegal */
683 LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
685 afp_errno = AFPERR_PARAM;
689 if (of_stat( path ) != 0 ) {
690 if (!(vol->v_flags & AFPVOL_CASEINSEN))
692 else if(caseenumerate(vol, path, dir) != 0)
696 if (!S_ISDIR(path->st.st_mode)) {
700 /* mac name is always with the right encoding (from cname()) */
701 if (( dir = adddir( vol, dir, path)) == NULL ) {
706 if ( movecwd( vol, dir ) < 0 ) {
713 /* -------------------------
714 appledouble mkdir afp error code.
716 static int netatalk_mkdir(const char *name)
718 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
721 return( AFPERR_NOOBJ );
723 return( AFPERR_VLOCK );
726 return( AFPERR_ACCESS );
728 return( AFPERR_EXIST );
731 return( AFPERR_DFULL );
733 return( AFPERR_PARAM );
739 /* ------------------- */
740 static int deletedir(char *dir)
742 char path[MAXPATHLEN + 1];
750 if ((len = strlen(dir)) +2 > sizeof(path))
754 if ((dp = opendir(dir)) == NULL)
760 remain = sizeof(path) -len -1;
761 while ((de = readdir(dp)) && err == AFP_OK) {
762 /* skip this and previous directory */
763 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
766 if (strlen(de->d_name) > remain) {
770 strcpy(path + len, de->d_name);
771 if (stat(path, &st)) {
774 if (S_ISDIR(st.st_mode)) {
775 err = deletedir(path);
777 err = netatalk_unlink(path);
782 /* okay. the directory is empty. delete it. note: we already got rid
785 err = netatalk_rmdir(dir);
790 /* do a recursive copy. */
791 static int copydir(const struct vol *vol, char *src, char *dst)
793 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
802 /* doesn't exist or the path is too long. */
803 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
804 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
805 ((dp = opendir(src)) == NULL))
808 /* try to create the destination directory */
809 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
814 /* set things up to copy */
818 srem = sizeof(spath) - slen -1;
823 drem = sizeof(dpath) - dlen -1;
826 while ((de = readdir(dp))) {
827 /* skip this and previous directory */
828 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
831 if (strlen(de->d_name) > srem) {
835 strcpy(spath + slen, de->d_name);
837 if (stat(spath, &st) == 0) {
838 if (strlen(de->d_name) > drem) {
842 strcpy(dpath + dlen, de->d_name);
844 if (S_ISDIR(st.st_mode)) {
845 if (AFP_OK != (err = copydir(vol, spath, dpath)))
847 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
851 /* keep the same time stamp. */
852 ut.actime = ut.modtime = st.st_mtime;
858 /* keep the same time stamp. */
859 if (stat(src, &st) == 0) {
860 ut.actime = ut.modtime = st.st_mtime;
870 /* --- public functions follow --- */
872 /* NOTE: we start off with at least one node (the root directory). */
873 static struct dir *dirinsert(struct vol *vol, struct dir *dir)
877 if ((node = dir_insert(vol, dir)))
880 /* recolor the tree. the current node is red. */
881 dir->d_color = DIRTREE_COLOR_RED;
883 /* parent of this node has to be black. if the parent node
884 * is red, then we have a grandparent. */
885 while ((dir != vol->v_root) &&
886 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
887 /* are we on the left tree? */
888 if (dir->d_back == dir->d_back->d_back->d_left) {
889 node = dir->d_back->d_back->d_right; /* get the right node */
890 if (node->d_color == DIRTREE_COLOR_RED) {
891 /* we're red. we need to change to black. */
892 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
893 node->d_color = DIRTREE_COLOR_BLACK;
894 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
895 dir = dir->d_back->d_back; /* finished. go up. */
897 if (dir == dir->d_back->d_right) {
899 dir_leftrotate(vol, dir);
901 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
902 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
903 dir_rightrotate(vol, dir->d_back->d_back);
906 node = dir->d_back->d_back->d_left;
907 if (node->d_color == DIRTREE_COLOR_RED) {
908 /* we're red. we need to change to black. */
909 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
910 node->d_color = DIRTREE_COLOR_BLACK;
911 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
912 dir = dir->d_back->d_back; /* finished. ascend */
914 if (dir == dir->d_back->d_left) {
916 dir_rightrotate(vol, dir);
918 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
919 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
920 dir_leftrotate(vol, dir->d_back->d_back);
925 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
929 /* ---------------------------- */
931 adddir(struct vol *vol, struct dir *dir, struct path *path)
933 struct dir *cdir, *edir;
941 upath = path->u_name;
943 upathlen = strlen(upath);
945 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
949 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
953 if ((cdir = dirnew(name, upath)) == NULL) {
954 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
957 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)) {
958 LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
959 cdir->d_m_name_ucs2 = NULL;
964 if ((edir = dirinsert( vol, cdir ))) {
965 /* it's not possible with LASTDID
967 - someone else have moved the directory.
968 - it's a symlink inside the share.
969 - it's an ID reused, the old directory was deleted but not
970 the cnid record and the server've reused the inode for
972 for HASH (we should get ride of HASH)
973 - someone else have moved the directory.
974 - it's an ID reused as above
975 - it's a hash duplicate and we are in big trouble
977 deleted = (edir->d_m_name == NULL);
979 dir_hash_del(vol, edir);
981 edir->d_m_name = cdir->d_m_name;
982 edir->d_u_name = cdir->d_u_name;
983 edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
986 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
987 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
988 hash_alloc_insert(vol->v_hash, cdir, cdir);
991 /* the old was not in the same folder */
993 dirchildremove(cdir->d_parent, cdir);
996 /* parent/child directories */
997 cdir->d_parent = dir;
998 dirchildadd(vol, dir, cdir);
1002 /* --- public functions follow --- */
1003 /* free everything down. we don't bother to recolor as this is only
1004 * called to free the entire tree */
1005 void dirfreename(struct dir *dir)
1007 if (dir->d_u_name != dir->d_m_name) {
1008 free(dir->d_u_name);
1010 if (dir->d_m_name_ucs2)
1011 free(dir->d_m_name_ucs2);
1012 free(dir->d_m_name);
1015 void dirfree(struct dir *dir)
1017 if (!dir || (dir == SENTINEL))
1020 if ( dir->d_left != SENTINEL ) {
1021 dirfree( dir->d_left );
1023 if ( dir->d_right != SENTINEL ) {
1024 dirfree( dir->d_right );
1027 if (dir != SENTINEL) {
1033 /* --------------------------------------------
1034 * most of the time mac name and unix name are the same
1036 struct dir *dirnew(const char *m_name, const char *u_name)
1040 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1044 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1049 if (m_name == u_name || !strcmp(m_name, u_name)) {
1050 dir->d_u_name = dir->d_m_name;
1052 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1053 free(dir->d_m_name);
1058 dir->d_m_name_ucs2 = NULL;
1059 dir->d_left = dir->d_right = SENTINEL;
1060 dir->d_next = dir->d_prev = dir;
1064 /* ------------------ */
1065 static hash_val_t hash_fun_dir(const void *key)
1067 const struct dir *k = key;
1069 static unsigned long randbox[] = {
1070 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1071 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1072 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1073 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1076 const unsigned char *str = k->d_u_name;
1080 acc ^= randbox[(*str + acc) & 0xf];
1081 acc = (acc << 1) | (acc >> 31);
1083 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1084 acc = (acc << 2) | (acc >> 30);
1090 /* ---------------- */
1091 static int hash_comp_dir(const void *key1, const void *key2)
1093 const struct dir *k1 = key1;
1094 const struct dir *k2 = key2;
1096 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1099 /* ---------------- */
1103 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1106 /* ------------------ */
1107 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1110 movecwd failed some of dir path are not there anymore.
1111 FIXME Is it true with other errors?
1112 so we remove dir from the cache
1114 if (dir->d_did == DIRDID_ROOT_PARENT)
1116 if (afp_errno == AFPERR_ACCESS) {
1117 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1120 /* FIXME should we set these?, don't need to call stat() after:
1122 ret->st_errno = EACCES;
1124 ret->m_name = dir->d_m_name;
1125 ret->u_name = dir->d_u_name;
1128 } else if (afp_errno == AFPERR_NOOBJ) {
1129 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1132 strcpy(ret->m_name, dir->d_m_name);
1133 if (dir->d_m_name == dir->d_u_name) {
1134 ret->u_name = ret->m_name;
1137 size_t tp = strlen(ret->m_name)+1;
1139 ret->u_name = ret->m_name +tp;
1140 strcpy(ret->u_name, dir->d_u_name);
1142 /* FIXME should we set :
1144 ret->st_errno = ENOENT;
1146 dir_invalidate(vol, dir);
1149 dir_invalidate(vol, dir);
1153 /* -------------------------------------------------- */
1159 stat the file or errno
1162 curdir: filename parent directory
1168 stat the dir or errno
1172 curdir: dir parent directory
1180 curdir: dir parent directory
1187 cname(const struct vol *vol, struct dir *dir, char **cpath)
1189 struct dir *cdir, *scdir=NULL;
1190 static char path[ MAXPATHLEN + 1];
1191 static struct path ret;
1203 afp_errno = AFPERR_NOOBJ;
1204 memset(&ret, 0, sizeof(ret));
1205 switch (ret.m_type = *data) { /* path type */
1208 len = (unsigned char) *data++;
1211 if (afp_version >= 30) {
1217 if (afp_version >= 30) {
1219 memcpy(&hint, data, sizeof(hint));
1221 data += sizeof(hint);
1223 memcpy(&len16, data, sizeof(len16));
1230 /* else it's an error */
1232 afp_errno = AFPERR_PARAM;
1235 *cpath += len + size;
1240 if (movecwd( vol, dir ) < 0 ) {
1241 return invalidate(vol, dir, &ret );
1243 if (*path == '\0') {
1250 if (*data == sep ) {
1254 while (*data == sep && len > 0 ) {
1255 if ( dir->d_parent == NULL ) {
1258 dir = dir->d_parent;
1263 /* would this be faster with strlen + strncpy? */
1265 while ( *data != sep && len > 0 ) {
1267 if (p > &path[ MAXPATHLEN]) {
1268 afp_errno = AFPERR_PARAM;
1274 /* short cut bits by chopping off a trailing \0. this also
1275 makes the traversal happy w/ filenames at the end of the
1282 if ( p == path ) { /* end of the name parameter */
1286 if (afp_version >= 30) {
1291 static char temp[ MAXPATHLEN + 1];
1293 if (dir->d_did == DIRDID_ROOT_PARENT) {
1295 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1296 So we compare it with the longname from the current volume and if they match
1297 we overwrite the requested path with the utf8 volume name so that the following
1300 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1301 if (strcasecmp( path, temp) == 0)
1302 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1305 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1306 afp_errno = AFPERR_PARAM;
1312 /* check for OS X mangled filename :( */
1314 t = demangle_osx(vol, path, dir->d_did, &fileid);
1317 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1318 * flags weren't the same
1320 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1321 /* at last got our view of mac name */
1326 if (ret.u_name == NULL) {
1327 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1328 afp_errno = AFPERR_PARAM;
1334 cdir = dir->d_child;
1336 if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1337 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
1338 CH_UCS2, path, strlen(path), (char **)&tmpname) )
1341 if (!cdir->d_m_name_ucs2) {
1342 LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1343 /* this shouldn't happen !!!! */
1347 if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1350 if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1353 cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1359 if (dir->d_did == DIRDID_ROOT_PARENT) {
1361 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1362 must check against the volume name.
1364 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1370 cdir = dirsearch_byname(vol, dir, ret.u_name);
1374 if (cdir == NULL && scdir != NULL) {
1376 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1379 if ( cdir == NULL ) {
1381 /* if dir == curdir it always succeed,
1382 even if curdir is deleted.
1383 it's not a pb because it will fail in extenddir
1385 if ( movecwd( vol, dir ) < 0 ) {
1386 /* dir is not valid anymore
1387 we delete dir from the cache and abort.
1389 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1390 afp_errno = AFPERR_NOOBJ;
1393 if (afp_errno == AFPERR_ACCESS)
1395 dir_invalidate(vol, dir);
1398 cdir = extenddir( vol, dir, &ret );
1402 cdir = extenddir( vol, dir, &ret );
1403 } /* if (!extend) */
1405 if ( cdir == NULL ) {
1407 if ( len > 0 || !ret.u_name ) {
1419 * Move curdir to dir, with a possible chdir()
1421 int movecwd(const struct vol *vol, struct dir *dir)
1423 char path[MAXPATHLEN + 1];
1428 if ( dir == curdir ) {
1431 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1432 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1436 p = path + sizeof(path) - 1;
1439 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1442 /* parent directory is deleted */
1443 afp_errno = AFPERR_NOOBJ;
1447 if (p -n -1 < path) {
1448 afp_errno = AFPERR_PARAM;
1455 if ( d != curdir ) {
1456 n = strlen( vol->v_path );
1457 if (p -n -1 < path) {
1458 afp_errno = AFPERR_PARAM;
1463 memcpy( p, vol->v_path, n );
1465 if ( chdir( p ) < 0 ) {
1469 afp_errno = AFPERR_ACCESS;
1472 afp_errno = AFPERR_NOOBJ;
1482 * We can't use unix file's perm to support Apple's inherited protection modes.
1483 * If we aren't the file's owner we can't change its perms when moving it and smb
1484 * nfs,... don't even try.
1486 #define AFP_CHECK_ACCESS
1488 int check_access(char *path, int mode)
1490 #ifdef AFP_CHECK_ACCESS
1498 accessmode(p, &ma, curdir, NULL);
1499 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1501 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1507 /* --------------------- */
1508 int file_access(struct path *path, int mode)
1512 accessmode(path->u_name, &ma, curdir, &path->st);
1513 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1515 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1521 /* --------------------- */
1522 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1524 dir->offcnt = count;
1525 dir->ctime = st->st_ctime;
1526 dir->d_flags &= ~DIRF_CNID;
1529 /* ---------------------
1530 * is our cached offspring count valid?
1533 int diroffcnt(struct dir *dir, struct stat *st)
1535 return st->st_ctime == dir->ctime;
1538 /* ---------------------
1539 * is our cached also for reenumerate id?
1542 int dirreenumerate(struct dir *dir, struct stat *st)
1544 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1547 /* --------------------- */
1548 static int invisible_dots(const struct vol *vol, const char *name)
1550 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1553 /* ------------------------------
1555 (name, dir) with curdir:name == dir, from afp_enumerate
1558 int getdirparams(const struct vol *vol,
1559 u_int16_t bitmap, struct path *s_path,
1561 char *buf, int *buflen )
1565 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1566 int bit = 0, isad = 0;
1572 struct stat *st = &s_path->st;
1573 char *upath = s_path->u_name;
1575 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1576 (1 << DIRPBIT_CDATE) |
1577 (1 << DIRPBIT_MDATE) |
1578 (1 << DIRPBIT_BDATE) |
1579 (1 << DIRPBIT_FINFO)))) {
1581 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1582 if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1587 if ( dir->d_did == DIRDID_ROOT) {
1588 pdid = DIRDID_ROOT_PARENT;
1589 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1592 pdid = dir->d_parent->d_did;
1596 while ( bitmap != 0 ) {
1597 while (( bitmap & 1 ) == 0 ) {
1605 ad_getattr(&ad, &ashort);
1606 } else if (invisible_dots(vol, dir->d_u_name)) {
1607 ashort = htons(ATTRBIT_INVISIBLE);
1610 ashort |= htons(ATTRBIT_SHARED);
1611 memcpy( data, &ashort, sizeof( ashort ));
1612 data += sizeof( ashort );
1616 memcpy( data, &pdid, sizeof( pdid ));
1617 data += sizeof( pdid );
1620 case DIRPBIT_CDATE :
1621 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1622 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1623 memcpy( data, &aint, sizeof( aint ));
1624 data += sizeof( aint );
1627 case DIRPBIT_MDATE :
1628 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1629 memcpy( data, &aint, sizeof( aint ));
1630 data += sizeof( aint );
1633 case DIRPBIT_BDATE :
1634 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1635 aint = AD_DATE_START;
1636 memcpy( data, &aint, sizeof( aint ));
1637 data += sizeof( aint );
1640 case DIRPBIT_FINFO :
1642 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1643 } else { /* no appledouble */
1644 memset( data, 0, 32 );
1645 /* set default view -- this also gets done in ad_open() */
1646 ashort = htons(FINDERINFO_CLOSEDVIEW);
1647 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1649 /* dot files are by default visible */
1650 if (invisible_dots(vol, dir->d_u_name)) {
1651 ashort = htons(FINDERINFO_INVISIBLE);
1652 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1658 case DIRPBIT_LNAME :
1659 if (dir->d_m_name) /* root of parent can have a null name */
1662 memset(data, 0, sizeof(u_int16_t));
1663 data += sizeof( u_int16_t );
1666 case DIRPBIT_SNAME :
1667 memset(data, 0, sizeof(u_int16_t));
1668 data += sizeof( u_int16_t );
1672 memcpy( data, &dir->d_did, sizeof( aint ));
1673 data += sizeof( aint );
1676 case DIRPBIT_OFFCNT :
1678 /* this needs to handle current directory access rights */
1679 if (diroffcnt(dir, st)) {
1680 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1682 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1683 setdiroffcnt(dir, st, ret);
1684 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1686 ashort = htons( ashort );
1687 memcpy( data, &ashort, sizeof( ashort ));
1688 data += sizeof( ashort );
1692 aint = htonl(st->st_uid);
1693 memcpy( data, &aint, sizeof( aint ));
1694 data += sizeof( aint );
1698 aint = htonl(st->st_gid);
1699 memcpy( data, &aint, sizeof( aint ));
1700 data += sizeof( aint );
1703 case DIRPBIT_ACCESS :
1704 accessmode( upath, &ma, dir , st);
1706 *data++ = ma.ma_user;
1707 *data++ = ma.ma_world;
1708 *data++ = ma.ma_group;
1709 *data++ = ma.ma_owner;
1712 /* Client has requested the ProDOS information block.
1713 Just pass back the same basic block for all
1714 directories. <shirsch@ibm.net> */
1715 case DIRPBIT_PDINFO :
1716 if (afp_version >= 30) { /* UTF8 name */
1717 utf8 = kTextEncodingUTF8;
1718 if (dir->d_m_name) /* root of parent can have a null name */
1721 memset(data, 0, sizeof(u_int16_t));
1722 data += sizeof( u_int16_t );
1724 memcpy(data, &aint, sizeof( aint ));
1725 data += sizeof( aint );
1727 else { /* ProDOS Info Block */
1730 ashort = htons( 0x0200 );
1731 memcpy( data, &ashort, sizeof( ashort ));
1732 data += sizeof( ashort );
1733 memset( data, 0, sizeof( ashort ));
1734 data += sizeof( ashort );
1738 case DIRPBIT_UNIXPR :
1739 aint = htonl(st->st_uid);
1740 memcpy( data, &aint, sizeof( aint ));
1741 data += sizeof( aint );
1742 aint = htonl(st->st_gid);
1743 memcpy( data, &aint, sizeof( aint ));
1744 data += sizeof( aint );
1747 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1748 memcpy( data, &aint, sizeof( aint ));
1749 data += sizeof( aint );
1751 accessmode( upath, &ma, dir , st);
1753 *data++ = ma.ma_user;
1754 *data++ = ma.ma_world;
1755 *data++ = ma.ma_group;
1756 *data++ = ma.ma_owner;
1761 ad_close_metadata( &ad );
1763 return( AFPERR_BITMAP );
1769 ashort = htons( data - buf );
1770 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1771 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1773 if ( utf_nameoff ) {
1774 ashort = htons( data - buf );
1775 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1776 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1779 ad_close_metadata( &ad );
1781 *buflen = data - buf;
1785 /* ----------------------------- */
1786 int path_error(struct path *path, int error)
1788 /* - a dir with access error
1789 * - no error it's a file
1792 if (path_isadir(path))
1794 if (path->st_valid && path->st_errno)
1796 return AFPERR_BADTYPE ;
1799 /* ----------------------------- */
1800 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1805 u_int16_t vid, bitmap;
1811 memcpy( &vid, ibuf, sizeof( vid ));
1812 ibuf += sizeof( vid );
1814 if (NULL == ( vol = getvolbyvid( vid )) ) {
1815 return( AFPERR_PARAM );
1818 if (vol->v_flags & AFPVOL_RO)
1819 return AFPERR_VLOCK;
1821 memcpy( &did, ibuf, sizeof( did ));
1822 ibuf += sizeof( int );
1824 if (NULL == ( dir = dirlookup( vol, did )) ) {
1828 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1829 bitmap = ntohs( bitmap );
1830 ibuf += sizeof( bitmap );
1832 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1833 return get_afp_errno(AFPERR_NOOBJ);
1836 if ( *path->m_name != '\0' ) {
1837 rc = path_error(path, AFPERR_NOOBJ);
1838 /* maybe we are trying to set perms back */
1839 if (rc != AFPERR_ACCESS)
1844 * If ibuf is odd, make it even.
1846 if ((u_long)ibuf & 1 ) {
1850 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1851 setvoltime(obj, vol );
1857 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1859 * assume path == '\0' eg. it's a directory in canonical form
1862 struct path Cur_Path = {
1865 ".", /* unix name */
1867 NULL,/* struct dir */
1868 0, /* stat is not set */
1871 /* ------------------ */
1872 static int set_dir_errors(struct path *path, const char *where, int err)
1877 return AFPERR_ACCESS;
1879 return AFPERR_VLOCK;
1881 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1882 return AFPERR_PARAM;
1885 /* ------------------ */
1886 int setdirparams(const struct vol *vol,
1887 struct path *path, u_int16_t d_bitmap, char *buf )
1899 u_int16_t ashort, bshort;
1901 int change_mdate = 0;
1902 int change_parent_mdate = 0;
1904 u_int16_t bitmap = d_bitmap;
1905 u_char finder_buf[32];
1908 u_int16_t upriv_bit = 0;
1911 upath = path->u_name;
1913 while ( bitmap != 0 ) {
1914 while (( bitmap & 1 ) == 0 ) {
1922 memcpy( &ashort, buf, sizeof( ashort ));
1923 buf += sizeof( ashort );
1925 case DIRPBIT_CDATE :
1927 memcpy(&cdate, buf, sizeof(cdate));
1928 buf += sizeof( cdate );
1930 case DIRPBIT_MDATE :
1931 memcpy(&newdate, buf, sizeof(newdate));
1932 buf += sizeof( newdate );
1934 case DIRPBIT_BDATE :
1936 memcpy(&bdate, buf, sizeof(bdate));
1937 buf += sizeof( bdate );
1939 case DIRPBIT_FINFO :
1941 memcpy( finder_buf, buf, 32 );
1944 case DIRPBIT_UID : /* What kind of loser mounts as root? */
1945 change_parent_mdate = 1;
1946 memcpy( &owner, buf, sizeof(owner));
1947 buf += sizeof( owner );
1950 change_parent_mdate = 1;
1951 memcpy( &group, buf, sizeof( group ));
1952 buf += sizeof( group );
1954 case DIRPBIT_ACCESS :
1956 change_parent_mdate = 1;
1957 ma.ma_user = *buf++;
1958 ma.ma_world = *buf++;
1959 ma.ma_group = *buf++;
1960 ma.ma_owner = *buf++;
1961 mpriv = mtoumode( &ma ) | vol->v_dperm;
1962 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
1963 err = set_dir_errors(path, "setdirmode", errno);
1967 /* Ignore what the client thinks we should do to the
1968 ProDOS information block. Skip over the data and
1969 report nothing amiss. <shirsch@ibm.net> */
1970 case DIRPBIT_PDINFO :
1971 if (afp_version < 30) {
1975 err = AFPERR_BITMAP;
1979 case DIRPBIT_UNIXPR :
1980 if (vol_unix_priv(vol)) {
1981 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
1982 buf += sizeof( owner );
1983 memcpy( &group, buf, sizeof( group ));
1984 buf += sizeof( group );
1987 change_parent_mdate = 1;
1988 memcpy( &upriv, buf, sizeof( upriv ));
1989 buf += sizeof( upriv );
1990 upriv = ntohl (upriv) | vol->v_dperm;
1991 if (dir_rx_set(upriv)) {
1992 /* maybe we are trying to set perms back */
1993 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
1995 err = set_dir_errors(path, "setdirunixmode", errno);
2006 err = AFPERR_BITMAP;
2014 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2016 if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2018 * Check to see what we're trying to set. If it's anything
2019 * but ACCESS, UID, or GID, give an error. If it's any of those
2020 * three, we don't need the ad to be open, so just continue.
2022 * note: we also don't need to worry about mdate. also, be quiet
2023 * if we're using the noadouble option.
2025 if (!vol_noadouble(vol) && (d_bitmap &
2026 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2027 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2028 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2029 return AFPERR_ACCESS;
2035 * Check to see if a create was necessary. If it was, we'll want
2036 * to set our name, etc.
2038 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2039 ad_setname(&ad, curdir->d_m_name);
2045 while ( bitmap != 0 ) {
2046 while (( bitmap & 1 ) == 0 ) {
2054 ad_getattr(&ad, &bshort);
2055 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2056 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2057 change_parent_mdate = 1;
2058 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2059 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2063 ad_setattr(&ad, bshort);
2066 case DIRPBIT_CDATE :
2068 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2071 case DIRPBIT_MDATE :
2073 case DIRPBIT_BDATE :
2075 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2078 case DIRPBIT_FINFO :
2080 /* Fixes #2802236 */
2081 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2082 *fflags &= htons(~FINDERINFO_ISHARED);
2084 if ( dir->d_did == DIRDID_ROOT ) {
2086 * Alright, we admit it, this is *really* sick!
2087 * The 4 bytes that we don't copy, when we're dealing
2088 * with the root of a volume, are the directory's
2089 * location information. This eliminates that annoying
2090 * behavior one sees when mounting above another mount
2093 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2094 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2096 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2100 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2101 if ( (dir->d_did == DIRDID_ROOT) &&
2102 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2103 err = set_dir_errors(path, "setdeskowner", errno);
2104 if (isad && err == AFPERR_PARAM) {
2105 err = AFP_OK; /* ???*/
2108 goto setdirparam_done;
2111 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2112 err = set_dir_errors(path, "setdirowner", errno);
2113 goto setdirparam_done;
2117 if (dir->d_did == DIRDID_ROOT)
2118 setdeskowner( -1, ntohl(group) );
2119 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2120 err = set_dir_errors(path, "setdirowner", errno);
2121 goto setdirparam_done;
2124 case DIRPBIT_ACCESS :
2125 if (dir->d_did == DIRDID_ROOT) {
2127 if (!dir_rx_set(mpriv)) {
2128 /* we can't remove read and search for owner on volume root */
2129 err = AFPERR_ACCESS;
2130 goto setdirparam_done;
2134 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2135 err = set_dir_errors(path, "setdirmode", errno);
2136 goto setdirparam_done;
2139 case DIRPBIT_PDINFO :
2140 if (afp_version >= 30) {
2141 err = AFPERR_BITMAP;
2142 goto setdirparam_done;
2145 case DIRPBIT_UNIXPR :
2146 if (vol_unix_priv(vol)) {
2147 if (dir->d_did == DIRDID_ROOT) {
2148 if (!dir_rx_set(upriv)) {
2149 /* we can't remove read and search for owner on volume root */
2150 err = AFPERR_ACCESS;
2151 goto setdirparam_done;
2153 setdeskowner( -1, ntohl(group) );
2154 setdeskmode( upriv );
2156 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2157 err = set_dir_errors(path, "setdirowner", errno);
2158 goto setdirparam_done;
2161 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2162 err = set_dir_errors(path, "setdirunixmode", errno);
2163 goto setdirparam_done;
2167 err = AFPERR_BITMAP;
2168 goto setdirparam_done;
2172 err = AFPERR_BITMAP;
2173 goto setdirparam_done;
2182 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2183 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2187 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2188 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2193 if (path->st_valid && !path->st_errno) {
2194 struct stat *st = &path->st;
2196 if (dir && dir->d_parent) {
2197 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2201 ad_close_metadata( &ad);
2204 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2205 && gettimeofday(&tv, NULL) == 0) {
2206 if (!movecwd(vol, dir->d_parent)) {
2207 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2208 /* be careful with bitmap because now dir is null */
2209 bitmap = 1<<DIRPBIT_MDATE;
2210 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2211 /* should we reset curdir ?*/
2218 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2232 memcpy( &vid, ibuf, sizeof( vid ));
2233 ibuf += sizeof( vid );
2234 if (NULL == (vol = getvolbyvid( vid )) ) {
2235 return( AFPERR_PARAM );
2238 memcpy( &did, ibuf, sizeof( did ));
2239 ibuf += sizeof( did );
2243 * if it's CNID 2 our only choice to meet the specs is call sync.
2244 * For any other CNID just sync that dir. To my knowledge the
2245 * intended use of FPSyncDir is to sync the volume so all we're
2246 * ever going to see here is probably CNID 2. Anyway, we' prepared.
2249 if ( ntohl(did) == 2 ) {
2252 if (NULL == ( dir = dirlookup( vol, did )) ) {
2253 return afp_errno; /* was AFPERR_NOOBJ */
2256 if (movecwd( vol, dir ) < 0 )
2257 return ( AFPERR_NOOBJ );
2260 * Assuming only OSens that have dirfd also may require fsyncing directories
2261 * in order to flush metadata e.g. Linux.
2265 if (NULL == ( dp = opendir( "." )) ) {
2268 return( AFPERR_NOOBJ );
2270 return( AFPERR_ACCESS );
2272 return( AFPERR_PARAM );
2276 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2279 if ( fsync ( dfd ) < 0 )
2280 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2281 dir->d_u_name, strerror(errno) );
2282 closedir(dp); /* closes dfd too */
2285 if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2288 return( AFPERR_NOOBJ );
2290 return( AFPERR_ACCESS );
2292 return( AFPERR_PARAM );
2296 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2297 vol->ad_path(".", ADFLAGS_DIR) );
2299 if ( fsync(dfd) < 0 )
2300 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2301 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2308 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2314 struct path *s_path;
2322 memcpy( &vid, ibuf, sizeof( vid ));
2323 ibuf += sizeof( vid );
2324 if (NULL == ( vol = getvolbyvid( vid )) ) {
2325 return( AFPERR_PARAM );
2328 if (vol->v_flags & AFPVOL_RO)
2329 return AFPERR_VLOCK;
2331 memcpy( &did, ibuf, sizeof( did ));
2332 ibuf += sizeof( did );
2333 if (NULL == ( dir = dirlookup( vol, did )) ) {
2334 return afp_errno; /* was AFPERR_NOOBJ */
2336 /* for concurrent access we need to be sure we are not in the
2337 * folder we want to create...
2341 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2342 return get_afp_errno(AFPERR_PARAM);
2344 /* cname was able to move curdir to it! */
2345 if (*s_path->m_name == '\0')
2346 return AFPERR_EXIST;
2348 upath = s_path->u_name;
2350 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2354 if (of_stat(s_path) < 0) {
2358 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2362 if ( movecwd( vol, dir ) < 0 ) {
2363 return( AFPERR_PARAM );
2366 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2367 if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2368 if (vol_noadouble(vol))
2369 goto createdir_done;
2370 return( AFPERR_ACCESS );
2372 ad_setname(&ad, s_path->m_name);
2373 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2376 ad_close_metadata( &ad);
2379 #ifdef HAVE_NFSv4_ACLS
2380 /* FIXME: are we really inside the created dir? */
2381 addir_inherit_acl(vol);
2384 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2385 *rbuflen = sizeof( u_int32_t );
2386 setvoltime(obj, vol );
2391 * dst new unix filename (not a pathname)
2392 * newname new mac name
2396 int renamedir(const struct vol *vol, char *src, char *dst,
2398 struct dir *newparent,
2406 /* existence check moved to afp_moveandrename */
2407 if ( unix_rename( src, dst ) < 0 ) {
2410 return( AFPERR_NOOBJ );
2412 return( AFPERR_ACCESS );
2414 return AFPERR_VLOCK;
2416 /* tried to move directory into a subdirectory of itself */
2417 return AFPERR_CANTMOVE;
2419 /* this needs to copy and delete. bleah. that means we have
2420 * to deal with entire directory hierarchies. */
2421 if ((err = copydir(vol, src, dst)) < 0) {
2425 if ((err = deletedir(src)) < 0)
2429 return( AFPERR_PARAM );
2433 vol->vfs->vfs_renamedir(vol, src, dst);
2435 len = strlen( newname );
2436 /* rename() succeeded so we need to update our tree even if we can't open
2440 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2442 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2443 ad_setname(&ad, newname);
2445 ad_close_metadata( &ad);
2448 dir_hash_del(vol, dir);
2449 if (dir->d_m_name == dir->d_u_name)
2450 dir->d_u_name = NULL;
2452 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2453 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2454 /* FIXME : fatal ? */
2457 dir->d_m_name = buf;
2458 strcpy( dir->d_m_name, newname );
2460 if (newname == dst) {
2461 free(dir->d_u_name);
2462 dir->d_u_name = dir->d_m_name;
2465 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2466 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2469 dir->d_u_name = buf;
2470 strcpy( dir->d_u_name, dst );
2473 if (dir->d_m_name_ucs2)
2474 free(dir->d_m_name_ucs2);
2476 dir->d_m_name_ucs2 = NULL;
2477 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))
2478 dir->d_m_name_ucs2 = NULL;
2480 if (( parent = dir->d_parent ) == NULL ) {
2483 if ( parent == newparent ) {
2484 hash_alloc_insert(vol->v_hash, dir, dir);
2488 /* detach from old parent and add to new one. */
2489 dirchildremove(parent, dir);
2490 dir->d_parent = newparent;
2491 dirchildadd(vol, newparent, dir);
2495 /* delete an empty directory */
2496 int deletecurdir(const struct vol *vol)
2506 if ( curdir->d_parent == NULL ) {
2507 return( AFPERR_ACCESS );
2512 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2513 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2515 ad_getattr(&ad, &ashort);
2516 ad_close( &ad, ADFLAGS_HF );
2517 if ((ashort & htons(ATTRBIT_NODELETE))) {
2518 return AFPERR_OLOCK;
2521 err = vol->vfs->vfs_deletecurdir(vol);
2526 /* now get rid of dangling symlinks */
2527 if ((dp = opendir("."))) {
2528 while ((de = readdir(dp))) {
2529 /* skip this and previous directory */
2530 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2533 /* bail if it's not a symlink */
2534 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2536 return AFPERR_DIRNEMPT;
2539 if ((err = netatalk_unlink(de->d_name))) {
2546 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2551 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2552 dirchildremove(curdir, fdir);
2553 cnid_delete(vol->v_cdb, fdir->d_did);
2554 dir_remove( vol, fdir );
2559 /* inode is used as key for cnid.
2560 * Close the descriptor only after cnid_delete
2568 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2579 sfunc = (unsigned char) *ibuf++;
2583 if (sfunc >= 3 && sfunc <= 6) {
2584 if (afp_version < 30) {
2585 return( AFPERR_PARAM );
2592 case 3 :/* unicode */
2593 memcpy( &id, ibuf, sizeof( id ));
2596 if (( pw = getpwuid( id )) == NULL ) {
2597 return( AFPERR_NOITEM );
2599 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2600 pw->pw_name, strlen(pw->pw_name), &name);
2607 case 4 : /* unicode */
2608 memcpy( &id, ibuf, sizeof( id ));
2611 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2612 return( AFPERR_NOITEM );
2614 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2615 gr->gr_name, strlen(gr->gr_name), &name);
2621 #ifdef HAVE_NFSv4_ACLS
2622 case 5 : /* UUID -> username */
2623 case 6 : /* UUID -> groupname */
2624 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2625 return AFPERR_PARAM;
2626 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2627 len = getnamefromuuid( ibuf, &name, &type);
2628 if (len != 0) /* its a error code, not len */
2629 return AFPERR_NOITEM;
2630 if (type == UUID_USER) {
2631 if (( pw = getpwnam( name )) == NULL )
2632 return( AFPERR_NOITEM );
2633 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2634 id = htonl(UUID_USER);
2635 memcpy( rbuf, &id, sizeof( id ));
2636 id = htonl( pw->pw_uid);
2637 rbuf += sizeof( id );
2638 memcpy( rbuf, &id, sizeof( id ));
2639 rbuf += sizeof( id );
2640 *rbuflen = 2 * sizeof( id );
2641 } else { /* type == UUID_GROUP */
2642 if (( gr = getgrnam( name )) == NULL )
2643 return( AFPERR_NOITEM );
2644 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2645 id = htonl(UUID_GROUP);
2646 memcpy( rbuf, &id, sizeof( id ));
2647 rbuf += sizeof( id );
2648 id = htonl( gr->gr_gid);
2649 memcpy( rbuf, &id, sizeof( id ));
2650 rbuf += sizeof( id );
2651 *rbuflen = 2 * sizeof( id );
2656 return( AFPERR_PARAM );
2660 len = strlen( name );
2663 u_int16_t tp = htons(len);
2664 memcpy(rbuf, &tp, sizeof(tp));
2673 memcpy( rbuf, name, len );
2681 int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2690 sfunc = (unsigned char) *ibuf++;
2692 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2695 case 2 : /* unicode */
2696 if (afp_version < 30) {
2697 return( AFPERR_PARAM );
2699 memcpy(&ulen, ibuf, sizeof(ulen));
2702 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2706 len = (unsigned char) *ibuf++;
2708 #ifdef HAVE_NFSv4_ACLS
2709 case 5 : /* username -> UUID */
2710 case 6 : /* groupname -> UUID */
2711 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2712 return AFPERR_PARAM;
2713 memcpy(&ulen, ibuf, sizeof(ulen));
2719 return( AFPERR_PARAM );
2725 return AFPERR_PARAM;
2728 case 1 : /* unicode */
2730 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2731 return( AFPERR_NOITEM );
2735 memcpy( rbuf, &id, sizeof( id ));
2736 *rbuflen = sizeof( id );
2739 case 2 : /* unicode */
2741 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2742 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2743 return( AFPERR_NOITEM );
2746 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2748 memcpy( rbuf, &id, sizeof( id ));
2749 *rbuflen = sizeof( id );
2751 #ifdef HAVE_NFSv4_ACLS
2752 case 5 : /* username -> UUID */
2753 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2754 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2755 return AFPERR_NOITEM;
2756 *rbuflen = UUID_BINSIZE;
2758 case 6 : /* groupname -> UUID */
2759 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2760 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2761 return AFPERR_NOITEM;
2762 *rbuflen = UUID_BINSIZE;
2770 /* ------------------------------------
2771 variable DID support
2773 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2784 /* do nothing as dids are static for the life of the process. */
2788 memcpy(&vid, ibuf, sizeof( vid ));
2789 ibuf += sizeof( vid );
2790 if (( vol = getvolbyvid( vid )) == NULL ) {
2791 return( AFPERR_PARAM );
2794 memcpy( &did, ibuf, sizeof( did ));
2795 ibuf += sizeof( did );
2796 if (( dir = dirlookup( vol, did )) == NULL ) {
2797 return( AFPERR_PARAM );
2800 /* dir_remove -- deletedid */
2806 /* did creation gets done automatically
2807 * there's a pb again with case but move it to cname
2809 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2812 struct dir *parentdir;
2820 memcpy(&vid, ibuf, sizeof(vid));
2821 ibuf += sizeof( vid );
2823 if (NULL == ( vol = getvolbyvid( vid )) ) {
2824 return( AFPERR_PARAM );
2827 memcpy(&did, ibuf, sizeof(did));
2828 ibuf += sizeof(did);
2830 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2834 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2835 return get_afp_errno(AFPERR_PARAM);
2838 if ( *path->m_name != '\0' ) {
2839 return path_error(path, AFPERR_NOOBJ);
2842 if ( !path->st_valid && of_stat(path ) < 0 ) {
2843 return( AFPERR_NOOBJ );
2845 if ( path->st_errno ) {
2846 return( AFPERR_NOOBJ );
2849 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2850 *rbuflen = sizeof(curdir->d_did);