From: Frank Lahm Date: Wed, 9 Jun 2010 13:59:30 +0000 (+0200) Subject: Merge from branch-2-1: umask on upriv volumes X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=commitdiff_plain;h=1575c0ad7f67747019a3eb550d942406daa87d6e Merge from branch-2-1: umask on upriv volumes --- 1575c0ad7f67747019a3eb550d942406daa87d6e diff --cc etc/afpd/directory.c index 467bc62d,0e13a162..03bb0146 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@@ -47,527 -62,573 +47,542 @@@ 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/"