2 * $Id: directory.c,v 1.96 2009-04-10 11:21:19 franklahm Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
7 * 19 jan 2000 implemented red-black trees for directory lookups
13 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
38 #include <sys/param.h>
41 #include <atalk/adouble.h>
43 #include <atalk/afp.h>
44 #include <atalk/util.h>
45 #include <atalk/cnid.h>
46 #include <atalk/logger.h>
47 #include <atalk/uuid.h>
49 #include "directory.h"
59 #ifdef HAVE_NFSv4_ACLS
60 extern void addir_inherit_acl(const struct vol *vol);
66 #define SENTINEL (&sentinel)
67 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
68 NULL, NULL, NULL, NULL, NULL, 0, 0,
70 static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
71 NULL, NULL, NULL, NULL, NULL, 0, 0,
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 #else /* ! REMOVE_NODES */
466 /* go searching for a node with at most one child */
467 if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
471 while (node->d_left != SENTINEL)
476 leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
479 leaf->d_back = node->d_back;
482 } else if (node == node->d_back->d_left) { /* left tree */
483 node->d_back->d_left = leaf;
485 node->d_back->d_right = leaf;
488 /* we want to free node, but we also want to free the data in dir.
489 * currently, that's d_name and the directory traversal bits.
490 * we just copy the necessary bits and then fix up all the
491 * various pointers to the directory. needless to say, there are
492 * a bunch of places that store the directory struct. */
494 struct dir save, *tmp;
496 memcpy(&save, dir, sizeof(save));
497 memcpy(dir, node, sizeof(struct dir));
499 /* restore the red-black bits */
500 dir->d_left = save.d_left;
501 dir->d_right = save.d_right;
502 dir->d_back = save.d_back;
503 dir->d_color = save.d_color;
505 if (node == vol->v_dir) {/* we may need to fix up this pointer */
507 rootpar.d_child = vol->v_dir;
509 /* if we aren't the root directory, we have parents and
510 * siblings to worry about */
511 if (dir->d_parent->d_child == node)
512 dir->d_parent->d_child = dir;
513 dir->d_next->d_prev = dir;
514 dir->d_prev->d_next = dir;
517 /* fix up children. */
521 tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
524 if (node == curdir) /* another pointer to fixup */
527 /* we also need to fix up oforks. bleah */
528 if ((of = dir->d_ofork)) {
529 last = of->of_d_prev;
532 of = (last == of) ? NULL : of->of_d_next;
536 /* set the node's d_name */
537 node->d_m_name = save.d_m_name;
538 node->d_u_name = save.d_u_name;
541 if (node->d_color == DIRTREE_COLOR_BLACK)
542 dir_rmrecolor(vol, leaf);
544 if (node->d_u_name != node->d_m_name) {
545 free(node->d_u_name);
547 free(node->d_m_name);
549 #endif /* ! REMOVE_NODES */
552 /* ---------------------------------------
553 * remove the node and its childs from the tree
555 * FIXME what about opened forks with refs to it?
556 * it's an afp specs violation because you can't delete
557 * an opened forks. Now afpd doesn't care about forks opened by other
558 * process. It's fixable within afpd if fnctl_lock, doable with smb and
559 * next to impossible for nfs and local filesystem access.
561 static void dir_invalidate( vol, dir )
562 const struct vol *vol;
566 /* v_root can't be deleted */
567 if (movecwd(vol, vol->v_root) < 0) {
568 LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
572 dirchildremove(dir->d_parent, dir);
573 dir_remove( vol, dir );
576 /* ------------------------------------ */
577 static struct dir *dir_insert(vol, dir)
578 const struct vol *vol;
584 while (pdir->d_did != dir->d_did ) {
585 if ( pdir->d_did > dir->d_did ) {
586 if ( pdir->d_left == SENTINEL ) {
593 if ( pdir->d_right == SENTINEL ) {
598 pdir = pdir->d_right;
605 * attempt to extend the current dir. tree to include path
606 * as a side-effect, movecwd to that point and return the new dir
609 extenddir( vol, dir, path )
616 if ( path->u_name == NULL) {
617 afp_errno = AFPERR_PARAM;
620 if (of_stat( path ) != 0 ) {
624 if (!S_ISDIR(path->st.st_mode)) {
628 /* mac name is always with the right encoding (from cname()) */
629 if (( dir = adddir( vol, dir, path)) == NULL ) {
634 if ( movecwd( vol, dir ) < 0 ) {
641 /* -------------------
642 system rmdir with afp error code.
643 ENOENT is not an error.
645 int netatalk_rmdir(const char *name)
647 if (rmdir(name) < 0) {
652 return AFPERR_DIRNEMPT;
655 return AFPERR_ACCESS;
665 /* -------------------------
666 appledouble mkdir afp error code.
668 static int netatalk_mkdir(const char *name)
670 if (ad_mkdir(name, DIRBITS | 0777) < 0) {
673 return( AFPERR_NOOBJ );
675 return( AFPERR_VLOCK );
678 return( AFPERR_ACCESS );
680 return( AFPERR_EXIST );
683 return( AFPERR_DFULL );
685 return( AFPERR_PARAM );
691 /* -------------------
692 system unlink with afp error code.
693 ENOENT is not an error.
695 int netatalk_unlink(const char *name)
697 if (unlink(name) < 0) {
705 return AFPERR_ACCESS;
713 /* ------------------- */
714 static int deletedir(char *dir)
716 char path[MAXPATHLEN + 1];
724 if ((len = strlen(dir)) +2 > sizeof(path))
728 if ((dp = opendir(dir)) == NULL)
734 remain = sizeof(path) -len -1;
735 while ((de = readdir(dp)) && err == AFP_OK) {
736 /* skip this and previous directory */
737 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
740 if (strlen(de->d_name) > remain) {
744 strcpy(path + len, de->d_name);
745 if (stat(path, &st)) {
748 if (S_ISDIR(st.st_mode)) {
749 err = deletedir(path);
751 err = netatalk_unlink(path);
756 /* okay. the directory is empty. delete it. note: we already got rid
759 err = netatalk_rmdir(dir);
764 /* do a recursive copy. */
765 static int copydir(const struct vol *vol, char *src, char *dst)
767 char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
776 /* doesn't exist or the path is too long. */
777 if (((slen = strlen(src)) > sizeof(spath) - 2) ||
778 ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
779 ((dp = opendir(src)) == NULL))
782 /* try to create the destination directory */
783 if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
788 /* set things up to copy */
792 srem = sizeof(spath) - slen -1;
797 drem = sizeof(dpath) - dlen -1;
800 while ((de = readdir(dp))) {
801 /* skip this and previous directory */
802 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
805 if (strlen(de->d_name) > srem) {
809 strcpy(spath + slen, de->d_name);
811 if (stat(spath, &st) == 0) {
812 if (strlen(de->d_name) > drem) {
816 strcpy(dpath + dlen, de->d_name);
818 if (S_ISDIR(st.st_mode)) {
819 if (AFP_OK != (err = copydir(vol, spath, dpath)))
821 } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
825 /* keep the same time stamp. */
826 ut.actime = ut.modtime = st.st_mtime;
832 /* keep the same time stamp. */
833 if (stat(src, &st) == 0) {
834 ut.actime = ut.modtime = st.st_mtime;
844 /* --- public functions follow --- */
846 /* NOTE: we start off with at least one node (the root directory). */
847 static struct dir *dirinsert( vol, dir )
853 if ((node = dir_insert(vol, dir)))
856 /* recolor the tree. the current node is red. */
857 dir->d_color = DIRTREE_COLOR_RED;
859 /* parent of this node has to be black. if the parent node
860 * is red, then we have a grandparent. */
861 while ((dir != vol->v_root) &&
862 (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
863 /* are we on the left tree? */
864 if (dir->d_back == dir->d_back->d_back->d_left) {
865 node = dir->d_back->d_back->d_right; /* get the right node */
866 if (node->d_color == DIRTREE_COLOR_RED) {
867 /* we're red. we need to change to black. */
868 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
869 node->d_color = DIRTREE_COLOR_BLACK;
870 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
871 dir = dir->d_back->d_back; /* finished. go up. */
873 if (dir == dir->d_back->d_right) {
875 dir_leftrotate(vol, dir);
877 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
878 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
879 dir_rightrotate(vol, dir->d_back->d_back);
882 node = dir->d_back->d_back->d_left;
883 if (node->d_color == DIRTREE_COLOR_RED) {
884 /* we're red. we need to change to black. */
885 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
886 node->d_color = DIRTREE_COLOR_BLACK;
887 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
888 dir = dir->d_back->d_back; /* finished. ascend */
890 if (dir == dir->d_back->d_left) {
892 dir_rightrotate(vol, dir);
894 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
895 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
896 dir_leftrotate(vol, dir->d_back->d_back);
901 vol->v_root->d_color = DIRTREE_COLOR_BLACK;
905 /* ---------------------------- */
907 adddir( vol, dir, path)
912 struct dir *cdir, *edir;
920 upath = path->u_name;
922 upathlen = strlen(upath);
924 id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
928 if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
932 if ((cdir = dirnew(name, upath)) == NULL) {
933 LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
939 if ((edir = dirinsert( vol, cdir ))) {
940 /* it's not possible with LASTDID
942 - someone else have moved the directory.
943 - it's a symlink inside the share.
944 - it's an ID reused, the old directory was deleted but not
945 the cnid record and the server've reused the inode for
947 for HASH (we should get ride of HASH)
948 - someone else have moved the directory.
949 - it's an ID reused as above
950 - it's a hash duplicate and we are in big trouble
952 deleted = (edir->d_m_name == NULL);
954 dir_hash_del(vol, edir);
956 edir->d_m_name = cdir->d_m_name;
957 edir->d_u_name = cdir->d_u_name;
960 LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
961 if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
962 hash_alloc_insert(vol->v_hash, cdir, cdir);
965 /* the old was not in the same folder */
967 dirchildremove(cdir->d_parent, cdir);
970 /* parent/child directories */
971 cdir->d_parent = dir;
972 dirchildadd(vol, dir, cdir);
976 /* --- public functions follow --- */
977 /* free everything down. we don't bother to recolor as this is only
978 * called to free the entire tree */
979 void dirfreename(struct dir *dir)
981 if (dir->d_u_name != dir->d_m_name) {
990 if (!dir || (dir == SENTINEL))
993 if ( dir->d_left != SENTINEL ) {
994 dirfree( dir->d_left );
996 if ( dir->d_right != SENTINEL ) {
997 dirfree( dir->d_right );
1000 if (dir != SENTINEL) {
1006 /* --------------------------------------------
1007 * most of the time mac name and unix name are the same
1009 struct dir *dirnew(const char *m_name, const char *u_name)
1013 dir = (struct dir *) calloc(1, sizeof( struct dir ));
1017 if ((dir->d_m_name = strdup(m_name)) == NULL) {
1022 if (m_name == u_name || !strcmp(m_name, u_name)) {
1023 dir->d_u_name = dir->d_m_name;
1025 else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1026 free(dir->d_m_name);
1031 dir->d_left = dir->d_right = SENTINEL;
1032 dir->d_next = dir->d_prev = dir;
1036 /* ------------------ */
1037 static hash_val_t hash_fun_dir(const void *key)
1039 const struct dir *k = key;
1041 static unsigned long randbox[] = {
1042 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1043 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1044 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1045 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1048 const unsigned char *str = (unsigned char *)k->d_u_name;
1052 acc ^= randbox[(*str + acc) & 0xf];
1053 acc = (acc << 1) | (acc >> 31);
1055 acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1056 acc = (acc << 2) | (acc >> 30);
1062 /* ---------------- */
1063 static int hash_comp_dir(const void *key1, const void *key2)
1065 const struct dir *k1 = key1;
1066 const struct dir *k2 = key2;
1068 return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1071 /* ---------------- */
1075 return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1078 /* ------------------ */
1079 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1082 movecwd failed some of dir path are not there anymore.
1083 FIXME Is it true with other errors?
1084 so we remove dir from the cache
1086 if (dir->d_did == DIRDID_ROOT_PARENT)
1088 if (afp_errno == AFPERR_ACCESS) {
1089 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1092 /* FIXME should we set these?, don't need to call stat() after:
1094 ret->st_errno = EACCES;
1096 ret->m_name = dir->d_m_name;
1097 ret->u_name = dir->d_u_name;
1100 } else if (afp_errno == AFPERR_NOOBJ) {
1101 if ( movecwd( vol, dir->d_parent ) < 0 ) {
1104 strcpy(ret->m_name, dir->d_m_name);
1105 if (dir->d_m_name == dir->d_u_name) {
1106 ret->u_name = ret->m_name;
1109 size_t tp = strlen(ret->m_name)+1;
1111 ret->u_name = ret->m_name +tp;
1112 strcpy(ret->u_name, dir->d_u_name);
1114 /* FIXME should we set :
1116 ret->st_errno = ENOENT;
1118 dir_invalidate(vol, dir);
1121 dir_invalidate(vol, dir);
1125 /* -------------------------------------------------- */
1131 stat the file or errno
1134 curdir: filename parent directory
1140 stat the dir or errno
1144 curdir: dir parent directory
1152 curdir: dir parent directory
1159 cname( vol, dir, cpath )
1160 const struct vol *vol;
1165 static char path[ MAXPATHLEN + 1];
1166 static struct path ret;
1178 afp_errno = AFPERR_NOOBJ;
1179 memset(&ret, 0, sizeof(ret));
1180 switch (ret.m_type = *data) { /* path type */
1183 len = (unsigned char) *data++;
1186 if (afp_version >= 30) {
1192 if (afp_version >= 30) {
1194 memcpy(&hint, data, sizeof(hint));
1196 data += sizeof(hint);
1198 memcpy(&len16, data, sizeof(len16));
1205 /* else it's an error */
1207 afp_errno = AFPERR_PARAM;
1210 *cpath += len + size;
1215 if (movecwd( vol, dir ) < 0 ) {
1216 return invalidate(vol, dir, &ret );
1218 if (*path == '\0') {
1225 if (*data == sep ) {
1229 while (*data == sep && len > 0 ) {
1230 if ( dir->d_parent == NULL ) {
1233 dir = dir->d_parent;
1238 /* would this be faster with strlen + strncpy? */
1240 while ( *data != sep && len > 0 ) {
1242 if (p > &path[ MAXPATHLEN]) {
1243 afp_errno = AFPERR_PARAM;
1249 /* short cut bits by chopping off a trailing \0. this also
1250 makes the traversal happy w/ filenames at the end of the
1257 if ( p == path ) { /* end of the name parameter */
1261 if (afp_version >= 30) {
1266 static char temp[ MAXPATHLEN + 1];
1268 if (dir->d_did == DIRDID_ROOT_PARENT) {
1270 With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1271 So we compare it with the longname from the current volume and if they match
1272 we overwrite the requested path with the utf8 volume name so that the following
1275 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1276 if (strcasecmp( path, temp) == 0)
1277 ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1280 if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1281 afp_errno = AFPERR_PARAM;
1287 /* check for OS X mangled filename :( */
1289 t = demangle_osx(vol, path, dir->d_did, &fileid);
1292 /* duplicate work but we can't reuse all convert_char we did in demangle_osx
1293 * flags weren't the same
1295 if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1296 /* at last got our view of mac name */
1301 if (ret.u_name == NULL) {
1302 if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1303 afp_errno = AFPERR_PARAM;
1308 if (dir->d_did == DIRDID_ROOT_PARENT) {
1310 root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1311 must check against the volume name.
1313 if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1319 cdir = dirsearch_byname(vol, dir, ret.u_name);
1322 if ( cdir == NULL ) {
1324 /* if dir == curdir it always succeed,
1325 even if curdir is deleted.
1326 it's not a pb because it will fail in extenddir
1328 if ( movecwd( vol, dir ) < 0 ) {
1329 /* dir is not valid anymore
1330 we delete dir from the cache and abort.
1332 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1333 afp_errno = AFPERR_NOOBJ;
1336 if (afp_errno == AFPERR_ACCESS)
1338 dir_invalidate(vol, dir);
1341 cdir = extenddir( vol, dir, &ret );
1345 cdir = extenddir( vol, dir, &ret );
1346 } /* if (!extend) */
1348 if ( cdir == NULL ) {
1362 * Move curdir to dir, with a possible chdir()
1364 int movecwd( vol, dir)
1365 const struct vol *vol;
1368 char path[MAXPATHLEN + 1];
1373 if ( dir == curdir ) {
1376 if ( dir->d_did == DIRDID_ROOT_PARENT) {
1377 afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1381 p = path + sizeof(path) - 1;
1384 for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1387 /* parent directory is deleted */
1388 afp_errno = AFPERR_NOOBJ;
1392 if (p -n -1 < path) {
1393 afp_errno = AFPERR_PARAM;
1400 if ( d != curdir ) {
1401 n = strlen( vol->v_path );
1402 if (p -n -1 < path) {
1403 afp_errno = AFPERR_PARAM;
1408 memcpy( p, vol->v_path, n );
1410 if ( chdir( p ) < 0 ) {
1414 afp_errno = AFPERR_ACCESS;
1417 afp_errno = AFPERR_NOOBJ;
1427 * We can't use unix file's perm to support Apple's inherited protection modes.
1428 * If we aren't the file's owner we can't change its perms when moving it and smb
1429 * nfs,... don't even try.
1431 #define AFP_CHECK_ACCESS
1433 int check_access(char *path, int mode)
1435 #ifdef AFP_CHECK_ACCESS
1443 accessmode(p, &ma, curdir, NULL);
1444 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1446 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1452 /* --------------------- */
1453 int file_access(struct path *path, int mode)
1457 accessmode(path->u_name, &ma, curdir, &path->st);
1458 if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1460 if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1466 /* --------------------- */
1467 void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
1469 dir->offcnt = count;
1470 dir->ctime = st->st_ctime;
1471 dir->d_flags &= ~DIRF_CNID;
1474 /* ---------------------
1475 * is our cached offspring count valid?
1478 int diroffcnt(struct dir *dir, struct stat *st)
1480 return st->st_ctime == dir->ctime;
1483 /* ---------------------
1484 * is our cached also for reenumerate id?
1487 int dirreenumerate(struct dir *dir, struct stat *st)
1489 return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1492 /* --------------------- */
1493 static int invisible_dots(const struct vol *vol, const char *name)
1495 return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
1498 /* ------------------------------
1500 (name, dir) with curdir:name == dir, from afp_enumerate
1503 int getdirparams(const struct vol *vol,
1504 u_int16_t bitmap, struct path *s_path,
1506 char *buf, int *buflen )
1510 char *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1511 int bit = 0, isad = 0;
1517 struct stat *st = &s_path->st;
1518 char *upath = s_path->u_name;
1520 if ((bitmap & ((1 << DIRPBIT_ATTR) |
1521 (1 << DIRPBIT_CDATE) |
1522 (1 << DIRPBIT_MDATE) |
1523 (1 << DIRPBIT_BDATE) |
1524 (1 << DIRPBIT_FINFO)))) {
1526 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1527 if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1532 if ( dir->d_did == DIRDID_ROOT) {
1533 pdid = DIRDID_ROOT_PARENT;
1534 } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1537 pdid = dir->d_parent->d_did;
1541 while ( bitmap != 0 ) {
1542 while (( bitmap & 1 ) == 0 ) {
1550 ad_getattr(&ad, &ashort);
1551 } else if (invisible_dots(vol, dir->d_u_name)) {
1552 ashort = htons(ATTRBIT_INVISIBLE);
1555 ashort |= htons(ATTRBIT_SHARED);
1556 memcpy( data, &ashort, sizeof( ashort ));
1557 data += sizeof( ashort );
1561 memcpy( data, &pdid, sizeof( pdid ));
1562 data += sizeof( pdid );
1565 case DIRPBIT_CDATE :
1566 if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1567 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1568 memcpy( data, &aint, sizeof( aint ));
1569 data += sizeof( aint );
1572 case DIRPBIT_MDATE :
1573 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1574 memcpy( data, &aint, sizeof( aint ));
1575 data += sizeof( aint );
1578 case DIRPBIT_BDATE :
1579 if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1580 aint = AD_DATE_START;
1581 memcpy( data, &aint, sizeof( aint ));
1582 data += sizeof( aint );
1585 case DIRPBIT_FINFO :
1587 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1588 } else { /* no appledouble */
1589 memset( data, 0, 32 );
1590 /* set default view -- this also gets done in ad_open() */
1591 ashort = htons(FINDERINFO_CLOSEDVIEW);
1592 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1594 /* dot files are by default visible */
1595 if (invisible_dots(vol, dir->d_u_name)) {
1596 ashort = htons(FINDERINFO_INVISIBLE);
1597 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1603 case DIRPBIT_LNAME :
1604 if (dir->d_m_name) /* root of parent can have a null name */
1607 memset(data, 0, sizeof(u_int16_t));
1608 data += sizeof( u_int16_t );
1611 case DIRPBIT_SNAME :
1612 memset(data, 0, sizeof(u_int16_t));
1613 data += sizeof( u_int16_t );
1617 memcpy( data, &dir->d_did, sizeof( aint ));
1618 data += sizeof( aint );
1621 case DIRPBIT_OFFCNT :
1623 /* this needs to handle current directory access rights */
1624 if (diroffcnt(dir, st)) {
1625 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1627 else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1628 setdiroffcnt(dir, st, ret);
1629 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1631 ashort = htons( ashort );
1632 memcpy( data, &ashort, sizeof( ashort ));
1633 data += sizeof( ashort );
1637 aint = htonl(st->st_uid);
1638 memcpy( data, &aint, sizeof( aint ));
1639 data += sizeof( aint );
1643 aint = htonl(st->st_gid);
1644 memcpy( data, &aint, sizeof( aint ));
1645 data += sizeof( aint );
1648 case DIRPBIT_ACCESS :
1649 accessmode( upath, &ma, dir , st);
1651 *data++ = ma.ma_user;
1652 *data++ = ma.ma_world;
1653 *data++ = ma.ma_group;
1654 *data++ = ma.ma_owner;
1657 /* Client has requested the ProDOS information block.
1658 Just pass back the same basic block for all
1659 directories. <shirsch@ibm.net> */
1660 case DIRPBIT_PDINFO :
1661 if (afp_version >= 30) { /* UTF8 name */
1662 utf8 = kTextEncodingUTF8;
1663 if (dir->d_m_name) /* root of parent can have a null name */
1666 memset(data, 0, sizeof(u_int16_t));
1667 data += sizeof( u_int16_t );
1669 memcpy(data, &aint, sizeof( aint ));
1670 data += sizeof( aint );
1672 else { /* ProDOS Info Block */
1675 ashort = htons( 0x0200 );
1676 memcpy( data, &ashort, sizeof( ashort ));
1677 data += sizeof( ashort );
1678 memset( data, 0, sizeof( ashort ));
1679 data += sizeof( ashort );
1683 case DIRPBIT_UNIXPR :
1684 aint = htonl(st->st_uid);
1685 memcpy( data, &aint, sizeof( aint ));
1686 data += sizeof( aint );
1687 aint = htonl(st->st_gid);
1688 memcpy( data, &aint, sizeof( aint ));
1689 data += sizeof( aint );
1692 aint = htonl ( aint & ~S_ISGID ); /* Remove SGID, OSX doesn't like it ... */
1693 memcpy( data, &aint, sizeof( aint ));
1694 data += sizeof( aint );
1696 accessmode( upath, &ma, dir , st);
1698 *data++ = ma.ma_user;
1699 *data++ = ma.ma_world;
1700 *data++ = ma.ma_group;
1701 *data++ = ma.ma_owner;
1706 ad_close_metadata( &ad );
1708 return( AFPERR_BITMAP );
1714 ashort = htons( data - buf );
1715 memcpy( l_nameoff, &ashort, sizeof( ashort ));
1716 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1718 if ( utf_nameoff ) {
1719 ashort = htons( data - buf );
1720 memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1721 data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1724 ad_close_metadata( &ad );
1726 *buflen = data - buf;
1730 /* ----------------------------- */
1731 int path_error(struct path *path, int error)
1733 /* - a dir with access error
1734 * - no error it's a file
1737 if (path_isadir(path))
1739 if (path->st_valid && path->st_errno)
1741 return AFPERR_BADTYPE ;
1744 /* ----------------------------- */
1745 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1747 char *ibuf, *rbuf _U_;
1748 int ibuflen _U_, *rbuflen;
1753 u_int16_t vid, bitmap;
1759 memcpy( &vid, ibuf, sizeof( vid ));
1760 ibuf += sizeof( vid );
1762 if (NULL == ( vol = getvolbyvid( vid )) ) {
1763 return( AFPERR_PARAM );
1766 if (vol->v_flags & AFPVOL_RO)
1767 return AFPERR_VLOCK;
1769 memcpy( &did, ibuf, sizeof( did ));
1770 ibuf += sizeof( int );
1772 if (NULL == ( dir = dirlookup( vol, did )) ) {
1776 memcpy( &bitmap, ibuf, sizeof( bitmap ));
1777 bitmap = ntohs( bitmap );
1778 ibuf += sizeof( bitmap );
1780 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1781 return get_afp_errno(AFPERR_NOOBJ);
1784 if ( *path->m_name != '\0' ) {
1785 rc = path_error(path, AFPERR_NOOBJ);
1786 /* maybe we are trying to set perms back */
1787 if (rc != AFPERR_ACCESS)
1792 * If ibuf is odd, make it even.
1794 if ((u_long)ibuf & 1 ) {
1798 if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1799 setvoltime(obj, vol );
1805 * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1807 * assume path == '\0' eg. it's a directory in canonical form
1810 struct path Cur_Path = {
1813 ".", /* unix name */
1815 NULL,/* struct dir */
1816 0, /* stat is not set */
1819 /* ------------------ */
1820 static int set_dir_errors(struct path *path, const char *where, int err)
1825 return AFPERR_ACCESS;
1827 return AFPERR_VLOCK;
1829 LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1830 return AFPERR_PARAM;
1833 /* ------------------ */
1834 int setdirparams(const struct vol *vol,
1835 struct path *path, u_int16_t d_bitmap, char *buf )
1847 u_int16_t ashort, bshort;
1849 int change_mdate = 0;
1850 int change_parent_mdate = 0;
1852 u_int16_t bitmap = d_bitmap;
1853 u_char finder_buf[32];
1856 u_int16_t upriv_bit = 0;
1859 upath = path->u_name;
1861 while ( bitmap != 0 ) {
1862 while (( bitmap & 1 ) == 0 ) {
1870 memcpy( &ashort, buf, sizeof( ashort ));
1871 buf += sizeof( ashort );
1873 case DIRPBIT_CDATE :
1875 memcpy(&cdate, buf, sizeof(cdate));
1876 buf += sizeof( cdate );
1878 case DIRPBIT_MDATE :
1879 memcpy(&newdate, buf, sizeof(newdate));
1880 buf += sizeof( newdate );
1882 case DIRPBIT_BDATE :
1884 memcpy(&bdate, buf, sizeof(bdate));
1885 buf += sizeof( bdate );
1887 case DIRPBIT_FINFO :
1889 memcpy( finder_buf, buf, 32 );
1892 case DIRPBIT_UID : /* What kind of loser mounts as root? */
1893 change_parent_mdate = 1;
1894 memcpy( &owner, buf, sizeof(owner));
1895 buf += sizeof( owner );
1898 change_parent_mdate = 1;
1899 memcpy( &group, buf, sizeof( group ));
1900 buf += sizeof( group );
1902 case DIRPBIT_ACCESS :
1904 change_parent_mdate = 1;
1905 ma.ma_user = *buf++;
1906 ma.ma_world = *buf++;
1907 ma.ma_group = *buf++;
1908 ma.ma_owner = *buf++;
1909 mpriv = mtoumode( &ma ) | vol->v_dperm;
1910 if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
1911 err = set_dir_errors(path, "setdirmode", errno);
1915 /* Ignore what the client thinks we should do to the
1916 ProDOS information block. Skip over the data and
1917 report nothing amiss. <shirsch@ibm.net> */
1918 case DIRPBIT_PDINFO :
1919 if (afp_version < 30) {
1923 err = AFPERR_BITMAP;
1927 case DIRPBIT_UNIXPR :
1928 if (vol_unix_priv(vol)) {
1929 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
1930 buf += sizeof( owner );
1931 memcpy( &group, buf, sizeof( group ));
1932 buf += sizeof( group );
1935 change_parent_mdate = 1;
1936 memcpy( &upriv, buf, sizeof( upriv ));
1937 buf += sizeof( upriv );
1938 upriv = ntohl (upriv) | vol->v_dperm;
1939 if (dir_rx_set(upriv)) {
1940 /* maybe we are trying to set perms back */
1941 if ( setdirunixmode(vol, upath, upriv) < 0 ) {
1943 err = set_dir_errors(path, "setdirunixmode", errno);
1954 err = AFPERR_BITMAP;
1962 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1964 if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
1966 * Check to see what we're trying to set. If it's anything
1967 * but ACCESS, UID, or GID, give an error. If it's any of those
1968 * three, we don't need the ad to be open, so just continue.
1970 * note: we also don't need to worry about mdate. also, be quiet
1971 * if we're using the noadouble option.
1973 if (!vol_noadouble(vol) && (d_bitmap &
1974 ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
1975 (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
1976 (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
1977 return AFPERR_ACCESS;
1983 * Check to see if a create was necessary. If it was, we'll want
1984 * to set our name, etc.
1986 if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
1987 ad_setname(&ad, curdir->d_m_name);
1993 while ( bitmap != 0 ) {
1994 while (( bitmap & 1 ) == 0 ) {
2002 ad_getattr(&ad, &bshort);
2003 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2004 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2005 change_parent_mdate = 1;
2006 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2007 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2011 ad_setattr(&ad, bshort);
2014 case DIRPBIT_CDATE :
2016 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2019 case DIRPBIT_MDATE :
2021 case DIRPBIT_BDATE :
2023 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2026 case DIRPBIT_FINFO :
2028 if ( dir->d_did == DIRDID_ROOT ) {
2030 * Alright, we admit it, this is *really* sick!
2031 * The 4 bytes that we don't copy, when we're dealing
2032 * with the root of a volume, are the directory's
2033 * location information. This eliminates that annoying
2034 * behavior one sees when mounting above another mount
2037 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2038 memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2040 memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2044 case DIRPBIT_UID : /* What kind of loser mounts as root? */
2045 if ( (dir->d_did == DIRDID_ROOT) &&
2046 (setdeskowner( ntohl(owner), -1 ) < 0)) {
2047 err = set_dir_errors(path, "setdeskowner", errno);
2048 if (isad && err == AFPERR_PARAM) {
2049 err = AFP_OK; /* ???*/
2052 goto setdirparam_done;
2055 if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2056 err = set_dir_errors(path, "setdirowner", errno);
2057 goto setdirparam_done;
2061 if (dir->d_did == DIRDID_ROOT)
2062 setdeskowner( -1, ntohl(group) );
2063 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2064 err = set_dir_errors(path, "setdirowner", errno);
2065 goto setdirparam_done;
2068 case DIRPBIT_ACCESS :
2069 if (dir->d_did == DIRDID_ROOT) {
2071 if (!dir_rx_set(mpriv)) {
2072 /* we can't remove read and search for owner on volume root */
2073 err = AFPERR_ACCESS;
2074 goto setdirparam_done;
2078 if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2079 err = set_dir_errors(path, "setdirmode", errno);
2080 goto setdirparam_done;
2083 case DIRPBIT_PDINFO :
2084 if (afp_version >= 30) {
2085 err = AFPERR_BITMAP;
2086 goto setdirparam_done;
2089 case DIRPBIT_UNIXPR :
2090 if (vol_unix_priv(vol)) {
2091 if (dir->d_did == DIRDID_ROOT) {
2092 if (!dir_rx_set(upriv)) {
2093 /* we can't remove read and search for owner on volume root */
2094 err = AFPERR_ACCESS;
2095 goto setdirparam_done;
2097 setdeskowner( -1, ntohl(group) );
2098 setdeskmode( upriv );
2100 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2101 err = set_dir_errors(path, "setdirowner", errno);
2102 goto setdirparam_done;
2105 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2106 err = set_dir_errors(path, "setdirunixmode", errno);
2107 goto setdirparam_done;
2111 err = AFPERR_BITMAP;
2112 goto setdirparam_done;
2116 err = AFPERR_BITMAP;
2117 goto setdirparam_done;
2126 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2127 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2131 ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2132 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2137 if (path->st_valid && !path->st_errno) {
2138 struct stat *st = &path->st;
2140 if (dir && dir->d_parent) {
2141 ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2145 ad_close_metadata( &ad);
2148 if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2149 && gettimeofday(&tv, NULL) == 0) {
2150 if (!movecwd(vol, dir->d_parent)) {
2151 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2152 /* be careful with bitmap because now dir is null */
2153 bitmap = 1<<DIRPBIT_MDATE;
2154 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2155 /* should we reset curdir ?*/
2163 int afp_syncdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2165 char *ibuf, *rbuf _U_;
2166 int ibuflen _U_, *rbuflen;
2180 memcpy( &vid, ibuf, sizeof( vid ));
2181 ibuf += sizeof( vid );
2182 if (NULL == (vol = getvolbyvid( vid )) ) {
2183 return( AFPERR_PARAM );
2186 memcpy( &did, ibuf, sizeof( did ));
2187 ibuf += sizeof( did );
2188 if (NULL == ( dir = dirlookup( vol, did )) ) {
2189 return afp_errno; /* was AFPERR_NOOBJ */
2192 if (movecwd( vol, dir ) < 0 )
2193 return ( AFPERR_NOOBJ );
2196 Assuming only OSens that have dirfd also may require fsyncing directories
2197 in order to flush metadata e.g. Linux.
2201 if (NULL == ( dp = opendir( "." )) ) {
2204 return( AFPERR_NOOBJ );
2206 return( AFPERR_ACCESS );
2208 return( AFPERR_PARAM );
2212 LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2215 if ( fsync ( dfd ) < 0 )
2216 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2217 dir->d_u_name, strerror(errno) );
2218 closedir(dp); /* closes dfd too */
2221 if ( -1 == (dfd = open(vol->vfs->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2224 return( AFPERR_NOOBJ );
2226 return( AFPERR_ACCESS );
2228 return( AFPERR_PARAM );
2232 LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2233 vol->vfs->ad_path(".", ADFLAGS_DIR) );
2235 if ( fsync(dfd) < 0 )
2236 LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2237 vol->vfs->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2243 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2246 int ibuflen _U_, *rbuflen;
2252 struct path *s_path;
2260 memcpy( &vid, ibuf, sizeof( vid ));
2261 ibuf += sizeof( vid );
2262 if (NULL == ( vol = getvolbyvid( vid )) ) {
2263 return( AFPERR_PARAM );
2266 if (vol->v_flags & AFPVOL_RO)
2267 return AFPERR_VLOCK;
2269 memcpy( &did, ibuf, sizeof( did ));
2270 ibuf += sizeof( did );
2271 if (NULL == ( dir = dirlookup( vol, did )) ) {
2272 return afp_errno; /* was AFPERR_NOOBJ */
2274 /* for concurrent access we need to be sure we are not in the
2275 * folder we want to create...
2279 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2280 return get_afp_errno(AFPERR_PARAM);
2282 /* cname was able to move curdir to it! */
2283 if (*s_path->m_name == '\0')
2284 return AFPERR_EXIST;
2286 upath = s_path->u_name;
2287 if (0 != (err = check_name(vol, upath))) {
2291 if (AFP_OK != (err = netatalk_mkdir( upath))) {
2295 if (of_stat(s_path) < 0) {
2299 if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2303 if ( movecwd( vol, dir ) < 0 ) {
2304 return( AFPERR_PARAM );
2307 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2308 if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0) {
2309 if (vol_noadouble(vol))
2310 goto createdir_done;
2311 return( AFPERR_ACCESS );
2313 ad_setname(&ad, s_path->m_name);
2314 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2317 ad_close_metadata( &ad);
2320 #ifdef HAVE_NFSv4_ACLS
2321 /* FIXME: are we really inside the created dir? */
2322 addir_inherit_acl(vol);
2325 memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2326 *rbuflen = sizeof( u_int32_t );
2327 setvoltime(obj, vol );
2332 * dst new unix filename (not a pathname)
2333 * newname new mac name
2337 int renamedir(vol, src, dst, dir, newparent, newname)
2338 const struct vol *vol;
2339 char *src, *dst, *newname;
2340 struct dir *dir, *newparent;
2347 /* existence check moved to afp_moveandrename */
2348 if ( unix_rename( src, dst ) < 0 ) {
2351 return( AFPERR_NOOBJ );
2353 return( AFPERR_ACCESS );
2355 return AFPERR_VLOCK;
2357 /* tried to move directory into a subdirectory of itself */
2358 return AFPERR_CANTMOVE;
2360 /* this needs to copy and delete. bleah. that means we have
2361 * to deal with entire directory hierarchies. */
2362 if ((err = copydir(vol, src, dst)) < 0) {
2366 if ((err = deletedir(src)) < 0)
2370 return( AFPERR_PARAM );
2374 vol->vfs->rf_renamedir(vol, src, dst);
2376 len = strlen( newname );
2377 /* rename() succeeded so we need to update our tree even if we can't open
2381 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2383 if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2384 ad_setname(&ad, newname);
2386 ad_close_metadata( &ad);
2389 dir_hash_del(vol, dir);
2390 if (dir->d_m_name == dir->d_u_name)
2391 dir->d_u_name = NULL;
2393 if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2394 LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2395 /* FIXME : fatal ? */
2398 dir->d_m_name = buf;
2399 strcpy( dir->d_m_name, newname );
2401 if (newname == dst) {
2402 free(dir->d_u_name);
2403 dir->d_u_name = dir->d_m_name;
2406 if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2407 LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2410 dir->d_u_name = buf;
2411 strcpy( dir->d_u_name, dst );
2414 if (( parent = dir->d_parent ) == NULL ) {
2417 if ( parent == newparent ) {
2418 hash_alloc_insert(vol->v_hash, dir, dir);
2422 /* detach from old parent and add to new one. */
2423 dirchildremove(parent, dir);
2424 dir->d_parent = newparent;
2425 dirchildadd(vol, newparent, dir);
2429 /* delete an empty directory */
2430 int deletecurdir( vol)
2431 const struct vol *vol;
2441 if ( curdir->d_parent == NULL ) {
2442 return( AFPERR_ACCESS );
2447 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2448 if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2450 ad_getattr(&ad, &ashort);
2451 ad_close( &ad, ADFLAGS_HF );
2452 if ((ashort & htons(ATTRBIT_NODELETE))) {
2453 return AFPERR_OLOCK;
2456 err = vol->vfs->rf_deletecurdir(vol);
2461 /* now get rid of dangling symlinks */
2462 if ((dp = opendir("."))) {
2463 while ((de = readdir(dp))) {
2464 /* skip this and previous directory */
2465 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2468 /* bail if it's not a symlink */
2469 if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2471 return AFPERR_DIRNEMPT;
2474 if ((err = netatalk_unlink(de->d_name))) {
2481 if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2486 if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2487 dirchildremove(curdir, fdir);
2488 cnid_delete(vol->v_cdb, fdir->d_did);
2489 dir_remove( vol, fdir );
2494 /* inode is used as key for cnid.
2495 * Close the descriptor only after cnid_delete
2503 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2506 int ibuflen _U_, *rbuflen;
2517 sfunc = (unsigned char) *ibuf++;
2521 if (sfunc >= 3 && sfunc <= 6) {
2522 if (afp_version < 30) {
2523 return( AFPERR_PARAM );
2530 case 3 :/* unicode */
2531 memcpy( &id, ibuf, sizeof( id ));
2534 if (( pw = getpwuid( id )) == NULL ) {
2535 return( AFPERR_NOITEM );
2537 len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2538 pw->pw_name, strlen(pw->pw_name), &name);
2545 case 4 : /* unicode */
2546 memcpy( &id, ibuf, sizeof( id ));
2549 if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2550 return( AFPERR_NOITEM );
2552 len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2553 gr->gr_name, strlen(gr->gr_name), &name);
2559 #ifdef HAVE_NFSv4_ACLS
2560 case 5 : /* UUID -> username */
2561 case 6 : /* UUID -> groupname */
2562 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2563 return AFPERR_PARAM;
2564 LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2565 len = getnamefromuuid( ibuf, &name, &type);
2566 if (len != 0) /* its a error code, not len */
2567 return AFPERR_NOITEM;
2568 if (type == UUID_USER) {
2569 if (( pw = getpwnam( name )) == NULL )
2570 return( AFPERR_NOITEM );
2571 LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2572 id = htonl(UUID_USER);
2573 memcpy( rbuf, &id, sizeof( id ));
2574 id = htonl( pw->pw_uid);
2575 rbuf += sizeof( id );
2576 memcpy( rbuf, &id, sizeof( id ));
2577 rbuf += sizeof( id );
2578 *rbuflen = 2 * sizeof( id );
2579 } else { /* type == UUID_GROUP */
2580 if (( gr = getgrnam( name )) == NULL )
2581 return( AFPERR_NOITEM );
2582 LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2583 id = htonl(UUID_GROUP);
2584 memcpy( rbuf, &id, sizeof( id ));
2585 rbuf += sizeof( id );
2586 id = htonl( gr->gr_gid);
2587 memcpy( rbuf, &id, sizeof( id ));
2588 rbuf += sizeof( id );
2589 *rbuflen = 2 * sizeof( id );
2594 return( AFPERR_PARAM );
2598 len = strlen( name );
2601 u_int16_t tp = htons(len);
2602 memcpy(rbuf, &tp, sizeof(tp));
2611 memcpy( rbuf, name, len );
2619 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2622 int ibuflen _U_, *rbuflen;
2631 sfunc = (unsigned char) *ibuf++;
2633 LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2636 case 2 : /* unicode */
2637 if (afp_version < 30) {
2638 return( AFPERR_PARAM );
2640 memcpy(&ulen, ibuf, sizeof(ulen));
2643 LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2647 len = (unsigned char) *ibuf++;
2649 #ifdef HAVE_NFSv4_ACLS
2650 case 5 : /* username -> UUID */
2651 case 6 : /* groupname -> UUID */
2652 if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2653 return AFPERR_PARAM;
2654 memcpy(&ulen, ibuf, sizeof(ulen));
2660 return( AFPERR_PARAM );
2666 return AFPERR_PARAM;
2669 case 1 : /* unicode */
2671 if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2672 return( AFPERR_NOITEM );
2676 memcpy( rbuf, &id, sizeof( id ));
2677 *rbuflen = sizeof( id );
2680 case 2 : /* unicode */
2682 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2683 if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2684 return( AFPERR_NOITEM );
2687 LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2689 memcpy( rbuf, &id, sizeof( id ));
2690 *rbuflen = sizeof( id );
2692 #ifdef HAVE_NFSv4_ACLS
2693 case 5 : /* username -> UUID */
2694 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2695 if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2696 return AFPERR_NOITEM;
2697 *rbuflen = UUID_BINSIZE;
2699 case 6 : /* groupname -> UUID */
2700 LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2701 if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2702 return AFPERR_NOITEM;
2703 *rbuflen = UUID_BINSIZE;
2711 /* ------------------------------------
2712 variable DID support
2714 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2716 char *ibuf _U_, *rbuf _U_;
2717 int ibuflen _U_, *rbuflen;
2728 /* do nothing as dids are static for the life of the process. */
2732 memcpy(&vid, ibuf, sizeof( vid ));
2733 ibuf += sizeof( vid );
2734 if (( vol = getvolbyvid( vid )) == NULL ) {
2735 return( AFPERR_PARAM );
2738 memcpy( &did, ibuf, sizeof( did ));
2739 ibuf += sizeof( did );
2740 if (( dir = dirlookup( vol, did )) == NULL ) {
2741 return( AFPERR_PARAM );
2744 /* dir_remove -- deletedid */
2750 /* did creation gets done automatically
2751 * there's a pb again with case but move it to cname
2753 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2756 int ibuflen _U_, *rbuflen;
2759 struct dir *parentdir;
2767 memcpy(&vid, ibuf, sizeof(vid));
2768 ibuf += sizeof( vid );
2770 if (NULL == ( vol = getvolbyvid( vid )) ) {
2771 return( AFPERR_PARAM );
2774 memcpy(&did, ibuf, sizeof(did));
2775 ibuf += sizeof(did);
2777 if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2781 if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2782 return get_afp_errno(AFPERR_PARAM);
2785 if ( *path->m_name != '\0' ) {
2786 return path_error(path, AFPERR_NOOBJ);
2789 if ( !path->st_valid && of_stat(path ) < 0 ) {
2790 return( AFPERR_NOOBJ );
2792 if ( path->st_errno ) {
2793 return( AFPERR_NOOBJ );
2796 memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2797 *rbuflen = sizeof(curdir->d_did);