================
* FIX: Fix errors searching volumes
+* NEW: Configurable symlink handling with a new volume option
+ 'followsymlinks'. Setting the option causes afpd to follow
+ symlinks on the server side.
Changes in 2.2.4
================
}
#endif
- EC_ZERO_LOG_ERR(lstat(path, &st), AFPERR_PARAM);
+ EC_ZERO_LOG_ERR(ostat(path, &st, vol_syml_opt(vol)), AFPERR_PARAM);
is_dir = !strcmp(path, ".");
memset( addr_filename_buff, 0, 256 );
- if(lstat(addr_filename, &cap_st) == 0) {
+ if (stat(addr_filename, &cap_st) == 0) {
if( S_ISREG(cap_st.st_mode) ) {
int len;
int capfd = open( addr_filename, O_RDONLY );
isdir = S_ISDIR(path->st.st_mode);
- if (!isdir && (of = of_findname(path))) {
+ if (!isdir && (of = of_findname(vol, path))) {
adp = of->of_ad;
} else {
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
memset(&path, 0, sizeof(path));
path.u_name = entry->d_name;
- if (of_stat(&path) != 0) {
+ if (of_stat(vol, &path) != 0) {
switch (errno) {
case EACCES:
case ELOOP:
path.u_name = name;
path.m_name = utompath(vol, name, cnid, utf8_encoding());
- if (of_stat(&path) != 0) {
+ if (of_stat(vol, &path) != 0) {
switch (errno) {
case EACCES:
case ELOOP:
}
isadir = path_isadir(path);
- if (isadir || !(of = of_findname(path))) {
+ if (isadir || !(of = of_findname(vol, path))) {
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
adp = &ad;
} else
upath = path->u_name;
isadir = path_isadir(path);
- if (isadir || !(of = of_findname(path))) {
+ if (isadir || !(of = of_findname(vol, path))) {
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
adp = &ad;
} else
}
isadir = path_isadir(path);
- if (isadir || !(of = of_findname(path))) {
+ if (isadir || !(of = of_findname(vol, path))) {
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
adp = &ad;
} else
}
/* ------------------- */
-static int deletedir(int dirfd, char *dir)
+static int deletedir(const struct vol *vol, int dirfd, char *dir)
{
char path[MAXPATHLEN + 1];
DIR *dp;
break;
}
strcpy(path + len, de->d_name);
- if (lstatat(dirfd, path, &st)) {
+ if (ostatat(dirfd, path, &st, vol_syml_opt(vol))) {
continue;
}
if (S_ISDIR(st.st_mode)) {
- err = deletedir(dirfd, path);
+ err = deletedir(vol, dirfd, path);
} else {
err = netatalk_unlinkat(dirfd, path);
}
}
strcpy(spath + slen, de->d_name);
- if (lstatat(dirfd, spath, &st) == 0) {
+ if (ostatat(dirfd, spath, &st, vol_syml_opt(vol)) == 0) {
if (strlen(de->d_name) > drem) {
err = AFPERR_PARAM;
break;
}
/* keep the same time stamp. */
- if (lstatat(dirfd, src, &st) == 0) {
+ if (ostatat(dirfd, src, &st, vol_syml_opt(vol)) == 0) {
ut.actime = ut.modtime = st.st_mtime;
utime(dst, &ut);
}
LOG(log_debug, logtype_afpd, "dirlookup(did: %u): stating \"%s\"",
ntohl(did), cfrombstr(fullpath));
- if (lstat(cfrombstr(fullpath), &st) != 0) { /* 5a */
+ if (ostat(cfrombstr(fullpath), &st, vol_syml_opt(vol)) != 0) { /* 5a */
switch (errno) {
case ENOENT:
afp_errno = AFPERR_NOOBJ;
if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
path->u_name = cname;
path->d_dir = NULL;
- if (of_stat( path ) == 0 ) {
+ if (of_stat(vol, path ) == 0 ) {
return 0;
}
/* something changed, we cannot stat ... */
strlcpy(cname, de->d_name, sizeof(cname));
path->u_name = cname;
path->d_dir = NULL;
- if (of_stat( path ) == 0 ) {
+ if (of_stat(vol, 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;
* and thus call continue which should terminate the while loop because
* len = 0. Ok?
*/
- if (of_stat(&ret) != 0) { /* 9 */
+ if (of_stat(vol, &ret) != 0) { /* 9 */
/*
* ret.u_name doesn't exist, might be afp_createfile|dir
* that means it should have been the last part
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 ) {
+ if ((ret = ochdir(cfrombstr(dir->d_fullpath), vol_syml_opt(vol))) != 0 ) {
LOG(log_debug, logtype_afpd, "movecwd(\"%s\"): %s",
cfrombstr(dir->d_fullpath), strerror(errno));
if (ret == 1) {
return err;
}
- if (of_stat(s_path) < 0) {
+ if (of_stat(vol, s_path) < 0) {
return AFPERR_MISC;
}
/* this needs to copy and delete. bleah. that means we have
* to deal with entire directory hierarchies. */
if ((err = copydir(vol, dirfd, src, dst)) < 0) {
- deletedir(-1, dst);
+ deletedir(vol, -1, dst);
return err;
}
- if ((err = deletedir(dirfd, src)) < 0)
+ if ((err = deletedir(vol, dirfd, src)) < 0)
return err;
break;
default :
return path_error(path, AFPERR_NOOBJ);
}
- if ( !path->st_valid && of_stat(path ) < 0 ) {
+ if ( !path->st_valid && of_stat(vol, path) < 0 ) {
return( AFPERR_NOOBJ );
}
if ( path->st_errno ) {
if ( sindex == 1 || curdir->d_did != sd.sd_did || vid != sd.sd_vid ) {
sd.sd_last = sd.sd_buf;
/* if dir was in the cache we don't have the inode */
- if (( !o_path->st_valid && lstat( ".", &o_path->st ) < 0 ) ||
+ if (( !o_path->st_valid && ostat(".", &o_path->st, vol_syml_opt(vol)) < 0 ) ||
(ret = for_each_dirent(vol, ".", enumerate_loop, (void *)&sd)) < 0)
{
LOG(log_error, logtype_afpd, "enumerate: loop error: %s (%d)", strerror(errno), errno);
}
memset(&s_path, 0, sizeof(s_path));
s_path.u_name = sd.sd_last;
- if (of_stat( &s_path) < 0 ) {
+ if (of_stat(vol, &s_path) < 0 ) {
/*
* Somebody else plays with the dir, well it can be us with
* "Empty Trash..."
}
}
- if (islink){
+ if (islink && !vol_syml_opt(vol)) {
u_int16_t linkflag;
memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
linkflag |= htons(FINDERINFO_ISALIAS);
upath = s_path->u_name;
/* if upath is deleted we already in trouble anyway */
- if ((of = of_findname(s_path))) {
+ if ((of = of_findname(vol, s_path))) {
adp = of->of_ad;
} else {
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
u_int16_t bitmap = f_bitmap;
u_int32_t cdate,bdate;
u_char finder_buf[32];
+ int fp;
+ ssize_t len;
+ char symbuf[MAXPATHLEN+1];
#ifdef DEBUG
LOG(log_debug9, logtype_afpd, "begin setfilparams:");
break;
case FILPBIT_FINFO :
change_mdate = 1;
- memcpy(finder_buf, buf, 32 );
- if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
- // SLFINFO
- int fp;
- ssize_t len;
- int erc=1;
- char buf[PATH_MAX+1];
- if ((fp=open(path->u_name,O_RDONLY))>=0){
- if ((len=read(fp,buf,PATH_MAX+1))){
- if (unlink(path->u_name)==0){
- buf[len]=0;
- erc = symlink(buf, path->u_name);
- if (!erc)
- of_stat(path);
- }
- }
- close(fp);
+ if (memcmp(buf,"slnkrhap",8) == 0
+ && !(S_ISLNK(path->st.st_mode))
+ && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
+ /* request to turn this into a symlink */
+ if ((fp = open(path->u_name, O_RDONLY)) == -1) {
+ err = AFPERR_MISC;
+ goto setfilparam_done;
}
- if (erc!=0){
- err=AFPERR_BITMAP;
+ len = read(fp, symbuf, MAXPATHLEN);
+ close(fp);
+ if (!(len > 0)) {
+ err = AFPERR_MISC;
goto setfilparam_done;
}
+ if (unlink(path->u_name) != 0) {
+ err = AFPERR_MISC;
+ goto setfilparam_done;
+ }
+ symbuf[len] = 0;
+ if (symlink(symbuf, path->u_name) != 0) {
+ err = AFPERR_MISC;
+ goto setfilparam_done;
+ }
+ of_stat(vol, path);
}
+ memcpy(finder_buf, buf, 32 );
buf += 32;
break;
case FILPBIT_UNIXPR :
cnid_t did = param->did;
cnid_t aint;
- if ( lstat(de->d_name, &path.st)<0 )
+ if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
return 0;
/* update or add to cnid */
}
/* FIXME use of_statdir ? */
- if (lstat(name, &st)) {
+ if (ostat(name, &st, vol_syml_opt(vol))) {
return -1;
}
memset(&path, 0, sizeof(path));
path.u_name = upath;
- if ( of_stat(&path) < 0 ) {
+ if (of_stat(vol, &path) < 0 ) {
#ifdef ESTALE
/* with nfs and our working directory is deleted */
if (errno == ESTALE) {
}
err = AFP_OK;
- if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
+ if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
switch (errno) {
case EACCES:
case EPERM:
}
/* ------------------------------ */
-static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
+static struct adouble *find_adouble(const struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
{
int ret;
return NULL;
}
- if ((*of = of_findname(path))) {
+ if ((*of = of_findname(vol, path))) {
/* reuse struct adouble so it won't break locks */
adp = (*of)->of_ad;
}
}
ad_init(&ads, vol->v_adouble, vol->v_ad_options);
- if (!(adsp = find_adouble( path, &s_of, &ads))) {
+ if (!(adsp = find_adouble(vol, path, &s_of, &ads))) {
return afp_errno;
}
}
ad_init(&add, vol->v_adouble, vol->v_ad_options);
- if (!(addp = find_adouble( path, &d_of, &add))) {
+ if (!(addp = find_adouble(vol, path, &d_of, &add))) {
err = afp_errno;
goto err_exchangefile;
}
if (did)
cnid_delete(vol->v_cdb, did);
- if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
+ if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
||
- (sid && ( (crossdev && lstat(p, &destst) < 0) ||
+ (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
) {
switch (errno) {
if ( uid != sb.st_uid )
{
seteuid(0);
- if (lchown(upath, sb.st_uid, sb.st_gid) < 0)
+ if (ochown(upath, sb.st_uid, sb.st_gid, vol_syml_opt(vol)) < 0)
{
LOG(log_error, logtype_afpd,
"matchfile2dirperms(%s): Error changing owner/gid: %s",
upath, strerror(errno));
ret = AFPERR_ACCESS;
}
- else if ((!S_ISLNK(st->st_mode)) && (chmod_acl(upath,(st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0))
- {
+ else if (ochmod(upath,
+ (st.st_mode & ~default_options.umask) | S_IRGRP | S_IROTH,
+ &sb,
+ vol_syml_opt(vol) | O_NETATALK_ACL) < 0) {
LOG(log_error, logtype_afpd,
"matchfile2dirperms(%s): Error adding file read permissions: %s",
upath, strerror(errno));
ret = AFPERR_ACCESS;
}
- else if (lchown(adpath, sb.st_uid, sb.st_gid) < 0)
+ else if (ochown(adpath, sb.st_uid, sb.st_gid, vol_syml_opt(vol)) < 0)
{
LOG(log_error, logtype_afpd,
"matchfile2dirperms(%s): Error changing AppleDouble owner/gid: %s",
adpath, strerror(errno));
ret = AFPERR_ACCESS;
}
- else if (chmod_acl(adpath, (st.st_mode&~default_options.umask)| S_IRGRP| S_IROTH) < 0)
+ else if (ochmod(adpath,
+ (st.st_mode & ~default_options.umask) | S_IRGRP| S_IROTH,
+ &st,
+ vol_syml_opt(vol) | O_NETATALK_ACL) < 0)
{
LOG(log_error, logtype_afpd,
"matchfile2dirperms(%s): Error adding AD file read permissions: %s",
if ( !isdir ) {
path.st_valid = 1;
path.st_errno = errno;
- if (of_findname(&path)) {
+ if (of_findname(vol, &path)) {
rc = AFPERR_EXIST; /* was AFPERR_BUSY; */
} else {
rc = renamefile(vol, sdir_fd, oldunixname, upath, newname, adp );
fce_register_delete_dir(cfrombstr(dname));
bdestroy(dname);
}
- } else if (of_findname(s_path)) {
+ } else if (of_findname(vol, s_path)) {
rc = AFPERR_BUSY;
} else {
/* it's a file st_valid should always be true
if (!isdir && !vol_unix_priv(vol)) {
int admode = ad_mode("", 0777) | vol->v_fperm;
- setfilmode(upath, admode, NULL, vol->v_umask);
- vol->vfs->vfs_setfilmode(vol, upath, admode, NULL);
+ setfilmode(vol, upath, admode, path->st_valid ? &path->st : NULL);
+ vol->vfs->vfs_setfilmode(vol, upath, admode, path->st_valid ? &path->st : NULL);
}
setvoltime(obj, vol );
}
ad_open so that we can keep file locks together.
FIXME: add the fork we are opening?
*/
- if ((opened = of_findname(s_path))) {
+ if ((opened = of_findname(vol, s_path))) {
adsame = opened->of_ad;
}
struct stat *);
extern void of_dealloc (struct ofork *);
extern struct ofork *of_find (const u_int16_t);
-extern struct ofork *of_findname (struct path *);
+extern struct ofork *of_findname (const struct vol *vol, struct path *);
extern int of_rename (const struct vol *,
struct ofork *,
struct dir *, const char *,
struct dir *, const char *);
extern int of_flush (const struct vol *);
extern void of_pforkdesc (FILE *);
-extern int of_stat (struct path *);
+extern int of_stat (const struct vol *vol, struct path *);
extern int of_statdir (struct vol *vol, struct path *);
extern int of_closefork (struct ofork *ofork);
extern void of_closevol (const struct vol *vol);
}
/* -------------------------- */
-int of_stat(struct path *path)
+int of_stat(const struct vol *vol, struct path *path)
{
int ret;
path->st_errno = 0;
path->st_valid = 1;
- if ((ret = lstat(path->u_name, &path->st)) < 0) {
+ if ((ret = ostat(path->u_name, &path->st, vol_syml_opt(vol))) < 0) {
LOG(log_debug, logtype_afpd, "of_stat('%s/%s': %s)",
cfrombstr(curdir->d_fullpath), path->u_name, strerror(errno));
path->st_errno = errno;
if (*path->m_name) {
/* not curdir */
- return of_stat (path);
+ return of_stat(vol, path);
}
path->st_errno = 0;
path->st_valid = 1;
LOG(log_debug, logtype_afpd, "of_statdir: stating: '%s'", pathname);
- if (!(ret = lstat(pathname, &path->st)))
+ if (!(ret = ostat(pathname, &path->st, vol_syml_opt(vol))))
return 0;
path->st_errno = errno;
return -1;
path->st_errno = 0;
- if ((ret = lstat(cfrombstr(path->d_dir->d_u_name), &path->st)) < 0)
+ if ((ret = ostat(cfrombstr(path->d_dir->d_u_name), &path->st, vol_syml_opt(vol))) < 0)
path->st_errno = errno;
}
}
/* -------------------------- */
-struct ofork *of_findname(struct path *path)
+struct ofork *of_findname(const struct vol *vol, struct path *path)
{
struct ofork *of;
struct file_key key;
if (!path->st_valid) {
- of_stat(path);
+ of_stat(vol, path);
}
if (path->st_errno)
struct ofork *of;
struct adouble *adp;
- if ((of = of_findname(path))) {
+ if ((of = of_findname(vol, path))) {
adp = of->of_ad;
} else {
ad_init(ad, vol->v_adouble, vol->v_ad_options);
#include <atalk/logger.h>
#include <atalk/afp.h>
#include <atalk/compat.h>
+#include <atalk/util.h>
#include "auth.h"
#include "volume.h"
dev_t devno;
static struct mnttab mnt;
- if ( lstat( file, &sb ) < 0 ) {
+ if (stat(file, &sb) < 0) {
return( NULL );
}
devno = sb.st_dev;
while ( getmntent( mtab, &mnt ) == 0 ) {
/* local fs */
- if ( (lstat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
+ if ( (stat( mnt.mnt_special, &sb ) == 0) && (devno == sb.st_rdev)) {
fclose( mtab );
return mnt.mnt_mountp;
}
/* check for nfs. we probably should use
* strcmp(mnt.mnt_fstype, MNTTYPE_NFS), but that's not as fast. */
- if ((lstat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
+ if ((stat(mnt.mnt_mountp, &sb) == 0) && (devno == sb.st_dev) &&
strchr(mnt.mnt_special, ':')) {
*nfs = 1;
fclose( mtab );
struct mntent *mnt;
int found=0;
- if ( lstat( file, &sb ) < 0 ) {
+ if (stat(file, &sb) < 0 ) {
return( NULL );
}
devno = sb.st_dev;
while (( mnt = getmntent( mtab )) != NULL ) {
/* check for local fs */
- if ( (lstat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
+ if ( (stat( mnt->mnt_fsname, &sb ) == 0) && devno == sb.st_rdev) {
found = 1;
break;
}
/* check for an nfs mount entry. the alternative is to use
* strcmp(mnt->mnt_type, MNTTYPE_NFS) instead of the strchr. */
- if ((lstat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
+ if ((stat(mnt->mnt_dir, &sb) == 0) && (devno == sb.st_dev) &&
strchr(mnt->mnt_fsname, ':')) {
*nfs = 1;
found = 1;
ma->ma_user = ma->ma_owner = ma->ma_world = ma->ma_group = 0;
if (!st) {
- if (lstat(path, &sb) != 0)
+ if (ostat(path, &sb, vol_syml_opt(vol)) != 0)
return;
st = &sb;
}
#define EXEC_MODE (S_IXGRP | S_IXUSR | S_IXOTH)
+/* Using chmod_acl() instead of ochmod is ok here */
int setdeskmode(const mode_t mode)
{
char wd[ MAXPATHLEN + 1];
*m = '\0';
strcat( modbuf, subp->d_name );
/* XXX: need to preserve special modes */
- if (lstat(modbuf, &st) < 0) {
+ if (stat(modbuf, &st) < 0) {
LOG(log_error, logtype_afpd, "setdeskmode: stat %s: %s",fullpathname(modbuf), strerror(errno) );
continue;
}
int setfilunixmode (const struct vol *vol, struct path* path, mode_t mode)
{
if (!path->st_valid) {
- of_stat(path);
+ of_stat(vol, path);
}
if (path->st_errno) {
mode |= vol->v_fperm;
- if (setfilmode( path->u_name, mode, &path->st, vol->v_umask) < 0)
+ if (setfilmode(vol, path->u_name, mode, &path->st) < 0)
return -1;
/* we need to set write perm if read set for resource fork */
return vol->vfs->vfs_setfilmode(vol, path->u_name, mode, &path->st);
if ( *dirp->d_name == '.' && (!osx || dirp->d_name[1] != '_')) {
continue;
}
- if ( lstat( dirp->d_name, &st ) < 0 ) {
+ if (ostat(dirp->d_name, &st, vol_syml_opt(vol)) < 0 ) {
LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s",dirp->d_name, strerror(errno) );
continue;
}
if (!S_ISDIR(st.st_mode)) {
int setmode = (osx && *dirp->d_name == '.')?hf_mode:mode;
- if (setfilmode(dirp->d_name, setmode, &st, vol->v_umask) < 0) {
+ if (setfilmode(vol, dirp->d_name, setmode, &st) < 0) {
closedir( dir );
LOG(log_error, logtype_afpd, "setdirmode: chmod %s: %s",dirp->d_name, strerror(errno) );
return -1;
{
if (!path->st_valid) {
- of_stat(path);
+ of_stat(vol, path);
}
if (path->st_errno) {
return -1;
}
- if ( lchown( path->u_name, uid, gid ) < 0 && errno != EPERM ) {
+ if (ochown( path->u_name, uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) {
LOG(log_debug, logtype_afpd, "setfilowner: chown %d/%d %s: %s",
uid, gid, path->u_name, strerror(errno) );
return -1;
if ( *dirp->d_name == '.' && (!osx || dirp->d_name[1] != '_')) {
continue;
}
- if ( lstat( dirp->d_name, &st ) < 0 ) {
+ if (ostat(dirp->d_name, &st, vol_syml_opt(vol)) < 0 ) {
LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s",
fullpathname(dirp->d_name), strerror(errno) );
continue;
}
if (( st.st_mode & S_IFMT ) == S_IFREG ) {
- if ( lchown( dirp->d_name, uid, gid ) < 0 && errno != EPERM ) {
+ if (ochown(dirp->d_name, uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) {
LOG(log_debug, logtype_afpd, "setdirowner: chown %s: %s",
fullpathname(dirp->d_name), strerror(errno) );
/* return ( -1 ); Sometimes this is okay */
return -1;
}
- if ( lstat( ".", &st ) < 0 ) {
+ if (ostat(".", &st, vol_syml_opt(vol)) < 0 ) {
return( -1 );
}
- if ( gid && gid != st.st_gid && lchown( ".", uid, gid ) < 0 && errno != EPERM ) {
+ if ( gid && gid != st.st_gid && ochown(".", uid, gid, vol_syml_opt(vol)) < 0 && errno != EPERM ) {
LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
uid, gid, fullpathname("."), strerror(errno) );
}
return -1;
}
- if (lstat(path, &sbuf) < 0) {
+ if (stat(path, &sbuf) < 0) {
LOG(log_error, logtype_afpd, "cannot chown() file [%s] (uid = %d): %s", path, uid, strerror(errno));
return -1;
}
options[VOLOPT_FLAGS].i_value |= AFPVOL_NONETIDS;
else if (strcasecmp(p, "noacls") == 0)
options[VOLOPT_FLAGS].i_value &= ~AFPVOL_ACLS;
+ else if (strcasecmp(p, "followsymlinks") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_FOLLOWSYM;
p = strtok(NULL, ",");
}
volume->v_ad_options |= ADVOL_INVDOTS;
if ((volume->v_flags & AFPVOL_NOADOUBLE))
volume->v_ad_options |= ADVOL_NOADOUBLE;
+ if ((volume->v_flags & AFPVOL_FOLLOWSYM))
+ volume->v_ad_options |= ADVOL_FOLLO_SYML;
if (options[VOLOPT_PASSWORD].c_value)
volume->v_password = strdup(options[VOLOPT_PASSWORD].c_value);
#ifdef HAVE_ACLS
+#define O_NETATALK_ACL (O_NOFOLLOW << 1)
+
#ifdef HAVE_SOLARIS_ACLS
#include <sys/acl.h>
#else /* HAVE_ACLS=no */
+#define O_NETATALK_ACL
#define chmod_acl chmod
#endif /* HAVE_ACLS */
#define ADVOL_UNIXPRIV (1 << 2) /* adouble unix priv */
#define ADVOL_INVDOTS (1 << 3) /* dot files (.DS_Store) are invisible) */
#define ADVOL_NOADOUBLE (1 << 4)
+#define ADVOL_FOLLO_SYML (1 << 5)
+
/* lock flags */
#define ADLOCK_CLR (0)
#define ad_get_HF_flags(ad) ((ad)->ad_resource_fork.adf_flags)
#define ad_get_MD_flags(ad) ((ad)->ad_md->adf_flags)
+#define ad_get_syml_opt(ad) (((ad)->ad_options & ADVOL_FOLLO_SYML) ? 0 : O_NOFOLLOW)
+
/* ad_flush.c */
extern int ad_rebuild_adouble_header (struct adouble *);
extern int ad_rebuild_sfm_header (struct adouble *);
extern int netatalk_unlink(const char *name);
extern int netatalk_unlinkat(int dirfd, const char *name);
extern int statat(int dirfd, const char *path, struct stat *st);
-extern int lstatat(int dirfd, const char *path, struct stat *st);
extern DIR *opendirat(int dirfd, const char *path);
/* rmdir ENOENT not an error */
extern int netatalk_rmdir(int dirfd, const char *name);
extern int netatalk_rmdir_all_errors(int dirfd, const char *name);
-extern int setfilmode(const char *, mode_t, struct stat *, mode_t);
+extern int setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st);
extern int dir_rx_set(mode_t mode);
extern int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask);
extern int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath);
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <poll.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+
#include <netatalk/at.h>
#include <atalk/unicode.h>
extern const char *getcwdpath(void);
extern const char *fullpathname(const char *);
extern char *stripped_slashes_basename(char *p);
-extern int lchdir(const char *dir);
extern void randombytes(void *buf, int n);
extern int daemonize(int nochdir, int noclose);
+extern int ochdir(const char *dir, int options);
+extern int ostat(const char *path, struct stat *buf, int options);
+extern int ostatat(int dirfd, const char *path, struct stat *st, int options);
+extern int ochown(const char *path, uid_t owner, gid_t group, int options);
+extern int ochmod(const char *path, mode_t mode, const struct stat *st, int options);
+
/******************************************************************
* cnid.c
*****************************************************************/
#define AFPVOL_ACLS (1 << 24) /* Volume supports ACLS */
#define AFPVOL_SEARCHDB (1 << 25) /* Use fast CNID db search instead of filesystem */
#define AFPVOL_NONETIDS (1 << 26) /* signal the client it shall do privelege mapping */
+#define AFPVOL_FOLLOWSYM (1 << 27) /* follow symlinks on the server, default is not to */
/* Extended Attributes vfs indirection */
#define AFPVOL_EA_NONE 0 /* No EAs */
#define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
#define vol_unix_priv(vol) (afp_version >= 30 && ((vol)->v_flags & AFPVOL_UNIX_PRIV))
#define vol_inv_dots(vol) (((vol)->v_flags & AFPVOL_INV_DOTS) ? 1 : 0)
-
+#define vol_syml_opt(vol) (((vol)->v_flags & AFPVOL_FOLLOWSYM) ? 0 : O_NOFOLLOW)
#endif
noinst_LTLIBRARIES = libacl.la
libacl_la_SOURCES = cache.c unix.c uuid.c
libacl_la_LDFLAGS =
+libacl_la_LIBADD = @ACL_LIBS@
if HAVE_LDAP
libacl_la_SOURCES += ldap.c ldap_config.c
if (!p) {
return -1;
}
-//FIXME!
- return lstat( p, stbuf );
+
+ return stat(p, stbuf);
}
/* ----------------
if (default_uid != (uid_t)-1) {
/* we are root (admin) */
id = (default_uid)?default_uid:stbuf->st_uid;
- ret = lchown( path, id, stbuf->st_gid );
+ ret = chown(path, id, stbuf->st_gid);
}
#endif
return ret;
}
}
- ad->ad_data_fork.adf_fd =open( path, hoflags | O_NOFOLLOW, admode );
+ ad->ad_data_fork.adf_fd = open(path, hoflags | ad_get_syml_opt(ad), admode);
if (ad->ad_data_fork.adf_fd == -1) {
if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
hoflags = oflags;
- ad->ad_data_fork.adf_fd = open( path, hoflags | O_NOFOLLOW, admode );
+ ad->ad_data_fork.adf_fd = open( path, hoflags | ad_get_syml_opt(ad), admode );
}
if (ad->ad_data_fork.adf_fd == -1 && errno == OPEN_NOFOLLOW_ERRNO) {
int lsz;
if (!(adflags & ADFLAGS_RDONLY)) {
hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
}
- ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 0 );
+ ad->ad_md->adf_fd = open(ad_p, hoflags | ad_get_syml_opt(ad), 0);
if (ad->ad_md->adf_fd < 0 ) {
if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
hoflags = oflags & ~(O_CREAT | O_EXCL);
- ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 0 );
+ ad->ad_md->adf_fd = open(ad_p, hoflags | ad_get_syml_opt(ad), 0);
}
}
}
admode = ad_hf_mode(admode);
if ((errno == ENOENT) && (ad->ad_flags != AD_VERSION2_OSX)) {
- if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
+ if (ad->ad_ops->ad_mkrf(ad_p) < 0) {
return ad_error(ad, adflags);
}
admode = mode;
memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
}
- if (lstat(path, &st) < 0) {
+ if (ostat(path, &st, ad_get_syml_opt(ad)) < 0) {
return -1;
}
return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
}
+/*********************************************************************************
+ * chdir(), chmod(), chown(), stat() wrappers taking an additional option.
+ * Currently the only used options are O_NOFOLLOW, used to switch between symlink
+ * behaviour, and O_NETATALK_ACL for ochmod() indicating chmod_acl() shall be
+ * called which does special ACL handling depending on the filesytem
+ *********************************************************************************/
+
+int ostat(const char *path, struct stat *buf, int options)
+{
+ if (options & O_NOFOLLOW)
+ return lstat(path, buf);
+ else
+ return stat(path, buf);
+}
+
+int ochown(const char *path, uid_t owner, gid_t group, int options)
+{
+ if (options & O_NOFOLLOW)
+ return lchown(path, owner, group);
+ else
+ return chown(path, owner, group);
+}
+
+/*!
+ * chmod() wrapper for symlink and ACL handling
+ *
+ * @param path (r) path
+ * @param mode (r) requested mode
+ * @param sb (r) stat() of path or NULL
+ * @param option (r) O_NOFOLLOW | O_NETATALK_ACL
+ *
+ * Options description:
+ * O_NOFOLLOW: don't chmod() symlinks, do nothing, return 0
+ * O_NETATALK_ACL: call chmod_acl() instead of chmod()
+ */
+int ochmod(const char *path, mode_t mode, const struct stat *st, int options)
+{
+ struct stat sb;
+
+ if (!st) {
+ if (lstat(path, &sb) != 0)
+ return -1;
+ st = &sb;
+ }
+
+ if (options & O_NOFOLLOW)
+ if (S_ISLNK(st->st_mode))
+ return 0;
+
+ if (options & O_NETATALK_ACL) {
+ return chmod_acl(path, mode);
+ } else {
+ return chmod(path, mode);
+ }
+}
+
+/*
+ * @brief ostat/fsstatat multiplexer
+ *
+ * ostatat mulitplexes ostat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
+ *
+ * @param dirfd (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
+ * @param path (r) pathname
+ * @param st (rw) pointer to struct stat
+ */
+int ostatat(int dirfd, const char *path, struct stat *st, int options)
+{
+#ifdef HAVE_ATFUNCS
+ if (dirfd == -1)
+ dirfd = AT_FDCWD;
+ return fstatat(dirfd, path, st, (options & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
+#else
+ return ostat(path, st, options);
+#endif
+
+ /* DEADC0DE */
+ return -1;
+}
+
/*!
* @brief symlink safe chdir replacement
*
- * Only chdirs to dir if it doesn't contain symlinks.
+ * Only chdirs to dir if it doesn't contain symlinks or if symlink checking
+ * is disabled
*
* @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
*/
-int lchdir(const char *dir)
+int ochdir(const char *dir, int options)
{
char buf[MAXPATHLEN+1];
char cwd[MAXPATHLEN+1];
char *test;
int i;
+ if (!(options & O_NOFOLLOW))
+ return chdir(dir);
+
/*
dir is a canonical path (without "../" "./" "//" )
but may end with a /
if (ea->ea_count == 0) {
/* Check if EA header exists and remove it */
eaname = ea_path(ea, NULL, 0);
- if ((lstatat(ea->dirfd, eaname, &st)) == 0) {
+ if ((statat(ea->dirfd, eaname, &st)) == 0) {
if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) {
LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
eaname, strerror(errno));
}
}
- if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) {
+ if ((ochown(ea_path(&ea, NULL, 0), uid, gid, vol_syml_opt(vol))) != 0) {
switch (errno) {
case EPERM:
case EACCES:
ret = AFPERR_MISC;
goto exit;
}
- if ((lchown(eaname, uid, gid)) != 0) {
+ if ((ochown(eaname, uid, gid, vol_syml_opt(vol))) != 0) {
switch (errno) {
case EPERM:
case EACCES:
}
/* Set mode on EA header file */
- if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+ if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
switch (errno) {
case EPERM:
ret = AFPERR_MISC;
goto exit;
}
- if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
+ if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) {
LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
switch (errno) {
case EPERM:
}
/* Set mode on EA header */
- if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+ if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
switch (errno) {
case EPERM:
ret = AFPERR_MISC;
goto exit;
}
- if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
+ if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 0) {
LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
switch (errno) {
case EPERM:
a dropbox is a folder where w is set but not r eg:
rwx-wx-wx or rwx-wx--
rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
+ using chmod_acl() instead of ochmod is ok here
*/
int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask)
{
}
/* --------------------- */
-int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+int setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st)
{
struct stat sb;
mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO; /* rwx for owner group and other, by default */
st = &sb;
}
- if (S_ISLNK(st->st_mode))
- return 0; /* we don't want to change link permissions */
-
mode |= st->st_mode & ~mask; /* keep other bits from previous mode */
- if ( chmod_acl( name, mode & ~v_umask ) < 0 && errno != EPERM ) {
+ if (ochmod(name, mode & ~vol->v_umask, st, vol_syml_opt(vol) | O_NETATALK_ACL) < 0 && errno != EPERM ) {
return -1;
}
return 0;
return -1;
}
-/*
- * @brief lstat/fsstatat multiplexer
- *
- * lstatat mulitplexes lstat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
- *
- * @param dirfd (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
- * @param path (r) pathname
- * @param st (rw) pointer to struct stat
- */
-int lstatat(int dirfd, const char *path, struct stat *st)
-{
-#ifdef HAVE_ATFUNCS
- if (dirfd == -1)
- dirfd = AT_FDCWD;
- return (fstatat(dirfd, path, st, AT_SYMLINK_NOFOLLOW));
-#else
- return (lstat(path, st));
-#endif
-
- /* DEADC0DE */
- return -1;
-}
-
/*
* @brief opendir wrapper for *at semantics support
*
gid_t gid;
};
-typedef int (*rf_loop)(struct dirent *, char *, void *, int , mode_t );
+typedef int (*rf_loop)(const struct vol *, struct dirent *, char *, void *, int);
/* ----------------------------- */
static int
-for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag, mode_t v_umask)
+for_each_adouble(const char *from, const char *name, rf_loop fn, const struct vol *vol, void *data, int flag)
{
char buf[ MAXPATHLEN + 1];
char *m;
}
strlcat(buf, de->d_name, sizeof(buf));
- if (fn && (ret = fn(de, buf, data, flag, v_umask))) {
+ if (fn && (ret = fn(vol, de, buf, data, flag))) {
closedir(dp);
return ret;
}
}
/* ----------------- */
-static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask)
+static int deletecurdir_adouble_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_)
{
struct stat st;
int err;
/* delete stray .AppleDouble files. this happens to get .Parent files
as well. */
- if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1, vol->v_umask)))
+ if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, vol, NULL, 1)))
return err;
return netatalk_rmdir(-1, ".AppleDouble" );
}
/* ----------------- */
-static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+static int adouble_setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st)
{
- return setfilmode(name, ad_hf_mode(mode), st, v_umask);
+ return setfilmode(vol, name, ad_hf_mode(mode), st);
}
static int RF_setfilmode_adouble(VFS_FUNC_ARGS_SETFILEMODE)
{
- return adouble_setfilmode(vol->ad_path(name, ADFLAGS_HF ), mode, st, vol->v_umask);
+ return adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_HF ), mode, st);
}
/* ----------------- */
return -1;
}
- if (adouble_setfilmode(vol->ad_path(name, ADFLAGS_DIR ), mode, st, vol->v_umask) < 0)
+ if (adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_DIR ), mode, st) < 0)
return -1;
if (!dir_rx_set(mode)) {
}
/* ----------------- */
-static int setdirmode_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask)
+static int setdirmode_adouble_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag)
{
mode_t hf_mode = *(mode_t *)data;
struct stat st;
- if ( stat( name, &st ) < 0 ) {
+ if (ostat(name, &st, vol_syml_opt(vol)) < 0 ) {
if (flag)
return 0;
LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
}
else if (!S_ISDIR(st.st_mode)) {
- if (setfilmode(name, hf_mode , &st, v_umask) < 0) {
+ if (setfilmode(vol, name, hf_mode, &st) < 0) {
/* FIXME what do we do then? */
}
}
return -1;
}
- if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, vol_noadouble(vol), vol->v_umask))
+ if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, vol, &hf_mode, vol_noadouble(vol)))
return -1;
if (!dir_rx_set(mode)) {
}
/* ----------------- */
-static int setdirowner_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_)
+static int setdirowner_adouble_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag _U_)
{
struct perm *owner = data;
adouble_p = ad_dir(vol->ad_path(name, ADFLAGS_DIR ));
- if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, &owner, noadouble, vol->v_umask))
+ if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, vol, &owner, noadouble))
return -1;
/*
if (errno == ENOENT) {
struct adouble ad;
- if (lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */
+ if (ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* source has no ressource fork, */
return 0;
/* We are here because :
/*********************************************************************************
* sfm adouble format
*********************************************************************************/
-static int ads_chown_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_)
+static int ads_chown_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag _U_)
{
struct perm *owner = data;
if (chown( ad_p, uid, gid ) < 0) {
return -1;
}
- return for_each_adouble("chown_ads", ad_p, ads_chown_loop, &owner, 1, vol->v_umask);
+ return for_each_adouble("chown_ads", ad_p, ads_chown_loop, vol, &owner, 1);
}
/* --------------------------------- */
-static int deletecurdir_ads1_loop(struct dirent *de _U_, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
+static int deletecurdir_ads1_loop(const struct vol *vol _U_, struct dirent *de _U_, char *name, void *data _U_, int flag _U_)
{
return netatalk_unlink(name);
}
{
int err;
- if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1, 0)))
+ if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, NULL, 1)))
return err;
/* FIXME
* it's a problem for a nfs mounted folder, there's .nfsxxx around
* for linux the following line solve it.
* but it could fail if rm .nfsxxx create a new .nfsyyy :(
*/
- if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1, 0)))
+ if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, NULL, 1)))
return err;
return netatalk_rmdir(-1, name);
}
-static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
+static int deletecurdir_ads_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_)
{
struct stat st;
int err;
/* delete stray .AppleDouble files. this happens to get .Parent files as well. */
- if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, NULL, 1, 0)))
+ if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, vol, NULL, 1)))
return err;
return netatalk_rmdir(-1, ".AppleDouble" );
struct stat *st;
};
-static int ads_setfilmode_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask)
+static int ads_setfilmode_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag _U_)
{
struct set_mode *param = data;
- return setfilmode(name, param->mode, param->st, v_umask);
+ return setfilmode(vol, name, param->mode, NULL);
}
-static int ads_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+static int ads_setfilmode(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
{
mode_t file_mode = ad_hf_mode(mode);
mode_t dir_mode = file_mode;
/* change folder */
dir_mode |= DIRBITS;
if (dir_rx_set(dir_mode)) {
- if (chmod_acl( name, dir_mode ) < 0)
+ if (ochmod(name, dir_mode, st, vol_syml_opt(vol) | O_NETATALK_ACL) < 0)
return -1;
}
param.st = st;
param.mode = file_mode;
- if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, ¶m, 0, v_umask) < 0)
+ if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, vol, ¶m, 0) < 0)
return -1;
if (!dir_rx_set(dir_mode)) {
- if (chmod_acl( name, dir_mode ) < 0)
+ if (ochmod(name, dir_mode, st, vol_syml_opt(vol) | O_NETATALK_ACL) < 0)
return -1;
}
static int RF_setfilmode_ads(VFS_FUNC_ARGS_SETFILEMODE)
{
- return ads_setfilmode(ad_dir(vol->ad_path(name, ADFLAGS_HF )), mode, st, vol->v_umask);
+ return ads_setfilmode(vol, ad_dir(vol->ad_path(name, ADFLAGS_HF )), mode, st);
}
/* ------------------- */
return -1;
}
- if (ads_setfilmode(ad_dir(vol->ad_path(name, ADFLAGS_DIR)), mode, st, vol->v_umask) < 0)
+ if (ads_setfilmode(vol, ad_dir(vol->ad_path(name, ADFLAGS_DIR)), mode, st) < 0)
return -1;
if (!dir_rx_set(mode)) {
int dropbox;
};
-static int setdirmode_ads_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask)
+static int setdirmode_ads_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag)
{
struct dir_mode *param = data;
int ret = 0; /* 0 ignore error, -1 */
if (dir_rx_set(param->mode)) {
- if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, v_umask) < 0) {
+ if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, vol->v_umask) < 0) {
if (flag) {
return 0;
}
return ret;
}
}
- if (ads_setfilmode(name, param->mode, NULL, v_umask) < 0)
+ if (ads_setfilmode(vol, name, param->mode, NULL) < 0)
return ret;
if (!dir_rx_set(param->mode)) {
- if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, v_umask) < 0) {
+ if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, vol->v_umask) < 0) {
if (flag) {
return 0;
}
return -1;
}
- if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, ¶m, vol_noadouble(vol), vol->v_umask))
+ if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, vol, ¶m, vol_noadouble(vol)))
return -1;
if (!dir_rx_set(mode)) {
}
/* ------------------- */
-static int setdirowner_ads1_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_)
+static int setdirowner_ads1_loop(const struct vol *vol _U_, struct dirent *de _U_, char *name, void *data, int flag _U_)
{
struct perm *owner = data;
return 0;
}
-static int setdirowner_ads_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask _U_)
+static int setdirowner_ads_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag)
{
struct perm *owner = data;
- if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, data, flag, 0) < 0)
+ if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, vol, data, flag) < 0)
return -1;
if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
strlcpy(adouble_p, ad_dir(vol->ad_path(name, ADFLAGS_DIR )), sizeof(adouble_p));
- if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, &owner, noadouble, 0))
+ if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, vol, &owner, noadouble))
return -1;
/*
if (errno == ENOENT) {
struct adouble ad;
- if (lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */
+ if (ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* source has no ressource fork, */
return 0;
/* We are here because :
/* ---------------- */
static int RF_setdirunixmode_osx(VFS_FUNC_ARGS_SETDIRUNIXMODE)
{
- return adouble_setfilmode(vol->ad_path(name, ADFLAGS_DIR ), mode, st, vol->v_umask);
+ return adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_DIR), mode, st);
}
/* ---------------- */
struct stat st;
err = errno;
- if (errno == ENOENT && lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */
+ if (errno == ENOENT && ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* source has no ressource fork, */
return 0;
errno = err;
return -1;
'\" t
.\" Title: AppleVolumes.default
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 13 Oct 2011
-.\" Manual: Netatalk 2.2
-.\" Source: Netatalk 2.2
+.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
+.\" Date: 27 Dez 2012
+.\" Manual: Netatalk 3.0
+.\" Source: Netatalk 3.0
.\" Language: English
.\"
-.TH "APPLEVOLUMES\&.DEFAU" "5" "13 Oct 2011" "Netatalk 2.2" "Netatalk 2.2"
+.TH "APPLEVOLUMES\&.DEFAU" "5" "27 Dez 2012" "Netatalk 3.0" "Netatalk 3.0"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
The path name must be a fully qualified path name, or a path name using either the ~ shell shorthand or any of the substitution variables, which are listed below\&.
.PP
The volume name is the name that appears in the Chooser ot the "connect to server" dialog on Macintoshes to represent the appropriate share\&. If volumename is unspecified, the last component of pathname is used\&. No two volumes may have the same name\&. If there are spaces in the name, it should be in quotes (i\&.e\&. "File Share")\&. The volume name cannot contain the
-\':\'
+\*(Aq:\*(Aq
character\&. The volume name is mangled if it is very long\&. Mac codepage volume name is limited to 27 characters\&. UTF8\-MAC volume name is limited to \-volnamelen parameter in afpd\&.conf
.if n \{\
.sp
.sp .5v
.RE
.PP
-The leading\-dot lines specify file name extension mappings\&. The extension \'\&.\' sets the default creator and type for otherwise untyped Unix files\&.
+The leading\-dot lines specify file name extension mappings\&. The extension \*(Aq\&.\*(Aq sets the default creator and type for otherwise untyped Unix files\&.
.if n \{\
.sp
.\}
.br
\fBadouble:osx\fR
\fBcannot\fR
-be treated normally any longer\&. Its only aim was to temporarely share eg\&. FAT32 formatted FireWire harddrives written on a Macintosh with afpd\&. Apple\'s metadata scheme lacks several essential features, so using it on the server\'s side will break both CNIDs and MacOS 9 compatibility\&. AppleDouble file of Mac OS X 10\&.6 is incompatible to V1 and V2\&.
+be treated normally any longer\&. Its only aim was to temporarely share eg\&. FAT32 formatted FireWire harddrives written on a Macintosh with afpd\&. Apple\*(Aqs metadata scheme lacks several essential features, so using it on the server\*(Aqs side will break both CNIDs and MacOS 9 compatibility\&. AppleDouble file of Mac OS X 10\&.6 is incompatible to V1 and V2\&.
.sp .5v
.RE
.RE
.PP
upriv
.RS 4
-use AFP3 unix privileges\&. This should be set for OS X clients\&. Starting with Netatalk 2\&.1 it\'s part of the default config :DEFAULT: line\&. See also:
+use AFP3 unix privileges\&. This should be set for OS X clients\&. Starting with Netatalk 2\&.1 it\*(Aqs part of the default config :DEFAULT: line\&. See also:
\fBperm|fperm|dperm\fR\&.
.RE
.PP
usedots
.RS 4
-Don\'t do :hex translation for dot files\&. note: when this option gets set, certain file names become illegal\&. These are \&.Parent and anything that starts with \&.Apple\&. See also
+Don\*(Aqt do :hex translation for dot files\&. note: when this option gets set, certain file names become illegal\&. These are \&.Parent and anything that starts with \&.Apple\&. See also
\fBinvisibledots\fR\&.
.RE
+.PP
+followsymlinks
+.RS 4
+Follow symlinks on the server\&.
+.RE
.RE
.PP
password:\fI[password]\fR
.PP
veto:\fI[vetoed names]\fR
.RS 4
-hide files and directories,where the path matches one of the \'/\' delimited vetoed names\&. The veto string must always be terminated with a \'/\', eg\&. "veto1/", "veto1/veto2/"\&.
+hide files and directories,where the path matches one of the \*(Aq/\*(Aq delimited vetoed names\&. The veto string must always be terminated with a \*(Aq/\*(Aq, eg\&. "veto1/", "veto1/veto2/"\&.
.RE
.PP
volcharset:\fI[charset]\fR
.sp -1
.IP " 2." 4.2
.\}
-if you specify a known variable, but that variable doesn\'t have a value, it will get ignored\&.
+if you specify a known variable, but that variable doesn\*(Aqt have a value, it will get ignored\&.
.RE
.PP
The variables which can be used for substitutions are:
.PP
$c
.RS 4
-client\'s ip or appletalk address
+client\*(Aqs ip or appletalk address
.RE
.PP
$d
.PP
$i
.RS 4
-client\'s ip, without port
+client\*(Aqs ip, without port
.RE
.PP
$s
We define "groupdirs" for each primary group and use a personalized server name for homedir shares\&.
.SH "CNID BACKENDS"
.PP
-The AFP protocol mostly refers to files and directories by ID and not by name\&. Netatalk needs a way to store these ID\'s in a persistent way, to achieve this several different CNID backends are available\&. The CNID Databases are by default located in the
+The AFP protocol mostly refers to files and directories by ID and not by name\&. Netatalk needs a way to store these ID\*(Aqs in a persistent way, to achieve this several different CNID backends are available\&. The CNID Databases are by default located in the
\&.AppleDB
folder in the volume root\&.
.PP
cdb
.RS 4
-"Concurrent database", backend is based on Sleepycat\'s Berkely DB\&. With this backend several
+"Concurrent database", backend is based on Sleepycat\*(Aqs Berkely DB\&. With this backend several
\fBafpd\fR
deamons access the CNID database directly\&. Berkeley DB locking is used to synchronize access, if more than one
\fBafpd\fR
.PP
last
.RS 4
-This backend is an exception, in terms of ID persistency\&. ID\'s are only valid for the current session\&. This is basically what
+This backend is an exception, in terms of ID persistency\&. ID\*(Aqs are only valid for the current session\&. This is basically what
\fBafpd\fR
did in the 1\&.5 (and 1\&.6) versions\&. This backend is still available, as it is useful for e\&.g\&. sharing cdroms\&.
.sp
.PP
Even though
\fB\&./configure \-\-help\fR
-might show that there are other CNID backends available, be warned those are likely broken or mainly used for testing\&. Don\'t use them unless you know what you\'re doing, they may be removed without further notice from future versions\&.
+might show that there are other CNID backends available, be warned those are likely broken or mainly used for testing\&. Don\*(Aqt use them unless you know what you\*(Aqre doing, they may be removed without further notice from future versions\&.
.SH "CHARSET OPTIONS"
.PP
With OS X Apple introduced the AFP3 protocol\&. One of the most important changes was that AFP3 uses unicode names encoded as UTF\-8 decomposed\&. Previous AFP/OS versions used codepages, like MacRoman, MacCentralEurope, etc\&.
.PP
\fBafpd\fR
needs a way to preserve extended macintosh characters, or characters illegal in unix filenames, when saving files on a unix filesystem\&. Earlier versions used the the so called CAP encoding\&. An extended character (>0x7F) would be converted to a :xx sequence, e\&.g\&. the Apple Logo (MacRoman: 0XF0) was saved as
-:f0\&. Some special characters will be converted as to :xx notation as well\&. \'/\' will be encoded to
+:f0\&. Some special characters will be converted as to :xx notation as well\&. \*(Aq/\*(Aq will be encoded to
:2f, if
\fBusedots\fR
-is not specified, a leading dot \'\&.\' will be encoded as
+is not specified, a leading dot \*(Aq\&.\*(Aq will be encoded as
:2e\&.
.PP
-This version now uses UTF\-8 as the default encoding for names\&. Special characters, like \'/\' and a leading \'\&.\' will still be CAP style encoded \&.
+This version now uses UTF\-8 as the default encoding for names\&. Special characters, like \*(Aq/\*(Aq and a leading \*(Aq\&.\*(Aq will still be CAP style encoded \&.
.PP
The
\fB\-volcharset\fR
will convert the UTF\-8
character to
\fB\-maccharset\fR
-first\&. If this conversion fails, you\'ll receive a \-50 error on the mac\&.
+first\&. If this conversion fails, you\*(Aqll receive a \-50 error on the mac\&.
.PP
\fINote\fR: Whenever you can, please stick with the default UTF\-8 volume format\&.
.SH "COMPATIBILITY WITH EARLIER VERSIONS"
.PP
To use a volume created with an earlier
\fBafpd\fR
-version, you\'ll have to specify the following options:
+version, you\*(Aqll have to specify the following options:
.PP
\fBExample.\ \&use a 1.x style volume\fR
.sp
\fINote\fR: Using above example options will allow you to downgrade to 1\&.x netatalk again\&.
.PP
\fINote\fR: Some 1\&.x NLS files used non standard mappings, e\&.g\&.
-maccode\&.iso8859\-1\&.adapted\&. Three 1\&.x CAP double\-byte maccharsets are incompatible to netatalk 2\&.x; "MAC_CHINESE_TRAD", "MAC_JAPANESE" and "MAC_KOREAN"\&. These are not supported anymore\&. You\'ll have to copy the contents of those volumes files to a Mac and then back to the netatalk server, preferably to an UTF\-8 volume\&.
+maccode\&.iso8859\-1\&.adapted\&. Three 1\&.x CAP double\-byte maccharsets are incompatible to netatalk 2\&.x; "MAC_CHINESE_TRAD", "MAC_JAPANESE" and "MAC_KOREAN"\&. These are not supported anymore\&. You\*(Aqll have to copy the contents of those volumes files to a Mac and then back to the netatalk server, preferably to an UTF\-8 volume\&.
.SH "ADVANCED OPTIONS"
.PP
The following options should only be used after serious consideration\&. Be sure you fully understood the, sometimes complex, consequences, before using them\&.
\fBafpd\fR
to not automatically create \&.AppleDouble subdirs containing AD header files in every directory it enters (which will it do by default)\&.
.sp
-In case, you save or change files from mac clients, AD metadata files have to be written even in case you set this option\&. So you can\'t avoid the creation of \&.AppleDouble directories and its contents when you give macs write access to a share and they make use of it\&.
+In case, you save or change files from mac clients, AD metadata files have to be written even in case you set this option\&. So you can\*(Aqt avoid the creation of \&.AppleDouble directories and its contents when you give macs write access to a share and they make use of it\&.
.sp
Try to avoid
\fBnoadouble\fR
.RS 4
If set
\fBafpd\fR
-doesn\'t store the ID information in AppleDouble V2 header files\&. As these IDs are used for caching and as a database backup, this option normally shouldn\'t be set\&.
+doesn\*(Aqt store the ID information in AppleDouble V2 header files\&. As these IDs are used for caching and as a database backup, this option normally shouldn\*(Aqt be set\&.
.RE
.PP
nodev
.PP
nofileid
.RS 4
-don\'t advertise createfileid, resolveid, deleteid calls\&.
+don\*(Aqt advertise createfileid, resolveid, deleteid calls\&.
.RE
.PP
nohex
.RS 4
Disables :hex translations for anything except dot files\&. This option makes the
-\'/\' character illegal\&.
+\*(Aq/\*(Aq character illegal\&.
.RE
.PP
nostat
.RS 4
-don\'t stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script\&.
+don\*(Aqt stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script\&.
.RE
.PP
prodos