From b362b6f7b22b6e4e9e74760989f389149677917b Mon Sep 17 00:00:00 2001 From: Frank Lahm Date: Wed, 14 Nov 2012 16:02:41 +0100 Subject: [PATCH] Configurable symlink behaviour Add a new volumes option 'followsymlinks' which modifies Netatalk's handling of symlinks. Without the option (default behaviour) symlinks are not followed by afpd. Setting the volume option causes afpd to follow any symlink on the server. symlinks may then point outside of the AFP volume, currently afpd doesn't do any checks for "wide symlinks". In order to provide a somewhat consistent interface, add an API of o* function wrappers for filesytem related functions. These functions take an an additonal option arg (hence the o) which tells the functions whether symlinks shall be followed or not. --- NEWS | 3 + etc/afpd/acls.c | 2 +- etc/afpd/afp_asp.c | 2 +- etc/afpd/catsearch.c | 6 +- etc/afpd/desktop.c | 6 +- etc/afpd/directory.c | 28 ++++----- etc/afpd/enumerate.c | 4 +- etc/afpd/file.c | 68 ++++++++++++---------- etc/afpd/filedir.c | 23 +++++--- etc/afpd/fork.c | 2 +- etc/afpd/fork.h | 4 +- etc/afpd/ofork.c | 16 ++--- etc/afpd/quota.c | 13 +++-- etc/afpd/unix.c | 27 ++++----- etc/afpd/volume.c | 4 ++ include/atalk/acl.h | 3 + include/atalk/adouble.h | 4 ++ include/atalk/unix.h | 3 +- include/atalk/util.h | 10 +++- include/atalk/volume.h | 3 +- libatalk/acl/Makefile.am | 1 + libatalk/adouble/ad_open.c | 18 +++--- libatalk/util/unix.c | 87 +++++++++++++++++++++++++++- libatalk/vfs/ea.c | 14 ++--- libatalk/vfs/unix.c | 31 +--------- libatalk/vfs/vfs.c | 86 +++++++++++++-------------- man/man5/AppleVolumes.default.5.tmpl | 72 +++++++++++++---------- 27 files changed, 324 insertions(+), 216 deletions(-) diff --git a/NEWS b/NEWS index 233d35c6..6c5aab2f 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ Changes in 2.2.5 ================ * 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 ================ diff --git a/etc/afpd/acls.c b/etc/afpd/acls.c index 82d61eb5..7290b31b 100644 --- a/etc/afpd/acls.c +++ b/etc/afpd/acls.c @@ -1372,7 +1372,7 @@ static int check_acl_access(const struct vol *vol, } #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, "."); diff --git a/etc/afpd/afp_asp.c b/etc/afpd/afp_asp.c index 3e208f74..e2552275 100644 --- a/etc/afpd/afp_asp.c +++ b/etc/afpd/afp_asp.c @@ -81,7 +81,7 @@ static void afp_authprint_remove(AFPObj *obj) 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 ); diff --git a/etc/afpd/catsearch.c b/etc/afpd/catsearch.c index 907e999c..f1f55ef9 100644 --- a/etc/afpd/catsearch.c +++ b/etc/afpd/catsearch.c @@ -193,7 +193,7 @@ static struct adouble *adl_lkup(struct vol *vol, struct path *path, struct adoub 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); @@ -581,7 +581,7 @@ static int catsearch(struct vol *vol, 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: @@ -774,7 +774,7 @@ static int catsearch_db(struct vol *vol, 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: diff --git a/etc/afpd/desktop.c b/etc/afpd/desktop.c index 8b6efd7e..8446a848 100644 --- a/etc/afpd/desktop.c +++ b/etc/afpd/desktop.c @@ -688,7 +688,7 @@ static int ad_addcomment(struct vol *vol, struct path *path, char *ibuf) } 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 @@ -761,7 +761,7 @@ static int ad_getcomment(struct vol *vol, struct path *path, char *rbuf, size_t 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 @@ -838,7 +838,7 @@ static int ad_rmvcomment(struct vol *vol, struct path *path) } 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 diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c index a6b194b1..84b5093d 100644 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@ -132,7 +132,7 @@ static int netatalk_mkdir(const struct vol *vol, const char *name) } /* ------------------- */ -static int deletedir(int dirfd, char *dir) +static int deletedir(const struct vol *vol, int dirfd, char *dir) { char path[MAXPATHLEN + 1]; DIR *dp; @@ -163,11 +163,11 @@ static int deletedir(int dirfd, char *dir) 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); } @@ -229,7 +229,7 @@ static int copydir(const struct vol *vol, int dirfd, char *src, char *dst) } 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; @@ -251,7 +251,7 @@ static int copydir(const struct vol *vol, int dirfd, char *src, char *dst) } /* 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); } @@ -641,7 +641,7 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did) 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; @@ -729,7 +729,7 @@ int caseenumerate(const struct vol *vol, struct path *path, struct dir *dir) 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 ... */ @@ -760,7 +760,7 @@ int caseenumerate(const struct vol *vol, struct path *path, struct dir *dir) 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; @@ -1259,7 +1259,7 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) * 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 @@ -1377,7 +1377,7 @@ int movecwd(const struct vol *vol, struct dir *dir) 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) { @@ -2267,7 +2267,7 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_ return err; } - if (of_stat(s_path) < 0) { + if (of_stat(vol, s_path) < 0) { return AFPERR_MISC; } @@ -2335,10 +2335,10 @@ int renamedir(const struct vol *vol, /* 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 : @@ -2717,7 +2717,7 @@ int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si 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 ) { diff --git a/etc/afpd/enumerate.c b/etc/afpd/enumerate.c index fe5a66d9..2a389335 100644 --- a/etc/afpd/enumerate.c +++ b/etc/afpd/enumerate.c @@ -283,7 +283,7 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, 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); @@ -347,7 +347,7 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, } 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..." diff --git a/etc/afpd/file.c b/etc/afpd/file.c index 93c91c04..9f1ff318 100644 --- a/etc/afpd/file.c +++ b/etc/afpd/file.c @@ -115,7 +115,7 @@ void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *a } } - if (islink){ + if (islink && !vol_syml_opt(vol)) { u_int16_t linkflag; memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2); linkflag |= htons(FINDERINFO_ISALIAS); @@ -701,7 +701,7 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, 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); @@ -871,6 +871,9 @@ int setfilparams(struct vol *vol, 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:"); @@ -912,29 +915,32 @@ int setfilparams(struct vol *vol, 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 : @@ -1807,7 +1813,7 @@ static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data) 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 */ @@ -1852,7 +1858,7 @@ reenumerate_id(struct vol *vol, char *name, struct dir *dir) } /* FIXME use of_statdir ? */ - if (lstat(name, &st)) { + if (ostat(name, &st, vol_syml_opt(vol))) { return -1; } @@ -1931,7 +1937,7 @@ retry: 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) { @@ -2026,7 +2032,7 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_ } 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: @@ -2062,7 +2068,7 @@ delete: } /* ------------------------------ */ -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; @@ -2089,7 +2095,7 @@ static struct adouble *find_adouble(struct path *path, struct ofork **of, struct 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; } @@ -2180,7 +2186,7 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U } 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; } @@ -2213,7 +2219,7 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U } 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; } @@ -2271,10 +2277,10 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U 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) { diff --git a/etc/afpd/filedir.c b/etc/afpd/filedir.c index 6fe3c855..c6bcc34e 100644 --- a/etc/afpd/filedir.c +++ b/etc/afpd/filedir.c @@ -94,28 +94,33 @@ int matchfile2dirperms( 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", @@ -449,7 +454,7 @@ static int moveandrename(const struct vol *vol, 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 ); @@ -614,7 +619,7 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size 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 @@ -796,8 +801,8 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U 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 ); } diff --git a/etc/afpd/fork.c b/etc/afpd/fork.c index 33aaf66d..634f7ad8 100644 --- a/etc/afpd/fork.c +++ b/etc/afpd/fork.c @@ -331,7 +331,7 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si 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; } diff --git a/etc/afpd/fork.h b/etc/afpd/fork.h index 0ad46a97..bd70920a 100644 --- a/etc/afpd/fork.h +++ b/etc/afpd/fork.h @@ -60,14 +60,14 @@ extern struct ofork *of_alloc (struct vol *, struct dir *, 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); diff --git a/etc/afpd/ofork.c b/etc/afpd/ofork.c index 2d260089..47a12609 100644 --- a/etc/afpd/ofork.c +++ b/etc/afpd/ofork.c @@ -248,14 +248,14 @@ struct ofork *of_find(const u_int16_t ofrefnum ) } /* -------------------------- */ -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; @@ -294,7 +294,7 @@ int of_statdir(struct vol *vol, struct path *path) if (*path->m_name) { /* not curdir */ - return of_stat (path); + return of_stat(vol, path); } path->st_errno = 0; path->st_valid = 1; @@ -306,7 +306,7 @@ int of_statdir(struct vol *vol, struct path *path) 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; @@ -317,7 +317,7 @@ int of_statdir(struct vol *vol, struct path *path) 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; } @@ -325,13 +325,13 @@ int of_statdir(struct vol *vol, struct path *path) } /* -------------------------- */ -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) @@ -455,7 +455,7 @@ struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble * 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); diff --git a/etc/afpd/quota.c b/etc/afpd/quota.c index 577ddab7..53938442 100644 --- a/etc/afpd/quota.c +++ b/etc/afpd/quota.c @@ -42,6 +42,7 @@ char *strchr (), *strrchr (); #include #include #include +#include #include "auth.h" #include "volume.h" @@ -387,7 +388,7 @@ mountp( char *file, int *nfs) dev_t devno; static struct mnttab mnt; - if ( lstat( file, &sb ) < 0 ) { + if (stat(file, &sb) < 0) { return( NULL ); } devno = sb.st_dev; @@ -398,14 +399,14 @@ mountp( char *file, int *nfs) 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 ); @@ -475,7 +476,7 @@ special(char *file, int *nfs) struct mntent *mnt; int found=0; - if ( lstat( file, &sb ) < 0 ) { + if (stat(file, &sb) < 0 ) { return( NULL ); } devno = sb.st_dev; @@ -486,14 +487,14 @@ special(char *file, int *nfs) 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; diff --git a/etc/afpd/unix.c b/etc/afpd/unix.c index cc10d97b..d068c0b3 100644 --- a/etc/afpd/unix.c +++ b/etc/afpd/unix.c @@ -175,7 +175,7 @@ void accessmode(const struct vol *vol, char *path, struct maccess *ma, struct di 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; } @@ -234,6 +234,7 @@ mode_t mtoumode(struct maccess *ma) #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]; @@ -277,7 +278,7 @@ int setdeskmode(const mode_t mode) *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; } @@ -313,7 +314,7 @@ int setdeskmode(const mode_t mode) 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) { @@ -322,7 +323,7 @@ int setfilunixmode (const struct vol *vol, struct path* path, mode_t mode) 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); @@ -384,7 +385,7 @@ int setdirmode(const struct vol *vol, const char *name, mode_t mode) 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; } @@ -392,7 +393,7 @@ int setdirmode(const struct vol *vol, const char *name, mode_t mode) 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; @@ -479,14 +480,14 @@ int setfilowner(const struct vol *vol, const uid_t uid, const gid_t gid, struct { 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; @@ -520,13 +521,13 @@ int setdirowner(const struct vol *vol, const char *name, const uid_t uid, const 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 */ @@ -539,10 +540,10 @@ int setdirowner(const struct vol *vol, const char *name, const uid_t uid, const 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) ); } @@ -566,7 +567,7 @@ static int recursive_chown(const char *path, uid_t uid, gid_t gid) { 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; } diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c index 50bafbe5..50945539 100644 --- a/etc/afpd/volume.c +++ b/etc/afpd/volume.c @@ -513,6 +513,8 @@ static void volset(struct vol_option *options, struct vol_option *save, 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, ","); } @@ -778,6 +780,8 @@ static int creatvol(AFPObj *obj, struct passwd *pwd, 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); diff --git a/include/atalk/acl.h b/include/atalk/acl.h index 95aa0753..810be707 100644 --- a/include/atalk/acl.h +++ b/include/atalk/acl.h @@ -21,6 +21,8 @@ #ifdef HAVE_ACLS +#define O_NETATALK_ACL (O_NOFOLLOW << 1) + #ifdef HAVE_SOLARIS_ACLS #include @@ -51,6 +53,7 @@ extern int remove_acl_vfs(const char *name); #else /* HAVE_ACLS=no */ +#define O_NETATALK_ACL #define chmod_acl chmod #endif /* HAVE_ACLS */ diff --git a/include/atalk/adouble.h b/include/atalk/adouble.h index 78a35b02..4990022d 100644 --- a/include/atalk/adouble.h +++ b/include/atalk/adouble.h @@ -293,6 +293,8 @@ struct adouble_fops { #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) @@ -417,6 +419,8 @@ struct adouble_fops { #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 *); diff --git a/include/atalk/unix.h b/include/atalk/unix.h index cc7bf5b0..f8ab6927 100644 --- a/include/atalk/unix.h +++ b/include/atalk/unix.h @@ -27,14 +27,13 @@ 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); diff --git a/include/atalk/util.h b/include/atalk/util.h index 3a6dd244..9ce71584 100644 --- a/include/atalk/util.h +++ b/include/atalk/util.h @@ -17,6 +17,9 @@ #include #endif /* HAVE_UNISTD_H */ #include +#include +#include + #include #include @@ -187,10 +190,15 @@ extern int recv_fd(int fd, int nonblocking); 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 *****************************************************************/ diff --git a/include/atalk/volume.h b/include/atalk/volume.h index af2b411c..60bae6ff 100644 --- a/include/atalk/volume.h +++ b/include/atalk/volume.h @@ -139,6 +139,7 @@ struct vol { #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 */ @@ -202,6 +203,6 @@ int wincheck(const struct vol *vol, const char *path); #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 diff --git a/libatalk/acl/Makefile.am b/libatalk/acl/Makefile.am index e7f9753a..524b73e6 100644 --- a/libatalk/acl/Makefile.am +++ b/libatalk/acl/Makefile.am @@ -5,6 +5,7 @@ noinst_HEADERS = aclldap.h cache.h 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 diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index 7adb4abd..b2b291b6 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -1013,8 +1013,8 @@ int ad_stat(const char *path, struct stat *stbuf) if (!p) { return -1; } -//FIXME! - return lstat( p, stbuf ); + + return stat(p, stbuf); } /* ---------------- @@ -1036,7 +1036,7 @@ static int ad_chown(const char *path, struct stat *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; @@ -1284,12 +1284,12 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble } } - 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; @@ -1366,11 +1366,11 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble 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); } } @@ -1391,7 +1391,7 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble } 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; @@ -1673,7 +1673,7 @@ static int new_rfork(const char *path, struct adouble *ad, int adflags) 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; } diff --git a/libatalk/util/unix.c b/libatalk/util/unix.c index e4cb21e8..0ff282a0 100644 --- a/libatalk/util/unix.c +++ b/libatalk/util/unix.c @@ -168,20 +168,103 @@ char *stripped_slashes_basename(char *p) 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 / diff --git a/libatalk/vfs/ea.c b/libatalk/vfs/ea.c index abe266fe..25b758c2 100644 --- a/libatalk/vfs/ea.c +++ b/libatalk/vfs/ea.c @@ -894,7 +894,7 @@ int ea_close(struct ea * restrict ea) 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)); @@ -1571,7 +1571,7 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN) } } - 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: @@ -1588,7 +1588,7 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN) 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: @@ -1632,7 +1632,7 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE) } /* 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: @@ -1651,7 +1651,7 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE) 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: @@ -1708,7 +1708,7 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) } /* 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: @@ -1738,7 +1738,7 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) 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: diff --git a/libatalk/vfs/unix.c b/libatalk/vfs/unix.c index ffb524b8..ce6b70a3 100644 --- a/libatalk/vfs/unix.c +++ b/libatalk/vfs/unix.c @@ -27,6 +27,7 @@ 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) { @@ -76,7 +77,7 @@ int dir_rx_set(mode_t mode) } /* --------------------- */ -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 */ @@ -87,12 +88,9 @@ int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask) 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; @@ -330,29 +328,6 @@ int statat(int dirfd, const char *path, struct stat *st) 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 * diff --git a/libatalk/vfs/vfs.c b/libatalk/vfs/vfs.c index 0999b878..66e5d376 100644 --- a/libatalk/vfs/vfs.c +++ b/libatalk/vfs/vfs.c @@ -48,11 +48,11 @@ struct perm { 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; @@ -78,7 +78,7 @@ for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int } 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; } @@ -131,7 +131,7 @@ static int RF_renamedir_adouble(VFS_FUNC_ARGS_RENAMEDIR) } /* ----------------- */ -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; @@ -154,20 +154,20 @@ static int RF_deletecurdir_adouble(VFS_FUNC_ARGS_DELETECURDIR) /* 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); } /* ----------------- */ @@ -181,7 +181,7 @@ static int RF_setdirunixmode_adouble(VFS_FUNC_ARGS_SETDIRUNIXMODE) 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)) { @@ -192,18 +192,18 @@ static int RF_setdirunixmode_adouble(VFS_FUNC_ARGS_SETDIRUNIXMODE) } /* ----------------- */ -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? */ } } @@ -222,7 +222,7 @@ static int RF_setdirmode_adouble(VFS_FUNC_ARGS_SETDIRMODE) 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)) { @@ -233,7 +233,7 @@ static int RF_setdirmode_adouble(VFS_FUNC_ARGS_SETDIRMODE) } /* ----------------- */ -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; @@ -257,7 +257,7 @@ static int RF_setdirowner_adouble(VFS_FUNC_ARGS_SETDIROWNER) 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; /* @@ -297,7 +297,7 @@ static int RF_renamefile_adouble(VFS_FUNC_ARGS_RENAMEFILE) 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 : @@ -496,7 +496,7 @@ EC_CLEANUP: /********************************************************************************* * 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; @@ -526,11 +526,11 @@ static int RF_chown_ads(VFS_FUNC_ARGS_CHOWN) 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); } @@ -539,19 +539,19 @@ static int ads_delete_rf(char *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; @@ -569,7 +569,7 @@ static int RF_deletecurdir_ads(VFS_FUNC_ARGS_DELETECURDIR) 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" ); @@ -581,14 +581,14 @@ struct set_mode { 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; @@ -604,16 +604,16 @@ static int ads_setfilmode(const char * name, mode_t mode, struct stat *st, 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; } @@ -622,7 +622,7 @@ static int ads_setfilmode(const char * name, mode_t mode, struct stat *st, mode_ 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); } /* ------------------- */ @@ -645,7 +645,7 @@ static int RF_setdirunixmode_ads(VFS_FUNC_ARGS_SETDIRUNIXMODE) 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)) { @@ -663,25 +663,25 @@ struct dir_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; } @@ -708,7 +708,7 @@ static int RF_setdirmode_ads(VFS_FUNC_ARGS_SETDIRMODE) 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)) { @@ -719,7 +719,7 @@ static int RF_setdirmode_ads(VFS_FUNC_ARGS_SETDIRMODE) } /* ------------------- */ -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; @@ -731,11 +731,11 @@ static int setdirowner_ads1_loop(struct dirent *de _U_, char *name, void *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 ) { @@ -758,7 +758,7 @@ static int RF_setdirowner_ads(VFS_FUNC_ARGS_SETDIROWNER) 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; /* @@ -822,7 +822,7 @@ static int RF_renamefile_ads(VFS_FUNC_ARGS_RENAMEFILE) 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 : @@ -881,7 +881,7 @@ static int RF_deletecurdir_osx(VFS_FUNC_ARGS_DELETECURDIR) /* ---------------- */ 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); } /* ---------------- */ @@ -908,7 +908,7 @@ static int RF_renamefile_osx(VFS_FUNC_ARGS_RENAMEFILE) 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; diff --git a/man/man5/AppleVolumes.default.5.tmpl b/man/man5/AppleVolumes.default.5.tmpl index 6aabe036..d2926f73 100644 --- a/man/man5/AppleVolumes.default.5.tmpl +++ b/man/man5/AppleVolumes.default.5.tmpl @@ -1,13 +1,22 @@ '\" t .\" Title: AppleVolumes.default .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] -.\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" Date: 13 Oct 2011 -.\" Manual: Netatalk 2.2 -.\" Source: Netatalk 2.2 +.\" Generator: DocBook XSL Stylesheets v1.78.0 +.\" 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 .\" ----------------------------------------------------------------- @@ -58,7 +67,7 @@ path 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 @@ -79,7 +88,7 @@ line\&. Though newline escaping is supported\&. .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 .\} @@ -131,7 +140,7 @@ Specify the format of the metadata files, which are used for saving Mac resource .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 @@ -284,15 +293,20 @@ a non\-zero return code from root_preexec closes the volume immediately, prevent .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 @@ -362,7 +376,7 @@ Allows certain users and groups to have read/write access to a share\&. This fol .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 @@ -392,7 +406,7 @@ if you specify an unknown variable, it will not get converted\&. .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: @@ -404,7 +418,7 @@ basename .PP $c .RS 4 -client\'s ip or appletalk address +client\*(Aqs ip or appletalk address .RE .PP $d @@ -429,7 +443,7 @@ hostname .PP $i .RS 4 -client\'s ip, without port +client\*(Aqs ip, without port .RE .PP $s @@ -473,13 +487,13 @@ prints dollar sign ($) 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 @@ -500,7 +514,7 @@ processes communicate with the daemon for database reads and updates\&. If built .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 @@ -513,20 +527,20 @@ now relies heavily on a persistent ID database\&. Aliases will likely not work a .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 @@ -539,14 +553,14 @@ provided charset\&. If a character cannot be converted from the mac codepage to 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 @@ -580,7 +594,7 @@ You should consider converting old style volumes to the new UTF\-8/AD2 format\&. \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\&. @@ -660,7 +674,7 @@ to not create \&.AppleDouble directories unless macintosh metadata needs to be w \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 @@ -671,7 +685,7 @@ nocnidcache .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 @@ -681,18 +695,18 @@ always use 0 for device number, helps when the device number is not constant acr .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 -- 2.39.2