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
AC_MSG_ERROR([missing header <uuid/uuid.> 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)
#include <config.h>
#endif
+#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* version info */
#define AD_VERSION2 0x00020000
-#define AD_VERSION2_OSX 0x00020001
#define AD_VERSION2_EA 0x00020002
#define AD_VERSION AD_VERSION2_EA
#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 + \
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)
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),
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 */
/*
- $Id: ea.h,v 1.11 2010-03-12 15:16:49 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#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
{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},
/* 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.");
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;
}
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;
}
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;
}
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;
}
/*
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",
/* ----------------
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);
/*
* 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;
}
/* --------------------------- */
-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,
{
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;
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)) &&
/* 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 ));
if (!st_invalid) {
ad_chown(ad_p, &st_dir);
}
- }
- else {
+ } else {
return ad_error(ad, adflags);
}
} else {
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;
}
}
}
}
- /* ****************************************** */
- /* 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;
}
return 0 ;
}
+#endif
/*!
* @brief open metadata, possibly as root
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
+++ /dev/null
-/*
- Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-
- 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 <unistd.h>
-#include <stdint.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-#include <atalk/adouble.h>
-#include <atalk/ea.h>
-#include <atalk/afp.h>
-#include <atalk/logger.h>
-#include <atalk/volume.h>
-#include <atalk/vfs.h>
-#include <atalk/util.h>
-#include <atalk/unix.h>
-
-/*
- * 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;
-}
--- /dev/null
+/*
+ Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+
+ 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 <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <atalk/adouble.h>
+#include <atalk/ea.h>
+#include <atalk/afp.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/vfs.h>
+#include <atalk/util.h>
+#include <atalk/unix.h>
+
+/*
+ * 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;
+}
/*
- $Id: ea_sys.c,v 1.8 2010-04-13 08:05:06 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include <fcntl.h>
#include <dirent.h>
-#if HAVE_ATTR_XATTR_H
-#include <attr/xattr.h>
-#elif HAVE_SYS_XATTR_H
-#include <sys/xattr.h>
-#endif
-
-#ifdef HAVE_SYS_EA_H
-#include <sys/ea.h>
-#endif
-
-#ifdef HAVE_SYS_EXTATTR_H
-#include <sys/extattr.h>
-#endif
-
#include <atalk/adouble.h>
#include <atalk/ea.h>
#include <atalk/afp.h>
--- /dev/null
+/*
+ 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 <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#if HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#elif HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+
+#ifdef HAVE_SYS_EA_H
+#include <sys/ea.h>
+#endif
+
+#ifdef HAVE_ATTROPEN
+
+#include <dirent.h>
+#endif
+
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+
+#include <atalk/adouble.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+#include <atalk/ea.h>
+
+#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*/
+
+++ /dev/null
-/*
- 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 <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <errno.h>
-
-#if HAVE_ATTR_XATTR_H
-#include <attr/xattr.h>
-#elif HAVE_SYS_XATTR_H
-#include <sys/xattr.h>
-#endif
-
-#ifdef HAVE_SYS_EA_H
-#include <sys/ea.h>
-#endif
-
-#ifdef HAVE_ATTROPEN
-
-#include <dirent.h>
-#endif
-
-#ifdef HAVE_SYS_EXTATTR_H
-#include <sys/extattr.h>
-#endif
-
-#include <atalk/adouble.h>
-#include <atalk/util.h>
-#include <atalk/logger.h>
-#include <atalk/ea.h>
-
-#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*/
-
#include <sys/types.h>
#include <netinet/in.h> /* so that we can deal with sun's s_net #define */
-#ifdef MACOSX_SERVER
+#if defined(MACOSX_SERVER) && (!defined(NO_DDP))
#include <netat/appletalk.h>
#endif /* MACOSX_SERVER */