#include "unix.h"
#include "mangle.h"
#include "hash.h"
+ #include "fce_api.h"
-#ifdef HAVE_NFSv4_ACLS
-extern void addir_inherit_acl(const struct vol *vol);
-#endif
-
-/*
- * Directory caches
- * ================
- *
- * There are currently two cache structures where afpd caches directory information
- * a) a DID/dirname cache in a hashtable
- * b) a (red-black) tree with CNIDs as key
- *
- * a) is for searching by DID/dirname
- * b) is for searching by CNID
- *
- * Through additional parent, child, previous and next pointers, b) is also used to
- * represent the on-disk layout of the filesystem. parent and child point to parent
- * and child directory respectively, linking 2 or more subdirectories in one
- * directory with previous and next pointers.
- *
- * Usage examples, highlighting the main functions:
- *
- * a) is eg used in enumerate():
- * if IS_DIR
- * dir = dirsearch_byname() // search in cache
- * if (dir == NULL) // not found
- * dir = adddir() // add to cache
- * getdirparams()
- *
- * b) is eg used in afp_getfildirparams()
- * dirlookup() // wrapper for cache and db search
- * => dir = dirsearch() // search in cache
- * if (dir) // found
- * return
- * else // not found,
- * cnid_resolve() // resolve with CNID database
- * cname() // add to cache
+/*
+ * FIXMEs, loose ends after the dircache rewrite:
+ * o merge dircache_search_by_name and dir_add ??
+ * o case-insensitivity is gone from cname
*/
-struct dir *curdir;
-int afp_errno;
-
-#define SENTINEL (&sentinel)
-static struct dir sentinel = { SENTINEL, SENTINEL, NULL, /* left, right, back */
- DIRTREE_COLOR_BLACK, /* color */
- NULL, NULL, /* parent, child */
- NULL, NULL, /* previous, next */
- NULL, 0, 0, /* oforks, did, flags */
- 0, 0, /* ctime, offcnt */
- NULL, NULL, NULL}; /* mname, uname, ucs2-name */
-static struct dir rootpar = { SENTINEL, SENTINEL, NULL,
- 0,
- NULL, NULL,
- NULL, NULL,
- NULL, 0, 0,
- 0, 0,
- NULL, NULL, NULL};
-
-/* (from IM: Toolbox Essentials)
- * dirFinderInfo (DInfo) fields:
- * field bytes
- * frRect 8 folder's window rectangle
- * frFlags 2 flags
- * frLocation 4 folder's location in window
- * frView 2 folder's view (default == closedView (256))
- *
- * extended dirFinderInfo (DXInfo) fields:
- * frScroll 4 scroll position
- * frOpenChain: 4 directory ID chain of open folders
- * frScript: 1 script flag and code
- * frXFlags: 1 reserved
- * frComment: 2 comment ID
- * frPutAway: 4 home directory ID
- */
-static struct dir *
-vol_tree_root(const struct vol *vol, u_int32_t did)
-{
- struct dir *dir;
+/*******************************************************************************************
+ * Globals
+ ******************************************************************************************/
- if (vol->v_curdir && vol->v_curdir->d_did == did) {
- dir = vol->v_curdir;
- }
- else {
- dir = vol->v_root;
- }
- return dir;
-}
+int afp_errno;
+/* As long as directory.c hasn't got its own init call, this get initialized in dircache_init */
+struct dir rootParent = {
+ NULL, NULL, NULL, NULL, /* path, d_m_name, d_u_name, d_m_name_ucs2 */
+ NULL, 0, 0, /* qidx_node, ctime, d_flags */
+ 0, 0, 0, 0 /* pdid, did, offcnt, d_vid */
+};
+struct dir *curdir = &rootParent;
+struct path Cur_Path = {
+ 0,
+ "", /* mac name */
+ ".", /* unix name */
+ 0, /* id */
+ NULL,/* struct dir * */
+ 0, /* stat is not set */
+ 0, /* errno */
+ {0} /* struct stat */
+};
/*
- * redid did assignment for directories. now we use red-black trees.
- * how exciting.
+ * dir_remove queues struct dirs to be freed here. We can't just delete them immeidately
+ * eg in dircache_search_by_id, because a caller somewhere up the stack might be
+ * referencing it.
+ * So instead:
+ * - we mark it as invalid by setting d_did to CNID_INVALID (ie 0)
+ * - queue it in "invalid_dircache_entries" queue
+ * - which is finally freed at the end of every AFP func in afp_dsi.c.
*/
-struct dir *
-dirsearch(const struct vol *vol, u_int32_t did)
-{
- struct dir *dir;
+q_t *invalid_dircache_entries;
- /* check for 0 did */
- if (!did) {
- afp_errno = AFPERR_PARAM;
- return NULL;
- }
- if ( did == DIRDID_ROOT_PARENT ) {
- if (!rootpar.d_did)
- rootpar.d_did = DIRDID_ROOT_PARENT;
- rootpar.d_child = vol->v_dir;
- return( &rootpar );
- }
-
- dir = vol_tree_root(vol, did);
+/*******************************************************************************************
+ * Locals
+ ******************************************************************************************/
- afp_errno = AFPERR_NOOBJ;
- while ( dir != SENTINEL ) {
- if (dir->d_did == did)
- return dir->d_m_name ? dir : NULL;
- dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
- }
- return NULL;
-}
-/* ------------------- */
-int get_afp_errno(const int param)
+/* -------------------------
+ appledouble mkdir afp error code.
+*/
+static int netatalk_mkdir(const struct vol *vol, const char *name)
{
- if (afp_errno != AFPERR_DID1)
- return afp_errno;
- return param;
-}
+ int ret;
+ struct stat st;
-/* ------------------- */
-struct dir *
-dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
-{
- struct dir *dir = NULL;
+ if (vol->v_flags & AFPVOL_UNIX_PRIV) {
+ if (lstat(".", &st) < 0)
+ return AFPERR_MISC;
+ int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask);
+ LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}",
+ name, st.st_mode, vol->v_umask);
- if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
- struct dir key;
- hnode_t *hn;
+ ret = mkdir(name, mode);
+ } else {
+ ret = ad_mkdir(name, DIRBITS | 0777);
+ }
- key.d_parent = cdir;
- key.d_u_name = name;
- hn = hash_lookup(vol->v_hash, &key);
- if (hn) {
- dir = hnode_get(hn);
+ if (ret < 0) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EROFS :
+ return( AFPERR_VLOCK );
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EEXIST :
+ return( AFPERR_EXIST );
+ case ENOSPC :
+ case EDQUOT :
+ return( AFPERR_DFULL );
+ default :
+ return( AFPERR_PARAM );
}
}
- return dir;
+ return AFP_OK;
}
-/* -----------------------------------------
- * if did is not in the cache resolve it with cnid
- *
- * FIXME
- * OSX call it with bogus id, ie file ID not folder ID,
- * and we are really bad in this case.
- */
-struct dir *
-dirlookup( struct vol *vol, u_int32_t did)
+/* ------------------- */
+static int deletedir(int dirfd, char *dir)
{
- struct dir *ret;
- char *upath;
- cnid_t id, cnid;
- static char path[MAXPATHLEN + 1];
- size_t len, pathlen;
- char *ptr;
- static char buffer[12 + MAXPATHLEN + 1];
- int buflen = 12 + MAXPATHLEN + 1;
- char *mpath;
- int utf8;
- size_t maxpath;
+ char path[MAXPATHLEN + 1];
+ DIR *dp;
+ struct dirent *de;
+ struct stat st;
+ size_t len;
+ int err = AFP_OK;
+ size_t remain;
- ret = dirsearch(vol, did);
- if (ret != NULL || afp_errno == AFPERR_PARAM)
- return ret;
+ if ((len = strlen(dir)) +2 > sizeof(path))
+ return AFPERR_PARAM;
- utf8 = utf8_encoding();
- maxpath = (utf8)?MAXPATHLEN -7:255;
- id = did;
- if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
- afp_errno = AFPERR_NOOBJ;
- return NULL;
- }
- ptr = path + MAXPATHLEN;
- if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
- afp_errno = AFPERR_NOOBJ;
- return NULL;
- }
- len = strlen(mpath);
- pathlen = len; /* no 0 in the last part */
+ /* already gone */
+ if ((dp = opendirat(dirfd, dir)) == NULL)
+ return AFP_OK;
+
+ strcpy(path, dir);
+ strcat(path, "/");
len++;
- strcpy(ptr - len, mpath);
- ptr -= len;
- while (1) {
- ret = dirsearch(vol,id);
- if (ret != NULL) {
+ remain = sizeof(path) -len -1;
+ while ((de = readdir(dp)) && err == AFP_OK) {
+ /* skip this and previous directory */
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ if (strlen(de->d_name) > remain) {
+ err = AFPERR_PARAM;
break;
}
- cnid = id;
- if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
- ||
- NULL == (mpath = utompath(vol, upath, cnid, utf8))
- ) {
- afp_errno = AFPERR_NOOBJ;
- return NULL;
+ strcpy(path + len, de->d_name);
+ if (lstatat(dirfd, path, &st)) {
+ continue;
}
-
- len = strlen(mpath) + 1;
- pathlen += len;
- if (pathlen > maxpath) {
- afp_errno = AFPERR_PARAM;
- return NULL;
+ if (S_ISDIR(st.st_mode)) {
+ err = deletedir(dirfd, path);
+ } else {
+ err = netatalk_unlinkat(dirfd, path);
}
- strcpy(ptr - len, mpath);
- ptr -= len;
- }
-
- /* fill the cache, another place where we know about the path type */
- if (utf8) {
- u_int16_t temp16;
- u_int32_t temp;
-
- ptr -= 2;
- temp16 = htons(pathlen);
- memcpy(ptr, &temp16, sizeof(temp16));
-
- temp = htonl(kTextEncodingUTF8);
- ptr -= 4;
- memcpy(ptr, &temp, sizeof(temp));
- ptr--;
- *ptr = 3;
- }
- else {
- ptr--;
- *ptr = (unsigned char)pathlen;
- ptr--;
- *ptr = 2;
}
- /* cname is not efficient */
- if (cname( vol, ret, &ptr ) == NULL )
- return NULL;
-
- return dirsearch(vol, did);
-}
+ closedir(dp);
-/* child addition/removal */
-static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
-{
- if (!a->d_child)
- a->d_child = b;
- else {
- b->d_next = a->d_child;
- b->d_prev = b->d_next->d_prev;
- b->d_next->d_prev = b;
- b->d_prev->d_next = b;
- }
- if (!hash_alloc_insert(vol->v_hash, b, b)) {
- LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
+ /* okay. the directory is empty. delete it. note: we already got rid
+ of .AppleDouble. */
+ if (err == AFP_OK) {
+ err = netatalk_rmdir(dirfd, dir);
}
+ return err;
}
-static void dirchildremove(struct dir *a,struct dir *b)
-{
- if (a->d_child == b)
- a->d_child = (b == b->d_next) ? NULL : b->d_next;
- b->d_next->d_prev = b->d_prev;
- b->d_prev->d_next = b->d_next;
- b->d_next = b->d_prev = b;
-}
-
-/* --------------------------- */
-/* rotate the tree to the left */
-static void dir_leftrotate(struct vol *vol, struct dir *dir)
+/* do a recursive copy. */
+static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
{
- struct dir *right = dir->d_right;
+ char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
+ DIR *dp;
+ struct dirent *de;
+ struct stat st;
+ struct utimbuf ut;
+ size_t slen, dlen;
+ size_t srem, drem;
+ int err;
- /* whee. move the right's left tree into dir's right tree */
- dir->d_right = right->d_left;
- if (right->d_left != SENTINEL)
- right->d_left->d_back = dir;
+ /* doesn't exist or the path is too long. */
+ if (((slen = strlen(src)) > sizeof(spath) - 2) ||
+ ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
+ ((dp = opendirat(dirfd, src)) == NULL))
+ return AFPERR_PARAM;
- if (right != SENTINEL) {
- right->d_back = dir->d_back;
- right->d_left = dir;
+ /* try to create the destination directory */
+ if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
+ closedir(dp);
+ return err;
}
- if (!dir->d_back) /* no parent. move the right tree to the top. */
- vol->v_root = right;
- else if (dir == dir->d_back->d_left) /* we were on the left */
- dir->d_back->d_left = right;
- else
- dir->d_back->d_right = right; /* we were on the right */
-
- /* re-insert dir on the left tree */
- if (dir != SENTINEL)
- dir->d_back = right;
-}
-
-
-
-/* rotate the tree to the right */
-static void dir_rightrotate(struct vol *vol, struct dir *dir)
-{
- struct dir *left = dir->d_left;
-
- /* whee. move the left's right tree into dir's left tree */
- dir->d_left = left->d_right;
- if (left->d_right != SENTINEL)
- left->d_right->d_back = dir;
+ /* set things up to copy */
+ strcpy(spath, src);
+ strcat(spath, "/");
+ slen++;
+ srem = sizeof(spath) - slen -1;
- if (left != SENTINEL) {
- left->d_back = dir->d_back;
- left->d_right = dir;
- }
+ strcpy(dpath, dst);
+ strcat(dpath, "/");
+ dlen++;
+ drem = sizeof(dpath) - dlen -1;
- if (!dir->d_back) /* no parent. move the left tree to the top. */
- vol->v_root = left;
- else if (dir == dir->d_back->d_right) /* we were on the right */
- dir->d_back->d_right = left;
- else
- dir->d_back->d_left = left; /* we were on the left */
+ err = AFP_OK;
+ while ((de = readdir(dp))) {
+ /* skip this and previous directory */
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
- /* re-insert dir on the right tree */
- if (dir != SENTINEL)
- dir->d_back = left;
-}
+ if (strlen(de->d_name) > srem) {
+ err = AFPERR_PARAM;
+ break;
+ }
+ strcpy(spath + slen, de->d_name);
-#if 0
-/* recolor after a removal */
-static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
-{
- struct dir *leaf;
-
- while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
- /* are we on the left tree? */
- if (dir == dir->d_back->d_left) {
- leaf = dir->d_back->d_right; /* get right side */
- if (leaf->d_color == DIRTREE_COLOR_RED) {
- /* we're red. we need to change to black. */
- leaf->d_color = DIRTREE_COLOR_BLACK;
- dir->d_back->d_color = DIRTREE_COLOR_RED;
- dir_leftrotate(vol, dir->d_back);
- leaf = dir->d_back->d_right;
+ if (lstatat(dirfd, spath, &st) == 0) {
+ if (strlen(de->d_name) > drem) {
+ err = AFPERR_PARAM;
+ break;
}
+ strcpy(dpath + dlen, de->d_name);
- /* right leaf has black end nodes */
- if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
- (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
- leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
- dir = dir->d_back; /* ascend */
- } else {
- if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
- leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
- leaf->d_color = DIRTREE_COLOR_RED;
- dir_rightrotate(vol, leaf);
- leaf = dir->d_back->d_right;
- }
- leaf->d_color = dir->d_back->d_color;
- dir->d_back->d_color = DIRTREE_COLOR_BLACK;
- leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
- dir_leftrotate(vol, dir->d_back);
- dir = vol->v_root;
- }
- } else { /* right tree */
- leaf = dir->d_back->d_left; /* left tree */
- if (leaf->d_color == DIRTREE_COLOR_RED) {
- leaf->d_color = DIRTREE_COLOR_BLACK;
- dir->d_back->d_color = DIRTREE_COLOR_RED;
- dir_rightrotate(vol, dir->d_back);
- leaf = dir->d_back->d_left;
- }
+ if (S_ISDIR(st.st_mode)) {
+ if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
+ goto copydir_done;
+ } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
+ goto copydir_done;
- /* left leaf has black end nodes */
- if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
- (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
- leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
- dir = dir->d_back; /* ascend */
} else {
- if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
- leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
- leaf->d_color = DIRTREE_COLOR_RED;
- dir_leftrotate(vol, leaf);
- leaf = dir->d_back->d_left;
- }
- leaf->d_color = dir->d_back->d_color;
- dir->d_back->d_color = DIRTREE_COLOR_BLACK;
- leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
- dir_rightrotate(vol, dir->d_back);
- dir = vol->v_root;
+ /* keep the same time stamp. */
+ ut.actime = ut.modtime = st.st_mtime;
+ utime(dpath, &ut);
}
}
}