From: Alexander Barton Date: Thu, 3 Jan 2013 13:56:35 +0000 (+0100) Subject: Merge branch 'product-2-2' of git://github.com/franklahm/Netatalk into alex-master X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=commitdiff_plain;h=9480b301ae3cd89122e7100f0ae052f3d6eaabf8;hp=18877ae5db9da89832c43d22d2448974cfb175fc Merge branch 'product-2-2' of git://github.com/franklahm/Netatalk into alex-master * 'product-2-2' of git://github.com/franklahm/Netatalk: Configurable symlink behaviour Trying to create an EA that already existed returned the wrong AFP error code Add become_root() capability to nfsv4_chmod() Fix an error where catalog search gave incomplete results 2.2.5dev Add missing source afp_asp.c Version 2.2.4 --- diff --git a/NEWS b/NEWS index 9375ba0c..6c5aab2f 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,11 @@ +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/VERSION b/VERSION index c3ff749c..8c99ff7b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.4dev \ No newline at end of file +2.2.5dev \ No newline at end of file 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 89a6d573..f1f55ef9 100644 --- a/etc/afpd/catsearch.c +++ b/etc/afpd/catsearch.c @@ -29,6 +29,9 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ +#include +#include +#include #include #include #include @@ -108,12 +111,8 @@ struct scrit { * */ struct dsitem { -// struct dir *dir; /* Structure describing this directory */ -// cnid_t did; /* CNID of this directory */ - int pidx; /* Parent's dsitem structure index. */ - int checked; /* Have we checked this directory ? */ - int path_len; - char *path; /* absolute UNIX path to this directory */ + cnid_t ds_did; /* CNID of this directory */ + int ds_checked; /* Have we checked this directory ? */ }; @@ -130,7 +129,6 @@ static void clearstack(void) save_cidx = -1; while (dsidx > 0) { dsidx--; - free(dstack[dsidx].path); } } @@ -155,23 +153,8 @@ static int addstack(char *uname, struct dir *dir, int pidx) /* Put new element. Allocate and copy lname and path. */ ds = dstack + dsidx++; -// ds->did = dir->d_did; - ds->pidx = pidx; - ds->checked = 0; - if (pidx >= 0) { - l = dstack[pidx].path_len; - u = strlen(uname) +1; - if (!(ds->path = malloc(l + u + 1) )) - return -1; - memcpy(ds->path, dstack[pidx].path, l); - ds->path[l] = '/'; - memcpy(&ds->path[l+1], uname, u); - ds->path_len = l +u; - } - else { - ds->path = strdup(uname); - ds->path_len = strlen(uname); - } + ds->ds_did = dir->d_did; + ds->ds_checked = 0; return 0; } @@ -186,9 +169,9 @@ static int reducestack(void) } while (dsidx > 0) { - if (dstack[dsidx-1].checked) { + if (dstack[dsidx-1].ds_checked) { dsidx--; - free(dstack[dsidx].path); +// free(dstack[dsidx].path); } else return dsidx - 1; } @@ -210,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); @@ -519,7 +502,8 @@ static int catsearch(struct vol *vol, int num_rounds = NUM_ROUNDS; int cwd = -1; int error; - + int unlen; + if (*pos != 0 && *pos != cur_pos) { result = AFPERR_CATCHNG; goto catsearch_end; @@ -553,20 +537,24 @@ static int catsearch(struct vol *vol, start_time = time(NULL); while ((cidx = reducestack()) != -1) { - LOG(log_debug, logtype_afpd, "catsearch: dir: \"%s\"", dstack[cidx].path); + if ((currentdir = dirlookup(vol, dstack[cidx].ds_did)) == NULL) { + result = AFPERR_MISC; + goto catsearch_end; + } + LOG(log_debug, logtype_afpd, "catsearch: current struct dir: \"%s\"", cfrombstr(currentdir->d_fullpath)); - error = lchdir(dstack[cidx].path); + error = movecwd(vol, currentdir); if (!error && dirpos == NULL) dirpos = opendir("."); if (dirpos == NULL) - dirpos = opendir(dstack[cidx].path); + dirpos = opendir(bdata(currentdir->d_fullpath)); if (error || dirpos == NULL) { switch (errno) { case EACCES: - dstack[cidx].checked = 1; + dstack[cidx].ds_checked = 1; continue; case EMFILE: case ENFILE: @@ -581,11 +569,6 @@ static int catsearch(struct vol *vol, goto catsearch_end; } - if ((currentdir = dirlookup_bypath(vol, dstack[cidx].path)) == NULL) { - result = AFPERR_MISC; - goto catsearch_end; - } - LOG(log_debug, logtype_afpd, "catsearch: current struct dir: \"%s\"", cfrombstr(currentdir->d_fullpath)); while ((entry = readdir(dirpos)) != NULL) { (*pos)++; @@ -598,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: @@ -613,12 +596,13 @@ static int catsearch(struct vol *vol, goto catsearch_end; } } - if (S_ISDIR(path.st.st_mode)) { + switch (S_IFMT & path.st.st_mode) { + case S_IFDIR: /* here we can short cut ie if in the same loop the parent dir wasn't in the cache ALL dirsearch_byname will fail. */ - int unlen = strlen(path.u_name); + unlen = strlen(path.u_name); path.d_dir = dircache_search_by_name(vol, currentdir, path.u_name, @@ -639,8 +623,12 @@ static int catsearch(struct vol *vol, result = AFPERR_MISC; goto catsearch_end; } - } else { + break; + case S_IFREG: path.d_dir = currentdir; + break; + default: + continue; } ccr = crit_check(vol, &path); @@ -672,7 +660,7 @@ static int catsearch(struct vol *vol, } /* while ((entry=readdir(dirpos)) != NULL) */ closedir(dirpos); dirpos = NULL; - dstack[cidx].checked = 1; + dstack[cidx].ds_checked = 1; } /* while (current_idx = reducestack()) != -1) */ /* We have finished traversing our tree. Return EOF here. */ @@ -786,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 7a513b6a..da39e9b1 100644 --- a/etc/afpd/desktop.c +++ b/etc/afpd/desktop.c @@ -721,7 +721,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 @@ -794,7 +794,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 @@ -871,7 +871,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 11f154b0..80799663 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 f6d191ca..f8ab6927 100644 --- a/include/atalk/unix.h +++ b/include/atalk/unix.h @@ -27,16 +27,18 @@ 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); extern int copy_file(int sfd, const char *src, const char *dst, mode_t mode); +extern void become_root(void); +extern void unbecome_root(void); + #endif /* ATALK_UNIX_H */ 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/acl/unix.c b/libatalk/acl/unix.c index 9bad9c94..8defd7d8 100644 --- a/libatalk/acl/unix.c +++ b/libatalk/acl/unix.c @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef HAVE_SOLARIS_ACLS @@ -47,7 +48,7 @@ int get_nfsv4_acl(const char *name, ace_t **retAces) *retAces = NULL; /* Only call acl() for regular files and directories, otherwise just return 0 */ if (lstat(name, &st) != 0) { - LOG(log_warning, logtype_afpd, "get_nfsv4_acl(\"%s/%s\"): %s", getcwdpath(), name, strerror(errno)); + LOG(log_debug, logtype_afpd, "get_nfsv4_acl(\"%s/%s\"): %s", getcwdpath(), name, strerror(errno)); return -1; } @@ -234,8 +235,16 @@ int nfsv4_chmod(char *name, mode_t mode) if (chmod(name, mode) != 0) /* (3) */ goto exit; - if ((nnaces = get_nfsv4_acl(name, &nacl)) == -1) /* (4) */ - goto exit; + if ((nnaces = get_nfsv4_acl(name, &nacl)) == -1) {/* (4) */ + if (errno != EACCES) + goto exit; + become_root(); + nnaces = get_nfsv4_acl(name, &nacl); + unbecome_root(); + if (nnaces == -1) + goto exit; + } + if ((nnaces = strip_nontrivial_aces(&nacl, nnaces)) == -1) /* (5) */ goto exit; @@ -243,8 +252,17 @@ int nfsv4_chmod(char *name, mode_t mode) goto exit; if ((ret = acl(name, ACE_SETACL, noaces + nnaces, cacl)) != 0) { - LOG(log_error, logtype_afpd, "nfsv4_chmod: error setting acl: %s", strerror(errno)); - goto exit; + if (errno != EACCES) { + LOG(log_error, logtype_afpd, "nfsv4_chmod: error setting acl: %s", strerror(errno)); + goto exit; + } + become_root(); + ret = acl(name, ACE_SETACL, noaces + nnaces, cacl); + unbecome_root(); + if (ret != 0) { + LOG(log_error, logtype_afpd, "nfsv4_chmod: error setting acl: %s", strerror(errno)); + goto exit; + } } exit: @@ -252,7 +270,7 @@ exit: if (nacl) free(nacl); if (cacl) free(cacl); - LOG(log_debug, logtype_afpd, "nfsv4_chmod(\"%s/%s\", %04o): result: %u", + LOG(log_debug, logtype_afpd, "nfsv4_chmod(\"%s/%s\", %04o): result: %d", getcwdpath(), name, mode, ret); return ret; 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 0e466307..0ff282a0 100644 --- a/libatalk/util/unix.c +++ b/libatalk/util/unix.c @@ -94,6 +94,22 @@ int daemonize(int nochdir, int noclose) return 0; } +static uid_t saved_uid = -1; + +void become_root(void) +{ + saved_uid = geteuid(); + if (seteuid(0) != 0) + AFP_PANIC("Can't seteuid(0)"); +} + +void unbecome_root(void) +{ + if (saved_uid == -1 || seteuid(saved_uid) < 0) + AFP_PANIC("Can't seteuid back"); + saved_uid = -1; +} + /*! * @brief get cwd in static buffer * @@ -152,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/sys_ea.c b/libatalk/vfs/sys_ea.c index 3db20b23..6269f0a4 100644 --- a/libatalk/vfs/sys_ea.c +++ b/libatalk/vfs/sys_ea.c @@ -723,14 +723,7 @@ static int solaris_unlinkat(int attrdirfd, const char *name) static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode) { - int filedes = attropen(path, attrpath, oflag, mode); - if (filedes == -1) { - if (errno != ENOENT) - LOG(log_error, logtype_default, "attropen(\"%s\", ea:'%s'): %s", - path, attrpath, strerror(errno)); - errno = ENOATTR; - } - return filedes; + return attropen(path, attrpath, oflag, mode); } static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode) 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 diff --git a/test/afpd/Makefile.am b/test/afpd/Makefile.am index ca379e0e..fec7a0e5 100644 --- a/test/afpd/Makefile.am +++ b/test/afpd/Makefile.am @@ -10,6 +10,7 @@ EXTRA_DIST = test.sh CLEANFILES = test.default test.conf test_SOURCES = test.c subtests.c afpfunc_helpers.c \ + $(top_builddir)/etc/afpd/afp_asp.c \ $(top_builddir)/etc/afpd/afp_avahi.c \ $(top_builddir)/etc/afpd/afp_config.c \ $(top_builddir)/etc/afpd/afp_dsi.c \