/*
- * $Id: directory.c,v 1.131.2.12 2010-02-09 14:56:30 franklahm Exp $
+ * $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.
"", /* mac name */
".", /* unix name */
0, /* id */
- NULL,/* struct dir */
+ NULL,/* struct dir * */
0, /* stat is not set */
+ 0, /* errno */
+ {0} /* struct stat */
};
}
/* ------------------- */
-static int deletedir(char *dir)
+static int deletedir(int dirfd, char *dir)
{
char path[MAXPATHLEN + 1];
DIR *dp;
return AFPERR_PARAM;
/* already gone */
- if ((dp = opendir(dir)) == NULL)
+ if ((dp = opendirat(dirfd, dir)) == NULL)
return AFP_OK;
strcpy(path, dir);
break;
}
strcpy(path + len, de->d_name);
- if (stat(path, &st)) {
+ if (lstatat(dirfd, path, &st)) {
continue;
}
if (S_ISDIR(st.st_mode)) {
- err = deletedir(path);
+ err = deletedir(dirfd, path);
} else {
- err = netatalk_unlink(path);
+ 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(dir);
+ err = netatalk_rmdir(dirfd, dir);
}
return err;
}
/* do a recursive copy. */
-static int copydir(const struct vol *vol, char *src, char *dst)
+static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
{
char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
DIR *dp;
/* doesn't exist or the path is too long. */
if (((slen = strlen(src)) > sizeof(spath) - 2) ||
((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
- ((dp = opendir(src)) == NULL))
+ ((dp = opendirat(dirfd, src)) == NULL))
return AFPERR_PARAM;
/* try to create the destination directory */
}
strcpy(spath + slen, de->d_name);
- if (stat(spath, &st) == 0) {
+ 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, spath, dpath)))
+ if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
goto copydir_done;
- } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
+ } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
goto copydir_done;
} else {
}
/* keep the same time stamp. */
- if (stat(src, &st) == 0) {
+ if (lstatat(dirfd, src, &st) == 0) {
ut.actime = ut.modtime = st.st_mtime;
utime(dst, &ut);
}
struct dir *dirlookup(const struct vol *vol, cnid_t did)
{
static char buffer[12 + MAXPATHLEN + 1];
- struct bstrList *pathlist = NULL;
- bstring fullpath = NULL;
struct stat st;
- struct dir *ret = NULL;
- char *upath, *mpath;
+ struct dir *ret = NULL, *pdir;
+ bstring fullpath = NULL;
+ char *upath = NULL, *mpath;
cnid_t cnid, pdid;
size_t maxpath;
int buflen = 12 + MAXPATHLEN + 1;
utf8 = utf8_encoding();
maxpath = (utf8) ? MAXPATHLEN - 7 : 255;
- /* Create list for path elements, request 16 list elements for now*/
- if ((pathlist = bstListCreateMin(16)) == NULL) { /* 4 */
- LOG(log_error, logtype_afpd, "dirlookup(did: %u): OOM: %s", ntohl(did), strerror(errno));
- return NULL;
- }
-
/* Get it from the database */
cnid = did;
- if ( (upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) { /* 3 */
+ 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;
- /* construct path, copy already found uname to path element list*/
- if ((bstrListPush(pathlist, bfromcstr(upath))) != BSTR_OK) { /* 4 */
- afp_errno = AFPERR_MISC;
- err = 1;
- goto exit;
- }
-
- LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {%u, %s}", ntohl(did), ntohl(pdid), upath);
-
- /* The stuff that follows is for building the full path to the directory */
-
- /* work upwards until we reach volume root */
- while (cnid != DIRDID_ROOT) {
- /* construct path, copy already found uname to path element list*/
- if ((bstrListPush(pathlist, bfromcstr(upath))) != BSTR_OK) { /* 4 */
- afp_errno = AFPERR_MISC;
- err = 1;
- goto exit;
- }
-
- /* next part */
- if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) { /* 3 */
- afp_errno = AFPERR_NOOBJ;
- err = 1;
- goto exit;
- }
- }
-
- if ((bstrListPush(pathlist, bfromcstr(vol->v_path))) != BSTR_OK) { /* 4 */
- afp_errno = AFPERR_MISC;
+ /*
+ * 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;
}
- if ((fullpath = bjoinInv(pathlist, bfromcstr("/"))) == NULL) { /* 4 */
- afp_errno = AFPERR_MISC;
+ /* build the fullpath */
+ if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL
+ || bconchar(fullpath, '/') != BSTR_OK
+ || bcatcstr(fullpath, upath) != BSTR_OK) {
err = 1;
goto exit;
}
- /* Finished building the fullpath */
/* stat it and check if it's a dir */
LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstring(fullpath));
ntohl(did), ntohl(pdid), cfrombstring(ret->d_fullpath));
exit:
- if (pathlist)
- bstrListDestroy(pathlist);
-
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) {
*
* Create a new struct dir from struct path. Then add it to the cache.
* The caller must have assured that the dir is not already in the cache,
- * cf the assertion.
+ * cf theAFP_ASSERTion.
* 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.
struct adouble *adp = NULL;
bstring fullpath;
- assert(vol);
- assert(dir);
- assert(path);
- assert(len > 0);
+ 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))) != NULL) {
/* there's a stray entry in the dircache */
*/
int dir_remove(const struct vol *vol, struct dir *dir)
{
- assert(vol);
- assert(dir);
+ AFP_ASSERT(vol);
+ AFP_ASSERT(dir);
if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
return 0;
*/
int movecwd(const struct vol *vol, struct dir *dir)
{
- assert(vol);
+ int ret;
- if (dir == NULL)
- return -1;
+ AFP_ASSERT(vol);
+ AFP_ASSERT(dir);
- LOG(log_maxdebug, logtype_afpd, "movecwd(curdir:'%s', cwd:'%s')",
+ LOG(log_maxdebug, logtype_afpd, "movecwd(curdir:'%s', cwd:'%s')",
cfrombstring(curdir->d_fullpath), getcwdpath());
if ( dir == curdir)
LOG(log_debug, logtype_afpd, "movecwd(did:%u, '%s')", ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
- if ( chdir(cfrombstring(dir->d_fullpath)) < 0 ) {
- LOG(log_debug, logtype_afpd, "movecwd('%s'): %s", cfrombstring(dir->d_fullpath), strerror(errno));
+ if ((ret = lchdir(cfrombstring(dir->d_fullpath))) != 0 ) {
+ LOG(log_debug, logtype_afpd, "movecwd('%s'): ret: %u, %s",
+ cfrombstring(dir->d_fullpath), ret, strerror(errno));
+ if (ret == 1) {
+ /* p is a symlink or getcwd failed */
+ afp_errno = AFPERR_BADTYPE;
+
+ if (chdir(vol->v_path ) < 0) {
+ 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:
* dst new unix filename (not a pathname)
* newname new mac name
* newparent curdir
+ * dirfd -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd
*/
-int renamedir(const struct vol *vol, char *src, char *dst,
+int renamedir(const struct vol *vol,
+ int dirfd,
+ char *src,
+ char *dst,
struct dir *dir,
struct dir *newparent,
char *newname)
int err;
/* existence check moved to afp_moveandrename */
- if ( unix_rename( src, dst ) < 0 ) {
+ if ( unix_rename(dirfd, src, -1, dst ) < 0 ) {
switch ( errno ) {
case ENOENT :
return( AFPERR_NOOBJ );
case EXDEV:
/* this needs to copy and delete. bleah. that means we have
* to deal with entire directory hierarchies. */
- if ((err = copydir(vol, src, dst)) < 0) {
- deletedir(dst);
+ if ((err = copydir(vol, dirfd, src, dst)) < 0) {
+ deletedir(-1, dst);
return err;
}
- if ((err = deletedir(src)) < 0)
+ if ((err = deletedir(dirfd, src)) < 0)
return err;
break;
default :
}
}
- vol->vfs->vfs_renamedir(vol, src, dst);
+ vol->vfs->vfs_renamedir(vol, dirfd, src, dst);
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
ad_getattr(&ad, &ashort);
- ad_close( &ad, ADFLAGS_HF );
+ ad_close_metadata(&ad);
if ((ashort & htons(ATTRBIT_NODELETE))) {
return AFPERR_OLOCK;
}
goto delete_done;
}
- err = netatalk_rmdir_all_errors(cfrombstring(fdir->d_u_name));
+ err = netatalk_rmdir_all_errors(-1, cfrombstring(fdir->d_u_name));
if ( err == AFP_OK || err == AFPERR_NOOBJ) {
cnid_delete(vol->v_cdb, fdir->d_did);
dir_remove( vol, fdir );