From: Frank Lahm Date: Thu, 25 Nov 2010 13:27:05 +0000 (+0100) Subject: Cleaunup and refactor ad_open X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7ac74af5a02bd5d211ea95a998395cecf202c3de;hp=ba93992178626137806dfb7568a11d3b21d82fc8;p=netatalk.git Cleaunup and refactor ad_open --- diff --git a/configure.in b/configure.in index 2895163a..3d25dc56 100644 --- a/configure.in +++ b/configure.in @@ -23,9 +23,6 @@ AC_PROG_GREP AC_PROG_PS AM_PROG_CC_C_O -dnl Request SUSv4 standard interfaces -CFLAGS="$CFLAGS -D_XOPEN_SOURCE=700" - dnl Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT @@ -1264,7 +1261,8 @@ AC_CHECK_HEADER([uuid/uuid.h], AC_MSG_ERROR([missing header from libuuid required for TimeMachine])) dnl --------------------- last minute substitutions - +dnl Request SUSv4 standard interfaces +CFLAGS="$CFLAGS -D_XOPEN_SOURCE=700" AC_SUBST(LIBS) AC_SUBST(CFLAGS) AC_SUBST(OVERWRITE_CONFIG) diff --git a/include/atalk/adouble.h b/include/atalk/adouble.h index b125fc74..6e334e6b 100644 --- a/include/atalk/adouble.h +++ b/include/atalk/adouble.h @@ -33,6 +33,7 @@ #include #endif +#include #include #include #include @@ -45,7 +46,6 @@ /* version info */ #define AD_VERSION2 0x00020000 -#define AD_VERSION2_OSX 0x00020001 #define AD_VERSION2_EA 0x00020002 #define AD_VERSION AD_VERSION2_EA @@ -112,8 +112,8 @@ #define ADEDLEN_PRIVID 4 #define ADEID_NUM_V2 13 -#define ADEID_NUM_OSX 2 #define ADEID_NUM_EA 5 + #define AD_DATASZ2 (AD_HEADER_LEN + ADEDLEN_NAME + ADEDLEN_COMMENT + ADEDLEN_FILEI + \ ADEDLEN_FINDERI + ADEDLEN_DID + ADEDLEN_AFPFILEI + ADEDLEN_SHORTNAME + \ ADEDLEN_PRODOSFILEI + ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \ @@ -190,23 +190,21 @@ struct adouble { char *ad_m_name; /* mac name for open fork */ int ad_m_namelen; struct adouble_fops *ad_ops; - u_int16_t ad_open_forks; /* open forks (by others) */ + uint16_t ad_open_forks; /* open forks (by others) */ #ifdef USE_MMAPPED_HEADERS char *ad_data; #else - char ad_data[AD_DATASZ_MAX]; + char ad_data[AD_DATASZ_MAX]; #endif }; #define ADFLAGS_DF (1<<0) #define ADFLAGS_HF (1<<1) #define ADFLAGS_DIR (1<<2) -#define ADFLAGS_V1COMPAT (1<<4) #define ADFLAGS_NOHF (1<<5) /* not an error if no ressource fork */ #define ADFLAGS_RDONLY (1<<6) /* don't try readwrite */ #define ADFLAGS_OPENFORKS (1<<7) /* check for open fork in ad_metadata function */ #define ADFLAGS_RF (1<<8) -#define ADFLAGS_MD ADFLAGS_HF /* (1<<9) */ #define ADFLAGS_CREATE (1<<9) #define ADVOL_NODEV (1 << 0) @@ -377,9 +375,9 @@ extern int ad_metadata (const char *, int, struct adouble *); extern int ad_metadataat (int, const char *, int, struct adouble *); #define ad_open_metadata(name, flags, mode, adp)\ - ad_open(name, ADFLAGS_MD|(flags), O_RDWR |(mode), 0666, (adp)) + ad_open(name, ADFLAGS_HF | (flags), O_RDWR |(mode), 0666, (adp)) -#define ad_close_metadata(adp) ad_close( (adp), ADFLAGS_MD) +#define ad_close_metadata(adp) ad_close( (adp), ADFLAGS_HF) /* build a resource fork mode from the data fork mode: * remove X mode and extend header to RW if R or W (W if R for locking), @@ -406,66 +404,43 @@ static inline mode_t ad_hf_mode (mode_t mode) return mode; } -/* ad_ea.c */ -ssize_t sys_getxattr (const char *path, const char *name, void *value, size_t size); -ssize_t sys_lgetxattr (const char *path, const char *name, void *value, size_t size); -ssize_t sys_fgetxattr (int filedes, const char *name, void *value, size_t size); -ssize_t sys_listxattr (const char *path, char *list, size_t size); -ssize_t sys_llistxattr (const char *path, char *list, size_t size); -ssize_t sys_flistxattr (int filedes, char *list, size_t size); -int sys_removexattr (const char *path, const char *name); -int sys_lremovexattr (const char *path, const char *name); -int sys_fremovexattr (int filedes, const char *name); -int sys_setxattr (const char *path, const char *name, const void *value, size_t size, int flags); -int sys_lsetxattr (const char *path, const char *name, const void *value, size_t size, int flags); -int sys_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags); -int sys_copyxattr (const char *src, const char *dst); - /* ad_read.c/ad_write.c */ extern int sys_ftruncate(int fd, off_t length); - -extern ssize_t ad_read (struct adouble *, const u_int32_t, - const off_t, char *, const size_t); -extern ssize_t ad_pread (struct ad_fd *, void *, size_t, off_t); -extern ssize_t ad_write (struct adouble *, const u_int32_t, off_t, - const int, const char *, const size_t); -extern ssize_t adf_pread (struct ad_fd *, void *, size_t, off_t); -extern ssize_t adf_pwrite (struct ad_fd *, const void *, size_t, off_t); -extern int ad_dtruncate (struct adouble *, const off_t); -extern int ad_rtruncate (struct adouble *, const off_t); +extern ssize_t ad_read(struct adouble *, uint32_t, off_t, char *, size_t); +extern ssize_t ad_pread(struct ad_fd *, void *, size_t, off_t); +extern ssize_t ad_write(struct adouble *, uint32_t, off_t, int, const char *, size_t); +extern ssize_t adf_pread(struct ad_fd *, void *, size_t, off_t); +extern ssize_t adf_pwrite(struct ad_fd *, const void *, size_t, off_t); +extern int ad_dtruncate(struct adouble *, off_t); +extern int ad_rtruncate(struct adouble *, off_t); /* ad_size.c */ -extern off_t ad_size (const struct adouble *, const u_int32_t ); +extern off_t ad_size (const struct adouble *, uint32_t ); /* ad_mmap.c */ -extern void *ad_mmapread (struct adouble *, const u_int32_t, - const off_t, const size_t); -extern void *ad_mmapwrite (struct adouble *, const u_int32_t, - const off_t, const int, const size_t); +extern void *ad_mmapread(struct adouble *, uint32_t, off_t, size_t); +extern void *ad_mmapwrite(struct adouble *, uint32_t, off_t, int, size_t); #define ad_munmap(buf, len) (munmap((buf), (len))) /* ad_date.c */ -extern int ad_setdate (struct adouble *, unsigned int, u_int32_t); -extern int ad_getdate (const struct adouble *, unsigned int, u_int32_t *); +extern int ad_setdate(struct adouble *, unsigned int, uint32_t); +extern int ad_getdate(const struct adouble *, unsigned int, uint32_t *); /* ad_attr.c */ -extern int ad_setattr (const struct adouble *, const u_int16_t); -extern int ad_getattr (const struct adouble *, u_int16_t *); -extern int ad_setname (struct adouble *, const char *); -extern int ad_setid (struct adouble *, const dev_t dev,const ino_t ino, const u_int32_t, const u_int32_t, const void *); -extern u_int32_t ad_getid (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *); -extern u_int32_t ad_forcegetid (struct adouble *adp); +extern int ad_setattr(const struct adouble *, uint16_t); +extern int ad_getattr(const struct adouble *, uint16_t *); +extern int ad_setname(struct adouble *, const char *); +extern int ad_setid(struct adouble *, dev_t dev, ino_t ino, uint32_t, uint32_t, const void *); +extern u_int32_t ad_getid(struct adouble *, dev_t, ino_t, cnid_t, const void *); +extern u_int32_t ad_forcegetid(struct adouble *adp); #ifdef WITH_SENDFILE -extern int ad_readfile_init(const struct adouble *ad, - const int eid, off_t *off, - const int end); +extern int ad_readfile_init(const struct adouble *ad, int eid, off_t *off, int end); #endif #if 0 #ifdef HAVE_SENDFILE_WRITE -extern ssize_t ad_writefile (struct adouble *, const int, - const int, off_t, const int, const size_t); +extern ssize_t ad_writefile(struct adouble *, int, int, off_t, int, size_t); #endif /* HAVE_SENDFILE_WRITE */ #endif /* 0 */ diff --git a/include/atalk/ea.h b/include/atalk/ea.h index c0165a1a..276e95e0 100644 --- a/include/atalk/ea.h +++ b/include/atalk/ea.h @@ -1,5 +1,4 @@ /* - $Id: ea.h,v 1.11 2010-03-12 15:16:49 franklahm Exp $ Copyright (c) 2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -56,6 +55,22 @@ enum { #define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */ #endif +/**************************************************************************************** + * Wrappers for native EA functions taken from Samba + ****************************************************************************************/ +ssize_t sys_getxattr (const char *path, const char *name, void *value, size_t size); +ssize_t sys_lgetxattr (const char *path, const char *name, void *value, size_t size); +ssize_t sys_fgetxattr (int filedes, const char *name, void *value, size_t size); +ssize_t sys_listxattr (const char *path, char *list, size_t size); +ssize_t sys_llistxattr (const char *path, char *list, size_t size); +ssize_t sys_flistxattr (int filedes, char *list, size_t size); +int sys_removexattr (const char *path, const char *name); +int sys_lremovexattr (const char *path, const char *name); +int sys_fremovexattr (int filedes, const char *name); +int sys_setxattr (const char *path, const char *name, const void *value, size_t size, int flags); +int sys_lsetxattr (const char *path, const char *name, const void *value, size_t size, int flags); +int sys_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags); +int sys_copyxattr (const char *src, const char *dst); /**************************************************************************************** * Stuff for our implementation of storing EAs in files in .AppleDouble dirs diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index bbd11376..57737e70 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -162,13 +162,6 @@ static const struct entry entry_order2[ADEID_NUM_V2 + 1] = { {0, 0, 0} }; -/* OS X adouble finder info and resource fork only */ -static const struct entry entry_order_osx[ADEID_NUM_OSX + 1] = { - {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI}, - {ADEID_RFORK, ADEDOFF_RFORK_OSX, ADEDLEN_INIT}, - {0, 0, 0} -}; - /* Using Extended Attributes */ static const struct entry entry_order_ea[ADEID_NUM_EA + 1] = { {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI}, @@ -523,8 +516,7 @@ static int ad_header_read(struct adouble *ad, struct stat *hst) /* tag broken v1 headers. just assume they're all right. * we detect two cases: null magic/version * byte swapped magic/version - * XXX: in the future, you'll need the v1compat flag. - * (ad->ad_flags & ADFLAGS_V1COMPAT) */ + * XXX: in the future, you'll need the v1compat flag. */ if (!ad->ad_magic && !ad->ad_version) { if (!warning) { LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version."); @@ -546,7 +538,7 @@ static int ad_header_read(struct adouble *ad, struct stat *hst) if ((ad->ad_magic != AD_MAGIC) || ((ad->ad_version != AD_VERSION1) && (ad->ad_version != AD_VERSION2))) { - LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header."); + LOG(log_error, logtype_default, "ad_open: can't parse AppleDouble header."); errno = EIO; return -1; } @@ -564,7 +556,7 @@ static int ad_header_read(struct adouble *ad, struct stat *hst) buf += AD_HEADER_LEN; if (len > header_len - AD_HEADER_LEN) { - LOG(log_debug, logtype_default, "ad_header_read: can't read entry info."); + LOG(log_error, logtype_default, "ad_header_read: can't read entry info."); errno = EIO; return -1; } @@ -576,13 +568,13 @@ static int ad_header_read(struct adouble *ad, struct stat *hst) if (!ad_getentryoff(ad, ADEID_RFORK) || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data)) ) { - LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset."); + LOG(log_error, logtype_default, "ad_header_read: problem with rfork entry offset."); errno = EIO; return -1; } if (ad_getentryoff(ad, ADEID_RFORK) > header_len) { - LOG(log_debug, logtype_default, "ad_header_read: can't read in entries."); + LOG(log_error, logtype_default, "ad_header_read: can't read in entries."); errno = EIO; return -1; } @@ -635,98 +627,43 @@ static int ad_header_read_ea(struct adouble *ad, struct stat *hst) memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic )); memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version )); - /* tag broken v1 headers. just assume they're all right. - * we detect two cases: null magic/version - * byte swapped magic/version - * XXX: in the future, you'll need the v1compat flag. - * (ad->ad_flags & ADFLAGS_V1COMPAT) */ - if (!ad->ad_magic && !ad->ad_version) { - if (!warning) { - LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version."); - warning++; - } - ad->ad_magic = AD_MAGIC; - ad->ad_version = AD_VERSION1; - - } else if (ad->ad_magic == AD_MAGIC && ad->ad_version == AD_VERSION1) { - if (!warning) { - LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version."); - warning++; - } - - } else { - ad->ad_magic = ntohl( ad->ad_magic ); - ad->ad_version = ntohl( ad->ad_version ); - } - - if ((ad->ad_magic != AD_MAGIC) || - ((ad->ad_version != AD_VERSION1) && (ad->ad_version != AD_VERSION2))) { - LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header."); - errno = EIO; - return -1; - } + ad->ad_magic = ntohl( ad->ad_magic ); + ad->ad_version = ntohl( ad->ad_version ); - memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler )); - memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries )); - nentries = ntohs( nentries ); + if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION_EA))) { + LOG(log_error, logtype_default, "ad_open: can't parse AppleDouble header."); + errno = EIO; + return -1; +} - /* read in all the entry headers. if we have more than the - * maximum, just hope that the rfork is specified early on. */ - len = nentries*AD_ENTRY_LEN; +memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries )); +nentries = ntohs( nentries ); - if (len + AD_HEADER_LEN > sizeof(ad->ad_data)) - len = sizeof(ad->ad_data) - AD_HEADER_LEN; - - buf += AD_HEADER_LEN; - if (len > header_len - AD_HEADER_LEN) { - LOG(log_debug, logtype_default, "ad_header_read: can't read entry info."); - errno = EIO; - return -1; - } +len = nentries * AD_ENTRY_LEN; +if (len + AD_HEADER_LEN > sizeof(ad->ad_data)) + len = sizeof(ad->ad_data) - AD_HEADER_LEN; - /* figure out all of the entry offsets and lengths. if we aren't - * able to read a resource fork entry, bail. */ - nentries = len / AD_ENTRY_LEN; - parse_entries(ad, buf, nentries); - if (!ad_getentryoff(ad, ADEID_RFORK) - || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data)) - ) { - LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset."); - errno = EIO; - return -1; - } +buf += AD_HEADER_LEN; +if (len > header_len - AD_HEADER_LEN) { + LOG(log_error, logtype_default, "ad_header_read: can't read entry info."); + errno = EIO; + return -1; +} - if (ad_getentryoff(ad, ADEID_RFORK) > header_len) { - LOG(log_debug, logtype_default, "ad_header_read: can't read in entries."); - errno = EIO; - return -1; - } +/* figure out all of the entry offsets and lengths */ +nentries = len / AD_ENTRY_LEN; +parse_entries(ad, buf, nentries); - if (hst == NULL) { - hst = &st; - if (fstat(ad->ad_md->adf_fd, &st) < 0) { - return 1; /* fail silently */ - } +if (hst == NULL) { + hst = &st; + if (fstat(ad->ad_md->adf_fd, &st) < 0) { + return 1; /* fail silently */ } - ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK); +} - /* fix up broken dates */ - if (ad->ad_version == AD_VERSION1) { - u_int32_t aint; +ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK); - /* check to see if the ad date is wrong. just see if we have - * a modification date in the future. */ - if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) && - (aint > TIMEWARP_DELTA + hst->st_mtime)) { - ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA); - ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint); - ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA); - ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint); - ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA); - } - } - - return 0; +return 0; } /* @@ -788,46 +725,6 @@ static int ad_mkrf(char *path) return 0; } - -/*********************************************************************** - * OS X adouble scheme funcs - ***********************************************************************/ - -/* - * Put the resource fork where it needs to be: ._name - */ -char *ad_path_osx(const char *path, int adflags _U_) -{ - static char pathbuf[ MAXPATHLEN + 1]; - char c, *slash, buf[MAXPATHLEN + 1]; - - if (!strcmp(path,".")) { - /* fixme */ - getcwd(buf, MAXPATHLEN); - } - else { - strlcpy(buf, path, MAXPATHLEN +1); - } - if (NULL != ( slash = strrchr( buf, '/' )) ) { - c = *++slash; - *slash = '\0'; - strlcpy( pathbuf, buf, MAXPATHLEN +1); - *slash = c; - } else { - pathbuf[ 0 ] = '\0'; - slash = buf; - } - strlcat( pathbuf, "._", MAXPATHLEN +1); - strlcat( pathbuf, slash, MAXPATHLEN +1); - return pathbuf; -} - -/* -------------------- */ -static int ad_mkrf_osx(char *path _U_) -{ - return 0; -} - /* ------------------------- * Support inherited protection modes for AppleDouble files. The supplied * mode is ANDed with the parent directory's mask value in lieu of "umask", @@ -957,8 +854,7 @@ static int ad_mode_st(const char *path, int *mode, struct stat *stbuf) /* ---------------- return access right of path parent directory */ -int -ad_mode( const char *path, int mode) +int ad_mode( const char *path, int mode) { struct stat stbuf; ad_mode_st(path, &mode, &stbuf); @@ -968,8 +864,7 @@ ad_mode( const char *path, int mode) /* * Use mkdir() with mode bits taken from ad_mode(). */ -int -ad_mkdir( const char *path, int mode) +int ad_mkdir( const char *path, int mode) { int ret; int st_invalid; @@ -1043,15 +938,6 @@ static int ad_header_upgrade_none(struct adouble *ad _U_, char *name _U_) } /* --------------------------- */ -static struct adouble_fops ad_osx = { - &ad_path_osx, - &ad_mkrf_osx, - &ad_rebuild_adouble_header, - &ad_check_size, - &ad_header_read, - &ad_header_upgrade, -}; - static struct adouble_fops ad_adouble = { &ad_path, &ad_mkrf, @@ -1074,64 +960,32 @@ void ad_init(struct adouble *ad, int flags, int options) { ad->ad_inited = 0; ad->ad_flags = flags; - if (flags == AD_VERSION2_OSX) { - ad->ad_ops = &ad_osx; - ad->ad_md = &ad->ad_resource_fork; - } - else if (flags == AD_VERSION2) { + if (flags == AD_VERSION2) { ad->ad_ops = &ad_adouble; ad->ad_md = &ad->ad_resource_fork; } - else { + else if (flags == AD_VERSION_EA) { ad->ad_ops = &ad_adouble_ea; ad->ad_md = &ad->ad_metadata_fork; + } else { + LOG(log_error, logtype_default, "ad_init: unknown AD version"); + errno = EIO; + return; } + ad->ad_options = options; ad_data_fileno(ad) = -1; ad_reso_fileno(ad) = -1; ad_meta_fileno(ad) = -1; + /* following can be read even if there's no meda data. */ memset(ad->ad_eid, 0, sizeof( ad->ad_eid )); ad->ad_rlen = 0; } -/*! - * Open data-, metadata(header)- or ressource fork - * - * You must call ad_init() before ad_open, usually you'll just call it like this: \n - * @code - * struct adoube ad; - * ad_init(&ad, vol->v_adouble, vol->v_ad_options); - * @endcode - * - * @param path Path to file or directory - * @param adflags ADFLAGS_DF: open data file/fork\n - * ADFLAGS_HF: open header (metadata) file\n - * ADFLAGS_RF: open ressource fork *** FIXME: not used ?! *** \n - * ADFLAGS_CREATE: indicate creation\n - * ADFLAGS_NOHF: it's not an error if header file couldn't be created\n - * ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags\n - * ADFLAGS_NOADOUBLE: dont create adouble files if not necessary\n - * ADFLAGS_RDONLY: open read only\n - * ADFLAGS_OPENFORKS: check for open forks from other processes\n - * ADFLAGS_MD: alias for ADFLAGS_HF\n - * ADFLAGS_V1COMPAT: obsolete - * @param oflags flags passed through to open syscall: \n - * O_RDONLY: *** FIXME *** \n - * O_RDWR: *** FIXME *** \n - * O_CREAT: create fork\n - * O_EXCL: fail if exists with O_CREAT - * @param mode passed to open with O_CREAT - * @param ad pointer to struct adouble - * - * @returns 0 on success - * - * @note It's not possible to open the header file O_RDONLY -- the read - * will fail and return an error. this refcounts things now.\n - * metadata(ressource)-fork only gets created with O_CREAT. - */ -int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble *ad) + +static int ad_open_df(const char *path, int adflags, int oflags, int mode, struct adouble *ad) { struct stat st_dir; struct stat st_meta; @@ -1141,89 +995,78 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble int st_invalid = -1; int open_df = 0; - if (ad->ad_inited != AD_INITED) { - ad->ad_inited = AD_INITED; - ad->ad_refcount = 1; - ad->ad_open_forks = 0; - ad->ad_adflags = adflags; - ad->ad_resource_fork.adf_refcount = 0; - ad->ad_data_fork.adf_refcount = 0; - ad->ad_data_fork.adf_syml=0; - } - else { - ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0); - /* XXX not true if we have a meta data fork ? */ - if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount)) - ad->ad_open_forks |= ATTRBIT_ROPEN; - } + if (ad_data_fileno(ad) == -1) { + hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; + admode = mode; + if ((oflags & O_CREAT)) { + st_invalid = ad_mode_st(path, &admode, &st_dir); + if ((ad->ad_options & ADVOL_UNIXPRIV)) { + admode = mode; + } + } - if ((adflags & ADFLAGS_DF)) { - if (ad_data_fileno(ad) == -1) { - hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; - admode = mode; - if ((oflags & O_CREAT)) { - st_invalid = ad_mode_st(path, &admode, &st_dir); - if ((ad->ad_options & ADVOL_UNIXPRIV)) { - admode = mode; - } + ad->ad_data_fork.adf_fd =open( path, hoflags | O_NOFOLLOW, 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 | O_NOFOLLOW, 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 ); - } - if (ad->ad_data_fork.adf_fd == -1 && errno == OPEN_NOFOLLOW_ERRNO) { - int lsz; - - ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1); - lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN); - if (lsz <= 0) { - free(ad->ad_data_fork.adf_syml); - return -1; - } - ad->ad_data_fork.adf_syml[lsz] = 0; - ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */ + if (ad->ad_data_fork.adf_fd == -1 && errno == OPEN_NOFOLLOW_ERRNO) { + int lsz; + + ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1); + lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN); + if (lsz <= 0) { + free(ad->ad_data_fork.adf_syml); + return -1; } + ad->ad_data_fork.adf_syml[lsz] = 0; + ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */ } + } - if ( ad->ad_data_fork.adf_fd == -1 ) - return -1; + if ( ad->ad_data_fork.adf_fd == -1 ) + return -1; - AD_SET(ad->ad_data_fork.adf_off); - ad->ad_data_fork.adf_flags = hoflags; - if (!st_invalid) { - /* just created, set owner if admin (root) */ - ad_chown(path, &st_dir); - } - adf_lock_init(&ad->ad_data_fork); + AD_SET(ad->ad_data_fork.adf_off); + ad->ad_data_fork.adf_flags = hoflags; + if (!st_invalid) { + /* just created, set owner if admin (root) */ + ad_chown(path, &st_dir); } - else { - /* the file is already open... but */ - if ((oflags & ( O_RDWR | O_WRONLY)) && /* we want write access */ - !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */ - { - errno = EACCES; - return -1; - } - /* FIXME - * for now ad_open is never called with O_TRUNC or O_EXCL if the file is - * already open. Should we check for it? ie - * O_EXCL --> error - * O_TRUNC --> truncate the fork. - * idem for ressource fork. - */ + adf_lock_init(&ad->ad_data_fork); + } else { + /* the file is already open... but */ + if ((oflags & ( O_RDWR | O_WRONLY)) /* we want write access */ + && + !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) { /* and it was denied the first time */ + errno = EACCES; + return -1; } - open_df = ADFLAGS_DF; - ad->ad_data_fork.adf_refcount++; + /* FIXME + * for now ad_open is never called with O_TRUNC or O_EXCL if the file is + * already open. Should we check for it? ie + * O_EXCL --> error + * O_TRUNC --> truncate the fork. + * idem for ressource fork. + */ } + open_df = ADFLAGS_DF; + ad->ad_data_fork.adf_refcount++; - if (!(adflags & ADFLAGS_HF)) - return 0; + return 0; +} - /* ****************************************** */ +static int ad_open_hf(const char *path, int adflags, int oflags, int mode, struct adouble *ad) +{ + struct stat st_dir; + struct stat st_meta; + struct stat *pst = NULL; + char *ad_p; + int hoflags, admode; + int st_invalid = -1; + int open_df = 0; if (ad_meta_fileno(ad) != -1) { /* the file is already open */ if ((oflags & ( O_RDWR | O_WRONLY)) && @@ -1239,7 +1082,7 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble /* it's not new anymore */ ad->ad_md->adf_flags &= ~( O_TRUNC | O_CREAT ); ad->ad_md->adf_refcount++; - goto sfm; + return 0; } memset(ad->ad_eid, 0, sizeof( ad->ad_eid )); @@ -1295,8 +1138,7 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble if (!st_invalid) { ad_chown(ad_p, &st_dir); } - } - else { + } else { return ad_error(ad, adflags); } } else { @@ -1304,11 +1146,8 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble if (fstat(ad->ad_md->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) { /* for 0 length files, treat them as new. */ ad->ad_md->adf_flags |= O_TRUNC; - } - else { - /* we have valid data in st_meta stat structure, reused it - in ad_header_read - */ + } else { + /* we have valid data in st_meta stat structure, reused it in ad_header_read */ pst = &st_meta; } } @@ -1342,13 +1181,85 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble } } - /* ****************************************** */ - /* open the resource fork if SFM */ -sfm: - if (ad->ad_flags != AD_VERSION1_SFM) { - return 0; + return 0; +} + +/*! + * Open data-, metadata(header)- or ressource fork + * + * You must call ad_init() before ad_open, usually you'll just call it like this: \n + * @code + * struct adoube ad; + * ad_init(&ad, vol->v_adouble, vol->v_ad_options); + * @endcode + * + * @param path Path to file or directory + * + * @param adflags ADFLAGS_DF: open data file/fork \n + * ADFLAGS_HF: open header (metadata) file \n + * ADFLAGS_CREATE: indicate creation \n + * ADFLAGS_NOHF: it's not an error if header file couldn't be created \n + * ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags \n + * ADFLAGS_NOADOUBLE: dont create adouble files if not necessary \n + * ADFLAGS_RDONLY: open read only \n + * ADFLAGS_OPENFORKS: check for open forks from other processes + * + * @param oflags flags passed through to open syscall: \n + * O_RDONLY: *** FIXME *** \n + * O_RDWR: *** FIXME *** \n + * O_CREAT: create fork \n + * O_EXCL: fail if exists with O_CREAT + * + * @param mode passed to open with O_CREAT + * @param ad pointer to struct adouble + * + * @returns 0 on success + * + * @note It's not possible to open the header file O_RDONLY -- the read + * will fail and return an error. this refcounts things now.\n + * metadata(ressource)-fork only gets created with O_CREAT. + */ +int ad_open(const char *path, int adflags, int oflags, int mode, struct adouble *ad) +{ + struct stat st_dir; + struct stat st_meta; + struct stat *pst = NULL; + char *ad_p; + int hoflags, admode; + int st_invalid = -1; + int open_df = 0; + + if (ad->ad_inited != AD_INITED) { + ad->ad_inited = AD_INITED; + ad->ad_refcount = 1; + ad->ad_open_forks = 0; + ad->ad_adflags = adflags; + ad->ad_resource_fork.adf_refcount = 0; + ad->ad_data_fork.adf_refcount = 0; + ad->ad_data_fork.adf_syml = 0; + } else { + ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0); + /* XXX not true if we have a meta data fork ? */ + if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount)) + ad->ad_open_forks |= ATTRBIT_ROPEN; + } + + if ((adflags & ADFLAGS_DF)) { + if (ad_open_df(path, adflags, oflags, mode, ad) != 0) + return -1; } + if ((adflags & ADFLAGS_HF)) { + if (ad_open_hf(path, adflags, oflags, mode, ad) != 0) + return -1; + } + + return 0; +} + +#if 0 +void sfm(void) +{ if ((adflags & ADFLAGS_DIR)) { /* no resource fork for directories / volumes XXX it's false! */ return 0; @@ -1406,6 +1317,7 @@ sfm: } return 0 ; } +#endif /*! * @brief open metadata, possibly as root diff --git a/libatalk/vfs/Makefile.am b/libatalk/vfs/Makefile.am index 9b8d1437..639f51b3 100644 --- a/libatalk/vfs/Makefile.am +++ b/libatalk/vfs/Makefile.am @@ -2,7 +2,7 @@ noinst_LTLIBRARIES = libvfs.la -libvfs_la_SOURCES = vfs.c unix.c ea.c sys_ea.c ea_sys.c +libvfs_la_SOURCES = vfs.c unix.c extattr.c ea_ad.c ea_sys.c if HAVE_ACLS libvfs_la_SOURCES += acl.c diff --git a/libatalk/vfs/ea.c b/libatalk/vfs/ea.c deleted file mode 100644 index fa3a0109..00000000 --- a/libatalk/vfs/ea.c +++ /dev/null @@ -1,1768 +0,0 @@ -/* - Copyright (c) 2009 Frank Lahm - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Store Extended Attributes inside .AppleDouble folders as follows: - * - * filename "fileWithEAs" with EAs "testEA1" and "testEA2" - * - * - create header with with the format struct adouble_ea_ondisk, the file is written to - * ".AppleDouble/fileWithEAs::EA" - * - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2" - */ - -/* - * Build mode for EA header from file mode - */ -static inline mode_t ea_header_mode(mode_t mode) -{ - /* Same as ad_hf_mode(mode) */ - mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); - /* Owner must be able to open, read and w-lock it, in order to chmod from eg 0000 -> 0xxxx*/ - mode |= S_IRUSR | S_IWUSR; - return mode; -} - -/* - * Build mode for EA file from file mode - */ -static inline mode_t ea_mode(mode_t mode) -{ - /* Same as ad_hf_mode(mode) */ - mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); - return mode; -} - -/* - Taken form afpd/desktop.c -*/ -static char *mtoupath(const struct vol *vol, const char *mpath) -{ - static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */ - const char *m; - char *u; - size_t inplen; - size_t outlen; - uint16_t flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON; - - if (!mpath) - return NULL; - - if ( *mpath == '\0' ) { - return( "." ); - } - - m = mpath; - u = upath; - - inplen = strlen(m); - outlen = MAXPATHLEN; - - if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC, - vol->v_volcharset, - vol->v_maccharset, - m, inplen, u, outlen, &flags)) ) { - return NULL; - } - - return( upath ); -} - - -/* - * Function: unpack_header - * - * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea - * - * Arguments: - * - * ea (rw) handle to struct ea - * - * Returns: 0 on success, -1 on error - * - * Effects: - * - * Verifies magic and version. - */ -static int unpack_header(struct ea * restrict ea) -{ - int ret = 0; - unsigned int count = 0; - uint32_t uint32; - char *buf; - - /* Check magic and version */ - buf = ea->ea_data; - if (*(uint32_t *)buf != htonl(EA_MAGIC)) { - LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf); - ret = -1; - goto exit; - } - buf += 4; - if (*(uint16_t *)buf != htons(EA_VERSION)) { - LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf); - ret = -1; - goto exit; - } - buf += 2; - - /* Get EA count */ - ea->ea_count = ntohs(*(uint16_t *)buf); - LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count); - buf += 2; - - if (ea->ea_count == 0) - return 0; - - /* Allocate storage for the ea_entries array */ - ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count); - if ( ! ea->ea_entries) { - LOG(log_error, logtype_afpd, "unpack_header: OOM"); - ret = -1; - goto exit; - } - - buf = ea->ea_data + EA_HEADER_SIZE; - while (count < ea->ea_count) { - memcpy(&uint32, buf, 4); /* EA size */ - buf += 4; - (*(ea->ea_entries))[count].ea_size = ntohl(uint32); - (*(ea->ea_entries))[count].ea_name = strdup(buf); - if (! (*(ea->ea_entries))[count].ea_name) { - LOG(log_error, logtype_afpd, "unpack_header: OOM"); - ret = -1; - goto exit; - } - (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name); - buf += (*(ea->ea_entries))[count].ea_namelen + 1; - - LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count, - (*(ea->ea_entries))[count].ea_name, - (*(ea->ea_entries))[count].ea_size, - (*(ea->ea_entries))[count].ea_namelen); - - count++; - } - -exit: - return ret; -} - -/* - * Function: pack_header - * - * Purpose: pack everything from struct ea into buffer at ea->ea_data - * - * Arguments: - * - * ea (rw) handle to struct ea - * - * Returns: 0 on success, -1 on error - * - * Effects: - * - * adjust ea->ea_count in case an ea entry deletetion is detected - */ -static int pack_header(struct ea * restrict ea) -{ - unsigned int count = 0, eacount = 0; - uint16_t uint16; - uint32_t uint32; - size_t bufsize = EA_HEADER_SIZE; - - char *buf = ea->ea_data + EA_HEADER_SIZE; - - LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u", - ea->filename, ea->ea_count, ea->ea_size); - - if (ea->ea_count == 0) - /* nothing to do, magic, version and count are still valid in buffer */ - return 0; - - while(count < ea->ea_count) { /* the names */ - /* Check if its a deleted entry */ - if ( ! ((*ea->ea_entries)[count].ea_name)) { - count++; - continue; - } - - bufsize += (*(ea->ea_entries))[count].ea_namelen + 1; - count++; - eacount++; - } - - bufsize += (eacount * 4); /* header + ea_size for each EA */ - if (bufsize > ea->ea_size) { - /* we must realloc */ - if ( ! (buf = realloc(ea->ea_data, bufsize)) ) { - LOG(log_error, logtype_afpd, "pack_header: OOM"); - return -1; - } - ea->ea_data = buf; - } - ea->ea_size = bufsize; - - /* copy count */ - uint16 = htons(eacount); - memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2); - - count = 0; - buf = ea->ea_data + EA_HEADER_SIZE; - while (count < ea->ea_count) { - /* Check if its a deleted entry */ - if ( ! ((*ea->ea_entries)[count].ea_name)) { - count++; - continue; - } - - /* First: EA size */ - uint32 = htonl((*(ea->ea_entries))[count].ea_size); - memcpy(buf, &uint32, 4); - buf += 4; - - /* Second: EA name as C-string */ - strcpy(buf, (*(ea->ea_entries))[count].ea_name); - buf += (*(ea->ea_entries))[count].ea_namelen + 1; - - LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count, - (*(ea->ea_entries))[count].ea_name, - (*(ea->ea_entries))[count].ea_size, - (*(ea->ea_entries))[count].ea_namelen); - - count++; - } - - ea->ea_count = eacount; - - LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u", - ea->filename, ea->ea_count, ea->ea_size); - - return 0; -} - -/* - * Function: ea_addentry - * - * Purpose: add one EA into ea->ea_entries[] - * - * Arguments: - * - * ea (rw) pointer to struct ea - * attruname (r) name of EA - * attrsize (r) size of ea - * bitmap (r) bitmap from FP func - * - * Returns: new number of EA entries, -1 on error - * - * Effects: - * - * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating. - * Otherwise realloc and put entry at the end. Increments ea->ea_count. - */ -static int ea_addentry(struct ea * restrict ea, - const char * restrict attruname, - size_t attrsize, - int bitmap) -{ - int ea_existed = 0; - unsigned int count = 0; - void *tmprealloc; - - /* First check if an EA of the requested name already exist */ - if (ea->ea_count > 0) { - while (count < ea->ea_count) { - if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) { - ea_existed = 1; - LOG(log_debug, logtype_afpd, "ea_addentry('%s', bitmap:0x%x): exists", attruname, bitmap); - if (bitmap & kXAttrCreate) - /* its like O_CREAT|O_EXCL -> fail */ - return -1; - (*(ea->ea_entries))[count].ea_size = attrsize; - return 0; - } - count++; - } - } - - if ((bitmap & kXAttrReplace) && ! ea_existed) - /* replace was requested, but EA didn't exist */ - return -1; - - if (ea->ea_count == 0) { - ea->ea_entries = malloc(sizeof(struct ea_entry)); - if ( ! ea->ea_entries) { - LOG(log_error, logtype_afpd, "ea_addentry: OOM"); - return -1; - } - } else if (! ea_existed) { - tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1)); - if ( ! tmprealloc) { - LOG(log_error, logtype_afpd, "ea_addentry: OOM"); - return -1; - } - ea->ea_entries = tmprealloc; - } - - /* We've grown the array, now store the entry */ - (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize; - (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname); - if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) { - LOG(log_error, logtype_afpd, "ea_addentry: OOM"); - goto error; - } - (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname); - - ea->ea_count++; - return ea->ea_count; - -error: - if (ea->ea_count == 0 && ea->ea_entries) { - /* We just allocated storage but had an error somewhere -> free storage*/ - free(ea->ea_entries); - ea->ea_entries = NULL; - } - ea->ea_count = 0; - return -1; -} - -/* - * Function: create_ea_header - * - * Purpose: create EA header file, only called from ea_open - * - * Arguments: - * - * uname (r) filename for which we have to create a header - * ea (rw) ea handle with already allocated storage pointed to - * by ea->ea_data - * - * Returns: fd of open header file on success, -1 on error, errno semantics: - * EEXIST: open with O_CREAT | O_EXCL failed - * - * Effects: - * - * Creates EA header file and initialize ea->ea_data buffer. - * Possibe race condition with other afpd processes: - * we were called because header file didn't exist in eg. ea_open. We then - * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic. - * What do we do then? Someone else is in the process of creating the header too, but - * it might not have finished it. That means we cant just open, read and use it! - * We therefor currently just break with an error. - * On return the header file is still r/w locked. - */ -static int create_ea_header(const char * restrict uname, - struct ea * restrict ea) -{ - int fd = -1, err = 0; - char *ptr; - - if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) { - LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname); - return -1; - } - - /* lock it */ - if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) { - LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname); - err = -1; - goto exit; - } - - /* Now init it */ - ptr = ea->ea_data; - *(uint32_t *)ptr = htonl(EA_MAGIC); - ptr += EA_MAGIC_LEN; - *(uint16_t *)ptr = htons(EA_VERSION); - ptr += EA_VERSION_LEN; - *(uint16_t *)ptr = 0; /* count */ - - ea->ea_size = EA_HEADER_SIZE; - ea->ea_inited = EA_INITED; - -exit: - if (err != 0) { - close(fd); - fd = -1; - } - return fd; -} - -/* - * Function: write_ea - * - * Purpose: write an EA to disk - * - * Arguments: - * - * ea (r) struct ea handle - * attruname (r) EA name - * ibuf (r) buffer with EA content - * attrsize (r) size of EA - * - * Returns: 0 on success, -1 on error - * - * Effects: - * - * Creates/overwrites EA file. - * - */ -static int write_ea(const struct ea * restrict ea, - const char * restrict attruname, - const char * restrict ibuf, - size_t attrsize) -{ - int fd = -1, ret = AFP_OK; - struct stat st; - char *eaname; - - if ((eaname = ea_path(ea, attruname, 1)) == NULL) { - LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname); - return AFPERR_MISC; - } - - LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname); - - /* Check if it exists, remove if yes*/ - if ((stat(eaname, &st)) == 0) { - if ((unlink(eaname)) != 0) { - if (errno == EACCES) - return AFPERR_ACCESS; - else - return AFPERR_MISC; - } - } - - if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) { - LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname); - return -1; - } - - /* lock it */ - if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) { - LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname); - ret = -1; - goto exit; - } - - if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) { - LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno)); - ret = -1; - goto exit; - } - -exit: - if (fd != -1) - close(fd); /* and unlock */ - return ret; -} - -/* - * Function: ea_delentry - * - * Purpose: delete one EA from ea->ea_entries[] - * - * Arguments: - * - * ea (rw) pointer to struct ea - * attruname (r) EA name - * - * Returns: new number of EA entries, -1 on error - * - * Effects: - * - * Remove entry from ea->ea_entries[]. Decrement ea->ea_count. - * Marks it as unused just by freeing name and setting it to NULL. - * ea_close and pack_buffer must honor this. - */ -static int ea_delentry(struct ea * restrict ea, const char * restrict attruname) -{ - int ret = 0; - unsigned int count = 0; - - if (ea->ea_count == 0) { - LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion"); - return -1; - } - - while (count < ea->ea_count) { - /* search matching EA */ - if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) { - free((*ea->ea_entries)[count].ea_name); - (*ea->ea_entries)[count].ea_name = NULL; - - LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u", - attruname, count + 1, ea->ea_count); - - break; - } - count++; - } - - return ret; -} - -/* - * Function: delete_ea_file - * - * Purpose: delete EA file from disk - * - * Arguments: - * - * ea (r) struct ea handle - * attruname (r) EA name - * - * Returns: 0 on success, -1 on error - */ -static int delete_ea_file(const struct ea * restrict ea, const char *eaname) -{ - int ret = 0; - char *eafile; - struct stat st; - - if ((eafile = ea_path(ea, eaname, 1)) == NULL) { - LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname); - return -1; - } - - /* Check if it exists, remove if yes*/ - if ((stat(eafile, &st)) == 0) { - if ((unlink(eafile)) != 0) { - LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s", - eafile, strerror(errno)); - ret = -1; - } else - LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile); - } - - return ret; -} - -/************************************************************************************* - * ea_path, ea_open and ea_close are only global so that dbd can call them - *************************************************************************************/ - -/* - * Function: ea_path - * - * Purpose: return name of ea header filename - * - * Arguments: - * - * ea (r) ea handle - * eaname (r) name of EA or NULL - * macname (r) if != 0 call mtoupath on eaname - * - * Returns: pointer to name in static buffer, NULL on error - * - * Effects: - * - * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme - * Files: "file" -> "file/.AppleDouble/file::EA" - * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA" - * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA" - */ -char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname) -{ - char *adname; - static char pathbuf[MAXPATHLEN + 1]; - - /* get name of a adouble file from uname */ - adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0); - /* copy it so we can work with it */ - strlcpy(pathbuf, adname, MAXPATHLEN + 1); - /* append "::EA" */ - strlcat(pathbuf, "::EA", MAXPATHLEN + 1); - - if (eaname) { - strlcat(pathbuf, "::", MAXPATHLEN + 1); - if (macname) - if ((eaname = mtoupath(ea->vol, eaname)) == NULL) - return NULL; - strlcat(pathbuf, eaname, MAXPATHLEN + 1); - } - - return pathbuf; -} - -/* - * Function: ea_open - * - * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE - * - * Arguments: - * - * vol (r) current volume - * uname (r) filename for which we have to open a header - * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created) - * EA_RDONLY: open read only - * EA_RDWR: open read/write - * Eiterh EA_RDONLY or EA_RDWR MUST be requested - * ea (w) pointer to a struct ea that we fill - * - * Returns: 0 on success - * -1 on misc error with errno = EFAULT - * -2 if no EA header exists with errno = ENOENT - * - * Effects: - * - * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size. - * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags. - * file is either read or write locked depending on the open flags. - * When you're done with struct ea you must call ea_close on it. - */ -int ea_open(const struct vol * restrict vol, - const char * restrict uname, - eaflags_t eaflags, - struct ea * restrict ea) -{ - int ret = 0; - char *eaname; - struct stat st; - - /* Enforce usage rules! */ - if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) { - LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname); - return -1; - } - - /* Set it all to 0 */ - memset(ea, 0, sizeof(struct ea)); - - ea->vol = vol; /* ea_close needs it */ - ea->ea_flags = eaflags; - ea->dirfd = -1; /* no *at (cf openat) semantics by default */ - - /* Dont care for errors, eg when removing the file is already gone */ - if (!stat(uname, &st) && S_ISDIR(st.st_mode)) - ea->ea_flags |= EA_DIR; - - if ( ! (ea->filename = strdup(uname))) { - LOG(log_error, logtype_afpd, "ea_open: OOM"); - return -1; - } - - eaname = ea_path(ea, NULL, 0); - LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname); - - /* Check if it exists, if not create it if EA_CREATE is in eaflags */ - if ((stat(eaname, &st)) != 0) { - if (errno == ENOENT) { - - /* It doesnt exist */ - - if ( ! (eaflags & EA_CREATE)) { - /* creation was not requested, so return with error */ - ret = -2; - goto exit; - } - - /* Now create a header file */ - - /* malloc buffer for minimal on disk data */ - ea->ea_data = malloc(EA_HEADER_SIZE); - if (! ea->ea_data) { - LOG(log_error, logtype_afpd, "ea_open: OOM"); - ret = -1; - goto exit; - } - - /* create it */ - ea->ea_fd = create_ea_header(eaname, ea); - if (ea->ea_fd == -1) { - ret = -1; - goto exit; - } - - return 0; - - } else {/* errno != ENOENT */ - ret = -1; - goto exit; - } - } - - /* header file exists, so read and parse it */ - - /* malloc buffer where we read disk file into */ - if (st.st_size < EA_HEADER_SIZE) { - LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname); - ret = -1; - goto exit; - } - ea->ea_size = st.st_size; - ea->ea_data = malloc(st.st_size); - if (! ea->ea_data) { - LOG(log_error, logtype_afpd, "ea_open: OOM"); - ret = -1; - goto exit; - } - - /* Now lock, open and read header file from disk */ - if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) { - LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno)); - ret = -1; - goto exit; - } - - /* lock it */ - if (ea->ea_flags & EA_RDONLY) { - /* read lock */ - if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) { - LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname); - ret = -1; - goto exit; - } - } else { /* EA_RDWR */ - /* write lock */ - if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) { - LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname); - ret = -1; - goto exit; - } - } - - /* read it */ - if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { - LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname); - ret = -1; - goto exit; - } - - if ((unpack_header(ea)) != 0) { - LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname); - ret = -1; - goto exit; - } - -exit: - switch (ret) { - case 0: - ea->ea_inited = EA_INITED; - break; - case -1: - errno = EFAULT; /* force some errno distinguishable from ENOENT */ - /* fall through */ - case -2: - if (ea->ea_data) { - free(ea->ea_data); - ea->ea_data = NULL; - } - if (ea->ea_fd) { - close(ea->ea_fd); - ea->ea_fd = -1; - } - break; - } - - return ret; -} - -/* - * Function: ea_openat - * - * Purpose: openat like wrapper for ea_open, takes a additional file descriptor - * - * Arguments: - * - * vol (r) current volume - * sfd (r) openat like file descriptor - * uname (r) filename for which we have to open a header - * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created) - * EA_RDONLY: open read only - * EA_RDWR: open read/write - * Eiterh EA_RDONLY or EA_RDWR MUST be requested - * ea (w) pointer to a struct ea that we fill - * - * Returns: 0 on success - * -1 on misc error with errno = EFAULT - * -2 if no EA header exists with errno = ENOENT - * - * Effects: - * - * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size. - * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags. - * file is either read or write locked depending on the open flags. - * When you're done with struct ea you must call ea_close on it. - */ -int ea_openat(const struct vol * restrict vol, - int dirfd, - const char * restrict uname, - eaflags_t eaflags, - struct ea * restrict ea) -{ - int ret = 0; - int cwdfd = -1; - - if (dirfd != -1) { - if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { - ret = -1; - goto exit; - } - } - - ret = ea_open(vol, uname, eaflags, ea); - ea->dirfd = dirfd; - - if (dirfd != -1) { - if (fchdir(cwdfd) != 0) { - LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting"); - exit(EXITERR_SYS); - } - } - - -exit: - if (cwdfd != -1) - close(cwdfd); - - return ret; - -} - -/* - * Function: ea_close - * - * Purpose: flushes and closes an ea handle - * - * Arguments: - * - * ea (rw) pointer to ea handle - * - * Returns: 0 on success, -1 on error - * - * Effects: - * - * Flushes and then closes and frees all resouces held by ea handle. - * Pack data in ea into ea_data, then write ea_data to disk - */ -int ea_close(struct ea * restrict ea) -{ - int ret = 0; - unsigned int count = 0; - char *eaname; - struct stat st; - - LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename); - - if (ea->ea_inited != EA_INITED) { - LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename); - return 0; - } - - /* pack header and write it to disk if it was opened EA_RDWR*/ - if (ea->ea_flags & EA_RDWR) { - if ((pack_header(ea)) != 0) { - LOG(log_error, logtype_afpd, "ea_close: pack header"); - ret = -1; - } else { - 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 ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) { - LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s", - eaname, strerror(errno)); - ret = -1; - } - else - LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname); - } else { - /* stat error */ - if (errno != ENOENT) { - LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s", - eaname, strerror(errno)); - ret = -1; - } - } - } else { /* ea->ea_count > 0 */ - if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) { - LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno)); - ret = -1; - goto exit; - } - - if ((ftruncate(ea->ea_fd, 0)) == -1) { - LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno)); - ret = -1; - goto exit; - } - - if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { - LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno)); - ret = -1; - } - } - } - } - -exit: - /* free names */ - while(count < ea->ea_count) { - if ( (*ea->ea_entries)[count].ea_name ) { - free((*ea->ea_entries)[count].ea_name); - (*ea->ea_entries)[count].ea_name = NULL; - } - count++; - } - ea->ea_count = 0; - - if (ea->filename) { - free(ea->filename); - ea->filename = NULL; - } - - if (ea->ea_entries) { - free(ea->ea_entries); - ea->ea_entries = NULL; - } - - if (ea->ea_data) { - free(ea->ea_data); - ea->ea_data = NULL; - } - if (ea->ea_fd != -1) { - close(ea->ea_fd); /* also releases the fcntl lock */ - ea->ea_fd = -1; - } - - return 0; -} - - - -/************************************************************************************ - * VFS funcs called from afp_ea* funcs - ************************************************************************************/ - -/* - * Function: get_easize - * - * Purpose: get size of an EA - * - * Arguments: - * - * vol (r) current volume - * rbuf (w) DSI reply buffer - * rbuflen (rw) current length of data in reply buffer - * uname (r) filename - * oflag (r) link and create flag - * attruname (r) name of attribute - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Copies EA size into rbuf in network order. Increments *rbuflen +4. - */ -int get_easize(VFS_FUNC_ARGS_EA_GETSIZE) -{ - int ret = AFPERR_MISC; - unsigned int count = 0; - uint32_t uint32; - struct ea ea; - - LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname); - - if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { - if (errno != ENOENT) - LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname); - - memset(rbuf, 0, 4); - *rbuflen += 4; - return ret; - } - - while (count < ea.ea_count) { - if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) { - uint32 = htonl((*ea.ea_entries)[count].ea_size); - memcpy(rbuf, &uint32, 4); - *rbuflen += 4; - ret = AFP_OK; - - LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u", - attruname, (*ea.ea_entries)[count].ea_size); - break; - } - count++; - } - - if ((ea_close(&ea)) != 0) { - LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname); - return AFPERR_MISC; - } - - return ret; -} - -/* - * Function: get_eacontent - * - * Purpose: copy EA into rbuf - * - * Arguments: - * - * vol (r) current volume - * rbuf (w) DSI reply buffer - * rbuflen (rw) current length of data in reply buffer - * uname (r) filename - * oflag (r) link and create flag - * attruname (r) name of attribute - * maxreply (r) maximum EA size as of current specs/real-life - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Copies EA into rbuf. Increments *rbuflen accordingly. - */ -int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT) -{ - int ret = AFPERR_MISC, fd = -1; - unsigned int count = 0; - uint32_t uint32; - size_t toread; - struct ea ea; - char *eafile; - - LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname); - - if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { - if (errno != ENOENT) - LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname); - memset(rbuf, 0, 4); - *rbuflen += 4; - return ret; - } - - while (count < ea.ea_count) { - if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) { - if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) { - ret = AFPERR_MISC; - break; - } - - if ((fd = open(eafile, O_RDONLY)) == -1) { - LOG(log_error, logtype_afpd, "get_eacontent('%s'): open error: %s", uname, strerror(errno)); - ret = AFPERR_MISC; - break; - } - - /* Check how much the client wants, give him what we think is right */ - maxreply -= MAX_REPLY_EXTRA_BYTES; - if (maxreply > MAX_EA_SIZE) - maxreply = MAX_EA_SIZE; - toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size; - LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread); - - /* Put length of EA data in reply buffer */ - uint32 = htonl(toread); - memcpy(rbuf, &uint32, 4); - rbuf += 4; - *rbuflen += 4; - - if (read(fd, rbuf, toread) != (ssize_t)toread) { - LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname); - close(fd); - ret = AFPERR_MISC; - break; - } - *rbuflen += toread; - close(fd); - - ret = AFP_OK; - break; - } - count++; - } - - if ((ea_close(&ea)) != 0) { - LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname); - return AFPERR_MISC; - } - - return ret; - -} - -/* - * Function: list_eas - * - * Purpose: copy names of EAs into attrnamebuf - * - * Arguments: - * - * vol (r) current volume - * attrnamebuf (w) store names a consecutive C strings here - * buflen (rw) length of names in attrnamebuf - * uname (r) filename - * oflag (r) link and create flag - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Copies names of all EAs of uname as consecutive C strings into rbuf. - * Increments *buflen accordingly. - */ -int list_eas(VFS_FUNC_ARGS_EA_LIST) -{ - unsigned int count = 0; - int attrbuflen = *buflen, ret = AFP_OK, len; - char *buf = attrnamebuf; - struct ea ea; - - LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname); - - if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { - if (errno != ENOENT) { - LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname); - return AFPERR_MISC; - } - else - return AFP_OK; - } - - while (count < ea.ea_count) { - /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */ - if ( ( len = convert_string(vol->v_volcharset, - CH_UTF8_MAC, - (*ea.ea_entries)[count].ea_name, - (*ea.ea_entries)[count].ea_namelen, - buf + attrbuflen, - 255)) - <= 0 ) { - ret = AFPERR_MISC; - goto exit; - } - if (len == 255) - /* convert_string didn't 0-terminate */ - attrnamebuf[attrbuflen + 255] = 0; - - LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s", - uname, (*ea.ea_entries)[count].ea_name); - - attrbuflen += len + 1; - if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) { - /* Next EA name could overflow, so bail out with error. - FIXME: evantually malloc/memcpy/realloc whatever. - Is it worth it ? */ - LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname); - ret = AFPERR_MISC; - goto exit; - } - count++; - } - -exit: - *buflen = attrbuflen; - - if ((ea_close(&ea)) != 0) { - LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname); - return AFPERR_MISC; - } - - return ret; -} - -/* - * Function: set_ea - * - * Purpose: set a Solaris native EA - * - * Arguments: - * - * vol (r) current volume - * uname (r) filename - * attruname (r) EA name - * ibuf (r) buffer with EA content - * attrsize (r) length EA in ibuf - * oflag (r) link and create flag - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Copies names of all EAs of uname as consecutive C strings into rbuf. - * Increments *rbuflen accordingly. - */ -int set_ea(VFS_FUNC_ARGS_EA_SET) -{ - int ret = AFP_OK; - struct ea ea; - - LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname); - - if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) { - LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname); - return AFPERR_MISC; - } - - if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) { - LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname); - ret = AFPERR_MISC; - goto exit; - } - - if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) { - LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname); - ret = AFPERR_MISC; - goto exit; - } - -exit: - if ((ea_close(&ea)) != 0) { - LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname); - ret = AFPERR_MISC; - goto exit; - } - - return ret; -} - -/* - * Function: remove_ea - * - * Purpose: remove a EA from a file - * - * Arguments: - * - * vol (r) current volume - * uname (r) filename - * attruname (r) EA name - * oflag (r) link and create flag - * - * Returns: AFP code: AFP_OK on success or appropiate AFP error code - * - * Effects: - * - * Removes EA attruname from file uname. - */ -int remove_ea(VFS_FUNC_ARGS_EA_REMOVE) -{ - int ret = AFP_OK; - struct ea ea; - - LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname); - - if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) { - LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname); - return AFPERR_MISC; - } - - if ((ea_delentry(&ea, attruname)) == -1) { - LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname); - ret = AFPERR_MISC; - goto exit; - } - - if ((delete_ea_file(&ea, attruname)) != 0) { - LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname); - ret = AFPERR_MISC; - goto exit; - } - -exit: - if ((ea_close(&ea)) != 0) { - LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname); - ret = AFPERR_MISC; - goto exit; - } - - return ret; -} - -/****************************************************************************************** - * EA VFS funcs that deal with file/dir cp/mv/rm - ******************************************************************************************/ - -int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE) -{ - unsigned int count = 0; - int ret = AFP_OK; - int cwd = -1; - struct ea ea; - - LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file); - - /* Open EA stuff */ - if ((ea_openat(vol, dirfd, file, EA_RDWR, &ea)) != 0) { - if (errno == ENOENT) - /* no EA files, nothing to do */ - return AFP_OK; - else { - LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file); - return AFPERR_MISC; - } - } - - if (dirfd != -1) { - if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { - ret = AFPERR_MISC; - goto exit; - } - } - - while (count < ea.ea_count) { - if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) { - ret = AFPERR_MISC; - continue; - } - free((*ea.ea_entries)[count].ea_name); - (*ea.ea_entries)[count].ea_name = NULL; - count++; - } - - /* ea_close removes the EA header file for us because all names are NULL */ - if ((ea_close(&ea)) != 0) { - LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file); - ret = AFPERR_MISC; - } - - if (dirfd != -1 && fchdir(cwd) != 0) { - LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!"); - exit(EXITERR_SYS); - } - -exit: - if (cwd != -1) - close(cwd); - - return ret; -} - -int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE) -{ - unsigned int count = 0; - int ret = AFP_OK; - size_t easize; - char srceapath[ MAXPATHLEN + 1]; - char *eapath; - char *eaname; - struct ea srcea; - struct ea dstea; - struct adouble ad; - - LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst); - - - /* Open EA stuff */ - if ((ea_openat(vol, dirfd, src, EA_RDWR, &srcea)) != 0) { - if (errno == ENOENT) - /* no EA files, nothing to do */ - return AFP_OK; - else { - LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src); - return AFPERR_MISC; - } - } - - if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { - if (errno == ENOENT) { - /* Possibly the .AppleDouble folder didn't exist, we create it and try again */ - ad_init(&ad, vol->v_adouble, vol->v_ad_options); - if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) { - LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst); - ret = AFPERR_MISC; - goto exit; - } - ad_close(&ad, ADFLAGS_HF); - if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { - ret = AFPERR_MISC; - goto exit; - } - } - } - - /* Loop through all EAs: */ - while (count < srcea.ea_count) { - /* Move EA */ - eaname = (*srcea.ea_entries)[count].ea_name; - easize = (*srcea.ea_entries)[count].ea_size; - - /* Build src and dst paths for rename() */ - if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { - ret = AFPERR_MISC; - goto exit; - } - strcpy(srceapath, eapath); - if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { - ret = AFPERR_MISC; - goto exit; - } - - LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", - src, dst, srceapath, eapath); - - /* Add EA to dstea */ - if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) { - LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", - src, dst, srceapath, eapath); - ret = AFPERR_MISC; - goto exit; - } - - /* Remove EA entry from srcea */ - if ((ea_delentry(&srcea, eaname)) == -1) { - LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", - src, dst, srceapath, eapath); - ea_delentry(&dstea, eaname); - ret = AFPERR_MISC; - goto exit; - } - - /* Now rename the EA */ - if ((unix_rename(dirfd, srceapath, -1, eapath)) < 0) { - LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", - src, dst, srceapath, eapath); - ret = AFPERR_MISC; - goto exit; - } - - count++; - } - - -exit: - ea_close(&srcea); - ea_close(&dstea); - return ret; -} - -int ea_copyfile(VFS_FUNC_ARGS_COPYFILE) -{ - unsigned int count = 0; - int ret = AFP_OK; - size_t easize; - char srceapath[ MAXPATHLEN + 1]; - char *eapath; - char *eaname; - struct ea srcea; - struct ea dstea; - struct adouble ad; - - LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst); - - /* Open EA stuff */ - if ((ea_openat(vol, sfd, src, EA_RDWR, &srcea)) != 0) { - if (errno == ENOENT) - /* no EA files, nothing to do */ - return AFP_OK; - else { - LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src); - return AFPERR_MISC; - } - } - - if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { - if (errno == ENOENT) { - /* Possibly the .AppleDouble folder didn't exist, we create it and try again */ - ad_init(&ad, vol->v_adouble, vol->v_ad_options); - if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) { - LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst); - ret = AFPERR_MISC; - goto exit; - } - ad_close(&ad, ADFLAGS_HF); - if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { - ret = AFPERR_MISC; - goto exit; - } - } - } - - /* Loop through all EAs: */ - while (count < srcea.ea_count) { - /* Copy EA */ - eaname = (*srcea.ea_entries)[count].ea_name; - easize = (*srcea.ea_entries)[count].ea_size; - - /* Build src and dst paths for copy_file() */ - if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { - ret = AFPERR_MISC; - goto exit; - } - strcpy(srceapath, eapath); - if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { - ret = AFPERR_MISC; - goto exit; - } - - LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'", - src, dst, srceapath, eapath); - - /* Add EA to dstea */ - if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) { - LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error", - src, dst, eaname); - ret = AFPERR_MISC; - goto exit; - } - - /* Now copy the EA */ - if ((copy_file(sfd, srceapath, eapath, (0666 & ~vol->v_umask))) < 0) { - LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'", - src, dst, srceapath, eapath); - ret = AFPERR_MISC; - goto exit; - } - - count++; - } - -exit: - ea_close(&srcea); - ea_close(&dstea); - return ret; -} - -int ea_chown(VFS_FUNC_ARGS_CHOWN) -{ - - unsigned int count = 0; - int ret = AFP_OK; - char *eaname; - struct ea ea; - - LOG(log_debug, logtype_afpd, "ea_chown('%s')", path); - /* Open EA stuff */ - if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) { - if (errno == ENOENT) - /* no EA files, nothing to do */ - return AFP_OK; - else { - LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path); - return AFPERR_MISC; - } - } - - if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) { - switch (errno) { - case EPERM: - case EACCES: - ret = AFPERR_ACCESS; - goto exit; - default: - ret = AFPERR_MISC; - goto exit; - } - } - - while (count < ea.ea_count) { - if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { - ret = AFPERR_MISC; - goto exit; - } - if ((lchown(eaname, uid, gid)) != 0) { - switch (errno) { - case EPERM: - case EACCES: - ret = AFPERR_ACCESS; - goto exit; - default: - ret = AFPERR_MISC; - goto exit; - } - continue; - } - - count++; - } - -exit: - if ((ea_close(&ea)) != 0) { - LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path); - return AFPERR_MISC; - } - - return ret; -} - -int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE) -{ - - unsigned int count = 0; - int ret = AFP_OK; - const char *eaname; - struct ea ea; - - LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name); - /* Open EA stuff */ - if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { - if (errno == ENOENT) - /* no EA files, nothing to do */ - return AFP_OK; - else - return AFPERR_MISC; - } - - /* Set mode on EA header file */ - if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { - LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); - switch (errno) { - case EPERM: - case EACCES: - ret = AFPERR_ACCESS; - goto exit; - default: - ret = AFPERR_MISC; - goto exit; - } - } - - /* Set mode on EA files */ - while (count < ea.ea_count) { - if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { - ret = AFPERR_MISC; - goto exit; - } - if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) { - LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno)); - switch (errno) { - case EPERM: - case EACCES: - ret = AFPERR_ACCESS; - goto exit; - default: - ret = AFPERR_MISC; - goto exit; - } - continue; - } - - count++; - } - -exit: - if ((ea_close(&ea)) != 0) { - LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name); - return AFPERR_MISC; - } - - return ret; -} - -int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) -{ - - int ret = AFP_OK; - unsigned int count = 0; - uid_t uid; - const char *eaname; - const char *eaname_safe = NULL; - struct ea ea; - - LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name); - /* .AppleDouble already might be inaccesible, so we must run as id 0 */ - uid = geteuid(); - if (seteuid(0)) { - LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno)); - return AFPERR_MISC; - } - - /* Open EA stuff */ - if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { - /* ENOENT --> no EA files, nothing to do */ - if (errno != ENOENT) - ret = AFPERR_MISC; - if (seteuid(uid) < 0) { - LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno)); - exit(EXITERR_SYS); - } - return ret; - } - - /* Set mode on EA header */ - if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { - LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); - switch (errno) { - case EPERM: - case EACCES: - ret = AFPERR_ACCESS; - goto exit; - default: - ret = AFPERR_MISC; - goto exit; - } - } - - /* Set mode on EA files */ - while (count < ea.ea_count) { - eaname = (*ea.ea_entries)[count].ea_name; - /* - * Be careful with EA names from the EA header! - * Eg NFS users might have access to them, can inject paths using ../ or /..... - * FIXME: - * Until the EA code escapes / in EA name requests from the client, these therefor wont work. - */ - if ((eaname_safe = strrchr(eaname, '/'))) { - LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname); - eaname = eaname_safe; - } - if ((eaname = ea_path(&ea, eaname, 1)) == NULL) { - ret = AFPERR_MISC; - goto exit; - } - if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) { - LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno)); - switch (errno) { - case EPERM: - case EACCES: - ret = AFPERR_ACCESS; - goto exit; - default: - ret = AFPERR_MISC; - goto exit; - } - continue; - } - - count++; - } - -exit: - if (seteuid(uid) < 0) { - LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno)); - exit(EXITERR_SYS); - } - - if ((ea_close(&ea)) != 0) { - LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name); - return AFPERR_MISC; - } - - return ret; -} diff --git a/libatalk/vfs/ea_ad.c b/libatalk/vfs/ea_ad.c new file mode 100644 index 00000000..fa3a0109 --- /dev/null +++ b/libatalk/vfs/ea_ad.c @@ -0,0 +1,1768 @@ +/* + Copyright (c) 2009 Frank Lahm + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Store Extended Attributes inside .AppleDouble folders as follows: + * + * filename "fileWithEAs" with EAs "testEA1" and "testEA2" + * + * - create header with with the format struct adouble_ea_ondisk, the file is written to + * ".AppleDouble/fileWithEAs::EA" + * - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2" + */ + +/* + * Build mode for EA header from file mode + */ +static inline mode_t ea_header_mode(mode_t mode) +{ + /* Same as ad_hf_mode(mode) */ + mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); + /* Owner must be able to open, read and w-lock it, in order to chmod from eg 0000 -> 0xxxx*/ + mode |= S_IRUSR | S_IWUSR; + return mode; +} + +/* + * Build mode for EA file from file mode + */ +static inline mode_t ea_mode(mode_t mode) +{ + /* Same as ad_hf_mode(mode) */ + mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); + return mode; +} + +/* + Taken form afpd/desktop.c +*/ +static char *mtoupath(const struct vol *vol, const char *mpath) +{ + static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */ + const char *m; + char *u; + size_t inplen; + size_t outlen; + uint16_t flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON; + + if (!mpath) + return NULL; + + if ( *mpath == '\0' ) { + return( "." ); + } + + m = mpath; + u = upath; + + inplen = strlen(m); + outlen = MAXPATHLEN; + + if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC, + vol->v_volcharset, + vol->v_maccharset, + m, inplen, u, outlen, &flags)) ) { + return NULL; + } + + return( upath ); +} + + +/* + * Function: unpack_header + * + * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea + * + * Arguments: + * + * ea (rw) handle to struct ea + * + * Returns: 0 on success, -1 on error + * + * Effects: + * + * Verifies magic and version. + */ +static int unpack_header(struct ea * restrict ea) +{ + int ret = 0; + unsigned int count = 0; + uint32_t uint32; + char *buf; + + /* Check magic and version */ + buf = ea->ea_data; + if (*(uint32_t *)buf != htonl(EA_MAGIC)) { + LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf); + ret = -1; + goto exit; + } + buf += 4; + if (*(uint16_t *)buf != htons(EA_VERSION)) { + LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf); + ret = -1; + goto exit; + } + buf += 2; + + /* Get EA count */ + ea->ea_count = ntohs(*(uint16_t *)buf); + LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count); + buf += 2; + + if (ea->ea_count == 0) + return 0; + + /* Allocate storage for the ea_entries array */ + ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count); + if ( ! ea->ea_entries) { + LOG(log_error, logtype_afpd, "unpack_header: OOM"); + ret = -1; + goto exit; + } + + buf = ea->ea_data + EA_HEADER_SIZE; + while (count < ea->ea_count) { + memcpy(&uint32, buf, 4); /* EA size */ + buf += 4; + (*(ea->ea_entries))[count].ea_size = ntohl(uint32); + (*(ea->ea_entries))[count].ea_name = strdup(buf); + if (! (*(ea->ea_entries))[count].ea_name) { + LOG(log_error, logtype_afpd, "unpack_header: OOM"); + ret = -1; + goto exit; + } + (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name); + buf += (*(ea->ea_entries))[count].ea_namelen + 1; + + LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count, + (*(ea->ea_entries))[count].ea_name, + (*(ea->ea_entries))[count].ea_size, + (*(ea->ea_entries))[count].ea_namelen); + + count++; + } + +exit: + return ret; +} + +/* + * Function: pack_header + * + * Purpose: pack everything from struct ea into buffer at ea->ea_data + * + * Arguments: + * + * ea (rw) handle to struct ea + * + * Returns: 0 on success, -1 on error + * + * Effects: + * + * adjust ea->ea_count in case an ea entry deletetion is detected + */ +static int pack_header(struct ea * restrict ea) +{ + unsigned int count = 0, eacount = 0; + uint16_t uint16; + uint32_t uint32; + size_t bufsize = EA_HEADER_SIZE; + + char *buf = ea->ea_data + EA_HEADER_SIZE; + + LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u", + ea->filename, ea->ea_count, ea->ea_size); + + if (ea->ea_count == 0) + /* nothing to do, magic, version and count are still valid in buffer */ + return 0; + + while(count < ea->ea_count) { /* the names */ + /* Check if its a deleted entry */ + if ( ! ((*ea->ea_entries)[count].ea_name)) { + count++; + continue; + } + + bufsize += (*(ea->ea_entries))[count].ea_namelen + 1; + count++; + eacount++; + } + + bufsize += (eacount * 4); /* header + ea_size for each EA */ + if (bufsize > ea->ea_size) { + /* we must realloc */ + if ( ! (buf = realloc(ea->ea_data, bufsize)) ) { + LOG(log_error, logtype_afpd, "pack_header: OOM"); + return -1; + } + ea->ea_data = buf; + } + ea->ea_size = bufsize; + + /* copy count */ + uint16 = htons(eacount); + memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2); + + count = 0; + buf = ea->ea_data + EA_HEADER_SIZE; + while (count < ea->ea_count) { + /* Check if its a deleted entry */ + if ( ! ((*ea->ea_entries)[count].ea_name)) { + count++; + continue; + } + + /* First: EA size */ + uint32 = htonl((*(ea->ea_entries))[count].ea_size); + memcpy(buf, &uint32, 4); + buf += 4; + + /* Second: EA name as C-string */ + strcpy(buf, (*(ea->ea_entries))[count].ea_name); + buf += (*(ea->ea_entries))[count].ea_namelen + 1; + + LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count, + (*(ea->ea_entries))[count].ea_name, + (*(ea->ea_entries))[count].ea_size, + (*(ea->ea_entries))[count].ea_namelen); + + count++; + } + + ea->ea_count = eacount; + + LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u", + ea->filename, ea->ea_count, ea->ea_size); + + return 0; +} + +/* + * Function: ea_addentry + * + * Purpose: add one EA into ea->ea_entries[] + * + * Arguments: + * + * ea (rw) pointer to struct ea + * attruname (r) name of EA + * attrsize (r) size of ea + * bitmap (r) bitmap from FP func + * + * Returns: new number of EA entries, -1 on error + * + * Effects: + * + * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating. + * Otherwise realloc and put entry at the end. Increments ea->ea_count. + */ +static int ea_addentry(struct ea * restrict ea, + const char * restrict attruname, + size_t attrsize, + int bitmap) +{ + int ea_existed = 0; + unsigned int count = 0; + void *tmprealloc; + + /* First check if an EA of the requested name already exist */ + if (ea->ea_count > 0) { + while (count < ea->ea_count) { + if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) { + ea_existed = 1; + LOG(log_debug, logtype_afpd, "ea_addentry('%s', bitmap:0x%x): exists", attruname, bitmap); + if (bitmap & kXAttrCreate) + /* its like O_CREAT|O_EXCL -> fail */ + return -1; + (*(ea->ea_entries))[count].ea_size = attrsize; + return 0; + } + count++; + } + } + + if ((bitmap & kXAttrReplace) && ! ea_existed) + /* replace was requested, but EA didn't exist */ + return -1; + + if (ea->ea_count == 0) { + ea->ea_entries = malloc(sizeof(struct ea_entry)); + if ( ! ea->ea_entries) { + LOG(log_error, logtype_afpd, "ea_addentry: OOM"); + return -1; + } + } else if (! ea_existed) { + tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1)); + if ( ! tmprealloc) { + LOG(log_error, logtype_afpd, "ea_addentry: OOM"); + return -1; + } + ea->ea_entries = tmprealloc; + } + + /* We've grown the array, now store the entry */ + (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize; + (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname); + if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) { + LOG(log_error, logtype_afpd, "ea_addentry: OOM"); + goto error; + } + (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname); + + ea->ea_count++; + return ea->ea_count; + +error: + if (ea->ea_count == 0 && ea->ea_entries) { + /* We just allocated storage but had an error somewhere -> free storage*/ + free(ea->ea_entries); + ea->ea_entries = NULL; + } + ea->ea_count = 0; + return -1; +} + +/* + * Function: create_ea_header + * + * Purpose: create EA header file, only called from ea_open + * + * Arguments: + * + * uname (r) filename for which we have to create a header + * ea (rw) ea handle with already allocated storage pointed to + * by ea->ea_data + * + * Returns: fd of open header file on success, -1 on error, errno semantics: + * EEXIST: open with O_CREAT | O_EXCL failed + * + * Effects: + * + * Creates EA header file and initialize ea->ea_data buffer. + * Possibe race condition with other afpd processes: + * we were called because header file didn't exist in eg. ea_open. We then + * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic. + * What do we do then? Someone else is in the process of creating the header too, but + * it might not have finished it. That means we cant just open, read and use it! + * We therefor currently just break with an error. + * On return the header file is still r/w locked. + */ +static int create_ea_header(const char * restrict uname, + struct ea * restrict ea) +{ + int fd = -1, err = 0; + char *ptr; + + if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) { + LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname); + return -1; + } + + /* lock it */ + if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) { + LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname); + err = -1; + goto exit; + } + + /* Now init it */ + ptr = ea->ea_data; + *(uint32_t *)ptr = htonl(EA_MAGIC); + ptr += EA_MAGIC_LEN; + *(uint16_t *)ptr = htons(EA_VERSION); + ptr += EA_VERSION_LEN; + *(uint16_t *)ptr = 0; /* count */ + + ea->ea_size = EA_HEADER_SIZE; + ea->ea_inited = EA_INITED; + +exit: + if (err != 0) { + close(fd); + fd = -1; + } + return fd; +} + +/* + * Function: write_ea + * + * Purpose: write an EA to disk + * + * Arguments: + * + * ea (r) struct ea handle + * attruname (r) EA name + * ibuf (r) buffer with EA content + * attrsize (r) size of EA + * + * Returns: 0 on success, -1 on error + * + * Effects: + * + * Creates/overwrites EA file. + * + */ +static int write_ea(const struct ea * restrict ea, + const char * restrict attruname, + const char * restrict ibuf, + size_t attrsize) +{ + int fd = -1, ret = AFP_OK; + struct stat st; + char *eaname; + + if ((eaname = ea_path(ea, attruname, 1)) == NULL) { + LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname); + return AFPERR_MISC; + } + + LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname); + + /* Check if it exists, remove if yes*/ + if ((stat(eaname, &st)) == 0) { + if ((unlink(eaname)) != 0) { + if (errno == EACCES) + return AFPERR_ACCESS; + else + return AFPERR_MISC; + } + } + + if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) { + LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname); + return -1; + } + + /* lock it */ + if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) { + LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname); + ret = -1; + goto exit; + } + + if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) { + LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno)); + ret = -1; + goto exit; + } + +exit: + if (fd != -1) + close(fd); /* and unlock */ + return ret; +} + +/* + * Function: ea_delentry + * + * Purpose: delete one EA from ea->ea_entries[] + * + * Arguments: + * + * ea (rw) pointer to struct ea + * attruname (r) EA name + * + * Returns: new number of EA entries, -1 on error + * + * Effects: + * + * Remove entry from ea->ea_entries[]. Decrement ea->ea_count. + * Marks it as unused just by freeing name and setting it to NULL. + * ea_close and pack_buffer must honor this. + */ +static int ea_delentry(struct ea * restrict ea, const char * restrict attruname) +{ + int ret = 0; + unsigned int count = 0; + + if (ea->ea_count == 0) { + LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion"); + return -1; + } + + while (count < ea->ea_count) { + /* search matching EA */ + if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) { + free((*ea->ea_entries)[count].ea_name); + (*ea->ea_entries)[count].ea_name = NULL; + + LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u", + attruname, count + 1, ea->ea_count); + + break; + } + count++; + } + + return ret; +} + +/* + * Function: delete_ea_file + * + * Purpose: delete EA file from disk + * + * Arguments: + * + * ea (r) struct ea handle + * attruname (r) EA name + * + * Returns: 0 on success, -1 on error + */ +static int delete_ea_file(const struct ea * restrict ea, const char *eaname) +{ + int ret = 0; + char *eafile; + struct stat st; + + if ((eafile = ea_path(ea, eaname, 1)) == NULL) { + LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname); + return -1; + } + + /* Check if it exists, remove if yes*/ + if ((stat(eafile, &st)) == 0) { + if ((unlink(eafile)) != 0) { + LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s", + eafile, strerror(errno)); + ret = -1; + } else + LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile); + } + + return ret; +} + +/************************************************************************************* + * ea_path, ea_open and ea_close are only global so that dbd can call them + *************************************************************************************/ + +/* + * Function: ea_path + * + * Purpose: return name of ea header filename + * + * Arguments: + * + * ea (r) ea handle + * eaname (r) name of EA or NULL + * macname (r) if != 0 call mtoupath on eaname + * + * Returns: pointer to name in static buffer, NULL on error + * + * Effects: + * + * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme + * Files: "file" -> "file/.AppleDouble/file::EA" + * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA" + * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA" + */ +char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname) +{ + char *adname; + static char pathbuf[MAXPATHLEN + 1]; + + /* get name of a adouble file from uname */ + adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0); + /* copy it so we can work with it */ + strlcpy(pathbuf, adname, MAXPATHLEN + 1); + /* append "::EA" */ + strlcat(pathbuf, "::EA", MAXPATHLEN + 1); + + if (eaname) { + strlcat(pathbuf, "::", MAXPATHLEN + 1); + if (macname) + if ((eaname = mtoupath(ea->vol, eaname)) == NULL) + return NULL; + strlcat(pathbuf, eaname, MAXPATHLEN + 1); + } + + return pathbuf; +} + +/* + * Function: ea_open + * + * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE + * + * Arguments: + * + * vol (r) current volume + * uname (r) filename for which we have to open a header + * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created) + * EA_RDONLY: open read only + * EA_RDWR: open read/write + * Eiterh EA_RDONLY or EA_RDWR MUST be requested + * ea (w) pointer to a struct ea that we fill + * + * Returns: 0 on success + * -1 on misc error with errno = EFAULT + * -2 if no EA header exists with errno = ENOENT + * + * Effects: + * + * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size. + * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags. + * file is either read or write locked depending on the open flags. + * When you're done with struct ea you must call ea_close on it. + */ +int ea_open(const struct vol * restrict vol, + const char * restrict uname, + eaflags_t eaflags, + struct ea * restrict ea) +{ + int ret = 0; + char *eaname; + struct stat st; + + /* Enforce usage rules! */ + if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) { + LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname); + return -1; + } + + /* Set it all to 0 */ + memset(ea, 0, sizeof(struct ea)); + + ea->vol = vol; /* ea_close needs it */ + ea->ea_flags = eaflags; + ea->dirfd = -1; /* no *at (cf openat) semantics by default */ + + /* Dont care for errors, eg when removing the file is already gone */ + if (!stat(uname, &st) && S_ISDIR(st.st_mode)) + ea->ea_flags |= EA_DIR; + + if ( ! (ea->filename = strdup(uname))) { + LOG(log_error, logtype_afpd, "ea_open: OOM"); + return -1; + } + + eaname = ea_path(ea, NULL, 0); + LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname); + + /* Check if it exists, if not create it if EA_CREATE is in eaflags */ + if ((stat(eaname, &st)) != 0) { + if (errno == ENOENT) { + + /* It doesnt exist */ + + if ( ! (eaflags & EA_CREATE)) { + /* creation was not requested, so return with error */ + ret = -2; + goto exit; + } + + /* Now create a header file */ + + /* malloc buffer for minimal on disk data */ + ea->ea_data = malloc(EA_HEADER_SIZE); + if (! ea->ea_data) { + LOG(log_error, logtype_afpd, "ea_open: OOM"); + ret = -1; + goto exit; + } + + /* create it */ + ea->ea_fd = create_ea_header(eaname, ea); + if (ea->ea_fd == -1) { + ret = -1; + goto exit; + } + + return 0; + + } else {/* errno != ENOENT */ + ret = -1; + goto exit; + } + } + + /* header file exists, so read and parse it */ + + /* malloc buffer where we read disk file into */ + if (st.st_size < EA_HEADER_SIZE) { + LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname); + ret = -1; + goto exit; + } + ea->ea_size = st.st_size; + ea->ea_data = malloc(st.st_size); + if (! ea->ea_data) { + LOG(log_error, logtype_afpd, "ea_open: OOM"); + ret = -1; + goto exit; + } + + /* Now lock, open and read header file from disk */ + if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) { + LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno)); + ret = -1; + goto exit; + } + + /* lock it */ + if (ea->ea_flags & EA_RDONLY) { + /* read lock */ + if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) { + LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname); + ret = -1; + goto exit; + } + } else { /* EA_RDWR */ + /* write lock */ + if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) { + LOG(log_error, logtype_afpd, "ea_open: lock error on header: %s", eaname); + ret = -1; + goto exit; + } + } + + /* read it */ + if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { + LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname); + ret = -1; + goto exit; + } + + if ((unpack_header(ea)) != 0) { + LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname); + ret = -1; + goto exit; + } + +exit: + switch (ret) { + case 0: + ea->ea_inited = EA_INITED; + break; + case -1: + errno = EFAULT; /* force some errno distinguishable from ENOENT */ + /* fall through */ + case -2: + if (ea->ea_data) { + free(ea->ea_data); + ea->ea_data = NULL; + } + if (ea->ea_fd) { + close(ea->ea_fd); + ea->ea_fd = -1; + } + break; + } + + return ret; +} + +/* + * Function: ea_openat + * + * Purpose: openat like wrapper for ea_open, takes a additional file descriptor + * + * Arguments: + * + * vol (r) current volume + * sfd (r) openat like file descriptor + * uname (r) filename for which we have to open a header + * flags (r) EA_CREATE: create if it doesn't exist (without it won't be created) + * EA_RDONLY: open read only + * EA_RDWR: open read/write + * Eiterh EA_RDONLY or EA_RDWR MUST be requested + * ea (w) pointer to a struct ea that we fill + * + * Returns: 0 on success + * -1 on misc error with errno = EFAULT + * -2 if no EA header exists with errno = ENOENT + * + * Effects: + * + * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size. + * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags. + * file is either read or write locked depending on the open flags. + * When you're done with struct ea you must call ea_close on it. + */ +int ea_openat(const struct vol * restrict vol, + int dirfd, + const char * restrict uname, + eaflags_t eaflags, + struct ea * restrict ea) +{ + int ret = 0; + int cwdfd = -1; + + if (dirfd != -1) { + if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { + ret = -1; + goto exit; + } + } + + ret = ea_open(vol, uname, eaflags, ea); + ea->dirfd = dirfd; + + if (dirfd != -1) { + if (fchdir(cwdfd) != 0) { + LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting"); + exit(EXITERR_SYS); + } + } + + +exit: + if (cwdfd != -1) + close(cwdfd); + + return ret; + +} + +/* + * Function: ea_close + * + * Purpose: flushes and closes an ea handle + * + * Arguments: + * + * ea (rw) pointer to ea handle + * + * Returns: 0 on success, -1 on error + * + * Effects: + * + * Flushes and then closes and frees all resouces held by ea handle. + * Pack data in ea into ea_data, then write ea_data to disk + */ +int ea_close(struct ea * restrict ea) +{ + int ret = 0; + unsigned int count = 0; + char *eaname; + struct stat st; + + LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename); + + if (ea->ea_inited != EA_INITED) { + LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename); + return 0; + } + + /* pack header and write it to disk if it was opened EA_RDWR*/ + if (ea->ea_flags & EA_RDWR) { + if ((pack_header(ea)) != 0) { + LOG(log_error, logtype_afpd, "ea_close: pack header"); + ret = -1; + } else { + 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 ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) { + LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s", + eaname, strerror(errno)); + ret = -1; + } + else + LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname); + } else { + /* stat error */ + if (errno != ENOENT) { + LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s", + eaname, strerror(errno)); + ret = -1; + } + } + } else { /* ea->ea_count > 0 */ + if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) { + LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno)); + ret = -1; + goto exit; + } + + if ((ftruncate(ea->ea_fd, 0)) == -1) { + LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno)); + ret = -1; + goto exit; + } + + if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) { + LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno)); + ret = -1; + } + } + } + } + +exit: + /* free names */ + while(count < ea->ea_count) { + if ( (*ea->ea_entries)[count].ea_name ) { + free((*ea->ea_entries)[count].ea_name); + (*ea->ea_entries)[count].ea_name = NULL; + } + count++; + } + ea->ea_count = 0; + + if (ea->filename) { + free(ea->filename); + ea->filename = NULL; + } + + if (ea->ea_entries) { + free(ea->ea_entries); + ea->ea_entries = NULL; + } + + if (ea->ea_data) { + free(ea->ea_data); + ea->ea_data = NULL; + } + if (ea->ea_fd != -1) { + close(ea->ea_fd); /* also releases the fcntl lock */ + ea->ea_fd = -1; + } + + return 0; +} + + + +/************************************************************************************ + * VFS funcs called from afp_ea* funcs + ************************************************************************************/ + +/* + * Function: get_easize + * + * Purpose: get size of an EA + * + * Arguments: + * + * vol (r) current volume + * rbuf (w) DSI reply buffer + * rbuflen (rw) current length of data in reply buffer + * uname (r) filename + * oflag (r) link and create flag + * attruname (r) name of attribute + * + * Returns: AFP code: AFP_OK on success or appropiate AFP error code + * + * Effects: + * + * Copies EA size into rbuf in network order. Increments *rbuflen +4. + */ +int get_easize(VFS_FUNC_ARGS_EA_GETSIZE) +{ + int ret = AFPERR_MISC; + unsigned int count = 0; + uint32_t uint32; + struct ea ea; + + LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname); + + if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { + if (errno != ENOENT) + LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname); + + memset(rbuf, 0, 4); + *rbuflen += 4; + return ret; + } + + while (count < ea.ea_count) { + if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) { + uint32 = htonl((*ea.ea_entries)[count].ea_size); + memcpy(rbuf, &uint32, 4); + *rbuflen += 4; + ret = AFP_OK; + + LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u", + attruname, (*ea.ea_entries)[count].ea_size); + break; + } + count++; + } + + if ((ea_close(&ea)) != 0) { + LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname); + return AFPERR_MISC; + } + + return ret; +} + +/* + * Function: get_eacontent + * + * Purpose: copy EA into rbuf + * + * Arguments: + * + * vol (r) current volume + * rbuf (w) DSI reply buffer + * rbuflen (rw) current length of data in reply buffer + * uname (r) filename + * oflag (r) link and create flag + * attruname (r) name of attribute + * maxreply (r) maximum EA size as of current specs/real-life + * + * Returns: AFP code: AFP_OK on success or appropiate AFP error code + * + * Effects: + * + * Copies EA into rbuf. Increments *rbuflen accordingly. + */ +int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT) +{ + int ret = AFPERR_MISC, fd = -1; + unsigned int count = 0; + uint32_t uint32; + size_t toread; + struct ea ea; + char *eafile; + + LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname); + + if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { + if (errno != ENOENT) + LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname); + memset(rbuf, 0, 4); + *rbuflen += 4; + return ret; + } + + while (count < ea.ea_count) { + if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) { + if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) { + ret = AFPERR_MISC; + break; + } + + if ((fd = open(eafile, O_RDONLY)) == -1) { + LOG(log_error, logtype_afpd, "get_eacontent('%s'): open error: %s", uname, strerror(errno)); + ret = AFPERR_MISC; + break; + } + + /* Check how much the client wants, give him what we think is right */ + maxreply -= MAX_REPLY_EXTRA_BYTES; + if (maxreply > MAX_EA_SIZE) + maxreply = MAX_EA_SIZE; + toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size; + LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread); + + /* Put length of EA data in reply buffer */ + uint32 = htonl(toread); + memcpy(rbuf, &uint32, 4); + rbuf += 4; + *rbuflen += 4; + + if (read(fd, rbuf, toread) != (ssize_t)toread) { + LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname); + close(fd); + ret = AFPERR_MISC; + break; + } + *rbuflen += toread; + close(fd); + + ret = AFP_OK; + break; + } + count++; + } + + if ((ea_close(&ea)) != 0) { + LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname); + return AFPERR_MISC; + } + + return ret; + +} + +/* + * Function: list_eas + * + * Purpose: copy names of EAs into attrnamebuf + * + * Arguments: + * + * vol (r) current volume + * attrnamebuf (w) store names a consecutive C strings here + * buflen (rw) length of names in attrnamebuf + * uname (r) filename + * oflag (r) link and create flag + * + * Returns: AFP code: AFP_OK on success or appropiate AFP error code + * + * Effects: + * + * Copies names of all EAs of uname as consecutive C strings into rbuf. + * Increments *buflen accordingly. + */ +int list_eas(VFS_FUNC_ARGS_EA_LIST) +{ + unsigned int count = 0; + int attrbuflen = *buflen, ret = AFP_OK, len; + char *buf = attrnamebuf; + struct ea ea; + + LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname); + + if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) { + if (errno != ENOENT) { + LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname); + return AFPERR_MISC; + } + else + return AFP_OK; + } + + while (count < ea.ea_count) { + /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */ + if ( ( len = convert_string(vol->v_volcharset, + CH_UTF8_MAC, + (*ea.ea_entries)[count].ea_name, + (*ea.ea_entries)[count].ea_namelen, + buf + attrbuflen, + 255)) + <= 0 ) { + ret = AFPERR_MISC; + goto exit; + } + if (len == 255) + /* convert_string didn't 0-terminate */ + attrnamebuf[attrbuflen + 255] = 0; + + LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s", + uname, (*ea.ea_entries)[count].ea_name); + + attrbuflen += len + 1; + if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) { + /* Next EA name could overflow, so bail out with error. + FIXME: evantually malloc/memcpy/realloc whatever. + Is it worth it ? */ + LOG(log_warning, logtype_afpd, "list_eas(%s): running out of buffer for EA names", uname); + ret = AFPERR_MISC; + goto exit; + } + count++; + } + +exit: + *buflen = attrbuflen; + + if ((ea_close(&ea)) != 0) { + LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname); + return AFPERR_MISC; + } + + return ret; +} + +/* + * Function: set_ea + * + * Purpose: set a Solaris native EA + * + * Arguments: + * + * vol (r) current volume + * uname (r) filename + * attruname (r) EA name + * ibuf (r) buffer with EA content + * attrsize (r) length EA in ibuf + * oflag (r) link and create flag + * + * Returns: AFP code: AFP_OK on success or appropiate AFP error code + * + * Effects: + * + * Copies names of all EAs of uname as consecutive C strings into rbuf. + * Increments *rbuflen accordingly. + */ +int set_ea(VFS_FUNC_ARGS_EA_SET) +{ + int ret = AFP_OK; + struct ea ea; + + LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname); + + if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) { + LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname); + return AFPERR_MISC; + } + + if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) { + LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname); + ret = AFPERR_MISC; + goto exit; + } + + if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) { + LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname); + ret = AFPERR_MISC; + goto exit; + } + +exit: + if ((ea_close(&ea)) != 0) { + LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname); + ret = AFPERR_MISC; + goto exit; + } + + return ret; +} + +/* + * Function: remove_ea + * + * Purpose: remove a EA from a file + * + * Arguments: + * + * vol (r) current volume + * uname (r) filename + * attruname (r) EA name + * oflag (r) link and create flag + * + * Returns: AFP code: AFP_OK on success or appropiate AFP error code + * + * Effects: + * + * Removes EA attruname from file uname. + */ +int remove_ea(VFS_FUNC_ARGS_EA_REMOVE) +{ + int ret = AFP_OK; + struct ea ea; + + LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname); + + if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) { + LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname); + return AFPERR_MISC; + } + + if ((ea_delentry(&ea, attruname)) == -1) { + LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname); + ret = AFPERR_MISC; + goto exit; + } + + if ((delete_ea_file(&ea, attruname)) != 0) { + LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname); + ret = AFPERR_MISC; + goto exit; + } + +exit: + if ((ea_close(&ea)) != 0) { + LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname); + ret = AFPERR_MISC; + goto exit; + } + + return ret; +} + +/****************************************************************************************** + * EA VFS funcs that deal with file/dir cp/mv/rm + ******************************************************************************************/ + +int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE) +{ + unsigned int count = 0; + int ret = AFP_OK; + int cwd = -1; + struct ea ea; + + LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file); + + /* Open EA stuff */ + if ((ea_openat(vol, dirfd, file, EA_RDWR, &ea)) != 0) { + if (errno == ENOENT) + /* no EA files, nothing to do */ + return AFP_OK; + else { + LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file); + return AFPERR_MISC; + } + } + + if (dirfd != -1) { + if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { + ret = AFPERR_MISC; + goto exit; + } + } + + while (count < ea.ea_count) { + if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) { + ret = AFPERR_MISC; + continue; + } + free((*ea.ea_entries)[count].ea_name); + (*ea.ea_entries)[count].ea_name = NULL; + count++; + } + + /* ea_close removes the EA header file for us because all names are NULL */ + if ((ea_close(&ea)) != 0) { + LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file); + ret = AFPERR_MISC; + } + + if (dirfd != -1 && fchdir(cwd) != 0) { + LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!"); + exit(EXITERR_SYS); + } + +exit: + if (cwd != -1) + close(cwd); + + return ret; +} + +int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE) +{ + unsigned int count = 0; + int ret = AFP_OK; + size_t easize; + char srceapath[ MAXPATHLEN + 1]; + char *eapath; + char *eaname; + struct ea srcea; + struct ea dstea; + struct adouble ad; + + LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst); + + + /* Open EA stuff */ + if ((ea_openat(vol, dirfd, src, EA_RDWR, &srcea)) != 0) { + if (errno == ENOENT) + /* no EA files, nothing to do */ + return AFP_OK; + else { + LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src); + return AFPERR_MISC; + } + } + + if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { + if (errno == ENOENT) { + /* Possibly the .AppleDouble folder didn't exist, we create it and try again */ + ad_init(&ad, vol->v_adouble, vol->v_ad_options); + if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) { + LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst); + ret = AFPERR_MISC; + goto exit; + } + ad_close(&ad, ADFLAGS_HF); + if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { + ret = AFPERR_MISC; + goto exit; + } + } + } + + /* Loop through all EAs: */ + while (count < srcea.ea_count) { + /* Move EA */ + eaname = (*srcea.ea_entries)[count].ea_name; + easize = (*srcea.ea_entries)[count].ea_size; + + /* Build src and dst paths for rename() */ + if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { + ret = AFPERR_MISC; + goto exit; + } + strcpy(srceapath, eapath); + if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { + ret = AFPERR_MISC; + goto exit; + } + + LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", + src, dst, srceapath, eapath); + + /* Add EA to dstea */ + if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) { + LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", + src, dst, srceapath, eapath); + ret = AFPERR_MISC; + goto exit; + } + + /* Remove EA entry from srcea */ + if ((ea_delentry(&srcea, eaname)) == -1) { + LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", + src, dst, srceapath, eapath); + ea_delentry(&dstea, eaname); + ret = AFPERR_MISC; + goto exit; + } + + /* Now rename the EA */ + if ((unix_rename(dirfd, srceapath, -1, eapath)) < 0) { + LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'", + src, dst, srceapath, eapath); + ret = AFPERR_MISC; + goto exit; + } + + count++; + } + + +exit: + ea_close(&srcea); + ea_close(&dstea); + return ret; +} + +int ea_copyfile(VFS_FUNC_ARGS_COPYFILE) +{ + unsigned int count = 0; + int ret = AFP_OK; + size_t easize; + char srceapath[ MAXPATHLEN + 1]; + char *eapath; + char *eaname; + struct ea srcea; + struct ea dstea; + struct adouble ad; + + LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst); + + /* Open EA stuff */ + if ((ea_openat(vol, sfd, src, EA_RDWR, &srcea)) != 0) { + if (errno == ENOENT) + /* no EA files, nothing to do */ + return AFP_OK; + else { + LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src); + return AFPERR_MISC; + } + } + + if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { + if (errno == ENOENT) { + /* Possibly the .AppleDouble folder didn't exist, we create it and try again */ + ad_init(&ad, vol->v_adouble, vol->v_ad_options); + if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) { + LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst); + ret = AFPERR_MISC; + goto exit; + } + ad_close(&ad, ADFLAGS_HF); + if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) { + ret = AFPERR_MISC; + goto exit; + } + } + } + + /* Loop through all EAs: */ + while (count < srcea.ea_count) { + /* Copy EA */ + eaname = (*srcea.ea_entries)[count].ea_name; + easize = (*srcea.ea_entries)[count].ea_size; + + /* Build src and dst paths for copy_file() */ + if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) { + ret = AFPERR_MISC; + goto exit; + } + strcpy(srceapath, eapath); + if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) { + ret = AFPERR_MISC; + goto exit; + } + + LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'", + src, dst, srceapath, eapath); + + /* Add EA to dstea */ + if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) { + LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error", + src, dst, eaname); + ret = AFPERR_MISC; + goto exit; + } + + /* Now copy the EA */ + if ((copy_file(sfd, srceapath, eapath, (0666 & ~vol->v_umask))) < 0) { + LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'", + src, dst, srceapath, eapath); + ret = AFPERR_MISC; + goto exit; + } + + count++; + } + +exit: + ea_close(&srcea); + ea_close(&dstea); + return ret; +} + +int ea_chown(VFS_FUNC_ARGS_CHOWN) +{ + + unsigned int count = 0; + int ret = AFP_OK; + char *eaname; + struct ea ea; + + LOG(log_debug, logtype_afpd, "ea_chown('%s')", path); + /* Open EA stuff */ + if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) { + if (errno == ENOENT) + /* no EA files, nothing to do */ + return AFP_OK; + else { + LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path); + return AFPERR_MISC; + } + } + + if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) { + switch (errno) { + case EPERM: + case EACCES: + ret = AFPERR_ACCESS; + goto exit; + default: + ret = AFPERR_MISC; + goto exit; + } + } + + while (count < ea.ea_count) { + if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { + ret = AFPERR_MISC; + goto exit; + } + if ((lchown(eaname, uid, gid)) != 0) { + switch (errno) { + case EPERM: + case EACCES: + ret = AFPERR_ACCESS; + goto exit; + default: + ret = AFPERR_MISC; + goto exit; + } + continue; + } + + count++; + } + +exit: + if ((ea_close(&ea)) != 0) { + LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path); + return AFPERR_MISC; + } + + return ret; +} + +int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE) +{ + + unsigned int count = 0; + int ret = AFP_OK; + const char *eaname; + struct ea ea; + + LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name); + /* Open EA stuff */ + if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { + if (errno == ENOENT) + /* no EA files, nothing to do */ + return AFP_OK; + else + return AFPERR_MISC; + } + + /* Set mode on EA header file */ + if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { + LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); + switch (errno) { + case EPERM: + case EACCES: + ret = AFPERR_ACCESS; + goto exit; + default: + ret = AFPERR_MISC; + goto exit; + } + } + + /* Set mode on EA files */ + while (count < ea.ea_count) { + if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) { + ret = AFPERR_MISC; + goto exit; + } + if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) { + LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno)); + switch (errno) { + case EPERM: + case EACCES: + ret = AFPERR_ACCESS; + goto exit; + default: + ret = AFPERR_MISC; + goto exit; + } + continue; + } + + count++; + } + +exit: + if ((ea_close(&ea)) != 0) { + LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name); + return AFPERR_MISC; + } + + return ret; +} + +int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE) +{ + + int ret = AFP_OK; + unsigned int count = 0; + uid_t uid; + const char *eaname; + const char *eaname_safe = NULL; + struct ea ea; + + LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name); + /* .AppleDouble already might be inaccesible, so we must run as id 0 */ + uid = geteuid(); + if (seteuid(0)) { + LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno)); + return AFPERR_MISC; + } + + /* Open EA stuff */ + if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) { + /* ENOENT --> no EA files, nothing to do */ + if (errno != ENOENT) + ret = AFPERR_MISC; + if (seteuid(uid) < 0) { + LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno)); + exit(EXITERR_SYS); + } + return ret; + } + + /* Set mode on EA header */ + if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) { + LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno)); + switch (errno) { + case EPERM: + case EACCES: + ret = AFPERR_ACCESS; + goto exit; + default: + ret = AFPERR_MISC; + goto exit; + } + } + + /* Set mode on EA files */ + while (count < ea.ea_count) { + eaname = (*ea.ea_entries)[count].ea_name; + /* + * Be careful with EA names from the EA header! + * Eg NFS users might have access to them, can inject paths using ../ or /..... + * FIXME: + * Until the EA code escapes / in EA name requests from the client, these therefor wont work. + */ + if ((eaname_safe = strrchr(eaname, '/'))) { + LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname); + eaname = eaname_safe; + } + if ((eaname = ea_path(&ea, eaname, 1)) == NULL) { + ret = AFPERR_MISC; + goto exit; + } + if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) { + LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno)); + switch (errno) { + case EPERM: + case EACCES: + ret = AFPERR_ACCESS; + goto exit; + default: + ret = AFPERR_MISC; + goto exit; + } + continue; + } + + count++; + } + +exit: + if (seteuid(uid) < 0) { + LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno)); + exit(EXITERR_SYS); + } + + if ((ea_close(&ea)) != 0) { + LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name); + return AFPERR_MISC; + } + + return ret; +} diff --git a/libatalk/vfs/ea_sys.c b/libatalk/vfs/ea_sys.c index a4e35a90..49d6b283 100644 --- a/libatalk/vfs/ea_sys.c +++ b/libatalk/vfs/ea_sys.c @@ -1,5 +1,4 @@ /* - $Id: ea_sys.c,v 1.8 2010-04-13 08:05:06 franklahm Exp $ Copyright (c) 2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -27,20 +26,6 @@ #include #include -#if HAVE_ATTR_XATTR_H -#include -#elif HAVE_SYS_XATTR_H -#include -#endif - -#ifdef HAVE_SYS_EA_H -#include -#endif - -#ifdef HAVE_SYS_EXTATTR_H -#include -#endif - #include #include #include diff --git a/libatalk/vfs/extattr.c b/libatalk/vfs/extattr.c new file mode 100644 index 00000000..76b435c8 --- /dev/null +++ b/libatalk/vfs/extattr.c @@ -0,0 +1,752 @@ +/* + Unix SMB/CIFS implementation. + Samba system utilities + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1998-2005 + Copyright (C) Timur Bakeyev 2005 + Copyright (C) Bjoern Jacke 2006-2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + sys_copyxattr modified from LGPL2.1 libattr copyright + Copyright (C) 2001-2002 Silicon Graphics, Inc. All Rights Reserved. + Copyright (C) 2001 Andreas Gruenbacher. + + Samba 3.0.28, modified for netatalk. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#if HAVE_ATTR_XATTR_H +#include +#elif HAVE_SYS_XATTR_H +#include +#endif + +#ifdef HAVE_SYS_EA_H +#include +#endif + +#ifdef HAVE_ATTROPEN + +#include +#endif + +#ifdef HAVE_SYS_EXTATTR_H +#include +#endif + +#include +#include +#include +#include + +#ifndef ENOATTR +#define ENOATTR ENODATA +#endif + +/******** Solaris EA helper function prototypes ********/ +#ifdef HAVE_ATTROPEN +#define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP +static int solaris_write_xattr(int attrfd, const char *value, size_t size); +static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size); +static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size); +static int solaris_unlinkat(int attrdirfd, const char *name); +static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode); +static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode); +#endif + +/************************************************************************** + Wrappers for extented attribute calls. Based on the Linux package with + support for IRIX and (Net|Free)BSD also. Expand as other systems have them. +****************************************************************************/ +static char attr_name[256 +5] = "user."; + +static const char *prefix(const char *uname) +{ +#if defined(HAVE_ATTROPEN) + return uname; +#else + strlcpy(attr_name +5, uname, 256); + return attr_name; +#endif +} + +ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size) +{ + const char *name = prefix(uname); + +#if defined(HAVE_GETXATTR) +#ifndef XATTR_ADD_OPT + return getxattr(path, name, value, size); +#else + int options = 0; + return getxattr(path, name, value, size, 0, options); +#endif +#elif defined(HAVE_GETEA) + return getea(path, name, value, size); +#elif defined(HAVE_EXTATTR_GET_FILE) + ssize_t retval; + /* + * The BSD implementation has a nasty habit of silently truncating + * the returned value to the size of the buffer, so we have to check + * that the buffer is large enough to fit the returned value. + */ + if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) { + if (size == 0) + /* size == 0 means only return size */ + return retval; + if (retval > size) { + errno = ERANGE; + return -1; + } + if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0) + return retval; + } + + LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno)); + return -1; +#elif defined(HAVE_ATTR_GET) + int retval, flags = 0; + int valuelength = (int)size; + char *attrname = strchr(name,'.') + 1; + + if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT; + + retval = attr_get(path, attrname, (char *)value, &valuelength, flags); + + return retval ? retval : valuelength; +#elif defined(HAVE_ATTROPEN) + ssize_t ret = -1; + int attrfd = solaris_attropen(path, name, O_RDONLY, 0); + if (attrfd >= 0) { + ret = solaris_read_xattr(attrfd, value, size); + close(attrfd); + } + return ret; +#else + errno = ENOSYS; + return -1; +#endif +} + +ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size) +{ + const char *name = prefix(uname); + +#if defined(HAVE_LGETXATTR) + return lgetxattr(path, name, value, size); +#elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT) + int options = XATTR_NOFOLLOW; + return getxattr(path, name, value, size, 0, options); +#elif defined(HAVE_LGETEA) + return lgetea(path, name, value, size); +#elif defined(HAVE_EXTATTR_GET_LINK) + ssize_t retval; + if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) { + if(retval > size) { + errno = ERANGE; + return -1; + } + if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0) + return retval; + } + + LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno)); + return -1; +#elif defined(HAVE_ATTR_GET) + int retval, flags = ATTR_DONTFOLLOW; + int valuelength = (int)size; + char *attrname = strchr(name,'.') + 1; + + if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT; + + retval = attr_get(path, attrname, (char *)value, &valuelength, flags); + + return retval ? retval : valuelength; +#elif defined(HAVE_ATTROPEN) + ssize_t ret = -1; + int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0); + if (attrfd >= 0) { + ret = solaris_read_xattr(attrfd, value, size); + close(attrfd); + } + return ret; +#else + errno = ENOSYS; + return -1; +#endif +} + +#if defined(HAVE_EXTATTR_LIST_FILE) + +#define EXTATTR_PREFIX(s) (s), (sizeof((s))-1) + +static struct { + int space; + const char *name; + size_t len; +} +extattr[] = { + { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") }, + { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") }, +}; + +typedef union { + const char *path; + int filedes; +} extattr_arg; + +static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size) +{ + ssize_t list_size; + int i, len; + + switch(type) { +#if defined(HAVE_EXTATTR_LIST_FILE) + case 0: + list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size); + break; +#endif +#if defined(HAVE_EXTATTR_LIST_LINK) + case 1: + list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size); + break; +#endif +#if defined(HAVE_EXTATTR_LIST_FD) + case 2: + list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size); + break; +#endif + default: + errno = ENOSYS; + return -1; + } + + /* Some error happend. Errno should be set by the previous call */ + if(list_size < 0) + return -1; + + /* No attributes */ + if(list_size == 0) + return 0; + + /* XXX: Call with an empty buffer may be used to calculate + necessary buffer size. Unfortunately, we can't say, how + many attributes were returned, so here is the potential + problem with the emulation. + */ + if(list == NULL) + return list_size; + + /* Buffer is too small to fit the results */ + if(list_size > size) { + errno = ERANGE; + return -1; + } + + /* Convert from pascal strings to C strings */ + len = list[0]; + memmove(list, list + 1, list_size); + + for(i = len; i < list_size; ) { + LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i); + + len = list[i]; + list[i] = '\0'; + i += len + 1; + } + + return list_size; +} + +#endif + +#if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H) +static char attr_buffer[ATTR_MAX_VALUELEN]; + +static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags) +{ + int retval = 0, index; + attrlist_cursor_t *cursor = 0; + int total_size = 0; + attrlist_t * al = (attrlist_t *)attr_buffer; + attrlist_ent_t *ae; + size_t ent_size, left = size; + char *bp = list; + + while (True) { + if (filedes) + retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor); + else + retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor); + if (retval) break; + for (index = 0; index < al->al_count; index++) { + ae = ATTR_ENTRY(attr_buffer, index); + ent_size = strlen(ae->a_name) + sizeof("user."); + if (left >= ent_size) { + strncpy(bp, "user.", sizeof("user.")); + strncat(bp, ae->a_name, ent_size - sizeof("user.")); + bp += ent_size; + left -= ent_size; + } else if (size) { + errno = ERANGE; + retval = -1; + break; + } + total_size += ent_size; + } + if (al->al_more == 0) break; + } + if (retval == 0) { + flags |= ATTR_ROOT; + cursor = 0; + while (True) { + if (filedes) + retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor); + else + retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor); + if (retval) break; + for (index = 0; index < al->al_count; index++) { + ae = ATTR_ENTRY(attr_buffer, index); + ent_size = strlen(ae->a_name) + sizeof("system."); + if (left >= ent_size) { + strncpy(bp, "system.", sizeof("system.")); + strncat(bp, ae->a_name, ent_size - sizeof("system.")); + bp += ent_size; + left -= ent_size; + } else if (size) { + errno = ERANGE; + retval = -1; + break; + } + total_size += ent_size; + } + if (al->al_more == 0) break; + } + } + return (ssize_t)(retval ? retval : total_size); +} + +#endif + +#if defined(HAVE_LISTXATTR) +static ssize_t remove_user(ssize_t ret, char *list, size_t size) +{ + size_t len; + char *ptr; + char *ptr1; + ssize_t ptrsize; + + if (ret <= 0 || size == 0) + return ret; + ptrsize = ret; + ptr = ptr1 = list; + while (ptrsize > 0) { + len = strlen(ptr1) +1; + ptrsize -= len; + if (strncmp(ptr1, "user.",5)) { + ptr1 += len; + continue; + } + memmove(ptr, ptr1 +5, len -5); + ptr += len -5; + ptr1 += len; + } + return ptr -list; +} +#endif + +ssize_t sys_listxattr (const char *path, char *list, size_t size) +{ +#if defined(HAVE_LISTXATTR) + ssize_t ret; + +#ifndef XATTR_ADD_OPT + ret = listxattr(path, list, size); +#else + int options = 0; + ret = listxattr(path, list, size, options); +#endif + return remove_user(ret, list, size); + +#elif defined(HAVE_LISTEA) + return listea(path, list, size); +#elif defined(HAVE_EXTATTR_LIST_FILE) + extattr_arg arg; + arg.path = path; + return bsd_attr_list(0, arg, list, size); +#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H) + return irix_attr_list(path, 0, list, size, 0); +#elif defined(HAVE_ATTROPEN) + ssize_t ret = -1; + int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0); + if (attrdirfd >= 0) { + ret = solaris_list_xattr(attrdirfd, list, size); + close(attrdirfd); + } + return ret; +#else + errno = ENOSYS; + return -1; +#endif +} + +ssize_t sys_llistxattr (const char *path, char *list, size_t size) +{ +#if defined(HAVE_LLISTXATTR) + ssize_t ret; + + ret = llistxattr(path, list, size); + return remove_user(ret, list, size); +#elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT) + ssize_t ret; + int options = XATTR_NOFOLLOW; + + ret = listxattr(path, list, size, options); + return remove_user(ret, list, size); + +#elif defined(HAVE_LLISTEA) + return llistea(path, list, size); +#elif defined(HAVE_EXTATTR_LIST_LINK) + extattr_arg arg; + arg.path = path; + return bsd_attr_list(1, arg, list, size); +#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H) + return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW); +#elif defined(HAVE_ATTROPEN) + ssize_t ret = -1; + int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0); + if (attrdirfd >= 0) { + ret = solaris_list_xattr(attrdirfd, list, size); + close(attrdirfd); + } + return ret; +#else + errno = ENOSYS; + return -1; +#endif +} + +int sys_removexattr (const char *path, const char *uname) +{ + const char *name = prefix(uname); +#if defined(HAVE_REMOVEXATTR) +#ifndef XATTR_ADD_OPT + return removexattr(path, name); +#else + int options = 0; + return removexattr(path, name, options); +#endif +#elif defined(HAVE_REMOVEEA) + return removeea(path, name); +#elif defined(HAVE_EXTATTR_DELETE_FILE) + return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname); +#elif defined(HAVE_ATTR_REMOVE) + int flags = 0; + char *attrname = strchr(name,'.') + 1; + + if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT; + + return attr_remove(path, attrname, flags); +#elif defined(HAVE_ATTROPEN) + int ret = -1; + int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0); + if (attrdirfd >= 0) { + ret = solaris_unlinkat(attrdirfd, name); + close(attrdirfd); + } + return ret; +#else + errno = ENOSYS; + return -1; +#endif +} + +int sys_lremovexattr (const char *path, const char *uname) +{ + const char *name = prefix(uname); +#if defined(HAVE_LREMOVEXATTR) + return lremovexattr(path, name); +#elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT) + int options = XATTR_NOFOLLOW; + return removexattr(path, name, options); +#elif defined(HAVE_LREMOVEEA) + return lremoveea(path, name); +#elif defined(HAVE_EXTATTR_DELETE_LINK) + return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname); +#elif defined(HAVE_ATTR_REMOVE) + int flags = ATTR_DONTFOLLOW; + char *attrname = strchr(name,'.') + 1; + + if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT; + + return attr_remove(path, attrname, flags); +#elif defined(HAVE_ATTROPEN) + int ret = -1; + int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0); + if (attrdirfd >= 0) { + ret = solaris_unlinkat(attrdirfd, name); + close(attrdirfd); + } + return ret; +#else + errno = ENOSYS; + return -1; +#endif +} + +int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags) +{ + const char *name = prefix(uname); +#if defined(HAVE_SETXATTR) +#ifndef XATTR_ADD_OPT + return setxattr(path, name, value, size, flags); +#else + int options = 0; + return setxattr(path, name, value, size, 0, options); +#endif +#elif defined(HAVE_SETEA) + return setea(path, name, value, size, flags); +#elif defined(HAVE_EXTATTR_SET_FILE) + int retval = 0; + if (flags) { + /* Check attribute existence */ + retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0); + if (retval < 0) { + /* REPLACE attribute, that doesn't exist */ + if (flags & XATTR_REPLACE && errno == ENOATTR) { + errno = ENOATTR; + return -1; + } + /* Ignore other errors */ + } + else { + /* CREATE attribute, that already exists */ + if (flags & XATTR_CREATE) { + errno = EEXIST; + return -1; + } + } + } + retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size); + return (retval < 0) ? -1 : 0; +#elif defined(HAVE_ATTR_SET) + int myflags = 0; + char *attrname = strchr(name,'.') + 1; + + if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT; + if (flags & XATTR_CREATE) myflags |= ATTR_CREATE; + if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE; + + return attr_set(path, attrname, (const char *)value, size, myflags); +#elif defined(HAVE_ATTROPEN) + int ret = -1; + int myflags = O_RDWR; + int attrfd; + if (flags & XATTR_CREATE) myflags |= O_EXCL; + if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT; + attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE); + if (attrfd >= 0) { + ret = solaris_write_xattr(attrfd, value, size); + close(attrfd); + } + return ret; +#else + errno = ENOSYS; + return -1; +#endif +} + +int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags) +{ + const char *name = prefix(uname); +#if defined(HAVE_LSETXATTR) + return lsetxattr(path, name, value, size, flags); +#elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT) + int options = XATTR_NOFOLLOW; + return setxattr(path, name, value, size, 0, options); +#elif defined(LSETEA) + return lsetea(path, name, value, size, flags); +#elif defined(HAVE_EXTATTR_SET_LINK) + int retval = 0; + if (flags) { + /* Check attribute existence */ + retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0); + if (retval < 0) { + /* REPLACE attribute, that doesn't exist */ + if (flags & XATTR_REPLACE && errno == ENOATTR) { + errno = ENOATTR; + return -1; + } + /* Ignore other errors */ + } + else { + /* CREATE attribute, that already exists */ + if (flags & XATTR_CREATE) { + errno = EEXIST; + return -1; + } + } + } + + retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size); + return (retval < 0) ? -1 : 0; +#elif defined(HAVE_ATTR_SET) + int myflags = ATTR_DONTFOLLOW; + char *attrname = strchr(name,'.') + 1; + + if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT; + if (flags & XATTR_CREATE) myflags |= ATTR_CREATE; + if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE; + + return attr_set(path, attrname, (const char *)value, size, myflags); +#elif defined(HAVE_ATTROPEN) + int ret = -1; + int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW; + int attrfd; + if (flags & XATTR_CREATE) myflags |= O_EXCL; + if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT; + attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE); + if (attrfd >= 0) { + ret = solaris_write_xattr(attrfd, value, size); + close(attrfd); + } + return ret; +#else + errno = ENOSYS; + return -1; +#endif +} + +/************************************************************************** + helper functions for Solaris' EA support +****************************************************************************/ +#ifdef HAVE_ATTROPEN +static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size) +{ + struct stat sbuf; + + if (fstat(attrfd, &sbuf) == -1) { + return -1; + } + + /* This is to return the current size of the named extended attribute */ + if (size == 0) { + return sbuf.st_size; + } + + /* check size and read xattr */ + if (sbuf.st_size > size) { + return -1; + } + + return read(attrfd, value, sbuf.st_size); +} + +static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size) +{ + ssize_t len = 0; + DIR *dirp; + struct dirent *de; + int newfd = dup(attrdirfd); + /* CAUTION: The originating file descriptor should not be + used again following the call to fdopendir(). + For that reason we dup() the file descriptor + here to make things more clear. */ + dirp = fdopendir(newfd); + + while ((de = readdir(dirp))) { + size_t listlen; + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") || + !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw")) + { + /* we don't want "." and ".." here: */ + LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name); + continue; + } + + listlen = strlen(de->d_name); + if (size == 0) { + /* return the current size of the list of extended attribute names*/ + len += listlen + 1; + } else { + /* check size and copy entry + nul into list. */ + if ((len + listlen + 1) > size) { + errno = ERANGE; + len = -1; + break; + } else { + strcpy(list + len, de->d_name); + len += listlen; + list[len] = '\0'; + ++len; + } + } + } + + if (closedir(dirp) == -1) { + LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno)); + return -1; + } + return len; +} + +static int solaris_unlinkat(int attrdirfd, const char *name) +{ + if (unlinkat(attrdirfd, name, 0) == -1) { + return -1; + } + return 0; +} + +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) { + LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno)); + errno = ENOATTR; + } + return filedes; +} + +static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode) +{ + int filedes = openat(fildes, path, oflag, mode); + if (filedes == -1) { + LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno)); + } + return filedes; +} + +static int solaris_write_xattr(int attrfd, const char *value, size_t size) +{ + if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) { + return 0; + } else { + LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n"); + return -1; + } +} + +#endif /*HAVE_ATTROPEN*/ + diff --git a/libatalk/vfs/sys_ea.c b/libatalk/vfs/sys_ea.c deleted file mode 100644 index 900eb07e..00000000 --- a/libatalk/vfs/sys_ea.c +++ /dev/null @@ -1,754 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Samba system utilities - Copyright (C) Andrew Tridgell 1992-1998 - Copyright (C) Jeremy Allison 1998-2005 - Copyright (C) Timur Bakeyev 2005 - Copyright (C) Bjoern Jacke 2006-2007 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - sys_copyxattr modified from LGPL2.1 libattr copyright - Copyright (C) 2001-2002 Silicon Graphics, Inc. All Rights Reserved. - Copyright (C) 2001 Andreas Gruenbacher. - - Samba 3.0.28, modified for netatalk. - $Id: sys_ea.c,v 1.6 2009-12-04 10:26:10 franklahm Exp $ - -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include - -#if HAVE_ATTR_XATTR_H -#include -#elif HAVE_SYS_XATTR_H -#include -#endif - -#ifdef HAVE_SYS_EA_H -#include -#endif - -#ifdef HAVE_ATTROPEN - -#include -#endif - -#ifdef HAVE_SYS_EXTATTR_H -#include -#endif - -#include -#include -#include -#include - -#ifndef ENOATTR -#define ENOATTR ENODATA -#endif - -/******** Solaris EA helper function prototypes ********/ -#ifdef HAVE_ATTROPEN -#define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP -static int solaris_write_xattr(int attrfd, const char *value, size_t size); -static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size); -static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size); -static int solaris_unlinkat(int attrdirfd, const char *name); -static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode); -static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode); -#endif - -/************************************************************************** - Wrappers for extented attribute calls. Based on the Linux package with - support for IRIX and (Net|Free)BSD also. Expand as other systems have them. -****************************************************************************/ -static char attr_name[256 +5] = "user."; - -static const char *prefix(const char *uname) -{ -#if defined(HAVE_ATTROPEN) - return uname; -#else - strlcpy(attr_name +5, uname, 256); - return attr_name; -#endif -} - -ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size) -{ - const char *name = prefix(uname); - -#if defined(HAVE_GETXATTR) -#ifndef XATTR_ADD_OPT - return getxattr(path, name, value, size); -#else - int options = 0; - return getxattr(path, name, value, size, 0, options); -#endif -#elif defined(HAVE_GETEA) - return getea(path, name, value, size); -#elif defined(HAVE_EXTATTR_GET_FILE) - ssize_t retval; - /* - * The BSD implementation has a nasty habit of silently truncating - * the returned value to the size of the buffer, so we have to check - * that the buffer is large enough to fit the returned value. - */ - if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) { - if (size == 0) - /* size == 0 means only return size */ - return retval; - if (retval > size) { - errno = ERANGE; - return -1; - } - if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0) - return retval; - } - - LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno)); - return -1; -#elif defined(HAVE_ATTR_GET) - int retval, flags = 0; - int valuelength = (int)size; - char *attrname = strchr(name,'.') + 1; - - if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT; - - retval = attr_get(path, attrname, (char *)value, &valuelength, flags); - - return retval ? retval : valuelength; -#elif defined(HAVE_ATTROPEN) - ssize_t ret = -1; - int attrfd = solaris_attropen(path, name, O_RDONLY, 0); - if (attrfd >= 0) { - ret = solaris_read_xattr(attrfd, value, size); - close(attrfd); - } - return ret; -#else - errno = ENOSYS; - return -1; -#endif -} - -ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size) -{ - const char *name = prefix(uname); - -#if defined(HAVE_LGETXATTR) - return lgetxattr(path, name, value, size); -#elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT) - int options = XATTR_NOFOLLOW; - return getxattr(path, name, value, size, 0, options); -#elif defined(HAVE_LGETEA) - return lgetea(path, name, value, size); -#elif defined(HAVE_EXTATTR_GET_LINK) - ssize_t retval; - if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) { - if(retval > size) { - errno = ERANGE; - return -1; - } - if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0) - return retval; - } - - LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno)); - return -1; -#elif defined(HAVE_ATTR_GET) - int retval, flags = ATTR_DONTFOLLOW; - int valuelength = (int)size; - char *attrname = strchr(name,'.') + 1; - - if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT; - - retval = attr_get(path, attrname, (char *)value, &valuelength, flags); - - return retval ? retval : valuelength; -#elif defined(HAVE_ATTROPEN) - ssize_t ret = -1; - int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0); - if (attrfd >= 0) { - ret = solaris_read_xattr(attrfd, value, size); - close(attrfd); - } - return ret; -#else - errno = ENOSYS; - return -1; -#endif -} - -#if defined(HAVE_EXTATTR_LIST_FILE) - -#define EXTATTR_PREFIX(s) (s), (sizeof((s))-1) - -static struct { - int space; - const char *name; - size_t len; -} -extattr[] = { - { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") }, - { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") }, -}; - -typedef union { - const char *path; - int filedes; -} extattr_arg; - -static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size) -{ - ssize_t list_size; - int i, len; - - switch(type) { -#if defined(HAVE_EXTATTR_LIST_FILE) - case 0: - list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size); - break; -#endif -#if defined(HAVE_EXTATTR_LIST_LINK) - case 1: - list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size); - break; -#endif -#if defined(HAVE_EXTATTR_LIST_FD) - case 2: - list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size); - break; -#endif - default: - errno = ENOSYS; - return -1; - } - - /* Some error happend. Errno should be set by the previous call */ - if(list_size < 0) - return -1; - - /* No attributes */ - if(list_size == 0) - return 0; - - /* XXX: Call with an empty buffer may be used to calculate - necessary buffer size. Unfortunately, we can't say, how - many attributes were returned, so here is the potential - problem with the emulation. - */ - if(list == NULL) - return list_size; - - /* Buffer is too small to fit the results */ - if(list_size > size) { - errno = ERANGE; - return -1; - } - - /* Convert from pascal strings to C strings */ - len = list[0]; - memmove(list, list + 1, list_size); - - for(i = len; i < list_size; ) { - LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i); - - len = list[i]; - list[i] = '\0'; - i += len + 1; - } - - return list_size; -} - -#endif - -#if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H) -static char attr_buffer[ATTR_MAX_VALUELEN]; - -static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags) -{ - int retval = 0, index; - attrlist_cursor_t *cursor = 0; - int total_size = 0; - attrlist_t * al = (attrlist_t *)attr_buffer; - attrlist_ent_t *ae; - size_t ent_size, left = size; - char *bp = list; - - while (True) { - if (filedes) - retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor); - else - retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor); - if (retval) break; - for (index = 0; index < al->al_count; index++) { - ae = ATTR_ENTRY(attr_buffer, index); - ent_size = strlen(ae->a_name) + sizeof("user."); - if (left >= ent_size) { - strncpy(bp, "user.", sizeof("user.")); - strncat(bp, ae->a_name, ent_size - sizeof("user.")); - bp += ent_size; - left -= ent_size; - } else if (size) { - errno = ERANGE; - retval = -1; - break; - } - total_size += ent_size; - } - if (al->al_more == 0) break; - } - if (retval == 0) { - flags |= ATTR_ROOT; - cursor = 0; - while (True) { - if (filedes) - retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor); - else - retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor); - if (retval) break; - for (index = 0; index < al->al_count; index++) { - ae = ATTR_ENTRY(attr_buffer, index); - ent_size = strlen(ae->a_name) + sizeof("system."); - if (left >= ent_size) { - strncpy(bp, "system.", sizeof("system.")); - strncat(bp, ae->a_name, ent_size - sizeof("system.")); - bp += ent_size; - left -= ent_size; - } else if (size) { - errno = ERANGE; - retval = -1; - break; - } - total_size += ent_size; - } - if (al->al_more == 0) break; - } - } - return (ssize_t)(retval ? retval : total_size); -} - -#endif - -#if defined(HAVE_LISTXATTR) -static ssize_t remove_user(ssize_t ret, char *list, size_t size) -{ - size_t len; - char *ptr; - char *ptr1; - ssize_t ptrsize; - - if (ret <= 0 || size == 0) - return ret; - ptrsize = ret; - ptr = ptr1 = list; - while (ptrsize > 0) { - len = strlen(ptr1) +1; - ptrsize -= len; - if (strncmp(ptr1, "user.",5)) { - ptr1 += len; - continue; - } - memmove(ptr, ptr1 +5, len -5); - ptr += len -5; - ptr1 += len; - } - return ptr -list; -} -#endif - -ssize_t sys_listxattr (const char *path, char *list, size_t size) -{ -#if defined(HAVE_LISTXATTR) - ssize_t ret; - -#ifndef XATTR_ADD_OPT - ret = listxattr(path, list, size); -#else - int options = 0; - ret = listxattr(path, list, size, options); -#endif - return remove_user(ret, list, size); - -#elif defined(HAVE_LISTEA) - return listea(path, list, size); -#elif defined(HAVE_EXTATTR_LIST_FILE) - extattr_arg arg; - arg.path = path; - return bsd_attr_list(0, arg, list, size); -#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H) - return irix_attr_list(path, 0, list, size, 0); -#elif defined(HAVE_ATTROPEN) - ssize_t ret = -1; - int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0); - if (attrdirfd >= 0) { - ret = solaris_list_xattr(attrdirfd, list, size); - close(attrdirfd); - } - return ret; -#else - errno = ENOSYS; - return -1; -#endif -} - -ssize_t sys_llistxattr (const char *path, char *list, size_t size) -{ -#if defined(HAVE_LLISTXATTR) - ssize_t ret; - - ret = llistxattr(path, list, size); - return remove_user(ret, list, size); -#elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT) - ssize_t ret; - int options = XATTR_NOFOLLOW; - - ret = listxattr(path, list, size, options); - return remove_user(ret, list, size); - -#elif defined(HAVE_LLISTEA) - return llistea(path, list, size); -#elif defined(HAVE_EXTATTR_LIST_LINK) - extattr_arg arg; - arg.path = path; - return bsd_attr_list(1, arg, list, size); -#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H) - return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW); -#elif defined(HAVE_ATTROPEN) - ssize_t ret = -1; - int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0); - if (attrdirfd >= 0) { - ret = solaris_list_xattr(attrdirfd, list, size); - close(attrdirfd); - } - return ret; -#else - errno = ENOSYS; - return -1; -#endif -} - -int sys_removexattr (const char *path, const char *uname) -{ - const char *name = prefix(uname); -#if defined(HAVE_REMOVEXATTR) -#ifndef XATTR_ADD_OPT - return removexattr(path, name); -#else - int options = 0; - return removexattr(path, name, options); -#endif -#elif defined(HAVE_REMOVEEA) - return removeea(path, name); -#elif defined(HAVE_EXTATTR_DELETE_FILE) - return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname); -#elif defined(HAVE_ATTR_REMOVE) - int flags = 0; - char *attrname = strchr(name,'.') + 1; - - if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT; - - return attr_remove(path, attrname, flags); -#elif defined(HAVE_ATTROPEN) - int ret = -1; - int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0); - if (attrdirfd >= 0) { - ret = solaris_unlinkat(attrdirfd, name); - close(attrdirfd); - } - return ret; -#else - errno = ENOSYS; - return -1; -#endif -} - -int sys_lremovexattr (const char *path, const char *uname) -{ - const char *name = prefix(uname); -#if defined(HAVE_LREMOVEXATTR) - return lremovexattr(path, name); -#elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT) - int options = XATTR_NOFOLLOW; - return removexattr(path, name, options); -#elif defined(HAVE_LREMOVEEA) - return lremoveea(path, name); -#elif defined(HAVE_EXTATTR_DELETE_LINK) - return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname); -#elif defined(HAVE_ATTR_REMOVE) - int flags = ATTR_DONTFOLLOW; - char *attrname = strchr(name,'.') + 1; - - if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT; - - return attr_remove(path, attrname, flags); -#elif defined(HAVE_ATTROPEN) - int ret = -1; - int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0); - if (attrdirfd >= 0) { - ret = solaris_unlinkat(attrdirfd, name); - close(attrdirfd); - } - return ret; -#else - errno = ENOSYS; - return -1; -#endif -} - -int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags) -{ - const char *name = prefix(uname); -#if defined(HAVE_SETXATTR) -#ifndef XATTR_ADD_OPT - return setxattr(path, name, value, size, flags); -#else - int options = 0; - return setxattr(path, name, value, size, 0, options); -#endif -#elif defined(HAVE_SETEA) - return setea(path, name, value, size, flags); -#elif defined(HAVE_EXTATTR_SET_FILE) - int retval = 0; - if (flags) { - /* Check attribute existence */ - retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0); - if (retval < 0) { - /* REPLACE attribute, that doesn't exist */ - if (flags & XATTR_REPLACE && errno == ENOATTR) { - errno = ENOATTR; - return -1; - } - /* Ignore other errors */ - } - else { - /* CREATE attribute, that already exists */ - if (flags & XATTR_CREATE) { - errno = EEXIST; - return -1; - } - } - } - retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size); - return (retval < 0) ? -1 : 0; -#elif defined(HAVE_ATTR_SET) - int myflags = 0; - char *attrname = strchr(name,'.') + 1; - - if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT; - if (flags & XATTR_CREATE) myflags |= ATTR_CREATE; - if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE; - - return attr_set(path, attrname, (const char *)value, size, myflags); -#elif defined(HAVE_ATTROPEN) - int ret = -1; - int myflags = O_RDWR; - int attrfd; - if (flags & XATTR_CREATE) myflags |= O_EXCL; - if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT; - attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE); - if (attrfd >= 0) { - ret = solaris_write_xattr(attrfd, value, size); - close(attrfd); - } - return ret; -#else - errno = ENOSYS; - return -1; -#endif -} - -int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags) -{ - const char *name = prefix(uname); -#if defined(HAVE_LSETXATTR) - return lsetxattr(path, name, value, size, flags); -#elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT) - int options = XATTR_NOFOLLOW; - return setxattr(path, name, value, size, 0, options); -#elif defined(LSETEA) - return lsetea(path, name, value, size, flags); -#elif defined(HAVE_EXTATTR_SET_LINK) - int retval = 0; - if (flags) { - /* Check attribute existence */ - retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0); - if (retval < 0) { - /* REPLACE attribute, that doesn't exist */ - if (flags & XATTR_REPLACE && errno == ENOATTR) { - errno = ENOATTR; - return -1; - } - /* Ignore other errors */ - } - else { - /* CREATE attribute, that already exists */ - if (flags & XATTR_CREATE) { - errno = EEXIST; - return -1; - } - } - } - - retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size); - return (retval < 0) ? -1 : 0; -#elif defined(HAVE_ATTR_SET) - int myflags = ATTR_DONTFOLLOW; - char *attrname = strchr(name,'.') + 1; - - if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT; - if (flags & XATTR_CREATE) myflags |= ATTR_CREATE; - if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE; - - return attr_set(path, attrname, (const char *)value, size, myflags); -#elif defined(HAVE_ATTROPEN) - int ret = -1; - int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW; - int attrfd; - if (flags & XATTR_CREATE) myflags |= O_EXCL; - if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT; - attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE); - if (attrfd >= 0) { - ret = solaris_write_xattr(attrfd, value, size); - close(attrfd); - } - return ret; -#else - errno = ENOSYS; - return -1; -#endif -} - -/************************************************************************** - helper functions for Solaris' EA support -****************************************************************************/ -#ifdef HAVE_ATTROPEN -static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size) -{ - struct stat sbuf; - - if (fstat(attrfd, &sbuf) == -1) { - return -1; - } - - /* This is to return the current size of the named extended attribute */ - if (size == 0) { - return sbuf.st_size; - } - - /* check size and read xattr */ - if (sbuf.st_size > size) { - return -1; - } - - return read(attrfd, value, sbuf.st_size); -} - -static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size) -{ - ssize_t len = 0; - DIR *dirp; - struct dirent *de; - int newfd = dup(attrdirfd); - /* CAUTION: The originating file descriptor should not be - used again following the call to fdopendir(). - For that reason we dup() the file descriptor - here to make things more clear. */ - dirp = fdopendir(newfd); - - while ((de = readdir(dirp))) { - size_t listlen; - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") || - !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw")) - { - /* we don't want "." and ".." here: */ - LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name); - continue; - } - - listlen = strlen(de->d_name); - if (size == 0) { - /* return the current size of the list of extended attribute names*/ - len += listlen + 1; - } else { - /* check size and copy entry + nul into list. */ - if ((len + listlen + 1) > size) { - errno = ERANGE; - len = -1; - break; - } else { - strcpy(list + len, de->d_name); - len += listlen; - list[len] = '\0'; - ++len; - } - } - } - - if (closedir(dirp) == -1) { - LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno)); - return -1; - } - return len; -} - -static int solaris_unlinkat(int attrdirfd, const char *name) -{ - if (unlinkat(attrdirfd, name, 0) == -1) { - return -1; - } - return 0; -} - -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) { - LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno)); - errno = ENOATTR; - } - return filedes; -} - -static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode) -{ - int filedes = openat(fildes, path, oflag, mode); - if (filedes == -1) { - LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno)); - } - return filedes; -} - -static int solaris_write_xattr(int attrfd, const char *value, size_t size) -{ - if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) { - return 0; - } else { - LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n"); - return -1; - } -} - -#endif /*HAVE_ATTROPEN*/ - diff --git a/sys/netatalk/at.h b/sys/netatalk/at.h index 9c9dceb7..ddb2ab50 100644 --- a/sys/netatalk/at.h +++ b/sys/netatalk/at.h @@ -24,7 +24,7 @@ #include #include /* so that we can deal with sun's s_net #define */ -#ifdef MACOSX_SERVER +#if defined(MACOSX_SERVER) && (!defined(NO_DDP)) #include #endif /* MACOSX_SERVER */