/*
- * $Id: directory.c,v 1.94 2009-03-17 14:30:57 franklahm Exp $
+ * $Id: directory.c,v 1.113 2009-10-22 13:40:11 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include <sys/param.h>
#include <errno.h>
#include <utime.h>
-#include <atalk/adouble.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
#include <atalk/afp.h>
#include <atalk/util.h>
#include <atalk/cnid.h>
#include <atalk/logger.h>
#include <atalk/uuid.h>
+#include <atalk/unix.h>
#include "directory.h"
#include "desktop.h"
#include "globals.h"
#include "unix.h"
#include "mangle.h"
+#include "hash.h"
#ifdef HAVE_NFSv4_ACLS
extern void addir_inherit_acl(const struct vol *vol);
#define SENTINEL (&sentinel)
static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
NULL, NULL, NULL, NULL, NULL, 0, 0,
- 0, 0, NULL, NULL};
+ 0, 0, NULL, NULL, NULL};
static struct dir rootpar = { SENTINEL, SENTINEL, NULL, 0,
NULL, NULL, NULL, NULL, NULL, 0, 0,
- 0, 0, NULL, NULL};
+ 0, 0, NULL, NULL, NULL};
/* (from IM: Toolbox Essentials)
* dirFinderInfo (DInfo) fields:
* how exciting.
*/
struct dir *
- dirsearch( vol, did )
- const struct vol *vol;
-u_int32_t did;
+ dirsearch(const struct vol *vol, u_int32_t did)
{
struct dir *dir;
}
/* ------------------- */
-#ifdef ATACC
-int path_isadir(struct path *o_path)
-{
- return o_path->d_dir != NULL;
-#if 0
- return o_path->m_name == '\0' || /* we are in a it */
- !o_path->st_valid || /* in cache but we can't chdir in it */
- (!o_path->st_errno && S_ISDIR(o_path->st.st_mode)); /* not in cache an can't chdir */
-#endif
-}
-#endif
-
int get_afp_errno(const int param)
{
if (afp_errno != AFPERR_DID1)
* and we are really bad in this case.
*/
struct dir *
- dirlookup( vol, did )
- const struct vol *vol;
-u_int32_t did;
+ dirlookup( struct vol *vol, u_int32_t did)
{
struct dir *ret;
char *upath;
/* --------------------------- */
/* rotate the tree to the left */
-static void dir_leftrotate(vol, dir)
-struct vol *vol;
-struct dir *dir;
+static void dir_leftrotate(struct vol *vol, struct dir *dir)
{
struct dir *right = dir->d_right;
/* rotate the tree to the right */
-static void dir_rightrotate(vol, dir)
-struct vol *vol;
-struct dir *dir;
+static void dir_rightrotate(struct vol *vol, struct dir *dir)
{
struct dir *left = dir->d_left;
#if 0
/* recolor after a removal */
-static struct dir *dir_rmrecolor(vol, dir)
- struct vol *vol;
-struct dir *dir;
+static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
{
struct dir *leaf;
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 */
/* 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);
+ 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);
}
* 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( vol, dir )
-const struct vol *vol;
-struct dir *dir;
+static void dir_invalidate( const struct vol *vol, struct dir *dir)
{
if (curdir == dir) {
/* v_root can't be deleted */
}
/* ------------------------------------ */
-static struct dir *dir_insert(vol, dir)
- const struct vol *vol;
-struct dir *dir;
+static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
{
struct dir *pdir;
return pdir;
}
+#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)
+{
+ DIR *dp;
+ struct dirent *de;
+ int ret;
+ static u_int32_t did = 0;
+ static char cname[MAXPATHLEN];
+ static char lname[MAXPATHLEN];
+ ucs2_t u2_path[MAXPATHLEN];
+ ucs2_t u2_dename[MAXPATHLEN];
+ char *tmp, *savepath;
+
+ if (!(vol->v_flags & AFPVOL_CASEINSEN))
+ return -1;
+
+ if (veto_file(ENUMVETO, path->u_name))
+ return -1;
+
+ savepath = path->u_name;
+
+ /* very simple cache */
+ if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
+ path->u_name = cname;
+ path->d_dir = NULL;
+ if (of_stat( path ) == 0 ) {
+ return 0;
+ }
+ /* something changed, we cannot stat ... */
+ did = 0;
+ }
+
+ if (NULL == ( dp = opendir( "." )) ) {
+ LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
+ return -1;
+ }
+
+
+ /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
+ if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, strlen(path->u_name), u2_path, sizeof(u2_path)) )
+ LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
+
+ /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
+ ret = -1;
+ for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
+ if (NULL == check_dirent(vol, de->d_name))
+ continue;
+
+ if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, strlen(de->d_name), u2_dename, sizeof(u2_dename)) )
+ continue;
+
+ if (strcasecmp_w( u2_path, u2_dename) == 0) {
+ tmp = path->u_name;
+ strlcpy(cname, de->d_name, sizeof(cname));
+ path->u_name = cname;
+ path->d_dir = NULL;
+ if (of_stat( path ) == 0 ) {
+ LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
+ strlcpy(lname, tmp, sizeof(lname));
+ did = dir->d_did;
+ ret = 0;
+ break;
+ }
+ else
+ path->u_name = tmp;
+ }
+
+ }
+ closedir(dp);
+
+ if (ret) {
+ /* invalidate cache */
+ cname[0] = 0;
+ did = 0;
+ path->u_name = savepath;
+ }
+ /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
+ return ret;
+}
+
+
/*
* attempt to extend the current dir. tree to include path
* as a side-effect, movecwd to that point and return the new dir
*/
static struct dir *
- extenddir( vol, dir, path )
-struct vol *vol;
-struct dir *dir;
-struct path *path;
+ extenddir(struct vol *vol, struct dir *dir, struct path *path)
{
path->d_dir = NULL;
afp_errno = AFPERR_PARAM;
return NULL;
}
- if (of_stat( path ) != 0 ) {
+
+ 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;
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 );
}
return( dir );
}
-/* -------------------
- system rmdir with afp error code.
- ENOENT is not an error.
- */
-int netatalk_rmdir(const char *name)
-{
- if (rmdir(name) < 0) {
- switch ( errno ) {
- case ENOENT :
- break;
- case ENOTEMPTY :
- return AFPERR_DIRNEMPT;
- case EPERM:
- case EACCES :
- return AFPERR_ACCESS;
- case EROFS:
- return AFPERR_VLOCK;
- default :
- return AFPERR_PARAM;
- }
- }
- return AFP_OK;
-}
-
/* -------------------------
appledouble mkdir afp error code.
*/
return AFP_OK;
}
-/* -------------------
- system unlink with afp error code.
- ENOENT is not an error.
- */
-int netatalk_unlink(const char *name)
-{
- if (unlink(name) < 0) {
- switch (errno) {
- case ENOENT :
- break;
- case EROFS:
- return AFPERR_VLOCK;
- case EPERM:
- case EACCES :
- return AFPERR_ACCESS;
- default :
- return AFPERR_PARAM;
- }
- }
- return AFP_OK;
-}
-
/* ------------------- */
static int deletedir(char *dir)
{
/* --- public functions follow --- */
/* NOTE: we start off with at least one node (the root directory). */
-static struct dir *dirinsert( vol, dir )
- struct vol *vol;
-struct dir *dir;
+static struct dir *dirinsert(struct vol *vol, struct dir *dir)
{
struct dir *node;
/* ---------------------------- */
struct dir *
- adddir( vol, dir, path)
-struct vol *vol;
-struct dir *dir;
-struct path *path;
+ adddir(struct vol *vol, struct dir *dir, struct path *path)
{
struct dir *cdir, *edir;
int upathlen;
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, strlen(path->m_name), (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;
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 (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( dir )
-struct dir *dir;
+void dirfree(struct dir *dir)
{
if (!dir || (dir == SENTINEL))
return;
return NULL;
}
+ dir->d_m_name_ucs2 = NULL;
dir->d_left = dir->d_right = SENTINEL;
dir->d_next = dir->d_prev = dir;
return dir;
0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
};
- const unsigned char *str = (unsigned char *)k->d_u_name;
+ const unsigned char *str = (unsigned char *)(k->d_u_name);
hash_val_t acc = 0;
while (*str) {
*/
struct path *
-cname( vol, dir, cpath )
-const struct vol *vol;
-struct dir *dir;
-char **cpath;
+cname(struct vol *vol, struct dir *dir, char **cpath)
{
- struct dir *cdir;
+ struct dir *cdir, *scdir=NULL;
static char path[ MAXPATHLEN + 1];
static struct path ret;
int size = 0;
char sep;
int toUTF8 = 0;
-
+
data = *cpath;
afp_errno = AFPERR_NOOBJ;
memset(&ret, 0, sizeof(ret));
}
}
if ( !extend ) {
- if (dir->d_did == DIRDID_ROOT_PARENT) {
- /*
- root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
- must check against the volume name.
- */
- if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
- cdir = vol->v_dir;
- else
- cdir = NULL;
+ 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, strlen(path), (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 {
- cdir = dirsearch_byname(vol, dir, ret.u_name);
+noucsfallback:
+ if (dir->d_did == DIRDID_ROOT_PARENT) {
+ /*
+ root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
+ must check against the volume name.
+ */
+ if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
+ cdir = vol->v_dir;
+ else
+ cdir = NULL;
+ }
+ else {
+ cdir = dirsearch_byname(vol, dir, ret.u_name);
+ }
+ }
+
+ 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 ) {
if ( cdir == NULL ) {
- if ( len > 0) {
+ if ( len > 0 || !ret.u_name ) {
return NULL;
}
/*
* Move curdir to dir, with a possible chdir()
*/
-int movecwd( vol, dir)
-const struct vol *vol;
-struct dir *dir;
+int movecwd(const struct vol *vol, struct dir *dir)
{
char path[MAXPATHLEN + 1];
struct dir *d;
* is our cached offspring count valid?
*/
-int diroffcnt(struct dir *dir, struct stat *st)
+static int diroffcnt(struct dir *dir, struct stat *st)
{
return st->st_ctime == dir->ctime;
}
int getdirparams(const struct vol *vol,
u_int16_t bitmap, struct path *s_path,
struct dir *dir,
- char *buf, int *buflen )
+ char *buf, size_t *buflen )
{
struct maccess ma;
struct adouble ad;
}
/* ----------------------------- */
-int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj *obj;
-char *ibuf, *rbuf _U_;
-int ibuflen _U_, *rbuflen;
+int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
{
struct vol *vol;
struct dir *dir;
break;
case DIRPBIT_FINFO :
if (isad) {
+ /* Fixes #2802236 */
+ u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
+ *fflags &= htons(~FINDERINFO_ISHARED);
+ /* #2802236 end */
if ( dir->d_did == DIRDID_ROOT ) {
/*
* Alright, we admit it, this is *really* sick!
return err;
}
-
-int afp_syncdir(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj *obj _U_;
-char *ibuf, *rbuf _U_;
-int ibuflen _U_, *rbuflen;
+int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
{
+#ifdef HAVE_DIRFD
DIR *dp;
+#endif
int dfd;
struct vol *vol;
struct dir *dir;
memcpy( &did, ibuf, sizeof( did ));
ibuf += sizeof( did );
- if (NULL == ( dir = dirlookup( vol, did )) ) {
- return afp_errno; /* was AFPERR_NOOBJ */
- }
- if (NULL == ( dp = opendir( "." )) ) {
- switch( errno ) {
- case ENOENT :
- return( AFPERR_NOOBJ );
- case EACCES :
+ /*
+ * Here's the deal:
+ * if it's CNID 2 our only choice to meet the specs is call sync.
+ * For any other CNID just sync that dir. To my knowledge the
+ * intended use of FPSyncDir is to sync the volume so all we're
+ * ever going to see here is probably CNID 2. Anyway, we' prepared.
+ */
+
+ if ( ntohl(did) == 2 ) {
+ sync();
+ } else {
+ if (NULL == ( dir = dirlookup( vol, did )) ) {
+ return afp_errno; /* was AFPERR_NOOBJ */
+ }
+
+ if (movecwd( vol, dir ) < 0 )
+ return ( AFPERR_NOOBJ );
+
+ /*
+ * Assuming only OSens that have dirfd also may require fsyncing directories
+ * in order to flush metadata e.g. Linux.
+ */
+
+#ifdef HAVE_DIRFD
+ if (NULL == ( dp = opendir( "." )) ) {
+ switch( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EACCES :
return( AFPERR_ACCESS );
- default :
- return( AFPERR_PARAM );
+ default :
+ return( AFPERR_PARAM );
+ }
+ }
+
+ LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
+
+ dfd = dirfd( dp );
+ if ( fsync ( dfd ) < 0 )
+ LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
+ dir->d_u_name, strerror(errno) );
+ closedir(dp); /* closes dfd too */
+#endif
+
+ if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
+ switch( errno ) {
+ case ENOENT:
+ return( AFPERR_NOOBJ );
+ case EACCES:
+ return( AFPERR_ACCESS );
+ default:
+ return( AFPERR_PARAM );
+ }
}
+
+ LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
+ vol->ad_path(".", ADFLAGS_DIR) );
+
+ if ( fsync(dfd) < 0 )
+ LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
+ vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
+ close(dfd);
}
- dfd = dirfd( dp );
- if ( fsync ( dfd ) < 0 ) {
- LOG(log_error, logtype_afpd, "syncdir(%s): ddir(%d) %s", dir->d_u_name, dfd, strerror(errno) );
- }
- closedir(dp);
-
return ( AFP_OK );
}
-int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj *obj;
-char *ibuf, *rbuf;
-int ibuflen _U_, *rbuflen;
+int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
{
struct adouble ad;
struct vol *vol;
return AFPERR_EXIST;
upath = s_path->u_name;
- if (0 != (err = check_name(vol, upath))) {
- return err;
- }
if (AFP_OK != (err = netatalk_mkdir( upath))) {
return err;
* newparent curdir
*
*/
-int renamedir(vol, src, dst, dir, newparent, newname)
-const struct vol *vol;
-char *src, *dst, *newname;
-struct dir *dir, *newparent;
+int renamedir(const struct vol *vol, char *src, char *dst,
+ struct dir *dir,
+ struct dir *newparent,
+ char *newname)
{
struct adouble ad;
struct dir *parent;
}
}
- vol->vfs->rf_renamedir(vol, src, dst);
+ vol->vfs->vfs_renamedir(vol, src, dst);
len = strlen( newname );
/* rename() succeeded so we need to update our tree even if we can't open
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, strlen(dir->d_m_name), (char**)&dir->d_m_name_ucs2))
+ dir->d_m_name_ucs2 = NULL;
+
if (( parent = dir->d_parent ) == NULL ) {
return( AFP_OK );
}
}
/* delete an empty directory */
-int deletecurdir( vol)
-const struct vol *vol;
+int deletecurdir(const struct vol *vol)
{
struct dirent *de;
struct stat st;
return AFPERR_OLOCK;
}
}
- err = vol->vfs->rf_deletecurdir(vol);
+ err = vol->vfs->vfs_deletecurdir(vol);
if (err) {
return err;
}
return err;
}
-int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj *obj;
-char *ibuf, *rbuf;
-int ibuflen _U_, *rbuflen;
+int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
{
struct passwd *pw;
struct group *gr;
u_int32_t id;
int len, sfunc;
int utf8 = 0;
- uuidtype_t type;
-
+
ibuf++;
sfunc = (unsigned char) *ibuf++;
*rbuflen = 0;
return( AFP_OK );
}
-int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj *obj _U_;
-char *ibuf, *rbuf;
-int ibuflen _U_, *rbuflen;
+int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
{
struct passwd *pw;
struct group *gr;
/* ------------------------------------
variable DID support
*/
-int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj *obj _U_;
-char *ibuf _U_, *rbuf _U_;
-int ibuflen _U_, *rbuflen;
+int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
{
#if 0
struct vol *vol;
/* did creation gets done automatically
* there's a pb again with case but move it to cname
*/
-int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj *obj _U_;
-char *ibuf, *rbuf;
-int ibuflen _U_, *rbuflen;
+int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
{
struct vol *vol;
struct dir *parentdir;