-2.2alpha5
\ No newline at end of file
+3.0dev
\ No newline at end of file
# Makefile.am for bin/megatron/
-INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/sys
-
-bin_PROGRAMS = megatron
-
-megatron_SOURCES = asingle.c hqx.c macbin.c megatron.c nad.c updcrc.c
-megatron_LDADD = $(top_builddir)/libatalk/libatalk.la
-
-noinst_HEADERS = asingle.h megatron.h hqx.h macbin.h nad.h updcrc.h
-
-LINKS = unbin unhex unsingle hqx2bin single2bin macbinary binheader nadheader
-
-install-exec-hook:
- @for LINK in $(LINKS); do \
- rm -f $(DESTDIR)$(bindir)/$$LINK; \
- $(LN_S) megatron $(DESTDIR)$(bindir)/$$LINK; \
- done
-
-uninstall-hook:
- @for LINK in $(LINKS); do \
- rm -f $(DESTDIR)$(bindir)/$$LINK; \
- done
+# EXTRADIST = asingle.c hqx.c macbin.c megatron.c nad.c updcrc.c \
+# asingle.h megatron.h hqx.h macbin.h nad.h updcrc.h
AC_PROG_PERL
AC_PROG_GREP
AC_PROG_PS
-
AM_PROG_CC_C_O
dnl Checks for header files.
-AC_HEADER_DIRENT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS(fcntl.h limits.h stdint.h strings.h time.h sys/param.h sys/fcntl.h sys/file.h sys/ioctl.h sys/time.h sys/mnttab.h sys/statvfs.h sys/stat.h sys/vfs.h mntent.h syslog.h unistd.h termios.h sys/termios.h netdb.h sgtty.h ufs/quota.h mount.h statfs.h sys/types.h dlfcn.h errno.h sys/errno.h sys/uio.h langinfo.h locale.h sys/filio.h)
NETATALK_WEBMIN
dnl --------------------- last minute substitutions
-
+dnl Request SUSv3 standard interfaces
+CFLAGS="$CFLAGS -D_XOPEN_SOURCE=600 -D__EXTENSIONS__"
AC_SUBST(LIBS)
AC_SUBST(CFLAGS)
AC_SUBST(OVERWRITE_CONFIG)
openf = O_RDWR|O_CREAT|O_EXCL;
}
- if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
+ if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF,
openf, 0666, adp) < 0 ) {
switch ( errno ) {
case EROFS:
return( AFPERR_PARAM );
}
}
- if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
+ if ( ad_meta_fileno( adp ) == -1 ) { /* Hard META / HF */
/* on noadouble volumes, just creating the data fork is ok */
if (vol_noadouble(vol)) {
ad_close( adp, ADFLAGS_DF );
/* extract the obj part of the server */
Obj = (char *) server;
+#ifndef NO_DDP
nbp_name(server, &Obj, &Type, &Zone);
+#endif
if ((size_t)-1 == (len = convert_string(
- options->unixcharset, options->maccharset,
- Obj, -1, buf, sizeof(buf))) ) {
- len = MIN(strlen(Obj), 31);
+ options->unixcharset, options->maccharset,
+ Obj, -1, buf, sizeof(buf))) ) {
+ len = MIN(strlen(Obj), 31);
*data++ = len;
memcpy( data, Obj, len );
- LOG ( log_error, logtype_afpd, "Could not set servername, using fallback");
+ LOG ( log_error, logtype_afpd, "Could not set servername, using fallback");
} else {
*data++ = len;
memcpy( data, buf, len );
/* extract the obj part of the server */
Obj = (char *) (options->server ? options->server : options->hostname);
+#ifndef NO_DDP
nbp_name(options->server ? options->server : options->hostname, &Obj, &Type, &Zone);
-
+#endif
if ((size_t) -1 == (len = convert_string (
- options->unixcharset, CH_UTF8_MAC,
- Obj, -1, data+sizeof(namelen), maxstatuslen-offset )) ) {
- LOG ( log_error, logtype_afpd, "Could not set utf8 servername");
+ options->unixcharset, CH_UTF8_MAC,
+ Obj, -1, data+sizeof(namelen), maxstatuslen-offset )) ) {
+ LOG ( log_error, logtype_afpd, "Could not set utf8 servername");
- /* set offset to 0 */
- memset(begin + *nameoffset, 0, sizeof(offset));
+ /* set offset to 0 */
+ memset(begin + *nameoffset, 0, sizeof(offset));
data = begin + offset;
- }
- else {
+ } else {
namelen = htons(len);
memcpy( data, &namelen, sizeof(namelen));
data += sizeof(namelen);
memcpy(begin + *nameoffset, &offset, sizeof(u_int16_t));
/* Now set the flag ... */
- memcpy(&status, begin + AFPSTATUS_FLAGOFF, sizeof(status));
- status = ntohs(status);
- status |= AFPSRVRINFO_SRVUTF8;
- status = htons(status);
- memcpy(begin + AFPSTATUS_FLAGOFF, &status, sizeof(status));
+ memcpy(&status, begin + AFPSTATUS_FLAGOFF, sizeof(status));
+ status = ntohs(status);
+ status |= AFPSRVRINFO_SRVUTF8;
+ status = htons(status);
+ memcpy(begin + AFPSTATUS_FLAGOFF, &status, sizeof(status));
}
/* return length of buffer */
switch (what) {
case UAM_OPTION_USERNAME:
- *buf = obj->username;
+ *buf = &(obj->username[0]);
if (len)
*len = sizeof(obj->username) - 1;
break;
if (!obj)
return AFPERR_PARAM;
+#ifndef NO_DDP
switch (obj->proto) {
case AFPPROTO_ASP:
if ((len = asp_wrtcont(obj->handle, buf, buflen )) < 0)
break;
case AFPPROTO_DSI:
+#endif
len = dsi_writeinit(obj->handle, buf, *buflen);
if (!len || ((len = action(handle, buf, len)) < 0)) {
dsi_writeflush(obj->handle);
goto uam_afp_read_err;
}
}
+#ifndef NO_DDP
break;
}
+#endif
return 0;
uam_afp_read_err:
struct dirent *dirp;
DIR *dir;
mode_t hf_mode;
- int osx = vol->v_adouble == AD_VERSION2_OSX;
int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
mode |= vol->v_dperm;
for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
/* FIXME */
- if ( *dirp->d_name == '.' && (!osx || dirp->d_name[1] != '_')) {
+ if (*dirp->d_name == '.') {
continue;
}
if ( lstat( dirp->d_name, &st ) < 0 ) {
}
if (!S_ISDIR(st.st_mode)) {
- int setmode = (osx && *dirp->d_name == '.')?hf_mode:mode;
-
- if (setfilmode(dirp->d_name, setmode, &st, vol->v_umask) < 0) {
+ if (setfilmode(dirp->d_name, mode, &st, vol->v_umask) < 0) {
LOG(log_error, logtype_afpd, "setdirmode: chmod %s: %s",dirp->d_name, strerror(errno) );
return -1;
}
struct stat st;
struct dirent *dirp;
DIR *dir;
- int osx = vol->v_adouble == AD_VERSION2_OSX;
if (( dir = opendir( name )) == NULL ) {
return( -1 );
}
for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
- if ( *dirp->d_name == '.' && (!osx || dirp->d_name[1] != '_')) {
+ if ( *dirp->d_name == '.') {
continue;
}
if ( lstat( dirp->d_name, &st ) < 0 ) {
#include <atalk/logger.h>
#include <atalk/vfs.h>
#include <atalk/uuid.h>
+#include <atalk/ea.h>
#ifdef CNID_DB
#include <atalk/cnid.h>
static const _special_folder special_folders[] = {
{"Network Trash Folder", 1, 0777, 1},
- {"Temporary Items", 1, 0777, 1},
{".AppleDesktop", 1, 0777, 0},
-#if 0
- {"TheFindByContentFolder", 0, 0, 1},
- {"TheVolumeSettingsFolder", 0, 0, 1},
-#endif
{NULL, 0, 0, 0}};
/* Forward declarations */
else if (strcasecmp(val + 1, "xlateupper") == 0)
options[VOLOPT_CASEFOLD].i_value = AFPVOL_ULOWERMUPPER;
} else if (optionok(tmp, "adouble:", val)) {
- if (strcasecmp(val + 1, "v1") == 0)
- options[VOLOPT_ADOUBLE].i_value = AD_VERSION1;
-#if AD_VERSION == AD_VERSION2
- else if (strcasecmp(val + 1, "v2") == 0)
+ if (strcasecmp(val + 1, "v2") == 0)
options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
- else if (strcasecmp(val + 1, "osx") == 0)
- options[VOLOPT_ADOUBLE].i_value = AD_VERSION2_OSX;
- else if (strcasecmp(val + 1, "sfm") == 0)
- options[VOLOPT_ADOUBLE].i_value = AD_VERSION1_SFM;
-#endif
+ else if (strcasecmp(val + 1, "ea") == 0)
+ options[VOLOPT_ADOUBLE].i_value = AD_VERSION_EA;
} else if (optionok(tmp, "options:", val)) {
char *p;
char *rbuf, size_t *rbuflen)
{
char *username;
- int len, ulen;
+ int len;
+ size_t ulen;
u_int16_t temp16;
*rbuflen = 0;
/*
- * $Id: adouble.h,v 1.55 2010-03-30 12:55:26 franklahm Exp $
* Copyright (c) 1990,1991 Regents of The University of Michigan.
* All Rights Reserved.
*
#include <config.h>
#endif
-/* -------------------
- * need pread() and pwrite()
- */
-#ifdef HAVE_PREAD
-
-#ifndef HAVE_PWRITE
-#undef HAVE_PREAD
-#endif
-
-#endif
-
-#ifdef HAVE_PWRITE
-#ifndef HAVE_PREAD
-#undef HAVE_PWRITE
-#endif
-#endif
-
-/*
- Still have to figure out which platforms really
- need _XOPEN_SOURCE defined for pread.
-*/
-#if defined(HAVE_PREAD) && !defined(SOLARIS) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(TRU64)
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 500
-#endif
-#endif
-
+#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
-
-#ifdef HAVE_UNISTD_H
-#undef __USE_MISC
-#define __USE_MISC
#include <unistd.h>
-#endif
-
#include <sys/cdefs.h>
-
-#ifdef HAVE_FCNTL_H
#include <fcntl.h>
-#endif
-
#include <sys/mman.h>
-#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
-#endif
+
#include <netatalk/endian.h>
/* version info */
-#define AD_VERSION1 0x00010000
-#define SFM_VERSION AD_VERSION1
-
#define AD_VERSION2 0x00020000
-#define AD_VERSION2_OSX 0x00020001
-/*
- #define AD_VERSION1_ADS 0x00010002
-*/
-#define AD_VERSION1_SFM 0x00010003
+#define AD_VERSION_EA 0x00020002
+
+/* default */
#define AD_VERSION AD_VERSION2
/*
*/
#define ADEID_DFORK 1
#define ADEID_RFORK 2
-#define ADEID_NAME 3 /* Note: starting with Netatalk 2.1 we do NOT alway set the name */
+#define ADEID_NAME 3
#define ADEID_COMMENT 4
#define ADEID_ICONBW 5
#define ADEID_ICONCOL 6
#define ADEID_AFPFILEI 14 /* where the rest of the FILEI info goes */
#define ADEID_DID 15
-#if AD_VERSION == AD_VERSION1
-#define ADEID_MAX 16
-#else
/* netatalk private note fileid reused DID */
#define ADEID_PRIVDEV 16
#define ADEID_PRIVINO 17
#define ADEID_PRIVSYN 18 /* in synch with database */
#define ADEID_PRIVID 19
-#define ADEID_SFMRESERVE1 20
-#define ADEID_SFMRESERVE2 21
+#define ADEID_MAX (ADEID_PRIVID + 1)
+/* These are the real ids for these entries, as stored in the adouble file */
#define AD_DEV 0x80444556
#define AD_INO 0x80494E4F
#define AD_SYN 0x8053594E
#define AD_ID 0x8053567E
-#define ADEID_MAX 22
-#endif
/* magic */
#define AD_APPLESINGLE_MAGIC 0x00051600
#define AD_APPLEDOUBLE_MAGIC 0x00051607
#define AD_MAGIC AD_APPLEDOUBLE_MAGIC
-#define SFM_MAGIC 0x00504641
/* sizes of relevant entry bits */
#define ADEDLEN_MAGIC 4
#define ADEDLEN_VERSION 4
#define ADEDLEN_FILLER 16
#define ADEDLEN_NENTRIES 2
-
-/* 26 */
-#define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
- ADEDLEN_FILLER + ADEDLEN_NENTRIES)
+#define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + ADEDLEN_FILLER + ADEDLEN_NENTRIES)
#define AD_ENTRY_LEN 12 /* size of a single entry header */
-/* v1 field widths */
-#define ADEDLEN_NAME 255
-#define ADEDLEN_COMMENT 200
-#define ADEDLEN_FILEI 16
-#define ADEDLEN_FINDERI 32
-
-/* v2 field widths */
+/* field widths */
+#define ADEDLEN_NAME 255
+#define ADEDLEN_COMMENT 200
+#define ADEDLEN_FILEI 16
+#define ADEDLEN_FINDERI 32
#define ADEDLEN_FILEDATESI 16
#define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
#define ADEDLEN_AFPFILEI 4
#define ADEDLEN_PRIVSYN 8
#define ADEDLEN_PRIVID 4
-#define ADEID_NUM_V1 5
#define ADEID_NUM_V2 13
+#define ADEID_NUM_EA 5
-// #define ADEID_NUM_SFM 5
-/* sizeof SFM meta data */
-#define AD_SFM_LEN 60
-
-/* 589 */
-#define AD_DATASZ1 (AD_HEADER_LEN + ADEDLEN_NAME + ADEDLEN_COMMENT + ADEDLEN_FILEI + ADEDLEN_FINDERI + \
- (ADEID_NUM_V1 * AD_ENTRY_LEN))
-
-#if AD_DATASZ1 != 589
-#error bad size for AD_DATASZ1
-#endif
-
-#define AD_NEWSZ2 (ADEDLEN_DID + ADEDLEN_AFPFILEI + ADEDLEN_SHORTNAME + ADEDLEN_PRODOSFILEI \
- + ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
-
-/* 725 */
-#define AD_DATASZ2 (AD_DATASZ1 + AD_NEWSZ2 + ((ADEID_NUM_V2 - ADEID_NUM_V1) * AD_ENTRY_LEN))
-
+#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 + \
+ ADEDLEN_PRIVSYN + ADEDLEN_PRIVID + (ADEID_NUM_V2 * AD_ENTRY_LEN))
#if AD_DATASZ2 != 741
#error bad size for AD_DATASZ2
#endif
-#define AD_DATASZ_MAX 1024
-#if AD_VERSION == AD_VERSION1
-#define AD_DATASZ AD_DATASZ1 /* hold enough for the entries */
-#elif AD_VERSION == AD_VERSION2
-#define AD_DATASZ AD_DATASZ2
+#define AD_DATASZ_EA (AD_HEADER_LEN + (ADEID_NUM_EA * AD_ENTRY_LEN) + ADEDLEN_FINDERI + \
+ ADEDLEN_COMMENT + ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + ADEDLEN_PRIVID)
+
+#if AD_DATASZ_EA != 342
+#error bad size for AD_DATASZ_EA
#endif
-/*
- * some legacy defines from netatalk-990130
- * (to keep from breaking certain packages)
- *
- */
+#define AD_DATASZ_MAX 1024
-#define ADEDOFF_RFORK 589
-#define ADEDOFF_NAME 86
-#define ADEDOFF_COMMENT 341
-#define ADEDOFF_FINDERI 557
-#ifndef ADEDOFF_FILEI
-#define ADEDOFF_FILEI 541
+#if AD_VERSION == AD_VERSION2
+#define AD_DATASZ AD_DATASZ2
+#elif AD_VERSION == AD_VERSION_EA
+#define AD_DATASZ AD_DATASZ_EA
#endif
typedef u_int32_t cnid_t;
-/*
- * The header of the AppleDouble Header File looks like this:
- *
- * NAME SIZE
- * ==== ====
- * Magic 4
- * Version 4
- * Home File System 16 (this becomes filler in ad v2)
- * Number of Entries 2
- * Entry Descriptors for each entry:
- * Entry ID 4
- * Offset 4
- * Length 4
- */
-
struct ad_entry {
- u_int32_t ade_off;
- u_int32_t ade_len;
+ uint32_t ade_off;
+ uint32_t ade_len;
};
typedef struct adf_lock_t {
struct ad_fd {
int adf_fd; /* -1: invalid, -2: symlink */
-
#ifndef HAVE_PREAD
off_t adf_off;
#endif
-
char *adf_syml;
int adf_flags;
int adf_excl;
/* some header protection */
#define AD_INITED 0xad494e54 /* ad"INT" */
-struct adouble_fops;
+
+struct adouble;
+
+struct adouble_fops {
+ char *(*ad_path)(const char *, int);
+ int (*ad_mkrf)(char *);
+ int (*ad_rebuild_header)(struct adouble *);
+ int (*ad_header_read)(struct adouble *, struct stat *);
+ int (*ad_header_upgrade)(struct adouble *, char *);
+};
struct adouble {
u_int32_t ad_magic;
struct ad_entry ad_eid[ ADEID_MAX ];
struct ad_fd ad_data_fork, ad_resource_fork, ad_metadata_fork;
struct ad_fd *ad_md; /* either ad_resource or ad_metadata */
-
int ad_flags; /* This really stores version info too (AD_VERSION*) */
int ad_adflags; /* ad_open flags adflags like ADFLAGS_DIR */
unsigned int ad_inited;
int ad_fileordir;
int ad_refcount; /* used in afpd/ofork.c */
off_t ad_rlen; /* ressource fork len with AFP 3.0
- the header parameter size is too small.
- */
+ the header parameter size is too small. */
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
};
-struct adouble_fops {
- char *(*ad_path)(const char *, int);
- int (*ad_mkrf)(char *);
- int (*ad_rebuild_header)(struct adouble *);
- int (*ad_check_header)(struct adouble *, struct stat *);
- int (*ad_header_read)(struct adouble *, struct stat *);
- int (*ad_header_upgrade)(struct adouble *, char *);
-};
-
#define ADFLAGS_DF (1<<0)
-#define ADFLAGS_HF (1<<1)
-#define ADFLAGS_DIR (1<<2)
-/*
-#define ADFLAGS_NOADOUBLE (1<<3)
-*/
-#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)
-
-/* adouble v2 cnid cache */
+#define ADFLAGS_RF (1<<1)
+#define ADFLAGS_HF (1<<2)
+#define ADFLAGS_DIR (1<<3)
+#define ADFLAGS_NOHF (1<<4) /* not an error if no ressource fork */
+#define ADFLAGS_RDONLY (1<<5) /* don't try readwrite */
+#define ADFLAGS_OPENFORKS (1<<6) /* check for open fork in ad_metadata function */
+#define ADFLAGS_CREATE (1<<7)
+
#define ADVOL_NODEV (1 << 0)
#define ADVOL_CACHE (1 << 1)
#define ADVOL_UNIXPRIV (1 << 2) /* adouble unix priv */
#define ad_unlock ad_fcntl_unlock
/* ad_open.c */
+extern uint32_t get_eid (uint32_t eid);
extern int ad_setfuid (const uid_t );
extern uid_t ad_getfuid (void );
extern char *ad_dir (const char *);
extern char *ad_path (const char *, int);
-extern char *ad_path_osx (const char *, int);
-extern char *ad_path_ads (const char *, int);
-extern char *ad_path_sfm (const char *, int);
+extern char *ad_path_ea (const char *, int);
extern int ad_mode (const char *, int);
extern int ad_mkdir (const char *, int);
extern void ad_init (struct adouble *, int, int );
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 *);
-
-/* Note: starting with Netatalk 2.1 we do NOT alway set the name */
-extern int ad_setname (struct adouble *, const char *);
-
-#if AD_VERSION == AD_VERSION2
-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);
-#else
-#define ad_setid(a, b, c)
-#endif
+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 */
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/time.h>
+#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <atalk/server_child.h>
#include <netatalk/endian.h>
-#ifdef __OpenBSD__
-#include <sys/socket.h>
-#endif
-
/* What a DSI packet looks like:
0 32
|-------------------------------|
/*
- $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
#include <config.h>
#endif
+#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
+
#ifdef HAVE_SOLARIS_ACLS
#include <sys/acl.h>
#endif
+#ifndef ENOATTR
+#define ENOATTR ENODATA
+#endif
+
#include <atalk/vfs.h>
/*
#define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */
#endif
+/* Names for our Extended Attributes adouble data */
+#define AD_EA_META "org.netatalk.Metadata"
+#define AD_EA_RESO "org.netatalk.ResourceFork"
+
+/****************************************************************************************
+ * 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
#include <sys/cdefs.h>
#include <sys/types.h>
-#ifdef HAVE_UNISTD_H
+#include <sys/socket.h>
#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
+
+#ifndef NO_DDP
#include <netatalk/at.h>
+#endif
#include <atalk/unicode.h>
/* exit error codes */
#define diatolower(x) _dialowermap[(unsigned char) (x)]
#define diatoupper(x) _diacasemap[(unsigned char) (x)]
+#ifndef NO_DDP
extern int atalk_aton (char *, struct at_addr *);
+#endif
extern void bprint (char *, int);
extern int strdiacasecmp (const char *, const char *);
extern int strndiacasecmp (const char *, const char *, size_t);
* unix.c
*****************************************************************/
+extern const char *abspath(const char *name);
extern const char *getcwdpath(void);
extern char *stripped_slashes_basename(char *p);
extern int lchdir(const char *dir);
# Makefile.am for libatalk/
-SUBDIRS = acl adouble asp atp bstring compat cnid dsi nbp netddp tdb util unicode vfs
+SUBDIRS = acl adouble bstring compat cnid dsi tdb util unicode vfs
lib_LTLIBRARIES = libatalk.la
libatalk_la_LIBADD = \
acl/libacl.la \
adouble/libadouble.la \
- asp/libasp.la \
- atp/libatp.la \
bstring/libbstring.la \
compat/libcompat.la \
dsi/libdsi.la \
- nbp/libnbp.la \
- netddp/libnetddp.la \
util/libutil.la \
tdb/libtdb.la \
unicode/libunicode.la \
libatalk_la_DEPENDENCIES = \
acl/libacl.la \
adouble/libadouble.la \
- asp/libasp.la \
- atp/libatp.la \
bstring/libbstring.la \
compat/libcompat.la \
dsi/libdsi.la \
- nbp/libnbp.la \
- netddp/libnetddp.la \
util/libutil.la \
tdb/libtdb.la \
unicode/libunicode.la \
libatalk_la_LDFLAGS = -static
+if USE_APPLETALK
+SUBDIRS += asp atp nbp netddp
+libatalk_la_LIBADD += \
+ asp/libasp.la \
+ atp/libatp.la \
+ nbp/libnbp.la \
+ netddp/libnetddp.la
+libatalk_la_DEPENDENCIES += \
+ asp/libasp.la \
+ atp/libatp.la \
+ nbp/libnbp.la \
+ netddp/libnetddp.la
+endif
/* sorry, no ACLs for symlinks */
return 0;
- if ( ! (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) {
+ if ( ! ((S_ISREG(st.st_mode)) || (S_ISDIR(st.st_mode)))) {
LOG(log_warning, logtype_afpd, "get_nfsv4_acl(\"%s/%s\"): special", getcwdpath(), name);
return 0;
}
u_int16_t fflags;
*attr = 0;
- if (ad->ad_version == AD_VERSION1) {
- if (ad_getentryoff(ad, ADEID_FILEI)) {
- memcpy(attr, ad_entry(ad, ADEID_FILEI) + FILEIOFF_ATTR,
- sizeof(u_int16_t));
- }
- }
-#if AD_VERSION == AD_VERSION2
- else if (ad->ad_version == AD_VERSION2) {
+ if (ad->ad_version == AD_VERSION2) {
if (ad_getentryoff(ad, ADEID_AFPFILEI)) {
memcpy(attr, ad_entry(ad, ADEID_AFPFILEI) + AFPFILEIOFF_ATTR, 2);
}
}
}
-#endif
- else
- return -1;
*attr |= htons(ad->ad_open_forks);
if (ad->ad_adflags & ADFLAGS_DIR)
attr &= ~(ATTRBIT_MULTIUSER | ATTRBIT_NOWRITE | ATTRBIT_NOCOPY);
- if (ad->ad_version == AD_VERSION1) {
- if (ad_getentryoff(ad, ADEID_FILEI)) {
- memcpy(ad_entry(ad, ADEID_FILEI) + FILEIOFF_ATTR, &attr,
- sizeof(attr));
- }
- }
-#if AD_VERSION == AD_VERSION2
- else if (ad->ad_version == AD_VERSION2) {
+ if (ad->ad_version == AD_VERSION2) {
if (ad_getentryoff(ad, ADEID_AFPFILEI) && ad_getentryoff(ad, ADEID_FINDERI)) {
memcpy(ad_entry(ad, ADEID_AFPFILEI) + AFPFILEIOFF_ATTR, &attr, sizeof(attr));
memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &fflags, 2);
}
}
-#endif
- else
- return -1;
return 0;
}
* save file/folder ID in AppleDoubleV2 netatalk private parameters
* return 1 if resource fork has been modified
*/
-#if AD_VERSION == AD_VERSION2
int ad_setid (struct adouble *adp, const dev_t dev, const ino_t ino , const u_int32_t id, const cnid_t did, const void *stamp)
{
if ((adp->ad_flags == AD_VERSION2) && (adp->ad_options & ADVOL_CACHE)) {
}
return 0;
}
-#endif
/* -----------------
* set resource fork filename attribute.
#include <string.h>
#include <atalk/adouble.h>
-int ad_setdate(struct adouble *ad,
- unsigned int dateoff, u_int32_t date)
+int ad_setdate(struct adouble *ad,
+ unsigned int dateoff, u_int32_t date)
{
- int xlate = (dateoff & AD_DATE_UNIX);
+ int xlate = (dateoff & AD_DATE_UNIX);
- dateoff &= AD_DATE_MASK;
- if (xlate)
- date = AD_DATE_FROM_UNIX(date);
+ dateoff &= AD_DATE_MASK;
+ if (xlate)
+ date = AD_DATE_FROM_UNIX(date);
- if (ad->ad_version == AD_VERSION1) {
-
- if (!ad_getentryoff(ad, ADEID_FILEI))
- return -1;
-
- if (dateoff > AD_DATE_BACKUP)
- return -1;
- memcpy(ad_entry(ad, ADEID_FILEI) + dateoff, &date, sizeof(date));
-
- } else if (ad->ad_version == AD_VERSION2) {
if (!ad_getentryoff(ad, ADEID_FILEDATESI))
return -1;
-
+
if (dateoff > AD_DATE_ACCESS)
return -1;
memcpy(ad_entry(ad, ADEID_FILEDATESI) + dateoff, &date, sizeof(date));
- } else
- return -1;
-
- return 0;
+ return 0;
}
int ad_getdate(const struct adouble *ad,
- unsigned int dateoff, u_int32_t *date)
+ unsigned int dateoff, u_int32_t *date)
{
- int xlate = (dateoff & AD_DATE_UNIX);
-
- dateoff &= AD_DATE_MASK;
- if (ad->ad_version == AD_VERSION1) {
- if (dateoff > AD_DATE_BACKUP)
- return -1;
- if (!ad_getentryoff(ad, ADEID_FILEI))
- return -1;
- memcpy(date, ad_entry(ad, ADEID_FILEI) + dateoff, sizeof(u_int32_t));
+ int xlate = (dateoff & AD_DATE_UNIX);
- } else if (ad->ad_version == AD_VERSION2) {
+ dateoff &= AD_DATE_MASK;
if (!ad_getentryoff(ad, ADEID_FILEDATESI))
return -1;
if (dateoff > AD_DATE_ACCESS)
- return -1;
+ return -1;
memcpy(date, ad_entry(ad, ADEID_FILEDATESI) + dateoff, sizeof(u_int32_t));
- } else
- return -1;
-
- if (xlate)
- *date = AD_DATE_TO_UNIX(*date);
- return 0;
+ if (xlate)
+ *date = AD_DATE_TO_UNIX(*date);
+ return 0;
}
/*
* Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * Copyright (c) 2010 Frank Lahm
* All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software and
#endif /* HAVE_CONFIG_H */
#include <string.h>
-#include <atalk/adouble.h>
-
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
-#include "ad_private.h"
-#if AD_VERSION == AD_VERSION1
-
-#define EID_DISK(a) (a)
+#include <atalk/adouble.h>
+#include <atalk/ea.h>
+#include <atalk/logger.h>
-#else
+#include "ad_private.h"
static const u_int32_t set_eid[] = {
0,1,2,3,4,5,6,7,8,
};
#define EID_DISK(a) (set_eid[a])
-#endif
-/* rebuild the adouble header
- * XXX should be in a separate file ?
+/*
+ * Rebuild any header information that might have changed.
*/
int ad_rebuild_adouble_header(struct adouble *ad)
{
u_int32_t eid;
u_int32_t temp;
-
u_int16_t nent;
char *buf, *nentp;
+ int len;
- /*
- * Rebuild any header information that might have changed.
- */
buf = ad->ad_data;
temp = htonl( ad->ad_magic );
memcpy(buf, &temp, sizeof( temp ));
buf += sizeof( temp );
- memcpy(buf, ad->ad_filler, sizeof( ad->ad_filler ));
buf += sizeof( ad->ad_filler );
nentp = buf;
}
nent = htons( nent );
memcpy(nentp, &nent, sizeof( nent ));
- return ad_getentryoff(ad, ADEID_RFORK);
+
+ switch (ad->ad_flags) {
+ case AD_VERSION2:
+ len = ad_getentryoff(ad, ADEID_RFORK);
+ break;
+ case AD_VERSION_EA:
+ len = AD_DATASZ_EA;
+ break;
+ default:
+ LOG(log_error, logtype_afpd, "Unexpected adouble version");
+ len = 0;
+ break;
+ }
+
+ return len;
}
/* -------------------
return 0;
}
-/* ------------------- */
-int ad_rebuild_sfm_header(struct adouble *ad)
-{
- u_int32_t temp;
-
- u_int16_t attr;
- char *buf;
-
- /*
- * Rebuild any header information that might have changed.
- */
- buf = ad->ad_data;
- /* FIXME */
-/* temp = htonl( ad->ad_magic ); */
- temp = ad->ad_magic;
- memcpy(buf, &temp, sizeof( temp ));
-
-/* temp = htonl( ad->ad_version ); */
- temp = ad->ad_version;
- memcpy(buf +4, &temp, sizeof( temp ));
-
- /* need to save attrib */
- if (!ad_getattr(ad, &attr)) {
- attr &= ~htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN);
-
- memcpy(buf +48 +4, &attr, sizeof(attr));
-
- }
- return AD_SFM_LEN;
-}
-
-
-int ad_flush( struct adouble *ad)
+int ad_flush(struct adouble *ad)
{
int len;
if (( ad->ad_md->adf_flags & O_RDWR )) {
- /* sync our header */
- if (ad->ad_rlen > 0xffffffff) {
- ad_setentrylen(ad, ADEID_RFORK, 0xffffffff);
- }
- else {
- ad_setentrylen(ad, ADEID_RFORK, ad->ad_rlen);
+ if (ad_getentryoff(ad, ADEID_RFORK)) {
+ if (ad->ad_rlen > 0xffffffff)
+ ad_setentrylen(ad, ADEID_RFORK, 0xffffffff);
+ else
+ ad_setentrylen(ad, ADEID_RFORK, ad->ad_rlen);
}
len = ad->ad_ops->ad_rebuild_header(ad);
- if (adf_pwrite(ad->ad_md, ad->ad_data, len, 0) != len) {
- if ( errno == 0 ) {
- errno = EIO;
+ switch (ad->ad_flags) {
+ case AD_VERSION2:
+ if (adf_pwrite(ad->ad_md, ad->ad_data, len, 0) != len) {
+ if (errno == 0)
+ errno = EIO;
+ return( -1 );
}
- return( -1 );
+ break;
+ case AD_VERSION_EA:
+ if (sys_fsetxattr(ad->ad_md->adf_fd, AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0) != 0) {
+ LOG(log_error, logtype_afpd, "ad_flush: sys_fsetxattr error: %s",
+ strerror(errno));
+ return -1;
+ }
+ break;
+ default:
+ LOG(log_error, logtype_afpd, "ad_flush: unexpected adouble version");
+ return -1;
}
}
return err;
}
- /* meta /resource fork */
+ /* meta/resource fork */
if ( ad_meta_fileno(ad) != -1 && !(--ad->ad_md->adf_refcount)) {
if ( close( ad_meta_fileno(ad) ) < 0 ) {
adf_lock_free(ad->ad_md);
}
- if (ad->ad_flags != AD_VERSION1_SFM) {
- return err;
- }
-
- if ((adflags & ADFLAGS_DIR)) {
- return err;
- }
-
- if ( ad_reso_fileno(ad) != -1 && !(--ad->ad_resource_fork.adf_refcount)) {
- if ( close( ad_reso_fileno(ad) ) < 0 ) {
- err = -1;
- }
- ad_reso_fileno(ad) = -1;
- adf_lock_free(&ad->ad_resource_fork);
- }
-
return err;
}
adf_unlock(&ad->ad_resource_fork, fork);
}
- if (ad->ad_flags != AD_VERSION1_SFM) {
+ if (ad->ad_flags != AD_VERSION_EA) {
return;
}
if (ad_meta_fileno(ad) != -1) {
/*
* Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
* Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * Copyright (c) 2010 Frank Lahm
+ *
* All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software and
#endif /* HAVE_CONFIG_H */
#include <errno.h>
-
-#include <atalk/adouble.h>
#include <sys/param.h>
-#include <atalk/logger.h>
+#include <stdlib.h>
+#include <string.h>
+#include <atalk/logger.h>
+#include <atalk/adouble.h>
#include <atalk/util.h>
-#include <string.h>
+#include <atalk/ea.h>
#include "ad_private.h"
-#include <stdlib.h>
#ifndef MAX
#define MAX(a, b) ((a) < (b) ? (b) : (a))
#endif /* ! MAX */
+#ifdef HAVE_PREAD
+# define AD_SET(a)
+#else
+# define AD_SET(a) a = 0
+#endif
+
/*
* AppleDouble entry default offsets.
* The layout looks like this:
*
* so, all we need to do is replace FILEI with FILEDATESI, move RFORK,
* and add in the new fields.
- *
- * NOTE: the HFS module will need similar modifications to interact with
- * afpd correctly.
*/
#define ADEDOFF_MAGIC (0)
#undef ADEDOFF_FILEI
#endif /* ADEDOFF_FILEI */
-#define ADEDOFF_NAME_V1 (AD_HEADER_LEN + ADEID_NUM_V1*AD_ENTRY_LEN)
-#define ADEDOFF_COMMENT_V1 (ADEDOFF_NAME_V1 + ADEDLEN_NAME)
-#define ADEDOFF_FILEI (ADEDOFF_COMMENT_V1 + ADEDLEN_COMMENT)
-#define ADEDOFF_FINDERI_V1 (ADEDOFF_FILEI + ADEDLEN_FILEI)
-#define ADEDOFF_RFORK_V1 (ADEDOFF_FINDERI_V1 + ADEDLEN_FINDERI)
-
/* i stick things in a slightly different order than their eid order in
* case i ever want to separate RootInfo behaviour from the rest of the
* stuff. */
+
+/* ad:v2 */
#define ADEDOFF_NAME_V2 (AD_HEADER_LEN + ADEID_NUM_V2*AD_ENTRY_LEN)
#define ADEDOFF_COMMENT_V2 (ADEDOFF_NAME_V2 + ADEDLEN_NAME)
#define ADEDOFF_FILEDATESI (ADEDOFF_COMMENT_V2 + ADEDLEN_COMMENT)
#define ADEDOFF_FINDERI_V2 (ADEDOFF_FILEDATESI + ADEDLEN_FILEDATESI)
-#define ADEDOFF_DID (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
+#define ADEDOFF_DID (ADEDOFF_FINDERI_V2 + ADEDLEN_FINDERI)
#define ADEDOFF_AFPFILEI (ADEDOFF_DID + ADEDLEN_DID)
#define ADEDOFF_SHORTNAME (ADEDOFF_AFPFILEI + ADEDLEN_AFPFILEI)
#define ADEDOFF_PRODOSFILEI (ADEDOFF_SHORTNAME + ADEDLEN_SHORTNAME)
#define ADEDOFF_PRIVINO (ADEDOFF_PRIVDEV + ADEDLEN_PRIVDEV)
#define ADEDOFF_PRIVSYN (ADEDOFF_PRIVINO + ADEDLEN_PRIVINO)
#define ADEDOFF_PRIVID (ADEDOFF_PRIVSYN + ADEDLEN_PRIVSYN)
-
#define ADEDOFF_RFORK_V2 (ADEDOFF_PRIVID + ADEDLEN_PRIVID)
-#define ADEID_NUM_OSX 2
-#define ADEDOFF_FINDERI_OSX (AD_HEADER_LEN + ADEID_NUM_OSX*AD_ENTRY_LEN)
-#define ADEDOFF_RFORK_OSX (ADEDOFF_FINDERI_OSX + ADEDLEN_FINDERI)
-
-/* we keep local copies of a bunch of stuff so that we can initialize things
- * correctly. */
+/* ad:ea */
+#define ADEDOFF_FINDERI_EA (AD_HEADER_LEN + ADEID_NUM_EA * AD_ENTRY_LEN)
+#define ADEDOFF_COMMENT_EA (ADEDOFF_FINDERI_EA + ADEDLEN_FINDERI)
+#define ADEDOFF_FILEDATESI_EA (ADEDOFF_COMMENT_EA + ADEDLEN_COMMENT)
+#define ADEDOFF_AFPFILEI_EA (ADEDOFF_FILEDATESI_EA + ADEDLEN_FILEDATESI)
/* this is to prevent changing timezones from causing problems with
- localtime volumes. the screw-up is 30 years. we use a delta of 5
- years. */
+ localtime volumes. the screw-up is 30 years. we use a delta of 5 years */
#define TIMEWARP_DELTA 157680000
-
struct entry {
- u_int32_t id, offset, len;
+ uint32_t id, offset, len;
};
-static const struct entry entry_order1[ADEID_NUM_V1 +1] = {
- {ADEID_NAME, ADEDOFF_NAME_V1, ADEDLEN_INIT}, /* 3 */
- {ADEID_COMMENT, ADEDOFF_COMMENT_V1, ADEDLEN_INIT}, /* 4 */
- {ADEID_FILEI, ADEDOFF_FILEI, ADEDLEN_FILEI}, /* 7 */
- {ADEID_FINDERI, ADEDOFF_FINDERI_V1, ADEDLEN_FINDERI}, /* 9 */
- {ADEID_RFORK, ADEDOFF_RFORK_V1, ADEDLEN_INIT}, /* 2 */
- {0, 0, 0}
+/* --------------------------- */
+static uid_t default_uid = -1;
+
+/* Forward declarations */
+static int ad_mkrf(char *path);
+static int ad_header_read(struct adouble *ad, struct stat *hst);
+static int ad_header_upgrade(struct adouble *ad, char *name);
+
+static int ad_mkrf_ea(char *path);
+static int ad_header_read_ea(struct adouble *ad, struct stat *hst);
+static int ad_header_upgrade_ea(struct adouble *ad, char *name);
+
+static struct adouble_fops ad_adouble = {
+ &ad_path,
+ &ad_mkrf,
+ &ad_rebuild_adouble_header,
+ &ad_header_read,
+ &ad_header_upgrade,
};
-#if AD_VERSION == AD_VERSION1
-#define DISK_EID(ad, a) (a)
+static struct adouble_fops ad_adouble_ea = {
+ &ad_path_ea,
+ &ad_mkrf_ea,
+ &ad_rebuild_adouble_header,
+ &ad_header_read_ea,
+ &ad_header_upgrade_ea,
+};
+
+static const struct entry entry_order2[ADEID_NUM_V2 + 1] = {
+ {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
+ {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
+ {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
+ {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
+ {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
+ {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
+ {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
+ {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
+ {ADEID_PRIVDEV, ADEDOFF_PRIVDEV, ADEDLEN_INIT},
+ {ADEID_PRIVINO, ADEDOFF_PRIVINO, ADEDLEN_INIT},
+ {ADEID_PRIVSYN, ADEDOFF_PRIVSYN, ADEDLEN_INIT},
+ {ADEID_PRIVID, ADEDOFF_PRIVID, ADEDLEN_INIT},
+ {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
+ {0, 0, 0}
+};
-#else /* AD_VERSION == AD_VERSION2 */
+/* Using Extended Attributes */
+static const struct entry entry_order_ea[ADEID_NUM_EA + 1] = {
+ {ADEID_FINDERI, ADEDOFF_FINDERI_EA, ADEDLEN_FINDERI},
+ {ADEID_COMMENT, ADEDOFF_COMMENT_EA, ADEDLEN_INIT},
+ {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_EA, ADEDLEN_FILEDATESI},
+ {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_EA, ADEDLEN_AFPFILEI},
+ {0, 0, 0}
+};
-static u_int32_t get_eid(struct adouble *ad, u_int32_t eid)
+uint32_t get_eid(uint32_t eid)
{
if (eid <= 15)
return eid;
- if (ad->ad_version == AD_VERSION1)
- return 0;
if (eid == AD_DEV)
return ADEID_PRIVDEV;
if (eid == AD_INO)
return 0;
}
-#define DISK_EID(ad, a) get_eid(ad, a)
-
-static const struct entry entry_order2[ADEID_NUM_V2 +1] = {
- {ADEID_NAME, ADEDOFF_NAME_V2, ADEDLEN_INIT},
- {ADEID_COMMENT, ADEDOFF_COMMENT_V2, ADEDLEN_INIT},
- {ADEID_FILEDATESI, ADEDOFF_FILEDATESI, ADEDLEN_FILEDATESI},
- {ADEID_FINDERI, ADEDOFF_FINDERI_V2, ADEDLEN_FINDERI},
- {ADEID_DID, ADEDOFF_DID, ADEDLEN_DID},
- {ADEID_AFPFILEI, ADEDOFF_AFPFILEI, ADEDLEN_AFPFILEI},
- {ADEID_SHORTNAME, ADEDOFF_SHORTNAME, ADEDLEN_INIT},
- {ADEID_PRODOSFILEI, ADEDOFF_PRODOSFILEI, ADEDLEN_PRODOSFILEI},
- {ADEID_PRIVDEV, ADEDOFF_PRIVDEV, ADEDLEN_INIT},
- {ADEID_PRIVINO, ADEDOFF_PRIVINO, ADEDLEN_INIT},
- {ADEID_PRIVSYN, ADEDOFF_PRIVSYN, ADEDLEN_INIT},
- {ADEID_PRIVID, ADEDOFF_PRIVID, ADEDLEN_INIT},
- {ADEID_RFORK, ADEDOFF_RFORK_V2, ADEDLEN_INIT},
-
- {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}
-};
-
-#define ADEID_NUM_SFM 3
-static const struct entry entry_order_sfm[ADEID_NUM_SFM +1] = {
- {ADEID_FINDERI, 16, ADEDLEN_FINDERI}, /* 9 */
- {ADEID_SFMRESERVE2, 16+32, 6}, /* 21 */
- {ADEID_FILEI, 60, ADEDLEN_FILEI}, /* 7 */
-
- {0, 0, 0}
-};
-
-#endif /* AD_VERSION == AD_VERSION2 */
-
-#if AD_VERSION == AD_VERSION2
-
-/* update a version 2 adouble resource fork with our private entries */
-static int ad_update(struct adouble *ad, const char *path)
+/* ----------------------------------- */
+static int new_ad_header(const char *path, struct adouble *ad, int adflags)
{
- struct stat st;
- u_int16_t nentries = 0;
- off_t off, shiftdata=0;
const struct entry *eid;
- static off_t entry_len[ADEID_MAX];
- static char databuf[ADEID_MAX][256], *buf;
- int fd;
- int ret = -1;
-
- /* check to see if we should convert this header. */
- if (!path || ad->ad_flags != AD_VERSION2)
- return 0;
-
- LOG(log_maxdebug, logtype_default, "ad_update: checking whether '%s' needs an upgrade.", path);
-
- if (!(ad->ad_md->adf_flags & O_RDWR)) {
- /* we were unable to open the file read write the last time */
- return 0;
- }
-
- if (ad->ad_eid[ADEID_RFORK].ade_off) {
- shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off;
- }
-
- memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof( nentries ));
- nentries = ntohs( nentries );
-
- if ( shiftdata == 0 && nentries == ADEID_NUM_V2)
- return 0;
-
- memset(entry_len, 0, sizeof(entry_len));
- memset(databuf, 0, sizeof(databuf));
-
- /* bail if we can't get a lock */
- if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0)
- goto bail_err;
-
- fd = ad->ad_md->adf_fd;
-
- if (fstat(fd, &st)) {
- goto bail_lock;
- }
-
- if (st.st_size > 0x7fffffff) {
- LOG(log_debug, logtype_default, "ad_update: file '%s' too big for update.", path);
- errno = EIO;
- goto bail_lock;
- }
-
- off = ad->ad_eid[ADEID_RFORK].ade_off;
- if (off > st.st_size) {
- LOG(log_error, logtype_default, "ad_update: invalid resource fork offset. (off: %u)", off);
- errno = EIO;
- goto bail_lock;
- }
-
- if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) {
- LOG(log_error, logtype_default, "ad_update: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len);
- errno = EIO;
- goto bail_lock;
- }
-
- if ((void *) (buf = (char *)
- mmap(NULL, st.st_size + shiftdata,
- PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
- MAP_FAILED) {
- goto bail_lock;
- }
-
- /* last place for failure. */
- if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) {
- munmap(buf, st.st_size + shiftdata);
- goto bail_lock;
- }
+ u_int16_t ashort;
+ struct stat st;
- /* move the RFORK. this assumes that the RFORK is at the end */
- if (off) {
- memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len);
+ ad->ad_magic = AD_MAGIC;
+ ad->ad_version = ad->ad_flags & 0x0f0000;
+ if (!ad->ad_version) {
+ ad->ad_version = AD_VERSION;
}
- munmap(buf, st.st_size + shiftdata);
- /* now, fix up our copy of the header */
- memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
+ memset(ad->ad_data, 0, sizeof(ad->ad_data));
- /* save the header entries */
- eid = entry_order2;
- while (eid->id) {
- if( ad->ad_eid[eid->id].ade_off != 0) {
- if ( eid->id > 2 && ad->ad_eid[eid->id].ade_len < 256)
- memcpy( databuf[eid->id], ad->ad_data +ad->ad_eid[eid->id].ade_off, ad->ad_eid[eid->id].ade_len);
- entry_len[eid->id] = ad->ad_eid[eid->id].ade_len;
- }
- eid++;
+ if (ad->ad_flags == AD_VERSION2)
+ eid = entry_order2;
+ else if (ad->ad_flags == AD_VERSION_EA)
+ eid = entry_order_ea;
+ else {
+ return -1;
}
- memset(ad->ad_data + AD_HEADER_LEN, 0, AD_DATASZ - AD_HEADER_LEN);
-
- /* copy the saved entries to the new header */
- eid = entry_order2;
while (eid->id) {
- if ( eid->id > 2 && entry_len[eid->id] > 0) {
- memcpy(ad->ad_data+eid->offset, databuf[eid->id], entry_len[eid->id]);
- }
ad->ad_eid[eid->id].ade_off = eid->offset;
- ad->ad_eid[eid->id].ade_len = entry_len[eid->id];
+ ad->ad_eid[eid->id].ade_len = eid->len;
eid++;
}
- /* rebuild the header and cleanup */
- LOG(log_debug, logtype_default, "updated AD2 header %s", path);
- ad_flush(ad );
- ret = 0;
-
-bail_lock:
- ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
-bail_err:
- return ret;
-}
-
-/* ------------------------------------------
- FIXME work only if < 2GB
-*/
-static int ad_convert(struct adouble *ad, const char *path)
-{
- struct stat st;
- u_int16_t attr;
- char *buf;
- int fd, off;
- int ret = -1;
- /* use resource fork offset from file */
- int shiftdata;
- int toV2;
- int toV1;
-
- if (!path) {
- return 0;
- }
-
- if (!(ad->ad_md->adf_flags & ( O_RDWR))) {
- /* we were unable to open the file read write the last time */
- return 0;
- }
-
- /* check to see if we should convert this header. */
- toV2 = ad->ad_version == AD_VERSION1 && ad->ad_flags == AD_VERSION2;
- toV1 = ad->ad_version == AD_VERSION2 && ad->ad_flags == AD_VERSION1;
-
- if (!toV2 && !toV1)
- return 0;
-
- /* convert from v1 to v2. what does this mean?
- * 1) change FILEI into FILEDATESI
- * 2) create space for SHORTNAME, AFPFILEI, DID, and PRODOSI
- * 3) move FILEI attributes into AFPFILEI
- * 4) initialize ACCESS field of FILEDATESI.
- * 5) move the resource fork
- */
-
- /* bail if we can't get a lock */
- if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0)
- goto bail_err;
-
- /* we reuse fd from the resource fork */
- fd = ad->ad_md->adf_fd;
-
- if (ad->ad_eid[ADEID_RFORK].ade_off) {
- shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off;
- }
- else {
- shiftdata = ADEDOFF_RFORK_V2 -ADEDOFF_RFORK_V1; /* 136 */
- }
-
- if (fstat(fd, &st)) {
- goto bail_lock;
- }
-
- if (st.st_size > 0x7fffffff -shiftdata) {
- LOG(log_debug, logtype_default, "ad_v1tov2: file too big.");
- errno = EIO;
- goto bail_lock;
- }
-
- off = ad->ad_eid[ADEID_RFORK].ade_off;
-
- if (off > st.st_size) {
- LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork offset. (off: %u)", off);
- errno = EIO;
- goto bail_lock;
- }
-
- if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) {
- LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len);
- errno = EIO;
- goto bail_lock;
- }
-
- if ((void *) (buf = (char *)
- mmap(NULL, st.st_size + shiftdata,
- PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) ==
- MAP_FAILED) {
- goto bail_lock;
+ /* put something sane in the directory finderinfo */
+ if ((adflags & ADFLAGS_DIR)) {
+ /* set default view */
+ ashort = htons(FINDERINFO_CLOSEDVIEW);
+ memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
+ } else {
+ /* set default creator/type fields */
+ memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
+ memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
}
- /* last place for failure. */
-
- if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) {
- goto bail_lock;
+ /* make things invisible */
+ if ((ad->ad_options & ADVOL_INVDOTS) && (*path == '.')) {
+ ashort = htons(ATTRBIT_INVISIBLE);
+ ad_setattr(ad, ashort);
+ ashort = htons(FINDERINFO_INVISIBLE);
+ memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
}
- /* move the RFORK. this assumes that the RFORK is at the end */
- if (off) {
- memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len);
- }
+ if (lstat(path, &st) < 0)
+ return -1;
- munmap(buf, st.st_size + shiftdata);
-
- /* now, fix up our copy of the header */
- memset(ad->ad_filler, 0, sizeof(ad->ad_filler));
-
- /* replace FILEI with FILEDATESI */
- ad_getattr(ad, &attr);
- ad->ad_eid[ADEID_FILEDATESI].ade_off = ADEDOFF_FILEDATESI;
- ad->ad_eid[ADEID_FILEDATESI].ade_len = ADEDLEN_FILEDATESI;
- ad->ad_eid[ADEID_FILEI].ade_off = 0;
- ad->ad_eid[ADEID_FILEI].ade_len = 0;
-
- /* add in the new entries */
- ad->ad_eid[ADEID_DID].ade_off = ADEDOFF_DID;
- ad->ad_eid[ADEID_DID].ade_len = ADEDLEN_DID;
- ad->ad_eid[ADEID_AFPFILEI].ade_off = ADEDOFF_AFPFILEI;
- ad->ad_eid[ADEID_AFPFILEI].ade_len = ADEDLEN_AFPFILEI;
- ad->ad_eid[ADEID_SHORTNAME].ade_off = ADEDOFF_SHORTNAME;
- ad->ad_eid[ADEID_SHORTNAME].ade_len = ADEDLEN_INIT;
- ad->ad_eid[ADEID_PRODOSFILEI].ade_off = ADEDOFF_PRODOSFILEI;
- ad->ad_eid[ADEID_PRODOSFILEI].ade_len = ADEDLEN_PRODOSFILEI;
-
- ad->ad_eid[ADEID_PRIVDEV].ade_off = ADEDOFF_PRIVDEV;
- ad->ad_eid[ADEID_PRIVDEV].ade_len = ADEDLEN_INIT;
- ad->ad_eid[ADEID_PRIVINO].ade_off = ADEDOFF_PRIVINO;
- ad->ad_eid[ADEID_PRIVINO].ade_len = ADEDLEN_INIT;
- ad->ad_eid[ADEID_PRIVSYN].ade_off = ADEDOFF_PRIVSYN;
- ad->ad_eid[ADEID_PRIVSYN].ade_len = ADEDLEN_INIT;
- ad->ad_eid[ADEID_PRIVID].ade_off = ADEDOFF_PRIVID;
- ad->ad_eid[ADEID_PRIVID].ade_len = ADEDLEN_INIT;
-
- /* shift the old entries (NAME, COMMENT, FINDERI, RFORK) */
- ad->ad_eid[ADEID_NAME].ade_off = ADEDOFF_NAME_V2;
- ad->ad_eid[ADEID_COMMENT].ade_off = ADEDOFF_COMMENT_V2;
- ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2;
- ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2;
-
- /* switch to dest version */
- ad->ad_version = (toV2)?AD_VERSION2:AD_VERSION1;
-
- /* move our data buffer to make space for the new entries. */
- memmove(ad->ad_data + ADEDOFF_NAME_V2, ad->ad_data + ADEDOFF_NAME_V1,
- ADEDOFF_RFORK_V1 - ADEDOFF_NAME_V1);
-
- /* now, fill in the space with appropriate stuff. we're
- operating as a v2 file now. */
+ /* put something sane in the date fields */
+ ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
+ ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
- memset(ad_entry(ad, ADEID_DID), 0, ADEDLEN_DID);
- memset(ad_entry(ad, ADEID_AFPFILEI), 0, ADEDLEN_AFPFILEI);
- ad_setattr(ad, attr);
- memset(ad_entry(ad, ADEID_SHORTNAME), 0, ADEDLEN_SHORTNAME);
- memset(ad_entry(ad, ADEID_PRODOSFILEI), 0, ADEDLEN_PRODOSFILEI);
-
- /* rebuild the header and cleanup */
- ad_flush(ad );
- ret = 0;
-
-bail_lock:
- ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0);
-bail_err:
- return ret;
+ ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
+ return 0;
}
-#endif /* AD_VERSION == AD_VERSION2 */
/* -------------------------------------
read in the entries
*/
-static void parse_entries(struct adouble *ad, char *buf,
- u_int16_t nentries)
+static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
{
- u_int32_t eid, len, off;
- int warning = 0;
+ uint32_t eid, len, off;
+ int warning = 0;
/* now, read in the entry bits */
for (; nentries > 0; nentries-- ) {
memcpy(&eid, buf, sizeof( eid ));
- eid = DISK_EID(ad, ntohl( eid ));
+ eid = get_eid(ntohl(eid));
buf += sizeof( eid );
memcpy(&off, buf, sizeof( off ));
off = ntohl( off );
buf += sizeof( len );
if (eid && eid < ADEID_MAX && off < sizeof(ad->ad_data) &&
- (off +len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
+ (off + len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
ad->ad_eid[ eid ].ade_off = off;
ad->ad_eid[ eid ].ade_len = len;
} else if (!warning) {
warning = 1;
- LOG(log_debug, logtype_default, "ad_refresh: nentries %hd eid %d",
- nentries, eid );
+ LOG(log_warning, logtype_default, "parse_entries: bogus eid: %d", eid);
}
}
}
-
/* this reads enough of the header so that we can figure out all of
* the entry lengths and offsets. once that's done, we just read/mmap
* the rest of the header in.
u_int16_t nentries;
int len;
ssize_t header_len;
- static int warning = 0;
struct stat st;
/* read the header */
memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
+ ad->ad_magic = ntohl( ad->ad_magic );
+ ad->ad_version = ntohl( 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)
-#if AD_VERSION == AD_VERSION2
- && (ad->ad_version != AD_VERSION2)
-#endif /* AD_VERSION == AD_VERSION2 */
- )) {
- LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
+ if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
+ LOG(log_error, logtype_default, "ad_open: can't parse AppleDouble header.");
errno = EIO;
return -1;
}
- memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
nentries = ntohs( nentries );
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;
}
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;
-
- /* 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);
- }
- }
+ ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
return 0;
}
-/* ---------------------------
- SFM structure
-*/
-#if 0
-typedef struct {
- byte afpi_Signature[4]; /* Must be 0x00504641 */
- byte afpi_Version[4]; /* Must be 0x00010000 */
- byte afpi_Reserved1[4];
- byte afpi_BackupTime[4]; /* Backup time for the file/dir */
- byte finderinfo[32]; /* Finder info */
- byte afpi_ProDosInfo[6]; /* ProDos Info */
- byte afpi_Reserved2[6];
-} sfm_info;
-#endif
-
-static int ad_header_sfm_read(struct adouble *ad, struct stat *hst)
+static int ad_header_read_ea(struct adouble *ad, struct stat *hst _U_)
{
- char *buf = ad->ad_data;
- const struct entry *eid;
- ssize_t header_len;
- struct stat st;
+ uint16_t nentries;
+ int len;
+ ssize_t header_len;
+ char *buf = ad->ad_data;
/* read the header */
- if ((header_len = adf_pread( ad->ad_md, buf, sizeof(ad->ad_data), 0)) < 0) {
+ if ((header_len = sys_fgetxattr(ad->ad_md->adf_fd, AD_EA_META, ad->ad_data, AD_DATASZ_EA)) < 1) {
+ LOG(log_debug, logtype_default, "ad_header_read_ea: %s (%u)", strerror(errno), errno);
return -1;
}
- if (header_len != AD_SFM_LEN) {
+
+ if (header_len < AD_HEADER_LEN) {
+ LOG(log_error, logtype_default, "ad_header_read_ea: bogus AppleDouble header.");
errno = EIO;
return -1;
}
memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
- memcpy(&ad->ad_version, buf + 4, sizeof( ad->ad_version ));
+ memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
- /* FIXME in the great Microsoft tradition they aren't in network order */
-#if 0
- if (ad->ad_magic == SFM_MAGIC && ad->ad_version == AD_VERSION1) {
- static int warning = 0;
- if (!warning) {
- LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
- warning++;
- }
+ ad->ad_magic = ntohl( ad->ad_magic );
+ ad->ad_version = ntohl( ad->ad_version );
- } else {
- ad->ad_magic = ntohl( ad->ad_magic );
- ad->ad_version = ntohl( ad->ad_version );
- }
-#endif
- if ((ad->ad_magic != SFM_MAGIC) || ((ad->ad_version != AD_VERSION1) )) {
+ if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION2)) {
+ LOG(log_error, logtype_default, "ad_header_read_ea: wrong magic or version");
errno = EIO;
- LOG(log_debug, logtype_default, "ad_header_sfm_read: can't parse AppleDouble header.");
return -1;
}
- /* reinit adouble table */
- eid = entry_order_sfm;
- while (eid->id) {
- ad->ad_eid[eid->id].ade_off = eid->offset;
- ad->ad_eid[eid->id].ade_len = eid->len;
- eid++;
- }
-
- /* steal some prodos for attribute */
- {
+ memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
+ nentries = ntohs( nentries );
- u_int16_t attribute;
- memcpy(&attribute, buf + 48 +4, sizeof(attribute));
- ad_setattr(ad, attribute );
+ /* Protect against bogus nentries */
+ len = nentries * AD_ENTRY_LEN;
+ if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
+ len = sizeof(ad->ad_data) - AD_HEADER_LEN;
+ if (len > header_len - AD_HEADER_LEN) {
+ LOG(log_error, logtype_default, "ad_header_read_ea: can't read entry info.");
+ errno = EIO;
+ return -1;
}
+ nentries = len / AD_ENTRY_LEN;
- if (ad->ad_resource_fork.adf_fd != -1) {
- /* we have a resource fork use it rather than the metadata */
- if (fstat(ad->ad_resource_fork.adf_fd, &st) < 0) {
- /* XXX set to zero ?
- ad->ad_rlen = 0;
- */
- return 1;
- }
- ad->ad_rlen = st.st_size;
- hst = &st;
- }
- else if (hst == NULL) {
- hst = &st;
- if (fstat(ad->ad_md->adf_fd, &st) < 0) {
- return 1; /* fail silently */
- }
- }
+ /* Now parse entries */
+ parse_entries(ad, buf + AD_HEADER_LEN, nentries);
+ return 0;
+}
- /* fix up broken dates */
- if (ad->ad_version == AD_VERSION1) {
- u_int32_t aint;
-
- /* 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);
- }
+static int ad_mkrf(char *path)
+{
+ char *slash;
+ /*
+ * Probably .AppleDouble doesn't exist, try to mkdir it.
+ */
+ if (NULL == ( slash = strrchr( path, '/' )) ) {
+ return -1;
}
-
+ *slash = '\0';
+ errno = 0;
+ if ( ad_mkdir( path, 0777 ) < 0 ) {
+ return -1;
+ }
+ *slash = '/';
return 0;
}
-/* ---------------------------------------
- * Put the .AppleDouble where it needs to be:
- *
- * / a/.AppleDouble/b
- * a/b
- * \ b/.AppleDouble/.Parent
- *
- * FIXME: should do something for pathname > MAXPATHLEN
- */
-char *
-ad_path( const char *path, int adflags)
+static int ad_mkrf_ea(char *path _U_)
{
- static char pathbuf[ MAXPATHLEN + 1];
- const char *slash;
- size_t l ;
+ AFP_PANIC("ad_mkrf_ea: dont use");
+ return 0;
+}
- if ( adflags & ADFLAGS_DIR ) {
- l = strlcpy( pathbuf, path, sizeof(pathbuf));
+/* ----------------
+ if we are root change path user/ group
+ It can be a native function for BSD cf. FAQ.Q10
+ path: pathname to chown
+ stbuf: parent directory inode
- if ( l && l < MAXPATHLEN) {
- pathbuf[l++] = '/';
- }
- strlcpy(pathbuf +l, ".AppleDouble/.Parent", sizeof(pathbuf) -l);
- } else {
- if (NULL != ( slash = strrchr( path, '/' )) ) {
- slash++;
- l = slash - path;
- /* XXX we must return NULL here and test in the caller */
- if (l > MAXPATHLEN)
- l = MAXPATHLEN;
- memcpy( pathbuf, path, l);
- } else {
- l = 0;
- slash = path;
- }
- l += strlcpy( pathbuf +l, ".AppleDouble/", sizeof(pathbuf) -l);
- strlcpy( pathbuf + l, slash, sizeof(pathbuf) -l);
- }
+ use fstat and fchown or lchown with linux?
+*/
+#define EMULATE_SUIDDIR
- return( pathbuf );
+static int ad_chown(const char *path, struct stat *stbuf)
+{
+ int ret = 0;
+#ifdef EMULATE_SUIDDIR
+ uid_t id;
+
+ if (default_uid != (uid_t)-1) {
+ /* we are root (admin) */
+ id = (default_uid)?default_uid:stbuf->st_uid;
+ ret = lchown( path, id, stbuf->st_gid );
+ }
+#endif
+ return ret;
}
-/* -------------------- */
-static int ad_mkrf(char *path)
+#define DEFMASK 07700 /* be conservative */
+
+/* ----------------
+ return access right and inode of path parent directory
+*/
+static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
{
- char *slash;
- /*
- * Probably .AppleDouble doesn't exist, try to mkdir it.
- */
- if (NULL == ( slash = strrchr( path, '/' )) ) {
+ if (*mode == 0) {
return -1;
}
- *slash = '\0';
- errno = 0;
- if ( ad_mkdir( path, 0777 ) < 0 ) {
+ if (ad_stat(path, stbuf) != 0) {
+ *mode &= DEFMASK;
return -1;
}
- *slash = '/';
+ *mode &= stbuf->st_mode;
return 0;
}
-/* ---------------------------------------
- * Put the resource fork where it needs to be:
- * ._name
- */
-char *
-ad_path_osx(const char *path, int adflags _U_)
+/* ----------------- */
+static int ad_error(struct adouble *ad, int adflags)
{
- static char pathbuf[ MAXPATHLEN + 1];
- char c, *slash, buf[MAXPATHLEN + 1];
+ int err = errno;
+ if ((adflags & ADFLAGS_NOHF)) {
+ /* FIXME double check : set header offset ?*/
+ return 0;
+ }
+ if ((adflags & ADFLAGS_DF)) {
+ ad_close( ad, ADFLAGS_DF );
+ err = errno;
+ }
+ return -1 ;
+}
+
+/* --------------------------- */
+static int ad_header_upgrade(struct adouble *ad _U_, char *name _U_)
+{
+ return 0;
+}
+
+static int ad_header_upgrade_ea(struct adouble *ad _U_, char *name _U_)
+{
+ AFP_PANIC("ad_header_upgrade_ea: dont use");
+ return 0;
+}
- if (!strcmp(path,".")) {
- /* fixme */
- getcwd(buf, MAXPATHLEN);
+static int ad_open_df(const char *path, int adflags, int oflags, int mode, struct adouble *ad)
+{
+ struct stat st_dir;
+ int hoflags, admode;
+ int st_invalid = -1;
+
+ LOG(log_maxdebug, logtype_default, "ad_open_df(\"%s/%s\", adf: 0x%04x, of: 0x%04x)",
+ getcwdpath(), path, adflags, oflags);
+
+ 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 );
+ }
+ 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;
+
+ 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);
+ } 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.
+ */
}
- else {
- strlcpy(buf, path, MAXPATHLEN +1);
+ ad->ad_data_fork.adf_refcount++;
+
+ return 0;
+}
+
+static int ad_open_hf_v2(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;
+
+ ad_p = ad->ad_ops->ad_path( path, adflags );
+
+ hoflags = oflags & ~(O_CREAT | O_EXCL);
+ if (!(adflags & ADFLAGS_RDONLY)) {
+ hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
}
- if (NULL != ( slash = strrchr( buf, '/' )) ) {
- c = *++slash;
- *slash = '\0';
- strlcpy( pathbuf, buf, MAXPATHLEN +1);
- *slash = c;
+ ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 0 );
+ if (ad->ad_md->adf_fd < 0 ) {
+ if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
+ hoflags = oflags & ~(O_CREAT | O_EXCL);
+ ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 0 );
+ }
+ }
+
+ if ( ad->ad_md->adf_fd < 0 ) {
+ if (errno == ENOENT && (oflags & O_CREAT) ) {
+ /*
+ * We're expecting to create a new adouble header file here
+ * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
+ */
+ LOG(log_debug, logtype_default, "ad_open(\"%s/%s\"): creating adouble file",
+ getcwdpath(), ad_p);
+ admode = mode;
+ errno = 0;
+ st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
+ if ((ad->ad_options & ADVOL_UNIXPRIV)) {
+ admode = mode;
+ }
+ admode = ad_hf_mode(admode);
+ if ((errno == ENOENT)) {
+ if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
+ return ad_error(ad, adflags);
+ }
+ admode = mode;
+ st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
+ if ((ad->ad_options & ADVOL_UNIXPRIV)) {
+ admode = mode;
+ }
+ admode = ad_hf_mode(admode);
+ }
+ /* retry with O_CREAT */
+ ad->ad_md->adf_fd = open( ad_p, oflags,admode );
+ if ( ad->ad_md->adf_fd < 0 ) {
+ return ad_error(ad, adflags);
+ }
+ ad->ad_md->adf_flags = oflags;
+ /* just created, set owner if admin owner (root) */
+ if (!st_invalid) {
+ ad_chown(ad_p, &st_dir);
+ }
+ } else {
+ return ad_error(ad, adflags);
+ }
} else {
- pathbuf[ 0 ] = '\0';
- slash = buf;
+ ad->ad_md->adf_flags = hoflags;
+ 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 */
+ pst = &st_meta;
+ }
+ }
+ AD_SET(ad->ad_md->adf_off);
+
+ if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) {
+ /* This is a new adouble header file, create it */
+ if (new_ad_header(path, ad, adflags) < 0) {
+ int err = errno;
+ /* the file is already deleted, perm, whatever, so return an error */
+ ad_close(ad, adflags);
+ errno = err;
+ return -1;
+ }
+ ad_flush(ad);
+ } else {
+ /* Read the adouble header in and parse it.*/
+ if (ad->ad_ops->ad_header_read( ad , pst) < 0
+ || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0) {
+ int err = errno;
+ ad_close( ad, adflags );
+ errno = err;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int ad_open_hf_ea(const char *path, int adflags, int oflags, int mode, struct adouble *ad)
+{
+ ssize_t rforklen;
+ int ret;
+ int err;
+
+ LOG(log_maxdebug, logtype_default, "ad_open_hf_ea(\"%s/%s\", adf: 0x%04x, of: 0x%04x)",
+ getcwdpath(), path, adflags, oflags);
+
+ if ((ad->ad_md->adf_fd = open(path, O_RDONLY | O_NOFOLLOW)) == -1)
+ return -1;
+
+ /* Read the adouble header in and parse it.*/
+ if (ad->ad_ops->ad_header_read(ad, NULL) != 0) {
+ if (!(oflags & O_CREAT)) {
+ err = errno;
+ ad_close(ad, adflags);
+ errno = err;
+ return -1;
+ }
+
+ /* It doesnt exist, EPERM or another error */
+ if (errno != ENOATTR && errno != ENOENT) {
+ LOG(log_error, logtype_default, "ad_open_hf_ea: unexpected: %s", strerror(errno));
+ ret = -1;
+ goto error;
+ }
+
+ /* Create one */
+ if (new_ad_header(path, ad, adflags) < 0) {
+ LOG(log_error, logtype_default, "ad_open_hf_ea: can't create new header: %s/%s",
+ getcwdpath(), path);
+ ret = -1;
+ goto error;
+ }
+
+ ad_flush(ad);
}
- strlcat( pathbuf, "._", MAXPATHLEN +1);
- strlcat( pathbuf, slash, MAXPATHLEN +1);
- return pathbuf;
+
+ ad->ad_md->adf_flags = O_RDWR; /* Pretend its rw, in fact for the EA API it is */
+
+ if ((rforklen = sys_fgetxattr(ad->ad_md->adf_fd, AD_EA_RESO, NULL, 0)) > 0)
+ ad->ad_rlen = rforklen;
+
+ return 0;
+
+error:
+ err = errno;
+ ad_close(ad, adflags);
+ errno = err;
+ return ret;
}
-/* -------------------- */
-static int ad_mkrf_osx(char *path _U_)
+
+static int ad_open_hf(const char *path, int adflags, int oflags, int mode, struct adouble *ad)
+{
+ int ret = 0;
+
+ LOG(log_maxdebug, logtype_default, "ad_open_hf(\"%s/%s\", adf: 0x%04x, of: 0x%04x)",
+ getcwdpath(), path, adflags, oflags);
+
+ if (ad_meta_fileno(ad) != -1) { /* the file is already open */
+ LOG(log_maxdebug, logtype_default,"the file is already open");
+ if ((oflags & ( O_RDWR | O_WRONLY))
+ && !(ad->ad_md->adf_flags & ( O_RDWR | O_WRONLY))) {
+ if ((adflags & ADFLAGS_HF) && ad->ad_data_fork.adf_refcount)
+ /* We just have openend the df, so we have to close it now */
+ ad_close(ad, ADFLAGS_DF);
+ errno = EACCES;
+ return -1;
+ }
+ ad_refresh(ad);
+ /* it's not new anymore */
+ ad->ad_md->adf_flags &= ~( O_TRUNC | O_CREAT );
+ ad->ad_md->adf_refcount++;
+ return 0;
+ }
+
+ memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
+ ad->ad_rlen = 0;
+ adf_lock_init(ad->ad_md);
+
+ switch (ad->ad_flags) {
+ case AD_VERSION2:
+ ret = ad_open_hf_v2(path, adflags, oflags, mode, ad);
+ break;
+ case AD_VERSION_EA:
+ ret = ad_open_hf_ea(path, adflags, oflags, mode, ad);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ ad->ad_md->adf_refcount = 1;
+
+ return ret;
+}
+
+static int ad_open_rf(const char *path, int adflags, int oflags, int mode, struct adouble *ad)
{
return 0;
}
-/* ---------------------------------------
+/***********************************************************************************
+ * API functions
+ ********************************************************************************* */
+
+char *ad_path_ea( const char *path, int adflags _U_)
+{
+ return path;
+}
+
+/*
* Put the .AppleDouble where it needs to be:
*
- * / a/.AppleDouble/b/AFP_AfpInfo
+ * / a/.AppleDouble/b
* a/b
- * \ b/.AppleDouble/.Parent/AFP_AfpInfo
+ * \ b/.AppleDouble/.Parent
*
+ * FIXME: should do something for pathname > MAXPATHLEN
*/
-char *
-ad_path_sfm( const char *path, int adflags)
+char *ad_path( const char *path, int adflags)
{
static char pathbuf[ MAXPATHLEN + 1];
- char c, *slash, buf[MAXPATHLEN + 1];
- size_t l;
+ const char *slash;
+ size_t l ;
- l = strlcpy(buf, path, MAXPATHLEN +1);
if ( adflags & ADFLAGS_DIR ) {
- strcpy( pathbuf, buf);
- if ( *buf != '\0' && l < MAXPATHLEN) {
+ l = strlcpy( pathbuf, path, sizeof(pathbuf));
+
+ if ( l && l < MAXPATHLEN) {
pathbuf[l++] = '/';
- pathbuf[l] = 0;
}
- slash = ".Parent";
+ strlcpy(pathbuf +l, ".AppleDouble/.Parent", sizeof(pathbuf) -l);
} else {
- if (NULL != ( slash = strrchr( buf, '/' )) ) {
- c = *++slash;
- *slash = '\0';
- strcpy( pathbuf, buf);
- *slash = c;
+ if (NULL != ( slash = strrchr( path, '/' )) ) {
+ slash++;
+ l = slash - path;
+ /* XXX we must return NULL here and test in the caller */
+ if (l > MAXPATHLEN)
+ l = MAXPATHLEN;
+ memcpy( pathbuf, path, l);
} else {
- pathbuf[ 0 ] = '\0';
- slash = buf;
+ l = 0;
+ slash = path;
}
+ l += strlcpy( pathbuf +l, ".AppleDouble/", sizeof(pathbuf) -l);
+ strlcpy( pathbuf + l, slash, sizeof(pathbuf) -l);
}
- strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
- strlcat( pathbuf, slash, MAXPATHLEN +1);
- if ((adflags == ADFLAGS_RF)) {
- strlcat( pathbuf, "/AFP_Resource", MAXPATHLEN +1);
- }
- else {
- strlcat( pathbuf, "/AFP_AfpInfo", MAXPATHLEN +1);
- }
return( pathbuf );
}
-/* -------------------- */
-static int ad_mkrf_sfm(char *path)
-{
- char *slash;
- /*
- * Probably .AppleDouble doesn't exist, try to mkdir it.
- */
- if (NULL == ( slash = strrchr( path, '/' )) ) {
- return -1;
- }
- *slash = 0;
- errno = 0;
- if ( ad_mkdir( path, 0777 ) < 0 ) {
- if ( errno == ENOENT ) {
- char *slash1;
-
- if (NULL == ( slash1 = strrchr( path, '/' )) )
- return -1;
- errno = 0;
- *slash1 = 0;
- if ( ad_mkdir( path, 0777 ) < 0 )
- return -1;
- *slash1 = '/';
- if ( ad_mkdir( path, 0777 ) < 0 )
- return -1;
- }
- else
- return -1;
- }
- *slash = '/';
- 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",
* and that value is returned.
*/
-
-#define DEFMASK 07700 /* be conservative */
-
-char
-*ad_dir(const char *path)
+char *ad_dir(const char *path)
{
static char modebuf[ MAXPATHLEN + 1];
char *slash;
return modebuf;
}
-/* ---------------- */
-static uid_t default_uid = -1;
-
int ad_setfuid(const uid_t id)
{
default_uid = id;
}
/* ----------------
- return inode of path parent directory
+ stat path parent directory
*/
int ad_stat(const char *path, struct stat *stbuf)
{
- char *p;
+ char *p;
p = ad_dir(path);
- if (!p) {
- return -1;
- }
-//FIXME!
- return lstat( p, stbuf );
-}
-
-/* ----------------
- if we are root change path user/ group
- It can be a native function for BSD cf. FAQ.Q10
- path: pathname to chown
- stbuf: parent directory inode
-
- use fstat and fchown or lchown with linux?
-*/
-#define EMULATE_SUIDDIR
-
-static int ad_chown(const char *path, struct stat *stbuf)
-{
- int ret = 0;
-#ifdef EMULATE_SUIDDIR
- uid_t id;
-
- if (default_uid != (uid_t)-1) {
- /* we are root (admin) */
- id = (default_uid)?default_uid:stbuf->st_uid;
- ret = lchown( path, id, stbuf->st_gid );
- }
-#endif
- return ret;
-}
-
-/* ----------------
- return access right and inode of path parent directory
-*/
-static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
-{
- if (*mode == 0) {
+ if (!p)
return -1;
- }
- if (ad_stat(path, stbuf) != 0) {
- *mode &= DEFMASK;
- return -1;
- }
- *mode &= stbuf->st_mode;
- return 0;
-}
-
-/* ----------------
- return access right of path parent directory
-*/
-int
-ad_mode( const char *path, int mode)
-{
- struct stat stbuf;
- ad_mode_st(path, &mode, &stbuf);
- return mode;
-}
-
-/*
- * Use mkdir() with mode bits taken from ad_mode().
- */
-int
-ad_mkdir( const char *path, int mode)
-{
- int ret;
- int st_invalid;
- struct stat stbuf;
-
- LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
- path, mode, getcwdpath());
-
- st_invalid = ad_mode_st(path, &mode, &stbuf);
- ret = mkdir( path, mode );
- if (ret || st_invalid)
- return ret;
- ad_chown(path, &stbuf);
-
- return ret;
-}
-
-/* ----------------- */
-static int ad_error(struct adouble *ad, int adflags)
-{
- int err = errno;
- if ((adflags & ADFLAGS_NOHF)) {
- /* FIXME double check : set header offset ?*/
- return 0;
- }
- if ((adflags & ADFLAGS_DF)) {
- ad_close( ad, ADFLAGS_DF );
- err = errno;
- }
- return -1 ;
-}
-
-static int new_rfork(const char *path, struct adouble *ad, int adflags);
-
-#ifdef HAVE_PREAD
-#define AD_SET(a)
-#else
-#define AD_SET(a) a = 0
-#endif
-
-/* --------------------------- */
-static int ad_check_size(struct adouble *ad _U_, struct stat *st)
-{
- if (st->st_size > 0 && st->st_size < AD_DATASZ1)
- return 1;
- return 0;
-}
-
-/* --------------------------- */
-static int ad_check_size_sfm(struct adouble *ad _U_, struct stat *st)
-{
- if (st->st_size > 0 && st->st_size < AD_SFM_LEN)
- return 1;
- return 0;
-}
-
-/* --------------------------- */
-static int ad_header_upgrade(struct adouble *ad, char *name)
-{
-#if AD_VERSION == AD_VERSION2
- int ret;
- if ( (ret = ad_convert(ad, name)) < 0 || (ret = ad_update(ad, name) < 0)) {
- return ret;
- }
-#endif
- return 0;
+ return lstat( p, stbuf );
}
-/* --------------------------- */
-static int ad_header_upgrade_none(struct adouble *ad _U_, char *name _U_)
+/* ----------------
+ return access right of path parent directory
+*/
+int ad_mode( const char *path, int mode)
{
- return 0;
+ struct stat stbuf;
+ ad_mode_st(path, &mode, &stbuf);
+ return mode;
}
-/* --------------------------- */
-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_sfm = {
- &ad_path_sfm,
- &ad_mkrf_sfm,
- &ad_rebuild_sfm_header,
- &ad_check_size_sfm,
-
- &ad_header_sfm_read,
- &ad_header_upgrade_none,
-};
+/*
+ * Use mkdir() with mode bits taken from ad_mode().
+ */
+int ad_mkdir( const char *path, int mode)
+{
+ int ret;
+ int st_invalid;
+ struct stat stbuf;
-static struct adouble_fops ad_adouble = {
- &ad_path,
- &ad_mkrf,
- &ad_rebuild_adouble_header,
- &ad_check_size,
+ LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
+ path, mode, getcwdpath());
- &ad_header_read,
- &ad_header_upgrade,
-};
+ st_invalid = ad_mode_st(path, &mode, &stbuf);
+ ret = mkdir( path, mode );
+ if (ret || st_invalid)
+ return ret;
+ ad_chown(path, &stbuf);
+ return ret;
+}
void ad_init(struct adouble *ad, int flags, int options)
{
ad->ad_inited = 0;
ad->ad_flags = flags;
- if (flags == AD_VERSION2_OSX) {
- ad->ad_ops = &ad_osx;
+ if (flags == AD_VERSION2) {
+ ad->ad_ops = &ad_adouble;
ad->ad_md = &ad->ad_resource_fork;
}
- else if (flags == AD_VERSION1_SFM) {
- ad->ad_ops = &ad_sfm;
+ 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;
}
- else {
- ad->ad_ops = &ad_adouble;
- ad->ad_md = &ad->ad_resource_fork;
- }
+
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.
- */
+
+ /* following can be read even if there's no meda data. */
memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
ad->ad_rlen = 0;
}
+static const char *adflags2logstr(int adflags)
+{
+ int first = 1;
+ static char buf[64];
+
+ buf[0] = 0;
+
+ if (adflags & ADFLAGS_DF) {
+ strlcat(buf, "DF", 64);
+ first = 0;
+ }
+ if (adflags & ADFLAGS_RF) {
+ if (!first)
+ strlcat(buf, "|", 64);
+ strlcat(buf, "RF", 64);
+ first = 0;
+ }
+ if (adflags & ADFLAGS_HF) {
+ if (!first)
+ strlcat(buf, "|", 64);
+ strlcat(buf, "HF", 64);
+ first = 0;
+ }
+ if (adflags & ADFLAGS_NOHF) {
+ if (!first)
+ strlcat(buf, "|", 64);
+ strlcat(buf, "NOHF", 64);
+ first = 0;
+ }
+ if (adflags & ADFLAGS_DIR) {
+ if (!first)
+ strlcat(buf, "|", 64);
+ strlcat(buf, "DIR", 64);
+ first = 0;
+ }
+ if (adflags & ADFLAGS_RDONLY) {
+ if (!first)
+ strlcat(buf, "|", 64);
+ strlcat(buf, "RDONLY", 64);
+ first = 0;
+ }
+ if (adflags & ADFLAGS_OPENFORKS) {
+ if (!first)
+ strlcat(buf, "|", 64);
+ strlcat(buf, "OF", 64);
+ first = 0;
+ }
+ return buf;
+}
+
+static const char *oflags2logstr(int oflags)
+{
+ int first = 1;
+ static char buf[64];
+
+ buf[0] = 0;
+
+ if (oflags & O_RDONLY) {
+ strlcat(buf, "O_RDONLY", 64);
+ first = 0;
+ }
+ if (oflags & O_RDWR) {
+ if (!first)
+ strlcat(buf, "|", 64);
+ strlcat(buf, "O_RDWR", 64);
+ first = 0;
+ }
+ if (oflags & O_CREAT) {
+ if (!first)
+ strlcat(buf, "|", 64);
+ strlcat(buf, "O_CREAT", 64);
+ first = 0;
+ }
+ if (oflags & O_EXCL) {
+ if (!first)
+ strlcat(buf, "|", 64);
+ strlcat(buf, "O_EXCL", 64);
+ first = 0;
+ }
+ return buf;
+}
+
/*!
* Open data-, metadata(header)- or ressource fork
*
*
* @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 adflags ADFLAGS_DF: open data fork \n
+ * ADFLAGS_RF: open ressource fork \n
+ * ADFLAGS_HF: open header (metadata) file \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_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_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
* 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)
+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;
+ LOG(log_debug, logtype_default, "ad_open(\"%s\", %s, %s, 0%04o)",
+ abspath(path), adflags2logstr(adflags), oflags2logstr(oflags), mode);
if (ad->ad_inited != AD_INITED) {
ad->ad_inited = AD_INITED;
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_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))
}
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 );
- }
- 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;
-
- 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);
- }
- 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.
- */
- }
- open_df = ADFLAGS_DF;
- ad->ad_data_fork.adf_refcount++;
- }
-
- if (!(adflags & ADFLAGS_HF))
- return 0;
-
- /* ****************************************** */
-
- if (ad_meta_fileno(ad) != -1) { /* the file is already open */
- if ((oflags & ( O_RDWR | O_WRONLY)) &&
- !(ad->ad_md->adf_flags & ( O_RDWR | O_WRONLY))) {
- if (open_df) {
- /* don't call with ADFLAGS_HF because we didn't open ressource fork */
- ad_close( ad, open_df );
- }
- errno = EACCES;
+ if (ad_open_df(path, adflags, oflags, mode, ad) != 0)
return -1;
- }
- ad_refresh(ad);
- /* it's not new anymore */
- ad->ad_md->adf_flags &= ~( O_TRUNC | O_CREAT );
- ad->ad_md->adf_refcount++;
- goto sfm;
- }
-
- memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
- ad->ad_rlen = 0;
- ad_p = ad->ad_ops->ad_path( path, adflags );
-
- hoflags = oflags & ~(O_CREAT | O_EXCL);
- if (!(adflags & ADFLAGS_RDONLY)) {
- hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
- }
- ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 0 );
- if (ad->ad_md->adf_fd < 0 ) {
- if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
- hoflags = oflags & ~(O_CREAT | O_EXCL);
- ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 0 );
- }
- }
-
- if ( ad->ad_md->adf_fd < 0 ) {
- if (errno == ENOENT && (oflags & O_CREAT) ) {
- /*
- * We're expecting to create a new adouble header file,
- * here.
- * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
- */
- LOG(log_debug, logtype_default, "ad_open(\"%s\"): {cwd: \"%s\"} creating adouble file",
- ad_p, getcwdpath());
- admode = mode;
- errno = 0;
- st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
- if ((ad->ad_options & ADVOL_UNIXPRIV)) {
- admode = mode;
- }
- admode = ad_hf_mode(admode);
- if ((errno == ENOENT) && (ad->ad_flags != AD_VERSION2_OSX)) {
- if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
- return ad_error(ad, adflags);
- }
- admode = mode;
- st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
- if ((ad->ad_options & ADVOL_UNIXPRIV)) {
- admode = mode;
- }
- admode = ad_hf_mode(admode);
- }
- /* retry with O_CREAT */
- ad->ad_md->adf_fd = open( ad_p, oflags,admode );
- if ( ad->ad_md->adf_fd < 0 ) {
- return ad_error(ad, adflags);
- }
- ad->ad_md->adf_flags = oflags;
- /* just created, set owner if admin owner (root) */
- if (!st_invalid) {
- ad_chown(ad_p, &st_dir);
- }
- }
- else {
- return ad_error(ad, adflags);
- }
- } else {
- ad->ad_md->adf_flags = hoflags;
- 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
- */
- pst = &st_meta;
- }
}
- AD_SET(ad->ad_md->adf_off);
-
- ad->ad_md->adf_refcount = 1;
- adf_lock_init(ad->ad_md);
- if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) {
- /*
- * This is a new adouble header file. Initialize the structure,
- * instead of reading it.
- */
- if (new_rfork(path, ad, adflags) < 0) {
- int err = errno;
- /* the file is already deleted, perm, whatever, so return an error*/
- ad_close(ad, adflags);
- errno = err;
- return -1;
- }
- ad_flush(ad);
- } else {
- /* Read the adouble header in and parse it.*/
- if (ad->ad_ops->ad_header_read( ad , pst) < 0
- || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0)
- {
- int err = errno;
- ad_close( ad, adflags );
- errno = err;
+ if ((adflags & ADFLAGS_HF)) {
+ if (ad_open_hf(path, adflags, oflags, mode, ad) != 0)
return -1;
- }
- }
-
- /* ****************************************** */
- /* open the resource fork if SFM */
-sfm:
- if (ad->ad_flags != AD_VERSION1_SFM) {
- return 0;
- }
-
- if ((adflags & ADFLAGS_DIR)) {
- /* no resource fork for directories / volumes XXX it's false! */
- return 0;
}
- /* untrue yet but ad_close will decremente it*/
- ad->ad_resource_fork.adf_refcount++;
-
- if (ad_reso_fileno(ad) != -1) { /* the file is already open */
- if ((oflags & ( O_RDWR | O_WRONLY)) &&
- !(ad->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
-
- ad_close( ad, open_df | ADFLAGS_HF);
- errno = EACCES;
+#if 0
+ if ((adflags & ADFLAGS_RF)) {
+ if (ad_open_rf(path, adflags, oflags, mode, ad) != 0)
return -1;
- }
- return 0;
- }
-
- ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF );
-
- admode = mode;
- st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
-
- if ((ad->ad_options & ADVOL_UNIXPRIV)) {
- admode = mode;
- }
-
- hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
- ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode );
-
- if (ad->ad_resource_fork.adf_fd < 0 ) {
- if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
- hoflags = oflags;
- ad->ad_resource_fork.adf_fd =open( ad_p, hoflags, admode );
- }
}
+#endif
- if ( ad->ad_resource_fork.adf_fd < 0) {
- int err = errno;
-
- ad_close( ad, adflags );
- errno = err;
- return -1;
- }
- adf_lock_init(&ad->ad_resource_fork);
- AD_SET(ad->ad_resource_fork.adf_off);
- ad->ad_resource_fork.adf_flags = hoflags;
- if ((oflags & O_CREAT) && !st_invalid) {
- /* just created, set owner if admin (root) */
- ad_chown(ad_p, &st_dir);
- }
- else if (!fstat(ad->ad_resource_fork.adf_fd, &st_meta)) {
- ad->ad_rlen = st_meta.st_size;
- }
- return 0 ;
+ return 0;
}
/*!
}
-/* ----------------------------------- */
-static int new_rfork(const char *path, struct adouble *ad, int adflags)
-{
- const struct entry *eid;
- u_int16_t ashort;
- struct stat st;
-
- ad->ad_magic = AD_MAGIC;
- ad->ad_version = ad->ad_flags & 0x0f0000;
- if (!ad->ad_version) {
- ad->ad_version = AD_VERSION;
- }
-
- memset(ad->ad_filler, 0, sizeof( ad->ad_filler ));
- memset(ad->ad_data, 0, sizeof(ad->ad_data));
-
-#if AD_VERSION == AD_VERSION2
- if (ad->ad_flags == AD_VERSION2)
- eid = entry_order2;
- else if (ad->ad_flags == AD_VERSION2_OSX)
- eid = entry_order_osx;
- else if (ad->ad_flags == AD_VERSION1_SFM) {
- ad->ad_magic = SFM_MAGIC;
- eid = entry_order_sfm;
- }
- else
-#endif
- eid = entry_order1;
-
- while (eid->id) {
- ad->ad_eid[eid->id].ade_off = eid->offset;
- ad->ad_eid[eid->id].ade_len = eid->len;
- eid++;
- }
-
- /* put something sane in the directory finderinfo */
- if ((adflags & ADFLAGS_DIR)) {
- /* set default view */
- ashort = htons(FINDERINFO_CLOSEDVIEW);
- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF,
- &ashort, sizeof(ashort));
- } else {
- /* set default creator/type fields */
- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
- }
-
- /* make things invisible */
- if ((ad->ad_options & ADVOL_INVDOTS) && !(adflags & ADFLAGS_CREATE) &&
- (*path == '.') && strcmp(path, ".") && strcmp(path, ".."))
- {
- ashort = htons(ATTRBIT_INVISIBLE);
- ad_setattr(ad, ashort);
- ashort = htons(FINDERINFO_INVISIBLE);
- memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
- }
-
- if (lstat(path, &st) < 0) {
- return -1;
- }
-
- /* put something sane in the date fields */
- ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
- ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
- ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
- ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
- return 0;
-}
-
-/* to do this with mmap, we need the hfs fs to understand how to mmap
- header files. */
int ad_refresh(struct adouble *ad)
{
- if (ad_meta_fileno(ad) < 0)
+ if (ad_meta_fileno(ad) == -1)
return -1;
return ad->ad_ops->ad_header_read(ad, NULL);
#if defined(NEED_RQUOTA) || (defined(sun) && defined(__svr4__)) || \
(defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ < 6)
+#ifndef u_int
+#define u_int unsigned
+#endif
+
#include <rpc/rpc.h>
#include <rpcsvc/rquota.h>
return strerror(errno);
}
+/*!
+ * Make argument path absoulte
+ *
+ * @returns pointer to path or pointer to error messages on error
+ */
+const char *abspath(const char *name)
+{
+ static char buf[MAXPATHLEN + 1];
+ char *p;
+ int n;
+
+ if (name[0] == '/')
+ return name;
+
+ if ((p = getcwd(buf, MAXPATHLEN)) == NULL)
+ return strerror(errno);
+
+ n = strlen(buf);
+ if (buf[n-1] != '/')
+ buf[n++] = '/';
+
+ strlcpy(buf + n, name, MAXPATHLEN - n);
+ return buf;
+}
+
/*!
* Takes a buffer with a path, strips slashs, returns basename
*
strcpy(vol->v_dbpath, value);
break;
case ADOUBLE_VER:
- if (strcasecmp(value, "v1") == 0) {
- vol->v_adouble = AD_VERSION1;
- vol->ad_path = ad_path;
- }
-#if AD_VERSION == AD_VERSION2
- else if (strcasecmp(value, "v2") == 0) {
+ if (strcasecmp(value, "v2") == 0) {
vol->ad_path = ad_path;
vol->v_adouble = AD_VERSION2;
- }
- else if (strcasecmp(value, "osx") == 0) {
- vol->v_adouble = AD_VERSION2_OSX;
- vol->ad_path = ad_path_osx;
- }
-#endif
- else {
+ } else if (strcasecmp(value, "ea") == 0) {
+ vol->ad_path = ad_path_ea;
+ vol->v_adouble = AD_VERSION_EA;
+ } else {
+
fprintf (stderr, "unknown adouble version: %s, %s", buf, value);
return -1;
}
strlcat(buf, item, sizeof(buf));
switch (vol->v_adouble) {
- case AD_VERSION1:
- strlcat(buf, "ADOUBLE_VER:v1\n", sizeof(buf));
- break;
case AD_VERSION2:
strlcat(buf, "ADOUBLE_VER:v2\n", sizeof(buf));
break;
- case AD_VERSION2_OSX:
- strlcat(buf, "ADOUBLE_VER:osx\n", sizeof(buf));
- break;
- case AD_VERSION1_SFM:
- strlcat(buf, "ADOUBLE_VER:sfm\n", sizeof(buf));
+ case AD_VERSION_EA:
+ strlcat(buf, "ADOUBLE_VER:ea\n", sizeof(buf));
break;
}
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 ea_ad.c ea_sys.c extattr.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>
#include <atalk/unix.h>
#include <atalk/compat.h>
-#ifndef ENOATTR
-#define ENOATTR ENODATA
-#endif
-
-
/**********************************************************************************
* EA VFS funcs for storing EAs in nativa filesystem EAs
**********************************************************************************/
--- /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>
+
+/******** 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_fgetxattr (int filedes, const char *uname, void *value, size_t size)
+{
+ const char *name = prefix(uname);
+
+#if defined(HAVE_FGETXATTR)
+#ifndef XATTR_ADD_OPT
+ return fgetxattr(filedes, name, value, size);
+#else
+ int options = 0;
+ return fgetxattr(filedes, name, value, size, 0, options);
+#endif
+#elif defined(HAVE_FGETEA)
+ return fgetea(filedes, name, value, size);
+#elif defined(HAVE_EXTATTR_GET_FD)
+ char *s;
+ ssize_t retval;
+ int attrnamespace = (strncmp(name, "system", 6) == 0) ?
+ EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
+ const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
+
+ if((retval=extattr_get_fd(filedes, attrnamespace, attrname, NULL, 0)) >= 0) {
+ if(retval > size) {
+ errno = ERANGE;
+ return -1;
+ }
+ if((retval=extattr_get_fd(filedes, attrnamespace, attrname, value, size)) >= 0)
+ return retval;
+ }
+
+ LOG(log_debug, logtype_default, "sys_fgetxattr: extattr_get_fd(): %s",
+ strerror(errno)));
+ return -1;
+#elif defined(HAVE_ATTR_GETF)
+ int retval, flags = 0;
+ int valuelength = (int)size;
+ char *attrname = strchr(name,'.') + 1;
+
+ if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+ retval = attr_getf(filedes, attrname, (char *)value, &valuelength, flags);
+
+ return retval ? retval : valuelength;
+#elif defined(HAVE_ATTROPEN)
+ ssize_t ret = -1;
+ int attrfd = solaris_openat(filedes, name, O_RDONLY|O_XATTR, 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_fsetxattr (int filedes, const char *uname, const void *value, size_t size, int flags)
+{
+ const char *name = prefix(uname);
+
+#if defined(HAVE_FSETXATTR)
+#ifndef XATTR_ADD_OPT
+ return fsetxattr(filedes, name, value, size, flags);
+#else
+ int options = 0;
+ return fsetxattr(filedes, name, value, size, 0, options);
+#endif
+#elif defined(HAVE_FSETEA)
+ return fsetea(filedes, name, value, size, flags);
+#elif defined(HAVE_EXTATTR_SET_FD)
+ char *s;
+ int retval = 0;
+ int attrnamespace = (strncmp(name, "system", 6) == 0) ?
+ EXTATTR_NAMESPACE_SYSTEM : EXTATTR_NAMESPACE_USER;
+ const char *attrname = ((s=strchr_m(name, '.')) == NULL) ? name : s + 1;
+ if (flags) {
+ /* Check attribute existence */
+ retval = extattr_get_fd(filedes, attrnamespace, attrname, 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 {
+ log_error, logtype_default /* CREATE attribute, that already exists */
+ if (flags & XATTR_CREATE) {
+ errno = EEXIST;
+ return -1;
+ }
+ }
+ }
+ retval = extattr_set_fd(filedes, attrnamespace, attrname, value, size);
+ return (retval < 0) ? -1 : 0;
+#elif defined(HAVE_ATTR_SETF)
+ 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_setf(filedes, attrname, (const char *)value, size, myflags);
+#elif defined(HAVE_ATTROPEN)
+ int ret = -1;
+ int myflags = O_RDWR | O_XATTR;
+ int attrfd;
+ if (flags & XATTR_CREATE) myflags |= O_EXCL;
+ if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
+ attrfd = solaris_openat(filedes, 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",
+ 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: %d, path: %s, errno: %s",
+ 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!");
+ 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*/
-
}
#endif
-/*********************************************************************************
- * sfm adouble format
- *********************************************************************************/
-static int ads_chown_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_)
-{
- struct perm *owner = data;
-
- if (chown( name , owner->uid, owner->gid ) < 0) {
- return -1;
- }
- return 0;
-}
-
-static int RF_chown_ads(VFS_FUNC_ARGS_CHOWN)
-{
- struct stat st;
- char *ad_p;
- struct perm owner;
-
- owner.uid = uid;
- owner.gid = gid;
-
-
- ad_p = ad_dir(vol->ad_path(path, ADFLAGS_HF ));
-
- if ( stat( ad_p, &st ) < 0 ) {
- /* ignore */
- return 0;
- }
-
- if (chown( ad_p, uid, gid ) < 0) {
- return -1;
- }
- return for_each_adouble("chown_ads", ad_p, ads_chown_loop, &owner, 1, vol->v_umask);
-}
-
-/* --------------------------------- */
-static int deletecurdir_ads1_loop(struct dirent *de _U_, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
-{
- return netatalk_unlink(name);
-}
-
-static int ads_delete_rf(char *name)
-{
- int err;
-
- if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1, 0)))
- return err;
- /* FIXME
- * it's a problem for a nfs mounted folder, there's .nfsxxx around
- * for linux the following line solve it.
- * but it could fail if rm .nfsxxx create a new .nfsyyy :(
- */
- if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1, 0)))
- return err;
- return netatalk_rmdir(-1, name);
-}
-
-static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
-{
- struct stat st;
-
- /* bail if the file exists in the current directory.
- * note: this will not fail with dangling symlinks */
-
- if (stat(de->d_name, &st) == 0) {
- return AFPERR_DIRNEMPT;
- }
- return ads_delete_rf(name);
-}
-
-static int RF_deletecurdir_ads(VFS_FUNC_ARGS_DELETECURDIR)
-{
- int err;
-
- /* delete stray .AppleDouble files. this happens to get .Parent files as well. */
- if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, NULL, 1, 0)))
- return err;
-
- return netatalk_rmdir(-1, ".AppleDouble" );
-}
-
-/* ------------------- */
-struct set_mode {
- mode_t mode;
- struct stat *st;
-};
-
-static int ads_setfilmode_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask)
+/*************************************************************************
+ * EA adouble format
+ ************************************************************************/
+static int validupath_ea(VFS_FUNC_ARGS_VALIDUPATH)
{
- struct set_mode *param = data;
-
- return setfilmode(name, param->mode, param->st, v_umask);
-}
+ return 1;
+}
-static int ads_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+/* ----------------- */
+static int RF_chown_ea(VFS_FUNC_ARGS_CHOWN)
{
- mode_t file_mode = ad_hf_mode(mode);
- mode_t dir_mode = file_mode;
- struct set_mode param;
-
- if ((dir_mode & (S_IRUSR | S_IWUSR )))
- dir_mode |= S_IXUSR;
- if ((dir_mode & (S_IRGRP | S_IWGRP )))
- dir_mode |= S_IXGRP;
- if ((dir_mode & (S_IROTH | S_IWOTH )))
- dir_mode |= S_IXOTH;
-
- /* change folder */
- dir_mode |= DIRBITS;
- if (dir_rx_set(dir_mode)) {
- if (chmod( name, dir_mode ) < 0)
- return -1;
- }
- param.st = st;
- param.mode = file_mode;
- if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, ¶m, 0, v_umask) < 0)
- return -1;
-
- if (!dir_rx_set(dir_mode)) {
- if (chmod( name, dir_mode ) < 0)
- return -1;
- }
-
return 0;
}
-static int RF_setfilmode_ads(VFS_FUNC_ARGS_SETFILEMODE)
+/* ---------------- */
+static int RF_renamedir_ea(VFS_FUNC_ARGS_RENAMEDIR)
{
- return ads_setfilmode(ad_dir(vol->ad_path(name, ADFLAGS_HF )), mode, st, vol->v_umask);
+ return 0;
}
-/* ------------------- */
-static int RF_setdirunixmode_ads(VFS_FUNC_ARGS_SETDIRUNIXMODE)
+/* ---------------- */
+static int RF_deletecurdir_ea(VFS_FUNC_ARGS_DELETECURDIR)
{
- char *adouble = vol->ad_path(name, ADFLAGS_DIR );
- char ad_p[ MAXPATHLEN + 1];
- int dropbox = vol->v_flags;
-
- strlcpy(ad_p,ad_dir(adouble), MAXPATHLEN + 1);
-
- if (dir_rx_set(mode)) {
-
- /* .AppleDouble */
- if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox, vol->v_umask) < 0)
- return -1;
-
- /* .AppleDouble/.Parent */
- if (stickydirmode(ad_p, DIRBITS | mode, dropbox, vol->v_umask) < 0)
- return -1;
- }
-
- if (ads_setfilmode(ad_dir(vol->ad_path(name, ADFLAGS_DIR)), mode, st, vol->v_umask) < 0)
- return -1;
-
- if (!dir_rx_set(mode)) {
- if (stickydirmode(ad_p, DIRBITS | mode, dropbox, vol->v_umask) < 0)
- return -1 ;
- if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox, vol->v_umask) < 0)
- return -1;
- }
return 0;
}
-/* ------------------- */
-struct dir_mode {
- mode_t mode;
- int dropbox;
-};
-
-static int setdirmode_ads_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask)
+/* ---------------- */
+static int RF_setdirunixmode_ea(VFS_FUNC_ARGS_SETDIRUNIXMODE)
{
-
- struct dir_mode *param = data;
- int ret = 0; /* 0 ignore error, -1 */
-
- if (dir_rx_set(param->mode)) {
- if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, v_umask) < 0) {
- if (flag) {
- return 0;
- }
- return ret;
- }
- }
- if (ads_setfilmode(name, param->mode, NULL, v_umask) < 0)
- return ret;
-
- if (!dir_rx_set(param->mode)) {
- if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, v_umask) < 0) {
- if (flag) {
- return 0;
- }
- return ret;
- }
- }
return 0;
}
-static int RF_setdirmode_ads(VFS_FUNC_ARGS_SETDIRMODE)
+static int RF_setfilmode_ea(VFS_FUNC_ARGS_SETFILEMODE)
{
- char *adouble = vol->ad_path(name, ADFLAGS_DIR );
- char ad_p[ MAXPATHLEN + 1];
- struct dir_mode param;
-
- param.mode = mode;
- param.dropbox = vol->v_flags;
-
- strlcpy(ad_p,ad_dir(adouble), sizeof(ad_p));
-
- if (dir_rx_set(mode)) {
- /* .AppleDouble */
- if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox, vol->v_umask) < 0)
- return -1;
- }
-
- if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, ¶m, vol_noadouble(vol), vol->v_umask))
- return -1;
-
- if (!dir_rx_set(mode)) {
- if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox, vol->v_umask) < 0 )
- return -1;
- }
return 0;
}
-/* ------------------- */
-static int setdirowner_ads1_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_)
+/* ---------------- */
+static int RF_setdirmode_ea(VFS_FUNC_ARGS_SETDIRMODE)
{
- struct perm *owner = data;
-
- if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
- LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
- owner->uid, owner->gid, fullpathname(name), strerror(errno) );
- /* return ( -1 ); Sometimes this is okay */
- }
return 0;
}
-static int setdirowner_ads_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask _U_)
+/* ---------------- */
+static int RF_setdirowner_ea(VFS_FUNC_ARGS_SETDIROWNER)
{
- struct perm *owner = data;
-
- if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, data, flag, 0) < 0)
- return -1;
-
- if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
- LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
- owner->uid, owner->gid, fullpathname(name), strerror(errno) );
- /* return ( -1 ); Sometimes this is okay */
- }
- return 0;
+ return 0;
}
-static int RF_setdirowner_ads(VFS_FUNC_ARGS_SETDIROWNER)
+static int RF_deletefile_ea(VFS_FUNC_ARGS_DELETEFILE)
{
- int noadouble = vol_noadouble(vol);
- char adouble_p[ MAXPATHLEN + 1];
- struct stat st;
- struct perm owner;
-
- owner.uid = uid;
- owner.gid = gid;
-
- strlcpy(adouble_p, ad_dir(vol->ad_path(name, ADFLAGS_DIR )), sizeof(adouble_p));
-
- if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, &owner, noadouble, 0))
- return -1;
-
- /*
- * We cheat: we know that chown doesn't do anything.
- */
- if ( stat( ".AppleDouble", &st ) < 0) {
- if (errno == ENOENT && noadouble)
- return 0;
- LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
- return -1;
- }
- if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
- LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
- uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
- /* return ( -1 ); Sometimes this is okay */
- }
return 0;
}
-
-/* ------------------- */
-static int RF_deletefile_ads(VFS_FUNC_ARGS_DELETEFILE)
+static int RF_copyfile_ea(VFS_FUNC_ARGS_COPYFILE)
{
- int ret = 0;
- int cwd = -1;
- char *ad_p;
-
- ad_p = ad_dir(vol->ad_path(file, ADFLAGS_HF ));
-
- if (dirfd != -1) {
- if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
- ret = AFPERR_MISC;
- goto exit;
- }
- }
-
- ret = ads_delete_rf(ad_p);
-
- if (dirfd != -1 && fchdir(cwd) != 0) {
- LOG(log_error, logtype_afpd, "RF_deletefile_ads: cant chdir back. exit!");
- exit(EXITERR_SYS);
- }
-
-exit:
- if (cwd != -1)
- close(cwd);
-
- return ret;
+ return 0;
}
-/* --------------------------- */
-static int RF_renamefile_ads(VFS_FUNC_ARGS_RENAMEFILE)
+/* ---------------- */
+static int RF_renamefile_ea(VFS_FUNC_ARGS_RENAMEFILE)
{
- char adsrc[ MAXPATHLEN + 1];
- int err = 0;
-
- strcpy( adsrc, ad_dir(vol->ad_path(src, 0 )));
- if (unix_rename(dirfd, adsrc, -1, ad_dir(vol->ad_path(dst, 0 ))) < 0) {
- struct stat st;
-
- err = errno;
- if (errno == ENOENT) {
- struct adouble ad;
-
- if (lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */
- return 0;
-
- /* We are here because :
- * -there's no dest folder.
- * -there's no .AppleDouble in the dest folder.
- * if we use the struct adouble passed in parameter it will not
- * create .AppleDouble if the file is already opened, so we
- * use a diff one, it's not a pb,ie it's not the same file, yet.
- */
- ad_init(&ad, vol->v_adouble, vol->v_ad_options);
- if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
- ad_close(&ad, ADFLAGS_HF);
-
- /* We must delete it */
- RF_deletefile_ads(vol, -1, dst );
- if (!unix_rename(dirfd, adsrc, -1, ad_dir(vol->ad_path(dst, 0 ))) )
- err = 0;
- else
- err = errno;
- }
- else { /* it's something else, bail out */
- err = errno;
- }
- }
- }
- if (err) {
- errno = err;
- return -1;
- }
- return 0;
+ return 0;
}
+#if 0
/*************************************************************************
* osx adouble format
************************************************************************/
}
return 0;
}
+#endif
/********************************************************************************************
* VFS chaining
};
/*
- * Primary adouble modules: default, osx, sfm
+ * Primary adouble modules: v2, ea
*/
-static struct vfs_ops netatalk_adouble = {
+static struct vfs_ops netatalk_adouble_v2 = {
/* vfs_validupath: */ validupath_adouble,
/* vfs_chown: */ RF_chown_adouble,
/* vfs_renamedir: */ RF_renamedir_adouble,
NULL
};
-static struct vfs_ops netatalk_adouble_osx = {
- /* vfs_validupath: */ validupath_osx,
- /* vfs_chown: */ RF_chown_adouble,
- /* vfs_renamedir: */ RF_renamedir_osx,
- /* vfs_deletecurdir: */ RF_deletecurdir_osx,
- /* vfs_setfilmode: */ RF_setfilmode_adouble,
- /* vfs_setdirmode: */ RF_setdirmode_osx,
- /* vfs_setdirunixmode:*/ RF_setdirunixmode_osx,
- /* vfs_setdirowner: */ RF_setdirowner_osx,
- /* vfs_deletefile: */ RF_deletefile_adouble,
- /* vfs_renamefile: */ RF_renamefile_osx,
- /* vfs_copyfile: */ NULL,
- NULL
-};
-
-/* samba sfm format. ad_path shouldn't be set her */
-static struct vfs_ops netatalk_adouble_sfm = {
- /* vfs_validupath: */ validupath_adouble,
- /* vfs_chown: */ RF_chown_ads,
- /* vfs_renamedir: */ RF_renamedir_adouble,
- /* vfs_deletecurdir: */ RF_deletecurdir_ads,
- /* vfs_setfilmode: */ RF_setfilmode_ads,
- /* vfs_setdirmode: */ RF_setdirmode_ads,
- /* vfs_setdirunixmode:*/ RF_setdirunixmode_ads,
- /* vfs_setdirowner: */ RF_setdirowner_ads,
- /* vfs_deletefile: */ RF_deletefile_ads,
- /* vfs_renamefile: */ RF_renamefile_ads,
- /* vfs_copyfile: */ NULL,
+static struct vfs_ops netatalk_adouble_ea = {
+ /* vfs_validupath: */ validupath_ea,
+ /* vfs_chown: */ RF_chown_ea,
+ /* vfs_renamedir: */ RF_renamedir_ea,
+ /* vfs_deletecurdir: */ RF_deletecurdir_ea,
+ /* vfs_setfilmode: */ RF_setfilmode_ea,
+ /* vfs_setdirmode: */ RF_setdirmode_ea,
+ /* vfs_setdirunixmode:*/ RF_setdirunixmode_ea,
+ /* vfs_setdirowner: */ RF_setdirowner_ea,
+ /* vfs_deletefile: */ RF_deletefile_ea,
+ /* vfs_renamefile: */ RF_renamefile_ea,
+ /* vfs_copyfile: */ RF_copyfile_ea,
NULL
};
vol->vfs = &vfs_master_funcs;
/* Default adouble stuff */
- if (vol->v_adouble == AD_VERSION2_OSX) {
- vol->vfs_modules[0] = &netatalk_adouble_osx;
- vol->ad_path = ad_path_osx;
- }
- else if (vol->v_adouble == AD_VERSION1_SFM) {
- vol->vfs_modules[0] = &netatalk_adouble_sfm;
- vol->ad_path = ad_path_sfm;
- }
- else {
- vol->vfs_modules[0] = &netatalk_adouble;
+ if (vol->v_adouble == AD_VERSION2) {
+ vol->vfs_modules[0] = &netatalk_adouble_v2;
vol->ad_path = ad_path;
+ } else {
+ vol->vfs_modules[0] = &netatalk_adouble_ea;
+ vol->ad_path = ad_path_ea;
}
/* Extended Attributes */
GENERATED_MANS = uniconv.1 asip-status.pl.1
TEMPLATE_FILES = uniconv.1.tmpl asip-status.pl.1.tmpl
+
NONGENERATED_MANS = ad.1 \
afppasswd.1 \
apple_dump.1 \
dbd.1 \
- hqx2bin.1 \
- macbinary.1 \
- megatron.1 \
- netatalk-config.1 \
- single2bin.1 \
- unbin.1 \
- unhex.1 \
- unsingle.1
+ netatalk-config.1
+
ATALK_MANS = aecho.1 \
getzones.1 \
nbp.1 \
#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 */