X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=blobdiff_plain;f=etc%2Fafpd%2Fdirectory.c;h=06ad94f3f10768e5fb9d380237c501ad14839b99;hp=4544d28e153084897194e44f1286c0952f501aba;hb=5116ed2346ea7ee6e5a9858dee94f3eacdc00d7e;hpb=33f44ea7f93b30678659d7e50b23c5056991dfd0 diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c index 4544d28e..06ad94f3 100644 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@ -1,42 +1,22 @@ /* - * $Id: directory.c,v 1.140 2010-03-12 15:16:49 franklahm Exp $ - * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. - * - * 19 jan 2000 implemented red-black trees for directory lookups - * (asun@cobalt.com). */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ -/* STDC check */ -#if STDC_HEADERS #include -#else /* STDC_HEADERS */ -#ifndef HAVE_STRCHR -#define strchr index -#define strrchr index -#endif /* HAVE_STRCHR */ -char *strchr (), *strrchr (); -#ifndef HAVE_MEMCPY -#define memcpy(d,s,n) bcopy ((s), (d), (n)) -#define memmove(d,s,n) bcopy ((s), (d), (n)) -#endif /* ! HAVE_MEMCPY */ -#endif /* STDC_HEADERS */ -#ifdef HAVE_STRINGS_H -#include -#endif #include #include - #include #include #include +#include #include #include +#include #include #include @@ -46,8 +26,12 @@ char *strchr (), *strrchr (); #include #include #include +#include +#include +#include #include "directory.h" +#include "dircache.h" #include "desktop.h" #include "volume.h" #include "fork.h" @@ -59,583 +43,653 @@ char *strchr (), *strrchr (); #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); } } } - dir->d_color = DIRTREE_COLOR_BLACK; - return dir; + /* keep the same time stamp. */ + if (lstatat(dirfd, src, &st) == 0) { + ut.actime = ut.modtime = st.st_mtime; + utime(dst, &ut); + } + +copydir_done: + closedir(dp); + return err; } -#endif /* 0 */ -/* --------------------- */ -static void dir_hash_del(const struct vol *vol, struct dir *dir) +/* --------------------- + * is our cached offspring count valid? + */ +static int diroffcnt(struct dir *dir, struct stat *st) { - 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); - } + return st->st_ctime == dir->ctime; } -/* 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. */ - -static void dir_remove( struct vol *vol, struct dir *dir) +/* --------------------- */ +static int invisible_dots(const struct vol *vol, const char *name) { -#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; - } - - /* get that child */ - leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right; + return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, ".."); +} - /* 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; +/* ------------------ */ +static int set_dir_errors(struct path *path, const char *where, int err) +{ + 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; +} + +/*! + * @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 = 0; + + 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); + } + } + + /* 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; + } } - /* 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; + return 0; +} - memcpy(&save, dir, sizeof(save)); - memcpy(dir, node, sizeof(struct 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) +{ + if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT) + return NULL; - /* 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; + switch (afp_errno) { - if (node == vol->v_dir) {/* we may need to fix up this pointer */ - vol->v_dir = dir; - rootpar.d_child = vol->v_dir; + case AFPERR_ACCESS: + if (movecwd( vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */ + return NULL; + + memcpy(ret->m_name, cfrombstr(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 { - /* 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; + ret->u_name = ret->m_name + blength(dir->d_m_name) + 1; + memcpy(ret->u_name, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1); } - /* fix up children. */ - tmp = dir->d_child; - while (tmp) { - tmp->d_parent = dir; - tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next; - } + ret->d_dir = dir; - if (node == curdir) /* another pointer to fixup */ - curdir = dir; + LOG(log_debug, logtype_afpd, "cname('%s') {path-from-dir: AFPERR_ACCESS. curdir:'%s', path:'%s'}", + cfrombstr(dir->d_fullpath), + cfrombstr(curdir->d_fullpath), + ret->u_name); - /* 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; - } + return ret; + + case AFPERR_NOOBJ: + if (movecwd(vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */ + return NULL; + + memcpy(ret->m_name, cfrombstr(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, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1); } - /* 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; + ret->d_dir = NULL; /* 4 */ + dir_remove(vol, dir); /* 5 */ + return ret; + + default: + return NULL; } - if (node->d_color == DIRTREE_COLOR_BLACK) - dir_rmrecolor(vol, leaf); + /* DEADC0DE: never get here */ + return NULL; +} - 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); - } - free(node->d_m_name); - free(node); -#endif /* ! REMOVE_NODES */ + +/********************************************************************************************* + * Interface + ********************************************************************************************/ + +int get_afp_errno(const int param) +{ + if (afp_errno != AFPERR_DID1) + return afp_errno; + return param; } -/* --------------------------------------- - * remove the node and its childs from the tree +/*! + * Resolve struct dir for an absolute path + * + * Given a path like "/Volumes/volume/dir/subdir" in a volume "/Volumes/volume" return + * a pointer to struct dir of "subdir". + * 1. Remove volue path from absolute path + * 2. start path + * 3. loop through all elements of the remaining path from 1. + * 4. we only allow dirs + * 5. search dircache + * 6. if not found in the dircache query the CNID database for the DID + * 7. and use dirlookup to resolve the DID to a it's struct dir * * - * 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. + * @param vol (r) volume the path is in, must be known + * @param path (r) absoule path + * + * @returns pointer to struct dir or NULL on error */ -static void dir_invalidate( struct vol *vol, struct dir *dir) +struct dir *dirlookup_bypath(const struct vol *vol, const char *path) { - 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); + EC_INIT; + + struct dir *dir = NULL; + cnid_t cnid, did; + bstring rpath = NULL; + bstring statpath = NULL; + struct bstrList *l = NULL; + struct stat st; + + cnid = htonl(2); + dir = vol->v_root; + + EC_NULL(rpath = rel_path_in_vol(path, vol->v_path)); /* 1. */ + EC_NULL(statpath = bfromcstr(vol->v_path)); /* 2. */ + + l = bsplit(rpath, '/'); + for (int i = 0; i < l->qty ; i++) { /* 3. */ + did = cnid; + EC_ZERO(bconcat(statpath, l->entry[i])); + EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st), + "lstat(rpath: %s, elem: %s): %s: %s", + cfrombstr(rpath), cfrombstr(l->entry[i]), + cfrombstr(statpath), strerror(errno)); + + if (!(S_ISDIR(st.st_mode))) /* 4. */ + EC_FAIL; + + if ((dir = dircache_search_by_name(vol, /* 5. */ + dir, + cfrombstr(l->entry[i]), + blength(l->entry[i]), + st.st_ctime)) == NULL) { + if ((cnid = cnid_add(vol->v_cdb, /* 6. */ + &st, + did, + cfrombstr(l->entry[i]), + blength(l->entry[i]), + 0)) == CNID_INVALID) { + EC_FAIL; + } + + if ((dir = dirlookup(vol, cnid)) == NULL) /* 7. */ + EC_FAIL; } + + EC_ZERO(bcatcstr(statpath, "/")); } - /* FIXME */ - dirchildremove(dir->d_parent, dir); - dir_remove( vol, dir ); + +EC_CLEANUP: + bdestroy(rpath); + bstrListDestroy(l); + bdestroy(statpath); + if (ret != 0) + return NULL; + + return dir; } -/* ------------------------------------ */ -static struct dir *dir_insert(const 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. + * 2a. Check if the DID is in the cache. + * 2b. Check if it's really a dir (d_fullpath != NULL) because we cache files too. + * 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 + */ +struct dir *dirlookup(const struct vol *vol, cnid_t did) { - 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; + 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)", ntohl(did)); + + /* check for did 0, 1 and 2 */ + if (did == 0 || vol == NULL) { /* 1 */ + afp_errno = AFPERR_PARAM; + ret = NULL; + goto exit; + } else if (did == DIRDID_ROOT_PARENT) { + rootParent.d_vid = vol->v_vid; + ret = &rootParent; + goto exit; + } else if (did == DIRDID_ROOT) { + ret = vol->v_root; + goto exit; + } + + /* Search the cache */ + if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2a */ + if (ret->d_fullpath == NULL) { /* 2b */ + afp_errno = AFPERR_BADTYPE; + ret = NULL; + goto exit; + } + if (lstat(cfrombstr(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; + ret = NULL; + goto exit; + default: + ret = ret; + goto exit; } - pdir = pdir->d_right; + /* DEADC0DE */ + ret = NULL; + goto exit; + } + ret = ret; + goto exit; + } + + utf8 = utf8_encoding(); + maxpath = (utf8) ? MAXPATHLEN - 7 : 255; + + /* Get it from the database */ + cnid = did; + if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL) { + afp_errno = AFPERR_NOOBJ; + err = 1; + goto exit; + } + if ((upath = strdup(upath)) == NULL) { /* 3 */ + afp_errno = AFPERR_NOOBJ; + err = 1; + goto exit; + } + pdid = cnid; + + /* + * Recurse up the tree, terminates in dirlookup when either + * - DIRDID_ROOT is hit + * - a cached entry is found + */ + LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {recursion for did: %u}", ntohl(pdid)); + if ((pdir = dirlookup(vol, pdid)) == NULL) { + err = 1; + goto exit; + } + + /* build the fullpath */ + if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL + || bconchar(fullpath, '/') != BSTR_OK + || bcatcstr(fullpath, upath) != BSTR_OK) { + err = 1; + goto exit; + } + + /* stat it and check if it's a dir */ + LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstr(fullpath)); + + if (stat(cfrombstr(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; + } + } else { + if ( ! S_ISDIR(st.st_mode)) { /* 5b */ + afp_errno = AFPERR_BADTYPE; + err = 1; + goto exit; + } + } + + /* Get macname from unix name */ + if ( (mpath = utompath(vol, upath, did, utf8)) == NULL ) { + afp_errno = AFPERR_NOOBJ; + err = 1; + goto exit; + } + + /* Create struct dir */ + if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, st.st_ctime)) == NULL) { /* 6 */ + LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno)); + err = 1; + goto exit; + } + + /* Add it to the cache only if it's a dir */ + if (dircache_add(vol, ret) != 0) { /* 7 */ + err = 1; + goto exit; + } + +exit: + if (upath) free(upath); + if (err) { + LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {exit_error: %s}", + ntohl(did), AfpErr2name(afp_errno)); + if (fullpath) + bdestroy(fullpath); + if (ret) { + dir_free(ret); + ret = NULL; } } - return pdir; + if (ret) + LOG(log_debug, logtype_afpd, "dirlookup(did: %u): pdid: %u, \"%s\"", + ntohl(ret->d_did), ntohl(ret->d_pdid), cfrombstr(ret->d_fullpath)); + + return ret; } #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/" -int -caseenumerate(const struct vol *vol, struct path *path, struct dir *dir) +int caseenumerate(const struct vol *vol, struct path *path, struct dir *dir) { DIR *dp; struct dirent *de; @@ -715,635 +769,370 @@ caseenumerate(const struct vol *vol, struct path *path, struct dir *dir) } -/* - * attempt to extend the current dir. tree to include path - * as a side-effect, movecwd to that point and return the new dir +/*! + * @brief Construct struct dir + * + * Construct struct dir from parameters. + * + * @param m_name (r) directory name in UTF8-dec + * @param u_name (r) directory name in server side encoding + * @param vol (r) pointer to struct vol + * @param pdid (r) Parent CNID + * @param did (r) CNID + * @param path (r) Full unix path to dir or NULL for files + * @param ctime (r) st_ctime from stat + * + * @returns pointer to new struct dir or NULL on error + * + * @note Most of the time mac name and unix name are the same. */ -static struct dir * -extenddir(struct vol *vol, struct dir *dir, struct path *path) +struct dir *dir_new(const char *m_name, + const char *u_name, + const struct vol *vol, + cnid_t pdid, + cnid_t did, + bstring path, + time_t ctime) { - path->d_dir = NULL; + struct dir *dir; - if ( path->u_name == NULL) { - afp_errno = AFPERR_PARAM; + dir = (struct dir *) calloc(1, sizeof( struct dir )); + if (!dir) return NULL; - } - if (check_name(vol, path->u_name)) { - /* the name is illegal */ - LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name); - path->u_name = NULL; - afp_errno = AFPERR_PARAM; + if ((dir->d_m_name = bfromcstr(m_name)) == NULL) { + free(dir); return NULL; } - if (of_stat( path ) != 0 ) { - if (!(vol->v_flags & AFPVOL_CASEINSEN)) - return NULL; - else if(caseenumerate(vol, path, dir) != 0) - return(NULL); - } - - if (!S_ISDIR(path->st.st_mode)) { - return( NULL ); - } - - /* mac name is always with the right encoding (from cname()) */ - if (( dir = adddir( vol, dir, path)) == NULL ) { - return( NULL ); - } - - path->d_dir = dir; - if ( movecwd( vol, dir ) < 0 ) { - return( NULL ); - } - - return( dir ); -} - -/* ------------------------- - appledouble mkdir afp error code. -*/ -static int netatalk_mkdir(const struct vol *vol, const char *name) -{ - 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); - - ret = mkdir(name, mode); - } else { - ret = ad_mkdir(name, DIRBITS | 0777); - } - - 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 AFP_OK; -} - -/* ------------------- */ -static int deletedir(int dirfd, char *dir) -{ - char path[MAXPATHLEN + 1]; - DIR *dp; - struct dirent *de; - struct stat st; - size_t len; - int err = AFP_OK; - size_t remain; - - if ((len = strlen(dir)) +2 > sizeof(path)) - return AFPERR_PARAM; - - /* already gone */ - if ((dp = opendirat(dirfd, dir)) == NULL) - return AFP_OK; - - 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; - - 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); - } - } - 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; -} - -/* do a recursive copy. */ -static int copydir(const struct vol *vol, int dirfd, char *src, char *dst) -{ - 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; - - /* 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; - - /* try to create the destination directory */ - if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) { - closedir(dp); - return err; + if (convert_string_allocate( (utf8_encoding()) ? CH_UTF8_MAC : vol->v_maccharset, + CH_UCS2, + m_name, + -1, (char **)&dir->d_m_name_ucs2) == (size_t)-1 ) { + LOG(log_error, logtype_afpd, "dir_new(did: %u) {%s, %s}: couldn't set UCS2 name", ntohl(did), m_name, u_name); + dir->d_m_name_ucs2 = 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(spath + slen, de->d_name); - - if (lstatat(dirfd, spath, &st) == 0) { - if (strlen(de->d_name) > drem) { - err = AFPERR_PARAM; - break; - } - strcpy(dpath + dlen, de->d_name); - - 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; - - } else { - /* keep the same time stamp. */ - ut.actime = ut.modtime = st.st_mtime; - utime(dpath, &ut); - } - } + if (m_name == u_name || !strcmp(m_name, u_name)) { + dir->d_u_name = dir->d_m_name; } - - /* keep the same time stamp. */ - if (lstatat(dirfd, src, &st) == 0) { - ut.actime = ut.modtime = st.st_mtime; - utime(dst, &ut); + else if ((dir->d_u_name = bfromcstr(u_name)) == NULL) { + bdestroy(dir->d_m_name); + free(dir); + return NULL; } -copydir_done: - closedir(dp); - return err; + dir->d_did = did; + dir->d_pdid = pdid; + dir->d_vid = vol->v_vid; + dir->d_fullpath = path; + dir->ctime_dircache = ctime; + return dir; } - -/* --- public functions follow --- */ - -/* NOTE: we start off with at least one node (the root directory). */ -static struct dir *dirinsert(struct vol *vol, struct dir *dir) +/*! + * @brief Free a struct dir and all its members + * + * @param (rw) pointer to struct dir + */ +void dir_free(struct dir *dir) { - struct dir *node; - - if ((node = dir_insert(vol, dir))) - return node; - - /* recolor the tree. the current node is red. */ - dir->d_color = DIRTREE_COLOR_RED; - - /* parent of this node has to be black. if the parent node - * is red, then we have a grandparent. */ - while ((dir != vol->v_root) && - (dir->d_back->d_color == DIRTREE_COLOR_RED)) { - /* are we on the left tree? */ - if (dir->d_back == dir->d_back->d_back->d_left) { - node = dir->d_back->d_back->d_right; /* get the right node */ - if (node->d_color == DIRTREE_COLOR_RED) { - /* we're red. we need to change to black. */ - dir->d_back->d_color = DIRTREE_COLOR_BLACK; - node->d_color = DIRTREE_COLOR_BLACK; - dir->d_back->d_back->d_color = DIRTREE_COLOR_RED; - dir = dir->d_back->d_back; /* finished. go up. */ - } else { - if (dir == dir->d_back->d_right) { - dir = dir->d_back; - dir_leftrotate(vol, dir); - } - dir->d_back->d_color = DIRTREE_COLOR_BLACK; - dir->d_back->d_back->d_color = DIRTREE_COLOR_RED; - dir_rightrotate(vol, dir->d_back->d_back); - } - } else { - node = dir->d_back->d_back->d_left; - if (node->d_color == DIRTREE_COLOR_RED) { - /* we're red. we need to change to black. */ - dir->d_back->d_color = DIRTREE_COLOR_BLACK; - node->d_color = DIRTREE_COLOR_BLACK; - dir->d_back->d_back->d_color = DIRTREE_COLOR_RED; - dir = dir->d_back->d_back; /* finished. ascend */ - } else { - if (dir == dir->d_back->d_left) { - dir = dir->d_back; - dir_rightrotate(vol, dir); - } - dir->d_back->d_color = DIRTREE_COLOR_BLACK; - dir->d_back->d_back->d_color = DIRTREE_COLOR_RED; - dir_leftrotate(vol, dir->d_back->d_back); - } - } + if (dir->d_u_name != dir->d_m_name) { + bdestroy(dir->d_u_name); } - - vol->v_root->d_color = DIRTREE_COLOR_BLACK; - return NULL; + if (dir->d_m_name_ucs2) + free(dir->d_m_name_ucs2); + bdestroy(dir->d_m_name); + bdestroy(dir->d_fullpath); + free(dir); } -/* ---------------------------- */ -struct dir * -adddir(struct vol *vol, struct dir *dir, struct path *path) +/*! + * @brief Create struct dir from struct path + * + * Create a new struct dir from struct path. Then add it to the cache. + * + * 1. Open adouble file, get CNID from it. + * 2. Search the database, hinting with the CNID from (1). + * 3. Build fullpath and create struct dir. + * 4. Add it to the cache. + * + * @param vol (r) pointer to struct vol, possibly modified in callee + * @param dir (r) pointer to parrent directory + * @param path (rw) pointer to struct path with valid path->u_name + * @param len (r) strlen of path->u_name + * + * @returns Pointer to new struct dir or NULL on error. + * + * @note Function also assigns path->m_name from path->u_name. + */ +struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, int len) { - struct dir *cdir, *edir; - int upathlen; - char *name; - char *upath; - struct stat *st; - int deleted; + int err = 0; + struct dir *cdir = NULL; + cnid_t id; struct adouble ad; struct adouble *adp = NULL; - cnid_t id; - - upath = path->u_name; - st = &path->st; - upathlen = strlen(upath); + bstring fullpath; + + AFP_ASSERT(vol); + AFP_ASSERT(dir); + AFP_ASSERT(path); + AFP_ASSERT(len > 0); + + if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name), path->st.st_ctime)) != NULL) { + /* there's a stray entry in the dircache */ + LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {stray cache entry: did:%u,'%s', removing}", + ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name, + ntohl(cdir->d_did), cfrombstr(dir->d_fullpath)); + if (dir_remove(vol, cdir) != 0) { + dircache_dump(); + AFP_PANIC("dir_add"); + } + } /* get_id needs adp for reading CNID from adouble file */ ad_init(&ad, vol->v_adouble, vol->v_ad_options); - if ((ad_open_metadata(upath, ADFLAGS_DIR, 0, &ad)) == 0) + if ((ad_open_metadata(path->u_name, ADFLAGS_DIR, 0, &ad)) == 0) /* 1 */ adp = &ad; - id = get_id(vol, adp, st, dir->d_did, upath, upathlen); + /* Get CNID */ + if ((id = get_id(vol, adp, &path->st, dir->d_did, path->u_name, len)) == 0) { /* 2 */ + err = 1; + goto exit; + } if (adp) ad_close_metadata(adp); - if (id == 0) { - return NULL; - } - if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) { - return NULL; - } - name = path->m_name; - if ((cdir = dirnew(name, upath)) == NULL) { - LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) ); - return NULL; - } - if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, path->m_name, -1, (char **)&cdir->d_m_name_ucs2)) { - LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name); - cdir->d_m_name_ucs2 = NULL; - } - - cdir->d_did = id; - - if ((edir = dirinsert( vol, cdir ))) { - /* it's not possible with LASTDID - for CNID: - - someone else have moved the directory. - - it's a symlink inside the share. - - it's an ID reused, the old directory was deleted but not - the cnid record and the server've reused the inode for - the new dir. - for HASH (we should get ride of HASH) - - someone else have moved the directory. - - it's an ID reused as above - - it's a hash duplicate and we are in big trouble - */ - deleted = (edir->d_m_name == NULL); - if (!deleted) - dir_hash_del(vol, edir); - dirfreename(edir); - edir->d_m_name = cdir->d_m_name; - edir->d_u_name = cdir->d_u_name; - edir->d_m_name_ucs2 = cdir->d_m_name_ucs2; - free(cdir); - cdir = edir; - LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name); - if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) { - hash_alloc_insert(vol->v_hash, cdir, cdir); - return cdir; + /* Get macname from unixname */ + if (path->m_name == NULL) { + if ((path->m_name = utompath(vol, path->u_name, id, utf8_encoding())) == NULL) { + LOG(log_error, logtype_afpd, "dir_add(\"%s\"): can't assign macname", path->u_name); + err = 2; + goto exit; } - /* the old was not in the same folder */ - if (!deleted) - dirchildremove(cdir->d_parent, cdir); - } - - /* parent/child directories */ - cdir->d_parent = dir; - dirchildadd(vol, dir, cdir); - return( cdir ); -} - -/* --- public functions follow --- */ -/* free everything down. we don't bother to recolor as this is only - * called to free the entire tree */ -void dirfreename(struct dir *dir) -{ - if (dir->d_u_name != dir->d_m_name) { - free(dir->d_u_name); - } - if (dir->d_m_name_ucs2) - free(dir->d_m_name_ucs2); - free(dir->d_m_name); -} - -void dirfree(struct dir *dir) -{ - if (!dir || (dir == SENTINEL)) - return; - - if ( dir->d_left != SENTINEL ) { - dirfree( dir->d_left ); - } - if ( dir->d_right != SENTINEL ) { - dirfree( dir->d_right ); - } - - if (dir != SENTINEL) { - dirfreename(dir); - free( dir ); - } -} - -/* -------------------------------------------- - * most of the time mac name and unix name are the same - */ -struct dir *dirnew(const char *m_name, const char *u_name) -{ - struct dir *dir; - - dir = (struct dir *) calloc(1, sizeof( struct dir )); - if (!dir) - return NULL; + } - if ((dir->d_m_name = strdup(m_name)) == NULL) { - free(dir); - return NULL; + /* Build fullpath */ + if ( ((fullpath = bstrcpy(dir->d_fullpath)) == NULL) /* 3 */ + || (bconchar(fullpath, '/') != BSTR_OK) + || (bcatcstr(fullpath, path->u_name)) != BSTR_OK) { + LOG(log_error, logtype_afpd, "dir_add: fullpath: %s", strerror(errno) ); + err = 3; + goto exit; } - if (m_name == u_name || !strcmp(m_name, u_name)) { - dir->d_u_name = dir->d_m_name; + /* Allocate and initialize struct dir */ + if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath, path->st.st_ctime)) == NULL) { /* 3 */ + err = 4; + goto exit; } - else if ((dir->d_u_name = strdup(u_name)) == NULL) { - free(dir->d_m_name); - free(dir); - return NULL; + + if ((dircache_add(vol, cdir)) != 0) { /* 4 */ + LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstr(fullpath)); + exit(EXITERR_SYS); } - dir->d_m_name_ucs2 = NULL; - dir->d_left = dir->d_right = SENTINEL; - dir->d_next = dir->d_prev = dir; - return dir; +exit: + if (err != 0) { + LOG(log_debug, logtype_afpd, "dir_add('%s/%s'): error: %u", + cfrombstr(dir->d_u_name), path->u_name, err); + + if (adp) + ad_close_metadata(adp); + if (!cdir && fullpath) + bdestroy(fullpath); + if (cdir) + dir_free(cdir); + cdir = NULL; + } else { + /* no error */ + LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {cached: %u,'%s'}", + ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name, + ntohl(cdir->d_did), cfrombstr(cdir->d_fullpath)); + } + + return(cdir); } -#if 0 -/* ------------------ */ -static hash_val_t hash_fun_dir(const void *key) +/*! + * Free the queue with invalid struct dirs + * + * This gets called at the end of every AFP func. + */ +void dir_free_invalid_q(void) { - const struct dir *k = key; - - static unsigned long randbox[] = { - 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, - 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, - 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, - 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, - }; - - const unsigned char *str = (unsigned char *)(k->d_u_name); - hash_val_t acc = k->d_parent->d_did; - - while (*str) { - acc ^= randbox[(*str + acc) & 0xf]; - acc = (acc << 1) | (acc >> 31); - acc &= 0xffffffffU; - acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; - acc = (acc << 2) | (acc >> 30); - acc &= 0xffffffffU; - } - return acc; + struct dir *dir; + while (dir = (struct dir *)dequeue(invalid_dircache_entries)) + dir_free(dir); } -#endif -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif +/*! + * @brief Remove a dir from a cache and queue it for freeing + * + * 1. Check if the dir is locked or has opened forks + * 2. Remove it from the cache + * 3. Queue it for removal + * 4. If it's a request to remove curdir, mark curdir as invalid + * 5. Mark it as invalid + * + * @param (r) pointer to struct vol + * @param (rw) pointer to struct dir + */ +int dir_remove(const struct vol *vol, struct dir *dir) +{ + AFP_ASSERT(vol); + AFP_ASSERT(dir); -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif + if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT) + return 0; -static hash_val_t hash_fun2_dir(const void *key) -{ - const struct dir *k = key; - const char *data = k->d_u_name; - int len = strlen(k->d_u_name); - hash_val_t hash = k->d_parent->d_did, tmp; - - int rem = len & 3; - len >>= 2; - - /* Main loop */ - for (;len > 0; len--) { - hash += get16bits (data); - tmp = (get16bits (data+2) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - data += 2*sizeof (uint16_t); - hash += hash >> 11; - } - - /* Handle end cases */ - switch (rem) { - case 3: hash += get16bits (data); - hash ^= hash << 16; - hash ^= data[sizeof (uint16_t)] << 18; - hash += hash >> 11; - break; - case 2: hash += get16bits (data); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: hash += *data; - hash ^= hash << 10; - hash += hash >> 1; - } + LOG(log_debug, logtype_afpd, "dir_remove(did:%u,'%s'): {removing}", + ntohl(dir->d_did), cfrombstr(dir->d_u_name)); - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; + dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); /* 2 */ + enqueue(invalid_dircache_entries, dir); /* 3 */ - return hash; -} + if (curdir == dir) /* 4 */ + curdir = NULL; -/* ---------------- */ -static int hash_comp_dir(const void *key1, const void *key2) -{ - const struct dir *k1 = key1; - const struct dir *k2 = key2; + dir->d_did = CNID_INVALID; /* 5 */ - return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name)); + return 0; } -/* ---------------- */ -hash_t * -dirhash(void) +#if 0 /* unused */ +/*! + * @brief Modify a struct dir, adjust cache + * + * Any value that is 0 or NULL is not changed. If new_uname is NULL it is set to new_mname. + * If given new_uname == new_mname, new_uname will point to new_mname. + * + * @param vol (r) pointer to struct vol + * @param dir (rw) pointer to struct dir + * @param pdid (r) new parent DID + * @param did (r) new DID + * @param new_mname (r) new mac-name + * @param new_uname (r) new unix-name + * @param pdir_fullpath (r) new fullpath of parent dir + */ +int dir_modify(const struct vol *vol, + struct dir *dir, + cnid_t pdid, + cnid_t did, + const char *new_mname, + const char *new_uname, + bstring pdir_fullpath) { - return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun2_dir); -} + int ret = 0; -/* ------------------ */ -static struct path *invalidate (struct vol *vol, struct dir *dir, struct path *ret) -{ - /* it's tricky: - movecwd failed some of dir path are not there anymore. - FIXME Is it true with other errors? - so we remove dir from the cache - */ - if (dir->d_did == DIRDID_ROOT_PARENT) - return NULL; - if (afp_errno == AFPERR_ACCESS) { - if ( movecwd( vol, dir->d_parent ) < 0 ) { - return NULL; - } - /* FIXME should we set these?, don't need to call stat() after: - ret->st_valid = 1; - ret->st_errno = EACCES; - */ - ret->m_name = dir->d_m_name; - ret->u_name = dir->d_u_name; - ret->d_dir = dir; - return ret; - } else if (afp_errno == AFPERR_NOOBJ) { - if ( movecwd( vol, dir->d_parent ) < 0 ) { - return NULL; - } - strcpy(ret->m_name, dir->d_m_name); - if (dir->d_m_name == dir->d_u_name) { - ret->u_name = ret->m_name; + /* Remove it from the cache */ + dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); + + if (pdid) + dir->d_pdid = pdid; + if (did) + dir->d_did = did; + + if (new_mname) { + /* free uname if it's not the same as mname */ + if (dir->d_m_name != dir->d_u_name) + bdestroy(dir->d_u_name); + + if (new_uname == NULL) + new_uname = new_mname; + + /* assign new name */ + if ((bassigncstr(dir->d_m_name, new_mname)) != BSTR_OK) { + LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) ); + return -1; } - else { - size_t tp = strlen(ret->m_name)+1; - ret->u_name = ret->m_name +tp; - strcpy(ret->u_name, dir->d_u_name); + if (new_mname == new_uname || (strcmp(new_mname, new_uname) == 0)) { + dir->d_u_name = dir->d_m_name; + } else { + if ((dir->d_u_name = bfromcstr(new_uname)) == NULL) { + LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) ); + return -1; + } } - /* FIXME should we set : - ret->st_valid = 1; - ret->st_errno = ENOENT; - */ - dir_invalidate(vol, dir); - return ret; } - dir_invalidate(vol, dir); - return NULL; -} -/* -------------------------------------------------- */ -/* cname - return - if it's a filename: - in extenddir: - compute unix name - stat the file or errno - return - filename - curdir: filename parent directory - - if it's a dirname: - not in the cache - in extenddir - compute unix name - stat the dir or errno - return - if chdir error - dirname - curdir: dir parent directory - sinon - dirname: "" - curdir: dir - in the cache - return - if chdir error - dirname - curdir: dir parent directory - else - dirname: "" - curdir: dir + if (pdir_fullpath) { + if (bassign(dir->d_fullpath, pdir_fullpath) != BSTR_OK) + return -1; + if (bcatcstr(dir->d_fullpath, "/") != BSTR_OK) + return -1; + if (bcatcstr(dir->d_fullpath, new_uname) != BSTR_OK) + return -1; + } -*/ -struct path * -cname(struct vol *vol, struct dir *dir, char **cpath) + if (dir->d_m_name_ucs2) + free(dir->d_m_name_ucs2); + if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, -1, (char**)&dir->d_m_name_ucs2)) + dir->d_m_name_ucs2 = NULL; + + /* Re-add it to the cache */ + if ((dircache_add(vol, dir)) != 0) { + dircache_dump(); + AFP_PANIC("dir_modify"); + } + + return ret; +} +#endif + +/*! + * @brief Resolve a catalog node name path + * + * 1. Evaluate path type + * 2. Move to start dir, if we cant, it might eg because of EACCES, build + * path from dirname, so eg getdirparams has sth it can chew on. curdir + * is dir parent then. All this is done in path_from_dir(). + * 3. Parse next cnode name in path, cases: + * 4. single "\0" -> do nothing + * 5. two or more consecutive "\0" -> chdir("..") one or more times + * 6. cnode name -> copy it to path.m_name + * 7. Get unix name from mac name + * 8. Special handling of request with did 1 + * 9. stat the cnode name + * 10. If it's not there, it's probably an afp_createfile|dir, + * return with curdir = dir parent, struct path = dirname + * 11. If it's there and it's a file, it must should be the last element of the requested + * path. Return with curdir = cnode name parent dir, struct path = filename + * 12. Treat symlinks like files, dont follow them + * 13. If it's a dir: + * 14. Search the dircache for it + * 15. If it's not in the cache, create a struct dir for it and add it to the cache + * 16. chdir into the dir and + * 17. set m_name to the mac equivalent of "." + * 18. goto 3 + */ +struct path *cname(struct vol *vol, struct dir *dir, char **cpath) { - struct dir *cdir, *scdir=NULL; static char path[ MAXPATHLEN + 1]; static struct path ret; + struct dir *cdir; char *data, *p; - int extend = 0; int len; u_int32_t hint; u_int16_t len16; int size = 0; - char sep; int toUTF8 = 0; + LOG(log_maxdebug, logtype_afpd, "came('%s'): {start}", cfrombstr(dir->d_fullpath)); + data = *cpath; afp_errno = AFPERR_NOOBJ; memset(&ret, 0, sizeof(ret)); - switch (ret.m_type = *data) { /* path type */ + + switch (ret.m_type = *data) { /* 1 */ case 2: data++; len = (unsigned char) *data++; size = 2; - sep = 0; if (afp_version >= 30) { ret.m_type = 3; toUTF8 = 1; @@ -1360,7 +1149,6 @@ cname(struct vol *vol, struct dir *dir, char **cpath) len = ntohs(len16); data += 2; size = 7; - sep = 0; /* '/';*/ break; } /* else it's an error */ @@ -1369,248 +1157,215 @@ cname(struct vol *vol, struct dir *dir, char **cpath) return( NULL ); } *cpath += len + size; - *path = '\0'; + + path[0] = 0; ret.m_name = path; - for ( ;; ) { - if ( len == 0 ) { - if (movecwd( vol, dir ) < 0 ) { - return invalidate(vol, dir, &ret ); - } - if (*path == '\0') { - ret.u_name = "."; - ret.d_dir = dir; - } - return &ret; - } - if (*data == sep ) { + if (movecwd(vol, dir) < 0 ) { + LOG(log_debug, logtype_afpd, "cname(did:%u): failed to chdir to '%s'", + ntohl(dir->d_did), cfrombstr(dir->d_fullpath)); + if (len == 0) + return path_from_dir(vol, dir, &ret); + else + return NULL; + } + + while (len) { /* 3 */ + if (*data == 0) { /* 4 or 5 */ data++; len--; - } - while (*data == sep && len > 0 ) { - if ( dir->d_parent == NULL ) { - return NULL; + while (len > 0 && *data == 0) { /* 5 */ + /* chdir to parrent dir */ + if ((dir = dirlookup(vol, dir->d_pdid)) == NULL) + return NULL; + if (movecwd( vol, dir ) < 0 ) { + dir_remove(vol, dir); + return NULL; + } + data++; + len--; } - dir = dir->d_parent; - data++; - len--; + continue; } - /* would this be faster with strlen + strncpy? */ - p = path; - while ( *data != sep && len > 0 ) { + /* 6*/ + for ( p = path; *data != 0 && len > 0; len-- ) { *p++ = *data++; - if (p > &path[ MAXPATHLEN]) { + if (p > &path[UTF8FILELEN_EARLY]) { /* FIXME safeguard, limit of early Mac OS X */ afp_errno = AFPERR_PARAM; - return( NULL ); + return NULL; } - len--; } + *p = 0; /* Terminate string */ + ret.u_name = NULL; - /* short cut bits by chopping off a trailing \0. this also - makes the traversal happy w/ filenames at the end of the - cname. */ - if (len == 1) - len--; - - *p = '\0'; - - if ( p == path ) { /* end of the name parameter */ - continue; + if (cname_mtouname(vol, dir, &ret, toUTF8) != 0) { /* 7 */ + LOG(log_error, logtype_afpd, "cname('%s'): error from cname_mtouname", path); + return NULL; } - ret.u_name = NULL; - if (afp_version >= 30) { - char *t; - cnid_t fileid; - if (toUTF8) { - static char temp[ MAXPATHLEN + 1]; + LOG(log_maxdebug, logtype_afpd, "came('%s'): {node: '%s}", cfrombstr(dir->d_fullpath), ret.u_name); - 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( path, temp) == 0) - ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN); - } else { - /* toUTF8 */ - if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) { - afp_errno = AFPERR_PARAM; - return( NULL ); - } - strcpy(path, temp); - } - } - /* check for OS X mangled filename :( */ + /* Prevent access to our special folders like .AppleDouble */ + if (check_name(vol, ret.u_name)) { + /* the name is illegal */ + LOG(log_info, logtype_afpd, "cname: illegal path: '%s'", ret.u_name); + afp_errno = AFPERR_PARAM; + return NULL; + } - t = demangle_osx(vol, path, dir->d_did, &fileid); - if (t != path) { - 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 (dir->d_did == DIRDID_ROOT_PARENT) { /* 8 */ + /* + * Special case: CNID 1 + * root parent (did 1) has one child: the volume. Requests for did=1 with + * some must check against the volume name. + */ + if ((strcmp(cfrombstr(vol->v_root->d_m_name), ret.m_name)) == 0) + cdir = vol->v_root; + else + return NULL; + } else { + /* + * CNID != 1, eg. most of the times we take this way. + * Now check if current path-part is a file or dir: + * o if it's dir we have to step into it + * o if it's a file we expect it to be the last part of the requested path + * and thus call continue which should terminate the while loop because + * len = 0. Ok? + */ + if (of_stat(&ret) != 0) { /* 9 */ + /* + * ret.u_name doesn't exist, might be afp_createfile|dir + * that means it should have been the last part */ - if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) { - /* at last got our view of mac name */ - strcpy(path,t); + if (len > 0) { + /* it wasn't the last part, so we have a bogus path request */ + afp_errno = AFPERR_NOOBJ; + return NULL; } + /* + * this will terminate clean in while (1) because len == 0, + * probably afp_createfile|dir + */ + LOG(log_maxdebug, logtype_afpd, "came('%s'): {leave-cnode ENOENT (possile create request): '%s'}", + cfrombstr(dir->d_fullpath), ret.u_name); + continue; /* 10 */ } - } - if (ret.u_name == NULL) { - if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) { - afp_errno = AFPERR_PARAM; - return NULL; - } - } - if ( !extend ) { - ucs2_t *tmpname; - cdir = dir->d_child; - scdir = NULL; - if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) && - (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset), - CH_UCS2, path, -1, (char **)&tmpname) ) - { - while (cdir) { - if (!cdir->d_m_name_ucs2) { - LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) ); - /* this shouldn't happen !!!! */ - goto noucsfallback; - } - if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) { - break; - } - if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) { - scdir = cdir; - } - cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next; - } - free(tmpname); - } - else { - noucsfallback: - if (dir->d_did == DIRDID_ROOT_PARENT) { - /* - root parent (did 1) has one child: the volume. Requests for did=1 with some - must check against the volume name. - */ - if (!strcmp(vol->v_dir->d_m_name, ret.m_name)) - cdir = vol->v_dir; - else - cdir = NULL; + switch (ret.st.st_mode & S_IFMT) { + case S_IFREG: /* 11 */ + LOG(log_debug, logtype_afpd, "came('%s'): {file: '%s'}", + cfrombstr(dir->d_fullpath), ret.u_name); + if (len > 0) { + /* it wasn't the last part, so we have a bogus path request */ + afp_errno = AFPERR_PARAM; + return NULL; } - else { - cdir = dirsearch_byname(vol, dir, ret.u_name); + continue; /* continues while loop */ + case S_IFLNK: /* 12 */ + LOG(log_debug, logtype_afpd, "came('%s'): {link: '%s'}", + cfrombstr(dir->d_fullpath), ret.u_name); + if (len > 0) { + LOG(log_warning, logtype_afpd, "came('%s'): {symlinked dir: '%s'}", + cfrombstr(dir->d_fullpath), ret.u_name); + afp_errno = AFPERR_PARAM; + return NULL; } + continue; /* continues while loop */ + case S_IFDIR: /* 13 */ + break; + default: + LOG(log_info, logtype_afpd, "cname: special file: '%s'", ret.u_name); + afp_errno = AFPERR_NODIR; + return NULL; } - if (cdir == NULL && scdir != NULL) { - cdir = scdir; - /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */ - } - - if ( cdir == NULL ) { - ++extend; - /* if dir == curdir it always succeed, - even if curdir is deleted. - it's not a pb because it will fail in extenddir - */ - if ( movecwd( vol, dir ) < 0 ) { - /* dir is not valid anymore - we delete dir from the cache and abort. - */ - if ( dir->d_did == DIRDID_ROOT_PARENT) { - afp_errno = AFPERR_NOOBJ; - return NULL; - } - if (afp_errno == AFPERR_ACCESS) - return NULL; - dir_invalidate(vol, dir); + /* Search the cache */ + int unamelen = strlen(ret.u_name); + cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen, ret.st.st_ctime); /* 14 */ + if (cdir == NULL) { + /* Not in cache, create one */ + if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */ + LOG(log_error, logtype_afpd, "cname(did:%u, name:'%s', cwd:'%s'): failed to add dir", + ntohl(dir->d_did), ret.u_name, getcwdpath()); return NULL; } - cdir = extenddir( vol, dir, &ret ); } + } /* if/else cnid==1 */ + + /* Now chdir to the evaluated dir */ + if (movecwd( vol, cdir ) < 0 ) { /* 16 */ + LOG(log_debug, logtype_afpd, "cname(cwd:'%s'): failed to chdir to new subdir '%s': %s", + cfrombstr(curdir->d_fullpath), cfrombstr(cdir->d_fullpath), strerror(errno)); + if (len == 0) + return path_from_dir(vol, cdir, &ret); + else + return NULL; + } + dir = cdir; + ret.m_name[0] = 0; /* 17, so we later know last token was a dir */ + } /* while (len) */ - } else { - cdir = extenddir( vol, dir, &ret ); - } /* if (!extend) */ + if (curdir->d_did == DIRDID_ROOT_PARENT) { + afp_errno = AFPERR_DID1; + return NULL; + } - if ( cdir == NULL ) { + if (ret.m_name[0] == 0) { + /* Last part was a dir */ + ret.u_name = mtoupath(vol, ret.m_name, 0, 1); /* Force "." into a useable static buffer */ + ret.d_dir = dir; + } - if ( len > 0 || !ret.u_name ) { - return NULL; - } + LOG(log_debug, logtype_afpd, "came('%s') {end: curdir:'%s', path:'%s'}", + cfrombstr(dir->d_fullpath), + cfrombstr(curdir->d_fullpath), + ret.u_name); - } else { - dir = cdir; - *path = '\0'; - } - } /* for (;;) */ + return &ret; } /* - * Move curdir to dir, with a possible chdir() + * @brief chdir() to dir + * + * @param vol (r) pointer to struct vol + * @param dir (r) pointer to struct dir + * + * @returns 0 on success, -1 on error with afp_errno set appropiately */ -int movecwd(struct vol *vol, struct dir *dir) +int movecwd(const struct vol *vol, struct dir *dir) { - char path[MAXPATHLEN + 1]; - struct dir *d; - char *p, *u; - int n; - int ret; + int ret; - if ( dir == curdir ) { - return( 0 ); - } - if ( dir->d_did == DIRDID_ROOT_PARENT) { - afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/ - return( -1 ); - } + AFP_ASSERT(vol); + AFP_ASSERT(dir); - p = path + sizeof(path) - 1; - *p = '\0'; - for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) { - u = d->d_u_name; - if (!u) { - /* parent directory is deleted */ - afp_errno = AFPERR_NOOBJ; - return -1; - } - n = strlen( u ); - if (p -n -1 < path) { - afp_errno = AFPERR_PARAM; - return -1; - } - *--p = '/'; - p -= n; - memcpy( p, u, n ); - } - if ( d != curdir ) { - n = strlen( vol->v_path ); - if (p -n -1 < path) { - afp_errno = AFPERR_PARAM; - return -1; - } - *--p = '/'; - p -= n; - memcpy( p, vol->v_path, n ); + LOG(log_maxdebug, logtype_afpd, "movecwd: from: curdir:\"%s\", cwd:\"%s\"", + curdir ? cfrombstr(curdir->d_fullpath) : "INVALID", getcwdpath()); + + if (dir->d_did == DIRDID_ROOT_PARENT) { + curdir = &rootParent; + return 0; } - if ( (ret = lchdir(p )) != 0 ) { - LOG(log_debug, logtype_afpd, "movecwd('%s'): ret:%d, %u/%s", p, ret, errno, strerror(errno)); + LOG(log_debug, logtype_afpd, "movecwd(to: did: %u, \"%s\")", + ntohl(dir->d_did), cfrombstr(dir->d_fullpath)); + + if ((ret = lchdir(cfrombstr(dir->d_fullpath))) != 0 ) { + LOG(log_debug, logtype_afpd, "movecwd(\"%s\"): ret: %u, %s", + cfrombstr(dir->d_fullpath), ret, strerror(errno)); if (ret == 1) { /* p is a symlink or getcwd failed */ afp_errno = AFPERR_BADTYPE; - vol->v_curdir = curdir = vol->v_dir; + if (chdir(vol->v_path ) < 0) { - LOG(log_debug, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno)); + LOG(log_error, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno)); /* XXX what do we do here? */ } + curdir = vol->v_root; return -1; } + switch (errno) { case EACCES: case EPERM: @@ -1618,11 +1373,11 @@ int movecwd(struct vol *vol, struct dir *dir) break; default: afp_errno = AFPERR_NOOBJ; - } return( -1 ); } - vol->v_curdir = curdir = dir; + + curdir = dir; return( 0 ); } @@ -1658,10 +1413,18 @@ int file_access(struct path *path, int mode) struct maccess ma; accessmode(path->u_name, &ma, curdir, &path->st); - if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE)) + + LOG(log_debug, logtype_afpd, "file_access(\"%s\"): mapped user mode: 0x%02x", + path->u_name, ma.ma_user); + + if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE)) { + LOG(log_debug, logtype_afpd, "file_access(\"%s\"): write access denied", path->u_name); return -1; - if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD)) + } + if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD)) { + LOG(log_debug, logtype_afpd, "file_access(\"%s\"): read access denied", path->u_name); return -1; + } return 0; } @@ -1674,30 +1437,15 @@ void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count) dir->d_flags &= ~DIRF_CNID; } -/* --------------------- - * is our cached offspring count valid? - */ - -static int diroffcnt(struct dir *dir, struct stat *st) -{ - return st->st_ctime == dir->ctime; -} /* --------------------- * is our cached also for reenumerate id? */ - int dirreenumerate(struct dir *dir, struct stat *st) { return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID); } -/* --------------------- */ -static int invisible_dots(const struct vol *vol, const char *name) -{ - return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, ".."); -} - /* ------------------------------ (".", curdir) (name, dir) with curdir:name == dir, from afp_enumerate @@ -1736,20 +1484,14 @@ int getdirparams(const struct vol *vol, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, - dir->d_parent->d_did, + dir->d_pdid, vol->v_stamp); ad_flush( &ad); } } } - if ( dir->d_did == DIRDID_ROOT) { - pdid = DIRDID_ROOT_PARENT; - } else if (dir->d_did == DIRDID_ROOT_PARENT) { - pdid = 0; - } else { - pdid = dir->d_parent->d_did; - } + pdid = dir->d_pdid; data = buf; while ( bitmap != 0 ) { @@ -1762,7 +1504,7 @@ int getdirparams(const struct vol *vol, case DIRPBIT_ATTR : if ( isad ) { ad_getattr(&ad, &ashort); - } else if (invisible_dots(vol, dir->d_u_name)) { + } else if (invisible_dots(vol, cfrombstr(dir->d_u_name))) { ashort = htons(ATTRBIT_INVISIBLE); } else ashort = 0; @@ -1774,6 +1516,8 @@ int getdirparams(const struct vol *vol, case DIRPBIT_PDID : memcpy( data, &pdid, sizeof( pdid )); data += sizeof( pdid ); + LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u", + s_path->u_name, ntohl(pdid)); break; case DIRPBIT_CDATE : @@ -1806,7 +1550,7 @@ int getdirparams(const struct vol *vol, memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort)); /* dot files are by default visible */ - if (invisible_dots(vol, dir->d_u_name)) { + if (invisible_dots(vol, cfrombstr(dir->d_u_name))) { ashort = htons(FINDERINFO_INVISIBLE); memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); } @@ -1830,6 +1574,8 @@ int getdirparams(const struct vol *vol, case DIRPBIT_DID : memcpy( data, &dir->d_did, sizeof( aint )); data += sizeof( aint ); + LOG(log_debug, logtype_afpd, "metadata('%s'): DID: %u", + s_path->u_name, ntohl(dir->d_did)); break; case DIRPBIT_OFFCNT : @@ -1927,12 +1673,12 @@ int getdirparams(const struct vol *vol, if ( l_nameoff ) { ashort = htons( data - buf ); memcpy( l_nameoff, &ashort, sizeof( ashort )); - data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0); + data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, 0); } if ( utf_nameoff ) { ashort = htons( data - buf ); memcpy( utf_nameoff, &ashort, sizeof( ashort )); - data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8); + data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, utf8); } if ( isad ) { ad_close_metadata( &ad ); @@ -2017,33 +1763,7 @@ int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_ * * assume path == '\0' eg. it's a directory in canonical form */ - -struct path Cur_Path = { - 0, - "", /* mac name */ - ".", /* unix name */ - 0, /* id */ - NULL,/* struct dir */ - 0, /* stat is not set */ -}; - -/* ------------------ */ -static int set_dir_errors(struct path *path, const char *where, int err) -{ - 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; -} - -/* ------------------ */ -int setdirparams(struct vol *vol, - struct path *path, u_int16_t d_bitmap, char *buf ) +int setdirparams(struct vol *vol, struct path *path, u_int16_t d_bitmap, char *buf ) { struct maccess ma; struct adouble ad; @@ -2195,7 +1915,7 @@ int setdirparams(struct vol *vol, * to set our name, etc. */ if ( (ad_get_HF_flags( &ad ) & O_CREAT)) { - ad_setname(&ad, curdir->d_m_name); + ad_setname(&ad, cfrombstr(curdir->d_m_name)); } } @@ -2352,8 +2072,8 @@ setdirparam_done: if (path->st_valid && !path->st_errno) { struct stat *st = &path->st; - if (dir && dir->d_parent) { - ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp); + if (dir && dir->d_pdid) { + ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_pdid, vol->v_stamp); } } ad_flush( &ad); @@ -2362,7 +2082,7 @@ setdirparam_done: if (change_parent_mdate && dir->d_did != DIRDID_ROOT && gettimeofday(&tv, NULL) == 0) { - if (!movecwd(vol, dir->d_parent)) { + if (movecwd(vol, dirlookup(vol, dir->d_pdid)) == 0) { newdate = AD_DATE_FROM_UNIX(tv.tv_sec); /* be careful with bitmap because now dir is null */ bitmap = 1<ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) ); + vol->ad_path(cfrombstr(dir->d_u_name), ADFLAGS_DIR), strerror(errno) ); close(dfd); } @@ -2513,8 +2233,10 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_ if (of_stat(s_path) < 0) { return AFPERR_MISC; } + curdir->offcnt++; - if ((dir = adddir( vol, curdir, s_path)) == NULL) { + + if ((dir = dir_add(vol, curdir, s_path, strlen(s_path->u_name))) == NULL) { return AFPERR_MISC; } @@ -2537,11 +2259,6 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_ ad_close_metadata( &ad); createdir_done: -#ifdef HAVE_NFSv4_ACLS - /* FIXME: are we really inside the created dir? */ - addir_inherit_acl(vol); -#endif - memcpy( rbuf, &dir->d_did, sizeof( u_int32_t )); *rbuflen = sizeof( u_int32_t ); setvoltime(obj, vol ); @@ -2563,9 +2280,7 @@ int renamedir(const struct vol *vol, char *newname) { struct adouble ad; - struct dir *parent; - char *buf; - int len, err; + int err; /* existence check moved to afp_moveandrename */ if ( unix_rename(dirfd, src, -1, dst ) < 0 ) { @@ -2596,11 +2311,6 @@ int renamedir(const struct vol *vol, vol->vfs->vfs_renamedir(vol, dirfd, src, dst); - len = strlen( newname ); - /* rename() succeeded so we need to update our tree even if we can't open - * metadata - */ - ad_init(&ad, vol->v_adouble, vol->v_ad_options); if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) { @@ -2609,50 +2319,6 @@ int renamedir(const struct vol *vol, ad_close_metadata( &ad); } - dir_hash_del(vol, dir); - if (dir->d_m_name == dir->d_u_name) - dir->d_u_name = NULL; - - if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) { - LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) ); - /* FIXME : fatal ? */ - return AFPERR_MISC; - } - dir->d_m_name = buf; - strcpy( dir->d_m_name, newname ); - - if (newname == dst) { - free(dir->d_u_name); - dir->d_u_name = dir->d_m_name; - } - else { - if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) { - LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) ); - return AFPERR_MISC; - } - dir->d_u_name = buf; - strcpy( dir->d_u_name, dst ); - } - - if (dir->d_m_name_ucs2) - free(dir->d_m_name_ucs2); - - dir->d_m_name_ucs2 = NULL; - if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, -1, (char**)&dir->d_m_name_ucs2)) - dir->d_m_name_ucs2 = NULL; - - if (( parent = dir->d_parent ) == NULL ) { - return( AFP_OK ); - } - if ( parent == newparent ) { - hash_alloc_insert(vol->v_hash, dir, dir); - return( AFP_OK ); - } - - /* detach from old parent and add to new one. */ - dirchildremove(parent, dir); - dir->d_parent = newparent; - dirchildadd(vol, newparent, dir); return( AFP_OK ); } @@ -2661,13 +2327,13 @@ int deletecurdir(struct vol *vol) { struct dirent *de; struct stat st; - struct dir *fdir; + struct dir *fdir, *pdir; DIR *dp; struct adouble ad; u_int16_t ashort; int err; - if ( curdir->d_parent == NULL ) { + if ((pdir = dirlookup(vol, curdir->d_pdid)) == NULL) { return( AFPERR_ACCESS ); } @@ -2685,6 +2351,8 @@ int deletecurdir(struct vol *vol) } err = vol->vfs->vfs_deletecurdir(vol); if (err) { + LOG(log_error, logtype_afpd, "deletecurdir: error deleting .AppleDouble in \"%s\"", + curdir->d_fullpath); return err; } @@ -2697,6 +2365,8 @@ int deletecurdir(struct vol *vol) /* bail if it's not a symlink */ if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) { + LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): not empty", + curdir->d_fullpath); closedir(dp); return AFPERR_DIRNEMPT; } @@ -2708,17 +2378,23 @@ int deletecurdir(struct vol *vol) } } - if ( movecwd( vol, curdir->d_parent ) < 0 ) { + if (movecwd(vol, pdir) < 0) { err = afp_errno; goto delete_done; } - err = netatalk_rmdir_all_errors(-1, fdir->d_u_name); + LOG(log_debug, logtype_afpd, "deletecurdir: moved to \"%s\"", + cfrombstr(curdir->d_fullpath)); + + err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name)); if ( err == AFP_OK || err == AFPERR_NOOBJ) { - dirchildremove(curdir, fdir); cnid_delete(vol->v_cdb, fdir->d_did); dir_remove( vol, fdir ); + } else { + LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): netatalk_rmdir_all_errors error", + curdir->d_fullpath); } + delete_done: if (dp) { /* inode is used as key for cnid. @@ -2743,7 +2419,6 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r sfunc = (unsigned char) *ibuf++; *rbuflen = 0; - if (sfunc >= 3 && sfunc <= 6) { if (afp_version < 30) { return( AFPERR_PARAM ); @@ -2782,17 +2457,18 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r name = NULL; } break; -#ifdef HAVE_NFSv4_ACLS + case 5 : /* UUID -> username */ case 6 : /* UUID -> groupname */ if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID )) return AFPERR_PARAM; LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request"); uuidtype_t type; - len = getnamefromuuid( ibuf, &name, &type); + len = getnamefromuuid((unsigned char*) ibuf, &name, &type); if (len != 0) /* its a error code, not len */ return AFPERR_NOITEM; - if (type == UUID_USER) { + switch (type) { + case UUID_USER: if (( pw = getpwnam( name )) == NULL ) return( AFPERR_NOITEM ); LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid); @@ -2803,7 +2479,8 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r memcpy( rbuf, &id, sizeof( id )); rbuf += sizeof( id ); *rbuflen = 2 * sizeof( id ); - } else { /* type == UUID_GROUP */ + break; + case UUID_GROUP: if (( gr = getgrnam( name )) == NULL ) return( AFPERR_NOITEM ); LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid); @@ -2814,9 +2491,15 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r memcpy( rbuf, &id, sizeof( id )); rbuf += sizeof( id ); *rbuflen = 2 * sizeof( id ); + break; + case UUID_LOCAL: + free(name); + return (AFPERR_NOITEM); + default: + return AFPERR_MISC; } break; -#endif + default : return( AFPERR_PARAM ); } @@ -2870,7 +2553,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz case 4 : len = (unsigned char) *ibuf++; break; -#ifdef HAVE_NFSv4_ACLS case 5 : /* username -> UUID */ case 6 : /* groupname -> UUID */ if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID )) @@ -2879,7 +2561,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz len = ntohs(ulen); ibuf += 2; break; -#endif default : return( AFPERR_PARAM ); } @@ -2913,20 +2594,18 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz memcpy( rbuf, &id, sizeof( id )); *rbuflen = sizeof( id ); break; -#ifdef HAVE_NFSv4_ACLS case 5 : /* username -> UUID */ LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf); - if (0 != getuuidfromname(ibuf, UUID_USER, rbuf)) + if (0 != getuuidfromname(ibuf, UUID_USER, (unsigned char *)rbuf)) return AFPERR_NOITEM; *rbuflen = UUID_BINSIZE; break; case 6 : /* groupname -> UUID */ LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf); - if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf)) + if (0 != getuuidfromname(ibuf, UUID_GROUP, (unsigned char *)rbuf)) return AFPERR_NOITEM; *rbuflen = UUID_BINSIZE; break; -#endif } } return( AFP_OK );