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;
+
+/*******************************************************************************************
+ * Globals
+ ******************************************************************************************/
+
int afp_errno;
+struct dir rootParent = {
+ NULL, NULL, NULL, NULL, /* path, d_m_name, d_u_name, d_m_name_ucs2 */
+ NULL, NULL, 0, 0, /* qidx_node, d_ofork, 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 */
+};
-#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;
+/*******************************************************************************************
+ * Locals
+ ******************************************************************************************/
- if (vol->v_curdir && vol->v_curdir->d_did == did) {
- dir = vol->v_curdir;
- }
- else {
- dir = vol->v_root;
- }
- return dir;
-}
-/*
- * redid did assignment for directories. now we use red-black trees.
- * how exciting.
- */
-struct dir *
-dirsearch(const struct vol *vol, u_int32_t did)
+/* -------------------------
+ appledouble mkdir afp error code.
+*/
- static int netatalk_mkdir(const char *name)
++static int netatalk_mkdir(const struct vol *vol, const char *name)
{
- if (ad_mkdir(name, DIRBITS | 0777) < 0) {
- struct dir *dir;
++ int ret;
++ struct stat st;
+
++ 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);
+
- /* 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 );
++ ret = mkdir(name, mode);
++ } else {
++ ret = ad_mkdir(name, DIRBITS | 0777);
+ }
+
- dir = vol_tree_root(vol, did);
-
- 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;
++ 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 NULL;
+ return AFP_OK;
}
/* ------------------- */
-int get_afp_errno(const int param)
+static int deletedir(int dirfd, char *dir)
{
- if (afp_errno != AFPERR_DID1)
- return afp_errno;
- return param;
-}
+ char path[MAXPATHLEN + 1];
+ DIR *dp;
+ struct dirent *de;
+ struct stat st;
+ size_t len;
+ int err = AFP_OK;
+ size_t remain;
-/* ------------------- */
-struct dir *
-dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
-{
- struct dir *dir = NULL;
+ if ((len = strlen(dir)) +2 > sizeof(path))
+ return AFPERR_PARAM;
+
+ /* already gone */
+ if ((dp = opendirat(dirfd, dir)) == NULL)
+ return AFP_OK;
- if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
- struct dir key;
- hnode_t *hn;
+ strcpy(path, dir);
+ strcat(path, "/");
+ len++;
+ 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;
- key.d_parent = cdir;
- key.d_u_name = name;
- hn = hash_lookup(vol->v_hash, &key);
- if (hn) {
- dir = hnode_get(hn);
+ if (strlen(de->d_name) > remain) {
+ err = AFPERR_PARAM;
+ break;
+ }
+ strcpy(path + len, de->d_name);
+ if (lstatat(dirfd, path, &st)) {
+ continue;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ err = deletedir(dirfd, path);
+ } else {
+ err = netatalk_unlinkat(dirfd, path);
}
}
- return dir;
+ closedir(dp);
+
+ /* 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;
}
-/* -----------------------------------------
- * 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)
+/* do a recursive copy. */
+static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
{
- 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 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;
- ret = dirsearch(vol, did);
- if (ret != NULL || afp_errno == AFPERR_PARAM)
- return ret;
+ /* 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;
- 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;
+ /* try to create the destination directory */
- if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
++ if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
+ closedir(dp);
+ return err;
}
- len = strlen(mpath);
- pathlen = len; /* no 0 in the last part */
- len++;
- strcpy(ptr - len, mpath);
- ptr -= len;
- while (1) {
- ret = dirsearch(vol,id);
- if (ret != NULL) {
- 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;
- }
- len = strlen(mpath) + 1;
- pathlen += len;
- if (pathlen > maxpath) {
- afp_errno = AFPERR_PARAM;
- return NULL;
+ /* set things up to copy */
+ strcpy(spath, src);
+ strcat(spath, "/");
+ slen++;
+ srem = sizeof(spath) - slen -1;
+
+ strcpy(dpath, dst);
+ strcat(dpath, "/");
+ dlen++;
+ drem = sizeof(dpath) - dlen -1;
+
+ err = AFP_OK;
+ while ((de = readdir(dp))) {
+ /* skip this and previous directory */
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ if (strlen(de->d_name) > srem) {
+ err = AFPERR_PARAM;
+ break;
}
- strcpy(ptr - len, mpath);
- ptr -= len;
- }
+ strcpy(spath + slen, de->d_name);
- /* fill the cache, another place where we know about the path type */
- if (utf8) {
- u_int16_t temp16;
- u_int32_t temp;
+ if (lstatat(dirfd, spath, &st) == 0) {
+ if (strlen(de->d_name) > drem) {
+ err = AFPERR_PARAM;
+ break;
+ }
+ strcpy(dpath + dlen, de->d_name);
- ptr -= 2;
- temp16 = htons(pathlen);
- memcpy(ptr, &temp16, sizeof(temp16));
+ 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;
- temp = htonl(kTextEncodingUTF8);
- ptr -= 4;
- memcpy(ptr, &temp, sizeof(temp));
- ptr--;
- *ptr = 3;
+ } else {
+ /* keep the same time stamp. */
+ ut.actime = ut.modtime = st.st_mtime;
+ utime(dpath, &ut);
+ }
+ }
}
- else {
- ptr--;
- *ptr = (unsigned char)pathlen;
- ptr--;
- *ptr = 2;
+
+ /* keep the same time stamp. */
+ if (lstatat(dirfd, src, &st) == 0) {
+ ut.actime = ut.modtime = st.st_mtime;
+ utime(dst, &ut);
}
- /* cname is not efficient */
- if (cname( vol, ret, &ptr ) == NULL )
- return NULL;
- return dirsearch(vol, did);
+copydir_done:
+ closedir(dp);
+ return err;
}
-/* child addition/removal */
-static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
+/* ---------------------
+ * is our cached offspring count valid?
+ */
+static int diroffcnt(struct dir *dir, struct stat *st)
{
- 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);
- }
+ return st->st_ctime == dir->ctime;
}
-static void dirchildremove(struct dir *a,struct dir *b)
+/* --------------------- */
+static int invisible_dots(const struct vol *vol, const char *name)
{
- 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;
+ return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
}
-/* --------------------------- */
-/* rotate the tree to the left */
-static void dir_leftrotate(struct vol *vol, struct dir *dir)
+/* ------------------ */
+static int set_dir_errors(struct path *path, const char *where, int err)
{
- struct dir *right = dir->d_right;
-
- /* 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;
-
- if (right != SENTINEL) {
- right->d_back = dir->d_back;
- right->d_left = dir;
+ switch ( err ) {
+ case EPERM :
+ case EACCES :
+ return AFPERR_ACCESS;
+ case EROFS :
+ return AFPERR_VLOCK;
}
+ LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
+ return AFPERR_PARAM;
+}
- 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 */
+/*!
+ * @brief Convert name in client encoding to server encoding
+ *
+ * Convert ret->m_name to ret->u_name from client encoding to server encoding.
+ * This only gets called from cname().
+ *
+ * @returns 0 on success, -1 on error
+ *
+ * @note If the passed ret->m_name is mangled, we'll demangle it
+ */
+static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct path *ret, int toUTF8)
+{
+ static char temp[ MAXPATHLEN + 1];
+ char *t;
+ cnid_t fileid;
+
+ if (afp_version >= 30) {
+ if (toUTF8) {
+ if (dir->d_did == DIRDID_ROOT_PARENT) {
+ /*
+ * With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
+ * So we compare it with the longname from the current volume and if they match
+ * we overwrite the requested path with the utf8 volume name so that the following
+ * strcmp can match.
+ */
+ ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
+ if (strcasecmp(ret->m_name, temp) == 0)
+ ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, ret->m_name, AFPVOL_U8MNAMELEN);
+ } else {
+ /* toUTF8 */
+ if (mtoUTF8(vol, ret->m_name, strlen(ret->m_name), temp, MAXPATHLEN) == (size_t)-1) {
+ afp_errno = AFPERR_PARAM;
+ return -1;
+ }
+ strcpy(ret->m_name, temp);
+ }
+ }
- /* re-insert dir on the left tree */
- if (dir != SENTINEL)
- dir->d_back = right;
-}
+ /* check for OS X mangled filename :( */
+ t = demangle_osx(vol, ret->m_name, dir->d_did, &fileid);
+ LOG(log_maxdebug, logtype_afpd, "cname_mtouname('%s',did:%u) {demangled:'%s', fileid:%u}",
+ ret->m_name, ntohl(dir->d_did), t, ntohl(fileid));
+
+ if (t != ret->m_name) {
+ ret->u_name = t;
+ /* duplicate work but we can't reuse all convert_char we did in demangle_osx
+ * flags weren't the same
+ */
+ if ( (t = utompath(vol, ret->u_name, fileid, utf8_encoding())) ) {
+ /* at last got our view of mac name */
+ strcpy(ret->m_name, t);
+ }
+ }
+ } /* afp_version >= 30 */
+ /* If we haven't got it by now, get it */
+ if (ret->u_name == NULL) {
+ if ((ret->u_name = mtoupath(vol, ret->m_name, dir->d_did, utf8_encoding())) == NULL) {
+ afp_errno = AFPERR_PARAM;
+ return -1;
+ }
+ }
+ return 0;
+}
-/* rotate the tree to the right */
-static void dir_rightrotate(struct vol *vol, struct dir *dir)
+/*!
+ * @brief Build struct path from struct dir
+ *
+ * The final movecwd in cname failed, possibly with EPERM or ENOENT. We:
+ * 1. move cwd into parent dir (we're often already there, but not always)
+ * 2. set struct path to the dirname
+ * 3. in case of
+ * AFPERR_ACCESS: the dir is there, we just cant chdir into it
+ * AFPERR_NOOBJ: the dir was there when we stated it in cname, so we have a race
+ * 4. indicate there's no dir for this path
+ * 5. remove the dir
+ */
+static struct path *path_from_dir(struct vol *vol, struct dir *dir, struct path *ret)
{
- struct dir *left = dir->d_left;
+ if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
+ return NULL;
- /* 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;
+ switch (afp_errno) {
- if (left != SENTINEL) {
- left->d_back = dir->d_back;
- left->d_right = dir;
- }
+ case AFPERR_ACCESS:
+ if (movecwd( vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
+ return NULL;
- 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 */
+ memcpy(ret->m_name, cfrombstring(dir->d_m_name), blength(dir->d_m_name) + 1); /* 3 */
+ if (dir->d_m_name == dir->d_u_name) {
+ ret->u_name = ret->m_name;
+ } else {
+ ret->u_name = ret->m_name + blength(dir->d_m_name) + 1;
+ memcpy(ret->u_name, cfrombstring(dir->d_u_name), blength(dir->d_u_name) + 1);
+ }
- /* re-insert dir on the right tree */
- if (dir != SENTINEL)
- dir->d_back = left;
-}
+ ret->d_dir = dir;
-#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;
- }
+ LOG(log_debug, logtype_afpd, "cname('%s') {path-from-dir: AFPERR_ACCESS. curdir:'%s', path:'%s'}",
+ cfrombstring(dir->d_fullpath),
+ cfrombstring(curdir->d_fullpath),
+ ret->u_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;
- }
+ return ret;
- /* 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;
- }
+ case AFPERR_NOOBJ:
+ if (movecwd(vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
+ return NULL;
+
+ memcpy(ret->m_name, cfrombstring(dir->d_m_name), blength(dir->d_m_name) + 1);
+ if (dir->d_m_name == dir->d_u_name) {
+ ret->u_name = ret->m_name;
+ } else {
+ ret->u_name = ret->m_name + blength(dir->d_m_name) + 1;
+ memcpy(ret->u_name, cfrombstring(dir->d_u_name), blength(dir->d_u_name) + 1);
}
+
+ ret->d_dir = NULL; /* 4 */
+ dir_remove(vol, dir); /* 5 */
+ return ret;
+
+ default:
+ return NULL;
}
- dir->d_color = DIRTREE_COLOR_BLACK;
- return dir;
+ /* DEADC0DE: never get here */
+ return NULL;
}
-#endif /* 0 */
-/* --------------------- */
-static void dir_hash_del(const struct vol *vol, struct dir *dir)
-{
- hnode_t *hn;
- hn = hash_lookup(vol->v_hash, dir);
- if (!hn) {
- LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
- }
- else {
- hash_delete(vol->v_hash, hn);
- }
-}
+/*********************************************************************************************
+ * Interface
+ ********************************************************************************************/
-/* remove the node from the tree. this is just like insertion, but
- * different. actually, it has to worry about a bunch of things that
- * insertion doesn't care about. */
+int get_afp_errno(const int param)
+{
+ if (afp_errno != AFPERR_DID1)
+ return afp_errno;
+ return param;
+}
-static void dir_remove( struct vol *vol, struct dir *dir)
+/*!
+ * @brief Resolve a DID
+ *
+ * Resolve a DID, allocate a struct dir for it
+ * 1. Check for special CNIDs 0 (invalid), 1 and 2.
+ * 2. Check if the DID is in the cache.
+ * 3. If it's not in the cache resolve it via the database.
+ * 4. Build complete server-side path to the dir.
+ * 5. Check if it exists and is a directory.
+ * 6. Create the struct dir and populate it.
+ * 7. Add it to the cache.
+ *
+ * @param vol (r) pointer to struct vol
+ * @param did (r) DID to resolve
+ *
+ * @returns pointer to struct dir
+ *
+ * @note FIXME: OSX calls it with bogus id, ie file ID not folder ID,
+ * and we are really bad in this case.
+ */
+struct dir *dirlookup(const struct vol *vol, cnid_t did)
{
-#ifdef REMOVE_NODES
- struct ofork *of, *last;
- struct dir *node, *leaf;
-#endif /* REMOVE_NODES */
-
- if (!dir || (dir == SENTINEL))
- return;
-
- /* i'm not sure if it really helps to delete stuff. */
- dir_hash_del(vol, dir);
- vol->v_curdir = NULL;
-#ifndef REMOVE_NODES
- dirfreename(dir);
- dir->d_m_name = NULL;
- dir->d_u_name = NULL;
- dir->d_m_name_ucs2 = NULL;
-#else /* ! REMOVE_NODES */
-
- /* go searching for a node with at most one child */
- if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
- node = dir;
- } else {
- node = dir->d_right;
- while (node->d_left != SENTINEL)
- node = node->d_left;
+ static char buffer[12 + MAXPATHLEN + 1];
+ struct stat st;
+ struct dir *ret = NULL, *pdir;
+ bstring fullpath = NULL;
+ char *upath = NULL, *mpath;
+ cnid_t cnid, pdid;
+ size_t maxpath;
+ int buflen = 12 + MAXPATHLEN + 1;
+ int utf8;
+ int err = 0;
+
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {start}", ntohl(did));
+
+ /* check for did 0, 1 and 2 */
+ if (did == 0 || vol == NULL) { /* 1 */
+ afp_errno = AFPERR_PARAM;
+ return NULL;
+ } else if (did == DIRDID_ROOT_PARENT) {
+ rootParent.d_vid = vol->v_vid;
+ return (&rootParent);
+ } else if (did == DIRDID_ROOT) {
+ return vol->v_root;
+ }
+
+ /* Search the cache */
+ if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2 */
+ if (lstat(cfrombstring(ret->d_fullpath), &st) != 0) {
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {lstat: %s}", ntohl(did), strerror(errno));
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ /* It's not there anymore, so remove it */
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {calling dir_remove()}", ntohl(did));
+ dir_remove(vol, ret);
+ afp_errno = AFPERR_NOOBJ;
+ return NULL;
+ default:
+ return ret;
+ }
+ /* DEADC0DE */
+ return NULL;
+ }
+ return ret;
}
- /* get that child */
- leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
+ utf8 = utf8_encoding();
+ maxpath = (utf8) ? MAXPATHLEN - 7 : 255;
- /* detach node */
- leaf->d_back = node->d_back;
- if (!node->d_back) {
- vol->v_root = leaf;
- } else if (node == node->d_back->d_left) { /* left tree */
- node->d_back->d_left = leaf;
- } else {
- node->d_back->d_right = leaf;
+ /* Get it from the database */
+ cnid = did;
+ if ( (upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL
+ || (upath = strdup(upath)) == NULL) { /* 3 */
+ afp_errno = AFPERR_NOOBJ;
+ err = 1;
+ goto exit;
}
+ pdid = cnid;
- /* we want to free node, but we also want to free the data in dir.
- * currently, that's d_name and the directory traversal bits.
- * we just copy the necessary bits and then fix up all the
- * various pointers to the directory. needless to say, there are
- * a bunch of places that store the directory struct. */
- if (node != dir) {
- struct dir save, *tmp;
-
- memcpy(&save, dir, sizeof(save));
- memcpy(dir, node, sizeof(struct dir));
+ /*
+ * Recurse up the tree, terminates in dirlookup when either
+ * - DIRDID_ROOT is hit
+ * - a cached entry is found
+ */
+ if ((pdir = dirlookup(vol, pdid)) == NULL) {
+ err = 1;
+ goto exit;
+ }
- /* restore the red-black bits */
- dir->d_left = save.d_left;
- dir->d_right = save.d_right;
- dir->d_back = save.d_back;
- dir->d_color = save.d_color;
+ /* build the fullpath */
+ if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL
+ || bconchar(fullpath, '/') != BSTR_OK
+ || bcatcstr(fullpath, upath) != BSTR_OK) {
+ err = 1;
+ goto exit;
+ }
- if (node == vol->v_dir) {/* we may need to fix up this pointer */
- vol->v_dir = dir;
- rootpar.d_child = vol->v_dir;
- } else {
- /* if we aren't the root directory, we have parents and
- * siblings to worry about */
- if (dir->d_parent->d_child == node)
- dir->d_parent->d_child = dir;
- dir->d_next->d_prev = dir;
- dir->d_prev->d_next = dir;
- }
+ /* stat it and check if it's a dir */
+ LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstring(fullpath));
- /* fix up children. */
- tmp = dir->d_child;
- while (tmp) {
- tmp->d_parent = dir;
- tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
+ if (stat(cfrombstring(fullpath), &st) != 0) { /* 5a */
+ switch (errno) {
+ case ENOENT:
+ afp_errno = AFPERR_NOOBJ;
+ err = 1;
+ goto exit;
+ case EPERM:
+ afp_errno = AFPERR_ACCESS;
+ err = 1;
+ goto exit;
+ default:
+ afp_errno = AFPERR_MISC;
+ err = 1;
+ goto exit;
}
-
- if (node == curdir) /* another pointer to fixup */
- curdir = dir;
-
- /* we also need to fix up oforks. bleah */
- if ((of = dir->d_ofork)) {
- last = of->of_d_prev;
- while (of) {
- of->of_dir = dir;
- of = (last == of) ? NULL : of->of_d_next;
- }
+ } else {
+ if ( ! S_ISDIR(st.st_mode)) { /* 5b */
+ afp_errno = AFPERR_BADTYPE;
+ err = 1;
+ goto exit;
}
-
- /* set the node's d_name */
- node->d_m_name = save.d_m_name;
- node->d_u_name = save.d_u_name;
- node->d_m_name_ucs2 = save.d_m_name_ucs2;
}
- if (node->d_color == DIRTREE_COLOR_BLACK)
- dir_rmrecolor(vol, leaf);
+ /* Get macname from unix name */
+ if ( (mpath = utompath(vol, upath, did, utf8)) == NULL ) {
+ afp_errno = AFPERR_NOOBJ;
+ err = 1;
+ goto exit;
+ }
- if (node->d_m_name_ucs2)
- free(node->d_u_name_ucs2);
- if (node->d_u_name != node->d_m_name) {
- free(node->d_u_name);
+ /* Create struct dir */
+ if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath)) == NULL) { /* 6 */
+ LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno));
+ err = 1;
+ goto exit;
}
- free(node->d_m_name);
- free(node);
-#endif /* ! REMOVE_NODES */
-}
-/* ---------------------------------------
- * remove the node and its childs from the tree
- *
- * FIXME what about opened forks with refs to it?
- * it's an afp specs violation because you can't delete
- * an opened forks. Now afpd doesn't care about forks opened by other
- * process. It's fixable within afpd if fnctl_lock, doable with smb and
- * next to impossible for nfs and local filesystem access.
- */
-static void dir_invalidate( struct vol *vol, struct dir *dir)
-{
- if (curdir == dir) {
- /* v_root can't be deleted */
- if (movecwd(vol, vol->v_root) < 0) {
- LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
- }
+ /* Add it to the cache only if it's a dir */
+ if (dircache_add(ret) != 0) { /* 7 */
+ err = 1;
+ goto exit;
}
- /* FIXME */
- dirchildremove(dir->d_parent, dir);
- dir_remove( vol, dir );
-}
-/* ------------------------------------ */
-static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
-{
- struct dir *pdir;
-
- pdir = vol_tree_root(vol, dir->d_did);
- while (pdir->d_did != dir->d_did ) {
- if ( pdir->d_did > dir->d_did ) {
- if ( pdir->d_left == SENTINEL ) {
- pdir->d_left = dir;
- dir->d_back = pdir;
- return NULL;
- }
- pdir = pdir->d_left;
- } else {
- if ( pdir->d_right == SENTINEL ) {
- pdir->d_right = dir;
- dir->d_back = pdir;
- return NULL;
- }
- pdir = pdir->d_right;
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {end: did:%u, path:'%s'}",
+ ntohl(did), ntohl(pdid), cfrombstring(ret->d_fullpath));
+
+exit:
+ if (err) {
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {exit_error: %s}",
+ ntohl(did), AfpErr2name(afp_errno));
+ free(upath);
+ if (fullpath)
+ bdestroy(fullpath);
+ if (ret) {
+ dir_free(ret);
+ ret = NULL;
}
}
- return pdir;
+ return ret;
}
#define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"