Extended VFS stack, so apps linking libatalk can also actually use adouble and EA code.
Missing: EA integration into AFP rm, cp, mv.
-dnl $Id: configure.in,v 1.219 2009-09-18 12:36:39 franklahm Exp $
+dnl $Id: configure.in,v 1.220 2009-10-02 09:32:40 franklahm Exp $
dnl configure.in for netatalk
AC_INIT(etc/afpd/main.c)
AC_SUBST(LIBATALK_ACLS)
dnl --------------------- check for Extended Attributes support
-neta_cv_extattrs="no"
-AC_MSG_CHECKING([if Extended Attribute Support should be enabled])
-AC_ARG_ENABLE(extattrs,
- [ --enable-extattrs enable Extended Attributes],[
- if test x"$enableval" = x"yes"; then
- AC_MSG_RESULT([yes])
- neta_cv_extattrs="yes"
- else
- AC_MSG_RESULT([no])
- fi],[
- AC_MSG_RESULT([no])
- ]
-)
-if test x$neta_cv_extattrs = xyes; then
- AC_CHECK_LIB(c,attropen,neta_cv_extattrs=yes,neta_cv_extattrs=no)
-fi
-if test x$neta_cv_extattrs = xyes; then
- AC_MSG_NOTICE([Enabling Extended Attributes support])
- AC_DEFINE([HAVE_EXT_ATTRS], 1, [Enable Extended Attributes])
+neta_cv_eas="adouble"
+if test "x$this_os" = "xsolaris"; then
+ AC_CHECK_LIB(c,attropen, [
+ neta_cv_eas="$neta_cv_eas Solaris"
+ AC_MSG_NOTICE([Enabling Solaris Extended Attributes support])
+ AC_DEFINE([HAVE_SOLARIS_EAS], 1, [Enable Extended Attributes])
+ ]
+ )
fi
dnl --------------------- last minute substitutions
libatalk/tdb/Makefile
libatalk/unicode/Makefile
libatalk/unicode/charsets/Makefile
+ libatalk/vfs/Makefile
macros/Makefile
man/Makefile
man/man1/Makefile
file.c enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c \
afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
- catsearch.c afprun.c vfs_adouble.c hash.c
+ catsearch.c afprun.c hash.c extattrs.c
if USE_NFSv4_ACLS
afpd_SOURCES += acls.c
endif
-if USE_EXT_ATTRS
-afpd_SOURCES += extattrs.c
-endif
afpd_LDADD = $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la
afpd_LDFLAGS = -export-dynamic
noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \
- uam_auth.h uid.h unix.h volume.h afp_vfs.h hash.h acls.h acl_mappings.h
+ uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h
LIBS = @LIBS@ @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@
/*
- $Id: acls.c,v 1.3 2009-02-26 14:00:18 franklahm Exp $
+ $Id: acls.c,v 1.4 2009-10-02 09:32:40 franklahm Exp $
Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include <sys/acl.h>
#include <atalk/adouble.h>
+#include <atalk/vfs.h>
#include <atalk/afp.h>
#include <atalk/util.h>
#include <atalk/cnid.h>
* Basic and helper funcs
********************************************************/
-/* Get ACL. Allocates storage as needed. Caller must free.
- * Returns no of ACEs or -1 on error. */
-static int get_nfsv4_acl(const char *name, ace_t **retAces)
-{
- int ace_count = -1;
- ace_t *aces;
-
- *retAces = NULL;
- ace_count = acl(name, ACE_GETACLCNT, 0, NULL);
- if (ace_count <= 0) {
- LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACLCNT) error");
- return -1;
- }
-
- aces = malloc(ace_count * sizeof(ace_t));
- if (aces == NULL) {
- LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
- return -1;
- }
-
- if ( (acl(name, ACE_GETACL, ace_count, aces)) == -1 ) {
- LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
- free(aces);
- return -1;
- }
-
- LOG(log_debug9, logtype_afpd, "get_nfsv4_acl: file: %s -> No. of ACEs: %d", name, ace_count);
- *retAces = aces;
-
- return ace_count;
-}
-
/*
Takes a users name, uid and primary gid and checks if user is member of any group
Returns -1 if no or error, 0 if yes
return err;
}
-/* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
-int remove_acl(const char *name)
-{
- int ret,i, ace_count, trivial_aces, new_aces_count;
- ace_t *old_aces = NULL;
- ace_t *new_aces = NULL;
-
- LOG(log_debug9, logtype_afpd, "remove_acl: BEGIN");
-
- /* Get existing ACL and count trivial ACEs */
- if ((ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
- return AFPERR_MISC;
- trivial_aces = 0;
- for ( i=0; i < ace_count; i++) {
- if (old_aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))
- trivial_aces++;
- }
-
- /* malloc buffer for new ACL */
- if ((new_aces = malloc(trivial_aces * sizeof(ace_t))) == NULL) {
- LOG(log_error, logtype_afpd, "remove_acl: malloc %s", strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
- }
-
- /* Now copy the trivial ACEs */
- new_aces_count = 0;
- for (i=0; i < ace_count; i++) {
- if (old_aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
- memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
- new_aces_count++;
- }
- }
-
- if ( (acl(name, ACE_SETACL, trivial_aces, new_aces)) == 0)
- ret = AFP_OK;
- else {
- LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
- if (errno == (EACCES | EPERM))
- ret = AFPERR_ACCESS;
- else if (errno == ENOENT)
- ret = AFPERR_NOITEM;
- else
- ret = AFPERR_MISC;
- }
-
-exit:
- free(old_aces);
- free(new_aces);
-
- LOG(log_debug9, logtype_afpd, "remove_acl: END");
- return ret;
-}
-
/* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
static int remove_acl_vfs(const struct vol *vol,const char *path, int dir)
{
/*
- * $Id: auth.c,v 1.65 2009-09-28 12:04:51 franklahm Exp $
+ * $Id: auth.c,v 1.66 2009-10-02 09:32:40 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include "switch.h"
#include "status.h"
#include "fork.h"
+#include "extattrs.h"
#ifdef HAVE_NFSv4_ACLS
#include "acls.h"
#endif
-#ifdef HAVE_EXT_ATTRS
-#include "extattrs.h"
-#endif
int afp_version = 11;
static int afp_version_index;
afp_switch = postauth_switch;
switch (afp_version) {
case 32:
- uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL);
- uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL);
- uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL);
#ifdef HAVE_NFSv4_ACLS
uam_afpserver_action(AFP_GETACL, UAM_AFPSERVER_POSTAUTH, afp_getacl, NULL);
uam_afpserver_action(AFP_SETACL, UAM_AFPSERVER_POSTAUTH, afp_setacl, NULL);
uam_afpserver_action(AFP_ACCESS, UAM_AFPSERVER_POSTAUTH, afp_access, NULL);
#endif
-#ifdef HAVE_EXT_ATTRS
uam_afpserver_action(AFP_GETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_getextattr, NULL);
uam_afpserver_action(AFP_SETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_setextattr, NULL);
uam_afpserver_action(AFP_REMOVEATTR, UAM_AFPSERVER_POSTAUTH, afp_remextattr, NULL);
uam_afpserver_action(AFP_LISTEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_listextattr, NULL);
-#endif
case 31:
+ uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL);
+ uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL);
+ uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL);
uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL);
case 30:
uam_afpserver_action(AFP_ENUMERATE_EXT, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext, NULL);
/*
- * $Id: directory.c,v 1.104 2009-09-14 00:02:21 didg Exp $
+ * $Id: directory.c,v 1.105 2009-10-02 09:32:40 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include <sys/param.h>
#include <errno.h>
#include <utime.h>
-#include <atalk/adouble.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
#include <atalk/afp.h>
#include <atalk/util.h>
#include <atalk/cnid.h>
#include "globals.h"
#include "unix.h"
#include "mangle.h"
+#include "hash.h"
#ifdef HAVE_NFSv4_ACLS
extern void addir_inherit_acl(const struct vol *vol);
return( dir );
}
-/* -------------------
- system rmdir with afp error code.
- ENOENT is not an error.
- */
-int netatalk_rmdir(const char *name)
-{
- if (rmdir(name) < 0) {
- switch ( errno ) {
- case ENOENT :
- break;
- case ENOTEMPTY :
- return AFPERR_DIRNEMPT;
- case EPERM:
- case EACCES :
- return AFPERR_ACCESS;
- case EROFS:
- return AFPERR_VLOCK;
- default :
- return AFPERR_PARAM;
- }
- }
- return AFP_OK;
-}
-
/* -------------------------
appledouble mkdir afp error code.
*/
return AFP_OK;
}
-/* -------------------
- system unlink with afp error code.
- ENOENT is not an error.
- */
-int netatalk_unlink(const char *name)
-{
- if (unlink(name) < 0) {
- switch (errno) {
- case ENOENT :
- break;
- case EROFS:
- return AFPERR_VLOCK;
- case EPERM:
- case EACCES :
- return AFPERR_ACCESS;
- default :
- return AFPERR_PARAM;
- }
- }
- return AFP_OK;
-}
-
/* ------------------- */
static int deletedir(char *dir)
{
/*
- * $Id: directory.h,v 1.27 2009-09-14 00:02:21 didg Exp $
+ * $Id: directory.h,v 1.28 2009-10-02 09:32:40 franklahm Exp $
*
* Copyright (c) 1990,1991 Regents of The University of Michigan.
* All Rights Reserved.
#include <sys/sysmacros.h>
#endif
+#include <atalk/directory.h>
+
#include "globals.h"
#include "volume.h"
-/* the did tree is now a red-black tree while the parent/child
- * tree is a circular doubly-linked list. how exciting. */
-struct dir {
- struct dir *d_left, *d_right, *d_back; /* for red-black tree */
- int d_color;
- struct dir *d_parent, *d_child; /* parent-child */
- struct dir *d_prev, *d_next; /* siblings */
- void *d_ofork; /* oforks using this directory. */
- u_int32_t d_did;
- int d_flags;
-
- time_t ctime; /* inode ctime */
- u_int32_t offcnt; /* offspring count */
-
- char *d_m_name; /* mac name */
- char *d_u_name; /* unix name */
- ucs2_t *d_m_name_ucs2; /* mac name as UCS2 */
-};
-
-struct path {
- int m_type; /* mac name type (long name, unicode */
- char *m_name; /* mac name */
- char *u_name; /* unix name */
- cnid_t id; /* file id (only for getmetadata) */
- struct dir *d_dir; /* */
- int st_valid; /* does st_errno and st set */
- int st_errno;
- struct stat st;
-};
-
-static inline int path_isadir(struct path *o_path)
-{
- return o_path->d_dir != NULL;
-#if 0
- return o_path->m_name == '\0' || /* we are in a it */
- !o_path->st_valid || /* in cache but we can't chdir in it */
- (!o_path->st_errno && S_ISDIR(o_path->st.st_mode)); /* not in cache an can't chdir */
-#endif
-}
-
#define DIRTREE_COLOR_RED 0
#define DIRTREE_COLOR_BLACK 1
-/* setgid directories */
-#ifndef DIRBITS
-# ifdef AFS
-# define DIRBITS 0
-# else /* AFS */
-# define DIRBITS S_ISGID
-# endif /* AFS */
-#endif /* DIRBITS */
-
#define DIRF_FSMASK (3<<0)
#define DIRF_NOFS (0<<0)
#define DIRF_AFS (1<<0)
#define DIRF_OFFCNT (1<<4) /* offsprings count is valid */
#define DIRF_CNID (1<<5) /* renumerate id */
-
#define AFPDIR_READ (1<<0)
/* directory bits */
extern int check_access __P((char *name , int mode));
extern int file_access __P((struct path *path, int mode));
-extern int netatalk_rmdir __P((const char *name));
extern int netatalk_unlink __P((const char *name));
extern int caseenumerate __P((const struct vol *, struct path *, struct dir *));
/*
- * $Id: enumerate.c,v 1.43 2005-04-30 21:33:41 didg Exp $
+ * $Id: enumerate.c,v 1.44 2009-10-02 09:32:40 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-
-#include <atalk/logger.h>
#include <sys/file.h>
#include <sys/param.h>
+#include <atalk/logger.h>
#include <atalk/afp.h>
#include <atalk/adouble.h>
+#include <atalk/vfs.h>
#include <atalk/cnid.h>
#include "desktop.h"
#include "directory.h"
/*
- $Id: extattrs.c,v 1.3 2009-08-31 14:50:46 franklahm Exp $
+ $Id: extattrs.c,v 1.4 2009-10-02 09:32:40 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
GNU General Public License for more details.
*/
-/* According to man fsattr.5 we must define _ATFILE_SOURCE */
-#define _ATFILE_SOURCE
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
-#ifdef HAVE_EXT_ATTRS
-
-#include <errno.h>
#include <unistd.h>
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <atalk/adouble.h>
+#include <atalk/vfs.h>
#include <atalk/afp.h>
#include <atalk/logger.h>
+#include <atalk/ea.h>
#include "globals.h"
#include "volume.h"
static char *ea_resourcefork = "com.apple.ResourceFork";
/* This should be big enough to consecutively store the names of all attributes */
-#define ATTRNAMEBUFSIZ 4096
static char attrnamebuf[ATTRNAMEBUFSIZ];
static void hexdump(void *m, size_t l) {
}
}
-static int getextattr_size(char *rbuf, int *rbuflen, char *uname, int oflag, char *attruname)
-{
- int ret, attrdirfd;
- uint32_t attrsize;
- struct stat st;
-
- LOG(log_debug7, logtype_afpd, "getextattr_size(%s): attribute: \"%s\"", uname, attruname);
-
- if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
- if (errno == ELOOP) {
- /* its a symlink and client requested O_NOFOLLOW */
- LOG(log_debug, logtype_afpd, "getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
-
- memset(rbuf, 0, 4);
- *rbuflen += 4;
-
- return AFP_OK;
- }
- LOG(log_error, logtype_afpd, "getextattr_size: attropen error: %s", strerror(errno));
- return AFPERR_MISC;
- }
-
- if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
- LOG(log_error, logtype_afpd, "getextattr_size: fstatat error: %s", strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
- }
- attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
-
- /* Start building reply packet */
-
- LOG(log_debug7, logtype_afpd, "getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
-
- /* length of attribute data */
- attrsize = htonl(attrsize);
- memcpy(rbuf, &attrsize, 4);
- *rbuflen += 4;
-
- ret = AFP_OK;
-
-exit:
- close(attrdirfd);
- return ret;
-}
-
-static int getextattr_content(char *rbuf, int *rbuflen, char *uname, int oflag, char *attruname, int maxreply)
-{
- int ret, attrdirfd;
- size_t toread, okread = 0, len;
- char *datalength;
- struct stat st;
-
- if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
- if (errno == ELOOP) {
- /* its a symlink and client requested O_NOFOLLOW */
- LOG(log_debug, logtype_afpd, "getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
-
- memset(rbuf, 0, 4);
- *rbuflen += 4;
-
- return AFP_OK;
- }
- LOG(log_error, logtype_afpd, "getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
- return AFPERR_MISC;
- }
-
- if ( -1 == (fstat(attrdirfd, &st))) {
- LOG(log_error, logtype_afpd, "getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
- }
-
- /* Start building reply packet */
-
- maxreply -= MAX_REPLY_EXTRA_BYTES;
- if (maxreply > MAX_EA_SIZE)
- maxreply = MAX_EA_SIZE;
-
- /* But never send more than the client requested */
- toread = (maxreply < st.st_size) ? maxreply : st.st_size;
-
- LOG(log_debug7, logtype_afpd, "getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
-
- /* remember where we must store length of attribute data in rbuf */
- datalength = rbuf;
- rbuf += 4;
- *rbuflen += 4;
-
- while (1) {
- len = read(attrdirfd, rbuf, toread);
- if (len == -1) {
- LOG(log_error, logtype_afpd, "getextattr_content(%s): read error: %s", attruname, strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
- }
- okread += len;
- rbuf += len;
- *rbuflen += len;
- if ((len == 0) || (okread == toread))
- break;
- }
-
- okread = htonl((uint32_t)okread);
- memcpy(datalength, &okread, 4);
-
- ret = AFP_OK;
-
-exit:
- close(attrdirfd);
- return ret;
-}
-
-int list_extattr(AFPObj *obj, char *attrnamebuf, int *buflen, char *uname, int oflag)
-{
- int ret, attrbuflen = *buflen, len, attrdirfd = 0;
- struct dirent *dp;
- DIR *dirp = NULL;
-
- /* Now list file attribute dir */
- if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
- if (errno == ELOOP) {
- /* its a symlink and client requested O_NOFOLLOW */
- ret = AFPERR_BADTYPE;
- goto exit;
- }
- LOG(log_error, logtype_afpd, "list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
- }
-
- if (NULL == (dirp = fdopendir(attrdirfd))) {
- LOG(log_error, logtype_afpd, "list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
- }
-
- while ((dp = readdir(dirp))) {
- /* check if its "." or ".." */
- if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
- (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
- continue;
-
- len = strlen(dp->d_name);
-
- if ( 0 >= ( len = convert_string(obj->options.unixcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
- ret = AFPERR_MISC;
- goto exit;
- }
- if (len == 255)
- /* convert_string didn't 0-terminate */
- attrnamebuf[attrbuflen + 255] = 0;
-
- LOG(log_debug7, logtype_afpd, "list_extattr(%s): attribute: %s", uname, dp->d_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_extattr(%s): running out of buffer for EA names", uname);
- ret = AFPERR_MISC;
- goto exit;
- }
- }
-
- ret = AFP_OK;
-
-exit:
- if (dirp)
- closedir(dirp);
-
- if (attrdirfd > 0)
- close(attrdirfd);
-
- *buflen = attrbuflen;
- return ret;
-}
-
/***************************************
- * Interface
+ * AFP funcs
****************************************/
/*
{
int count, ret, oflag = 0;
uint16_t vid, bitmap;
- uint32_t did, maxreply;
+ uint32_t did, maxreply, tmpattr;
struct vol *vol;
struct dir *dir;
struct path *s_path;
char *uname, *FinderInfo;
static int buf_valid = 0, attrbuflen = 0;
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "afp_listextattr: BEGIN");
-#endif
-
*rbuflen = 0;
ibuf += 2;
bitmap = ntohs( bitmap );
ibuf += 2;
+#ifdef HAVE_SOLARIS_EAS
if (bitmap & kXAttrNoFollow)
oflag = O_NOFOLLOW;
-
+#endif
/* Skip ReqCount, StartIndex and maxreply*/
ibuf += 10;
attrbuflen += strlen(ea_resourcefork) + 1;
}
- ret = list_extattr(obj, attrnamebuf, &attrbuflen, uname, oflag);
+ ret = vol->vfs->list_eas(vol, attrnamebuf, &attrbuflen, uname, oflag);
+
switch (ret) {
case AFPERR_BADTYPE:
/* its a symlink and client requested O_NOFOLLOW */
rbuf += 2;
*rbuflen += 2;
- attrbuflen = htonl(attrbuflen);
- memcpy( rbuf, &attrbuflen, 4);
+ tmpattr = htonl(attrbuflen);
+ memcpy( rbuf, &tmpattr, 4);
rbuf += 4;
*rbuflen += 4;
if (adp)
ad_close_metadata( adp);
- LOG(log_debug9, logtype_afpd, "afp_listextattr: END");
return ret;
}
struct path *s_path;
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "afp_getextattr: BEGIN");
-#endif
-
*rbuflen = 0;
ibuf += 2;
memcpy( &bitmap, ibuf, 2);
bitmap = ntohs( bitmap );
ibuf += 2;
+
+#ifdef HAVE_SOLARIS_EAS
if (bitmap & kXAttrNoFollow)
- oflag = AT_SYMLINK_NOFOLLOW;
+ oflag = O_NOFOLLOW;
+#endif
/* Skip Offset and ReqCount */
ibuf += 16;
if its non 0 we must return the attribute.
*/
if (maxreply == 0)
- ret = getextattr_size(rbuf, rbuflen, s_path->u_name, oflag, attruname);
+ ret = vol->vfs->get_easize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname);
else
- ret = getextattr_content(rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
-
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "afp_getextattr: END");
-#endif
+ ret = vol->vfs->get_eacontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
return ret;
}
int afp_setextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
{
- int len, oflag = O_CREAT | O_WRONLY, attrdirfd;
+ int oflag = O_CREAT | O_WRONLY, ret;
uint16_t vid, bitmap;
uint32_t did, attrnamelen, attrsize;
char attrmname[256], attruname[256];
struct dir *dir;
struct path *s_path;
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "afp_setextattr: BEGIN");
-#endif
-
*rbuflen = 0;
ibuf += 2;
memcpy( &bitmap, ibuf, 2);
bitmap = ntohs( bitmap );
ibuf += 2;
+
+#ifdef HAVE_SOLARIS_EAS
if (bitmap & kXAttrNoFollow)
oflag |= AT_SYMLINK_NOFOLLOW;
+#endif
+
if (bitmap & kXAttrCreate)
oflag |= O_EXCL;
else if (bitmap & kXAttrReplace)
LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, attrmname, attrsize);
- if ( -1 == (attrdirfd = attropen(s_path->u_name, attruname, oflag, 0666))) {
- if (errno == ELOOP) {
- /* its a symlink and client requested O_NOFOLLOW */
- LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
- return AFP_OK;
- }
- LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
- return AFPERR_MISC;
- }
-
- while (1) {
- len = write(attrdirfd, ibuf, attrsize);
- if (len == -1) {
- LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
- return AFPERR_MISC;
- }
- attrsize -= len;
- ibuf += len;
- if (attrsize == 0)
- break;
- }
-
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "afp_setextattr: END");
-#endif
+ ret = vol->vfs->set_ea(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
- return AFP_OK;
+ return ret;
}
int afp_remextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
{
- int oflag = O_RDONLY, attrdirfd;
+ int oflag = O_RDONLY, ret;
uint16_t vid, bitmap;
uint32_t did, attrnamelen;
char attrmname[256], attruname[256];
struct dir *dir;
struct path *s_path;
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "afp_remextattr: BEGIN");
-#endif
-
*rbuflen = 0;
ibuf += 2;
memcpy( &bitmap, ibuf, 2);
bitmap = ntohs( bitmap );
ibuf += 2;
+
+#ifdef HAVE_SOLARIS_EAS
if (bitmap & kXAttrNoFollow)
oflag |= AT_SYMLINK_NOFOLLOW;
+#endif
/* get name */
if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, attrmname);
- if ( -1 == (attrdirfd = attropen(s_path->u_name, ".", oflag))) {
- switch (errno) {
- case ELOOP:
- /* its a symlink and client requested O_NOFOLLOW */
- LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
- return AFP_OK;
- case EACCES:
- LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
- return AFPERR_ACCESS;
- default:
- LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
- return AFPERR_MISC;
- }
- }
-
- if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
- if (errno == EACCES) {
- LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
- return AFPERR_ACCESS;
- }
- LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
- return AFPERR_MISC;
- }
-
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "afp_remextattr: END");
-#endif
+ ret = vol->vfs->remove_ea(vol, s_path->u_name, attruname, oflag);
- return AFP_OK;
+ return ret;
}
-
-
-#endif /* HAVE_EXT_ATTRS */
-
/*
- $Id: extattrs.h,v 1.1 2009-02-16 13:49:20 franklahm Exp $
+ $Id: extattrs.h,v 1.2 2009-10-02 09:32:40 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#ifndef AFPD_EXT_ATTRS_H
#define AFPD_EXT_ATTRS_H
-/* This seems to be the current limit fo HFS+, we arbitrarily force that
- which also safes us from buffer overflows */
-#define MAX_EA_SIZE 3802
-
-/* At time of writing the 10.5.6 client adds 8 bytes to the
- length of the EA that we send him */
-#define MAX_REPLY_EXTRA_BYTES 8
-
-enum {
- kXAttrNoFollow = 0x1,
- kXAttrCreate = 0x2,
- kXAttrReplace = 0x4
-};
-
+/* AFP funcs */
extern int afp_listextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen);
extern int afp_getextattr(AFPObj *obj _U_ , char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen);
extern int afp_setextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen);
/*
- * $Id: file.c,v 1.110 2009-09-21 12:35:05 franklahm Exp $
+ * $Id: file.c,v 1.111 2009-10-02 09:32:40 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#endif /* ! HAVE_MEMCPY */
#endif /* STDC_HEADERS */
-#include <atalk/adouble.h>
#include <utime.h>
#include <dirent.h>
#include <errno.h>
-
-#include <atalk/logger.h>
#include <sys/param.h>
-
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/logger.h>
#include <atalk/afp.h>
#include <atalk/util.h>
#include <atalk/cnid.h>
/*
- * $Id: filedir.c,v 1.54 2009-09-01 13:15:13 franklahm Exp $
+ * $Id: filedir.c,v 1.55 2009-10-02 09:32:40 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include <dirent.h>
#include <errno.h>
#include <sys/param.h>
-#include <atalk/adouble.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
#include <atalk/afp.h>
#include <atalk/util.h>
#include <atalk/cnid.h>
if (!isdir && !vol_unix_priv(vol)) {
int admode = ad_mode("", 0777) | vol->v_fperm;
- setfilmode(upath, admode, NULL);
+ setfilmode(upath, admode, NULL, vol->v_umask);
vol->vfs->rf_setfilmode(vol, upath, admode, NULL);
}
setvoltime(obj, vol );
* into proprietary software; there is no requirement for such software to
* contain a copyright notice related to this source.
*
- * $Id: hash.h,v 1.1 2005-04-30 21:33:41 didg Exp $
+ * $Id: hash.h,v 1.2 2009-10-02 09:32:40 franklahm Exp $
* $Name: $
*/
#define HASH_H
#include <limits.h>
-#ifdef KAZLIB_SIDEEFFECT_DEBUG
-#include "sfx.h"
-#endif
-
-/*
- * Blurb for inclusion into C++ translation units
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef unsigned long hashcount_t;
-#define HASHCOUNT_T_MAX ULONG_MAX
-
-typedef unsigned long hash_val_t;
-#define HASH_VAL_T_MAX ULONG_MAX
-
-extern int hash_val_t_bit;
-
-#ifndef HASH_VAL_T_BIT
-#define HASH_VAL_T_BIT ((int) hash_val_t_bit)
-#endif
-
-/*
- * Hash chain node structure.
- * Notes:
- * 1. This preprocessing directive is for debugging purposes. The effect is
- * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the
- * inclusion of this header, then the structure shall be declared as having
- * the single member int __OPAQUE__. This way, any attempts by the
- * client code to violate the principles of information hiding (by accessing
- * the structure directly) can be diagnosed at translation time. However,
- * note the resulting compiled unit is not suitable for linking.
- * 2. This is a pointer to the next node in the chain. In the last node of a
- * chain, this pointer is null.
- * 3. The key is a pointer to some user supplied data that contains a unique
- * identifier for each hash node in a given table. The interpretation of
- * the data is up to the user. When creating or initializing a hash table,
- * the user must supply a pointer to a function for comparing two keys,
- * and a pointer to a function for hashing a key into a numeric value.
- * 4. The value is a user-supplied pointer to void which may refer to
- * any data object. It is not interpreted in any way by the hashing
- * module.
- * 5. The hashed key is stored in each node so that we don't have to rehash
- * each key when the table must grow or shrink.
- */
-
-typedef struct hnode_t {
- #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */
- struct hnode_t *hash_next; /* 2 */
- const void *hash_key; /* 3 */
- void *hash_data; /* 4 */
- hash_val_t hash_hkey; /* 5 */
- #else
- int hash_dummy;
- #endif
-} hnode_t;
-
-/*
- * The comparison function pointer type. A comparison function takes two keys
- * and produces a value of -1 if the left key is less than the right key, a
- * value of 0 if the keys are equal, and a value of 1 if the left key is
- * greater than the right key.
- */
-
-typedef int (*hash_comp_t)(const void *, const void *);
-
-/*
- * The hashing function performs some computation on a key and produces an
- * integral value of type hash_val_t based on that key. For best results, the
- * function should have a good randomness properties in *all* significant bits
- * over the set of keys that are being inserted into a given hash table. In
- * particular, the most significant bits of hash_val_t are most significant to
- * the hash module. Only as the hash table expands are less significant bits
- * examined. Thus a function that has good distribution in its upper bits but
- * not lower is preferrable to one that has poor distribution in the upper bits
- * but not the lower ones.
- */
-
-typedef hash_val_t (*hash_fun_t)(const void *);
-
-/*
- * allocator functions
- */
-
-typedef hnode_t *(*hnode_alloc_t)(void *);
-typedef void (*hnode_free_t)(hnode_t *, void *);
-
-/*
- * This is the hash table control structure. It keeps track of information
- * about a hash table, as well as the hash table itself.
- * Notes:
- * 1. Pointer to the hash table proper. The table is an array of pointers to
- * hash nodes (of type hnode_t). If the table is empty, every element of
- * this table is a null pointer. A non-null entry points to the first
- * element of a chain of nodes.
- * 2. This member keeps track of the size of the hash table---that is, the
- * number of chain pointers.
- * 3. The count member maintains the number of elements that are presently
- * in the hash table.
- * 4. The maximum count is the greatest number of nodes that can populate this
- * table. If the table contains this many nodes, no more can be inserted,
- * and the hash_isfull() function returns true.
- * 5. The high mark is a population threshold, measured as a number of nodes,
- * which, if exceeded, will trigger a table expansion. Only dynamic hash
- * tables are subject to this expansion.
- * 6. The low mark is a minimum population threshold, measured as a number of
- * nodes. If the table population drops below this value, a table shrinkage
- * will occur. Only dynamic tables are subject to this reduction. No table
- * will shrink beneath a certain absolute minimum number of nodes.
- * 7. This is the a pointer to the hash table's comparison function. The
- * function is set once at initialization or creation time.
- * 8. Pointer to the table's hashing function, set once at creation or
- * initialization time.
- * 9. The current hash table mask. If the size of the hash table is 2^N,
- * this value has its low N bits set to 1, and the others clear. It is used
- * to select bits from the result of the hashing function to compute an
- * index into the table.
- * 10. A flag which indicates whether the table is to be dynamically resized. It
- * is set to 1 in dynamically allocated tables, 0 in tables that are
- * statically allocated.
- */
-
-typedef struct hash_t {
- #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- struct hnode_t **hash_table; /* 1 */
- hashcount_t hash_nchains; /* 2 */
- hashcount_t hash_nodecount; /* 3 */
- hashcount_t hash_maxcount; /* 4 */
- hashcount_t hash_highmark; /* 5 */
- hashcount_t hash_lowmark; /* 6 */
- hash_comp_t hash_compare; /* 7 */
- hash_fun_t hash_function; /* 8 */
- hnode_alloc_t hash_allocnode;
- hnode_free_t hash_freenode;
- void *hash_context;
- hash_val_t hash_mask; /* 9 */
- int hash_dynamic; /* 10 */
- #else
- int hash_dummy;
- #endif
-} hash_t;
-
-/*
- * Hash scanner structure, used for traversals of the data structure.
- * Notes:
- * 1. Pointer to the hash table that is being traversed.
- * 2. Reference to the current chain in the table being traversed (the chain
- * that contains the next node that shall be retrieved).
- * 3. Pointer to the node that will be retrieved by the subsequent call to
- * hash_scan_next().
- */
-
-typedef struct hscan_t {
- #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
- hash_t *hash_table; /* 1 */
- hash_val_t hash_chain; /* 2 */
- hnode_t *hash_next; /* 3 */
- #else
- int hash_dummy;
- #endif
-} hscan_t;
+#include <atalk/hash.h>
extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);
extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *);
#define hnode_put(N, V) ((N)->hash_data = (V))
#endif
-#ifdef __cplusplus
-}
-#endif
-
#endif
/*
- * $Id: unix.c,v 1.52 2009-02-02 11:55:01 franklahm Exp $
+ * $Id: unix.c,v 1.53 2009-10-02 09:32:40 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include <sys/stat.h>
#include <atalk/logger.h>
#include <atalk/adouble.h>
+#include <atalk/vfs.h>
#include <atalk/afp.h>
#include <atalk/util.h>
return( mode );
}
-/* ----------------------------- */
-char *fullpathname(const char *name)
-{
- static char wd[ MAXPATHLEN + 1];
-
- if ( getcwd( wd , MAXPATHLEN) ) {
- strlcat(wd, "/", MAXPATHLEN);
- strlcat(wd, name, MAXPATHLEN);
- }
- else {
- strlcpy(wd, name, MAXPATHLEN);
- }
- return wd;
-}
-
-/* -----------------------------
- a dropbox is a folder where w is set but not r eg:
- rwx-wx-wx or rwx-wx--
- rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
-*/
-int stickydirmode(name, mode, dropbox)
-const char * name;
-const mode_t mode;
-const int dropbox;
-{
- int retval = 0;
-
-#ifdef DROPKLUDGE
- /* Turn on the sticky bit if this is a drop box, also turn off the setgid bit */
- if ((dropbox & AFPVOL_DROPBOX)) {
- int uid;
-
- if ( ( (mode & S_IWOTH) && !(mode & S_IROTH)) ||
- ( (mode & S_IWGRP) && !(mode & S_IRGRP)) )
- {
- uid=geteuid();
- if ( seteuid(0) < 0) {
- LOG(log_error, logtype_afpd, "stickydirmode: unable to seteuid root: %s", strerror(errno));
- }
- if ( (retval=chmod( name, ( (DIRBITS | mode | S_ISVTX) & ~default_options.umask) )) < 0) {
- LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
- } else {
-#ifdef DEBUG
- LOG(log_info, logtype_afpd, "stickydirmode: (debug) chmod \"%s\": %s", fullpathname(name), strerror(retval) );
-#endif /* DEBUG */
- }
- seteuid(uid);
- return retval;
- }
- }
-#endif /* DROPKLUDGE */
-
- /*
- * Ignore EPERM errors: We may be dealing with a directory that is
- * group writable, in which case chmod will fail.
- */
- if ( (chmod( name, (DIRBITS | mode) & ~default_options.umask ) < 0) && errno != EPERM &&
- !(errno == ENOENT && (dropbox & AFPVOL_NOADOUBLE)) )
- {
- LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
- retval = -1;
- }
-
- return retval;
-}
-
-/* ------------------------- */
-int dir_rx_set(mode_t mode)
-{
- return (mode & (S_IXUSR | S_IRUSR)) == (S_IXUSR | S_IRUSR);
-}
-
#define EXEC_MODE (S_IXGRP | S_IXUSR | S_IXOTH)
int setdeskmode( mode )
mode |= vol->v_fperm;
- if (setfilmode( path->u_name, mode, &path->st) < 0)
+ if (setfilmode( path->u_name, mode, &path->st, vol->v_umask) < 0)
return -1;
/* we need to set write perm if read set for resource fork */
return vol->vfs->rf_setfilmode(vol, path->u_name, mode, &path->st);
}
-/* --------------------- */
-int setfilmode(name, mode, st)
-const char * name;
-mode_t mode;
-struct stat *st;
-{
-struct stat sb;
-mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO; /* rwx for owner group and other, by default */
-
- if (!st) {
- if (stat(name, &sb) != 0)
- return -1;
- st = &sb;
- }
-
- mode |= st->st_mode & ~mask; /* keep other bits from previous mode */
- if ( chmod( name, mode & ~default_options.umask ) < 0 && errno != EPERM ) {
- return -1;
- }
- return 0;
-}
/* --------------------- */
int setdirunixmode( vol, name, mode )
if (dir_rx_set(mode)) {
/* extending right? dir first then .AppleDouble in rf_setdirmode */
- if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
+ if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 )
return -1;
}
if (vol->vfs->rf_setdirunixmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
return -1 ;
}
if (!dir_rx_set(mode)) {
- if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
+ if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 )
return -1;
}
return 0;
if (dir_rx_set(mode)) {
/* extending right? dir first */
- if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
+ if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 )
return -1;
}
if (!S_ISDIR(st.st_mode)) {
int setmode = (osx && *dirp->d_name == '.')?hf_mode:mode;
- if (setfilmode(dirp->d_name, setmode, &st) < 0) {
+ if (setfilmode(dirp->d_name, setmode, &st, vol->v_umask) < 0) {
LOG(log_error, logtype_afpd, "setdirmode: chmod %s: %s",dirp->d_name, strerror(errno) );
return -1;
}
}
if (!dir_rx_set(mode)) {
- if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
+ if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 )
return -1;
}
return( 0 );
/*
- * $Id: unix.h,v 1.20 2005-06-02 12:32:18 didg Exp $
+ * $Id: unix.h,v 1.21 2009-10-02 09:32:40 franklahm Exp $
*/
#ifndef AFPD_UNIX_H
extern int setdirmode __P((const struct vol *, const char *, mode_t));
extern int setdeskowner __P((const uid_t, const gid_t));
extern int setdirowner __P((const struct vol *, const char *, const uid_t, const gid_t));
-extern int setfilmode __P((const char *, mode_t , struct stat *));
extern int setfilunixmode __P((const struct vol *, struct path*, const mode_t));
extern int setfilowner __P((const struct vol *, const uid_t, const gid_t, struct path*));
-extern int unix_rename __P((const char *oldpath, const char *newpath));
-extern int dir_rx_set __P((mode_t mode));
-extern int stickydirmode __P((const char * name, const mode_t mode, const int dropbox));
-
extern void accessmode __P((char *, struct maccess *, struct dir *, struct stat *));
-extern char *fullpathname __P((const char *));
#ifdef AFS
#define accessmode afsmode
/*
- * $Id: volume.c,v 1.90 2009-09-11 09:14:16 franklahm Exp $
+ * $Id: volume.c,v 1.91 2009-10-02 09:32:40 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#define VOLOPT_DPERM 24 /* dperm default directories perms */
#define VOLOPT_FPERM 25 /* fperm default files perms */
#define VOLOPT_DFLTPERM 26 /* perm */
+#define VOLOPT_EA_VFS 27 /* Extended Attributes vfs indirection */
#define VOLOPT_MAX (VOLOPT_DFLTPERM +1)
#define VOLPASSLEN 8
#define VOLOPT_DEFAULT ":DEFAULT:"
#define VOLOPT_DEFAULT_LEN 9
- struct vol_option {
- char *c_value;
- int i_value;
- };
+
+struct vol_option {
+ char *c_value;
+ int i_value;
+};
typedef struct _special_folder {
const char *name;
{AFPVOL_CASEINSEN, "CASEINSENSITIVE"}, /* volume is case insensitive */
{AFPVOL_EILSEQ, "ILLEGALSEQ"}, /* encode illegal sequence */
{AFPVOL_CACHE, "CACHEID"}, /* Use adouble v2 CNID caching. Default: yes */
- {AFPVOL_EXT_ATTRS, "EXT_ATTRS"}, /* Vol supports Extened Attributes */
{AFPVOL_ACLS, "ACLS"}, /* Vol supports ACLs */
{AFPVOL_TM, "TM"}, /* Set "kSupportsTMLockSteal" is volume attributes */
{0, NULL}
options[VOLOPT_ROOTPREEXEC].i_value = 1;
else if (strcasecmp(p, "upriv") == 0)
options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV;
- else if (strcasecmp(p, "extattrs") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_EXT_ATTRS;
else if (strcasecmp(p, "acls") == 0)
options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
else if (strcasecmp(p, "nodev") == 0)
} else if (optionok(tmp, "denied_hosts:", val)) {
setoption(options, save, VOLOPT_DENIED_HOSTS, val);
+ } else if (optionok(tmp, "ea:", val)) {
+ if (strcasecmp(val + 1, "ad") == 0) /* the default anyway */
+ options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AD;
+ else if (strcasecmp(val + 1, "solaris") == 0)
+ options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_SOLARIS;
+
} else {
/* ignore unknown options */
LOG(log_debug, logtype_afpd, "ignoring unknown volume option: %s", tmp);
/* os X start at 1 and use network order ie. 1 2 3 */
volume->v_vid = ++lastvid;
volume->v_vid = htons(volume->v_vid);
+ volume->v_vfs_ea = AFPVOL_EA_AD;
/* handle options */
if (options) {
/* shift in some flags */
volume->v_flags = options[VOLOPT_FLAGS].i_value;
+
+ if (options[VOLOPT_EA_VFS].i_value != AFPVOL_EA_AD)
+ volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
volume->v_ad_options = 0;
if ((volume->v_flags & AFPVOL_NODEV))
ashort |= VOLPBIT_ATTR_RO;
}
ashort |= VOLPBIT_ATTR_CATSEARCH;
+ ashort |= VOLPBIT_ATTR_EXT_ATTRS;
if (afp_version >= 30) {
ashort |= VOLPBIT_ATTR_UTF8;
if (vol->v_flags & AFPVOL_UNIX_PRIV)
ashort |= VOLPBIT_ATTR_TM;
}
if (afp_version >= 32) {
- if (vol->v_flags & AFPVOL_EXT_ATTRS)
- ashort |= VOLPBIT_ATTR_EXT_ATTRS;
if (vol->v_flags & AFPVOL_ACLS)
- ashort |= VOLPBIT_ATTR_ACLS;
+ ashort |= VOLPBIT_ATTR_ACLS;
}
ashort = htons(ashort);
memcpy(data, &ashort, sizeof( ashort ));
/*
- * $Id: volume.h,v 1.33 2009-09-11 09:14:16 franklahm Exp $
+ * $Id: volume.h,v 1.34 2009-10-02 09:32:40 franklahm Exp $
*
* Copyright (c) 1990,1994 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include <sys/types.h>
#include <netatalk/endian.h>
-#include "atalk/unicode.h"
-#include "globals.h"
-#include "afp_vfs.h"
-#include "hash.h"
-
-#define AFPVOL_U8MNAMELEN 255 /* AFP3 sepc */
-#define AFPVOL_MACNAMELEN 27 /* AFP2 spec */
-
+#include <atalk/volume.h>
#include <atalk/cnid.h>
+#include <atalk/unicode.h>
-struct vol {
- struct vol *v_next;
- char *v_localname; /* as defined in AppleVolumes.default */
- ucs2_t *v_u8mname; /* converted to utf8-mac in ucs2 */
- ucs2_t *v_macname; /* mangled to legacy longname in ucs2 */
- ucs2_t *v_name; /* either v_u8mname or v_macname */
- char *v_path;
-
- struct dir *v_dir, *v_root;
- hash_t *v_hash;
- int v_flags;
-#ifdef __svr4__
- int v_qfd;
-#endif /*__svr4__*/
- char *v_gvs;
- time_t v_mtime;
- time_t v_ctime; /* volume creation date, not unix ctime */
-
- u_int16_t v_vid;
- void *v_nfsclient;
- int v_nfs;
-
- int v_casefold;
- size_t max_filename;
-
- char *v_password;
- char *v_veto;
-
- char *v_cnidscheme;
- char *v_dbpath;
- dev_t v_dev; /* Unix volume device */
- struct _cnid_db *v_cdb;
- char v_stamp[ADEDLEN_PRIVSYN];
- mode_t v_umask;
- mode_t v_perm; /* default permission value OR with requested perm*/
- mode_t v_dperm; /* default directories permission value OR with requested perm*/
- mode_t v_fperm; /* default files permission value OR with requested perm*/
-
-#ifdef FORCE_UIDGID
- char *v_forceuid;
- char *v_forcegid;
-#endif
-
- char *v_volcodepage;
- charset_t v_volcharset;
- struct charset_functions *v_vol;
- char *v_maccodepage;
- charset_t v_maccharset;
- struct charset_functions *v_mac;
-
- int v_deleted; /* volume open but deleted in new config file */
- int v_hide; /* new volume wait until old volume is closed */
- int v_new; /* volume deleted but there's a new one with the same name */
- int v_adouble; /* default adouble format */
- int v_ad_options; /* adouble option NODEV, NOCACHE, etc.. */
-
- char *v_root_preexec;
- char *v_preexec;
-
- char *v_root_postexec;
- char *v_postexec;
-
- int v_root_preexec_close;
- int v_preexec_close;
-
- /* adouble indirection */
- struct vfs_ops *vfs;
- int (*validupath)(const struct vol *, const char *);
- char *(*ad_path)(const char *, int);
-};
-
-#ifdef NO_LARGE_VOL_SUPPORT
-typedef u_int32_t VolSpace;
-#else /* NO_LARGE_VOL_SUPPORT */
-typedef u_int64_t VolSpace;
-#endif /* NO_LARGE_VOL_SUPPORT */
-
-#define AFPVOL_OPEN (1<<0)
-
-/* flags for AFS and quota 0xxx0 */
-#define AFPVOL_GVSMASK (7<<2)
-#define AFPVOL_NONE (0<<2)
-#define AFPVOL_AFSGVS (1<<2)
-#define AFPVOL_USTATFS (2<<2)
-#define AFPVOL_UQUOTA (4<<2)
-
-/*
- Flags that alter volume behaviour.
- Keep in sync with include/atalk/volinfo.h and libatalk/util/volinfo.c
-*/
-#define AFPVOL_A2VOL (1 << 5) /* prodos volume */
-#define AFPVOL_CRLF (1 << 6) /* cr/lf translation */
-#define AFPVOL_NOADOUBLE (1 << 7) /* don't create .AppleDouble by default */
-#define AFPVOL_RO (1 << 8) /* read-only volume */
-#define AFPVOL_MSWINDOWS (1 << 9) /* deal with ms-windows yuckiness.
-this is going away. */
-#define AFPVOL_NOHEX (1 << 10) /* don't do :hex translation */
-#define AFPVOL_USEDOTS (1 << 11) /* use real dots */
-#define AFPVOL_LIMITSIZE (1 << 12) /* limit size for older macs */
-#define AFPVOL_MAPASCII (1 << 13) /* map the ascii range as well */
-#define AFPVOL_DROPBOX (1 << 14) /* dropkludge dropbox support */
-#define AFPVOL_NOFILEID (1 << 15) /* don't advertise createid resolveid and deleteid calls */
-#define AFPVOL_NOSTAT (1 << 16) /* advertise the volume even if we can't stat() it
- * maybe because it will be mounted later in preexec */
-#define AFPVOL_UNIX_PRIV (1 << 17) /* support unix privileges */
-#define AFPVOL_NODEV (1 << 18) /* always use 0 for device number in cnid calls
- * help if device number is notconsistent across reboot
- * NOTE symlink to a different device will return an ACCESS error
- */
-#define AFPVOL_CASEINSEN (1 << 19) /* volume is case insensitive */
-#define AFPVOL_EILSEQ (1 << 20) /* encode illegal sequence 'asis' UCS2, ex "\217-", which is not
- a valid SHIFT-JIS char, is encoded as U\217 -*/
-
-#define AFPVOL_CACHE (1 << 21) /* Use adouble v2 CNID caching. Default: yes */
-#define AFPVOL_INV_DOTS (1 << 22) /* dots files are invisible */
-#define AFPVOL_EXT_ATTRS (1 << 23) /* Volume supports Extended Attributes */
-#define AFPVOL_TM (1 << 24) /* Supports TimeMachine */
-#define AFPVOL_ACLS (1 << 25) /* Volume supports ACLS */
-
-/* FPGetSrvrParms options */
-#define AFPSRVR_CONFIGINFO (1 << 0)
-#define AFPSRVR_PASSWD (1 << 7)
-
-/* handle casefolding */
-#define AFPVOL_MTOUUPPER (1 << 0)
-#define AFPVOL_MTOULOWER (1 << 1)
-#define AFPVOL_UTOMUPPER (1 << 2)
-#define AFPVOL_UTOMLOWER (1 << 3)
-#define AFPVOL_UMLOWER (AFPVOL_MTOULOWER | AFPVOL_UTOMLOWER)
-#define AFPVOL_UMUPPER (AFPVOL_MTOUUPPER | AFPVOL_UTOMUPPER)
-#define AFPVOL_UUPPERMLOWER (AFPVOL_MTOUUPPER | AFPVOL_UTOMLOWER)
-#define AFPVOL_ULOWERMUPPER (AFPVOL_MTOULOWER | AFPVOL_UTOMUPPER)
-
-#define MSWINDOWS_BADCHARS ":\t\\/<>*?|\""
-
-int wincheck(const struct vol *vol, const char *path);
-
-#define AFPVOLSIG_FLAT 0x0001 /* flat fs */
-#define AFPVOLSIG_FIX 0x0002 /* fixed ids */
-#define AFPVOLSIG_VAR 0x0003 /* variable ids */
-#define AFPVOLSIG_DEFAULT AFPVOLSIG_FIX
-
-/* volume attributes */
-#define VOLPBIT_ATTR_RO (1 << 0)
-#define VOLPBIT_ATTR_PASSWD (1 << 1)
-#define VOLPBIT_ATTR_FILEID (1 << 2)
-#define VOLPBIT_ATTR_CATSEARCH (1 << 3)
-#define VOLPBIT_ATTR_BLANKACCESS (1 << 4)
-#define VOLPBIT_ATTR_UNIXPRIV (1 << 5)
-#define VOLPBIT_ATTR_UTF8 (1 << 6)
-#define VOLPBIT_ATTR_NONETUID (1 << 7)
-#define VOLPBIT_ATTR_EXT_ATTRS (1 << 10)
-#define VOLPBIT_ATTR_ACLS (1 << 11)
-#define VOLPBIT_ATTR_TM (1 << 13)
-
-#define VOLPBIT_ATTR 0
-#define VOLPBIT_SIG 1
-#define VOLPBIT_CDATE 2
-#define VOLPBIT_MDATE 3
-#define VOLPBIT_BDATE 4
-#define VOLPBIT_VID 5
-#define VOLPBIT_BFREE 6
-#define VOLPBIT_BTOTAL 7
-#define VOLPBIT_NAME 8
-/* handle > 4GB volumes */
-#define VOLPBIT_XBFREE 9
-#define VOLPBIT_XBTOTAL 10
-#define VOLPBIT_BSIZE 11 /* block size */
-
-
-#define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? \
- ADFLAGS_NOADOUBLE : 0)
-#ifdef AFP3x
-#define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
+#include "globals.h"
+#if 0
+#include "hash.h"
#endif
-#define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
-
-#define vol_unix_priv(vol) (afp_version >= 30 && ((vol)->v_flags & AFPVOL_UNIX_PRIV))
-
-#define vol_inv_dots(vol) (((vol)->v_flags & AFPVOL_INV_DOTS) ? 1 : 0)
-
extern struct vol *getvolbyvid __P((const u_int16_t));
extern int ustatfs_getvolspace __P((const struct vol *,
VolSpace *, VolSpace *,
atalkincludedir = $(includedir)/atalk
atalkinclude_HEADERS = \
- adouble.h aep.h afp.h asp.h atp.h boolean.h \
+ adouble.h vfs.h aep.h afp.h asp.h atp.h boolean.h \
cnid.h compat.h ddp.h dsi.h ldapconfig.h list.h logger.h \
nbp.h netddp.h pap.h paths.h rtmp.h server_child.h \
server_ipc.h tdb.h uam.h unicode.h util.h uuid.h volinfo.h \
- zip.h
+ zip.h ea.h
noinst_HEADERS = cnid_dbd_private.h
/*
- * $Id: adouble.h,v 1.42 2009-09-14 14:11:45 franklahm Exp $
+ * $Id: adouble.h,v 1.43 2009-10-02 09:32:40 franklahm Exp $
* Copyright (c) 1990,1991 Regents of The University of Michigan.
* All Rights Reserved.
*
#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 */
#endif /* HAVE_SENDFILE_WRITE */
#endif /* 0 */
+/* ad_unix.c */
+extern int netatalk_unlink(const char *name);
+extern char *fullpathname(const char *);
+extern int netatalk_rmdir(const char *name);
+extern int setfilmode(const char *, mode_t, struct stat *, mode_t);
+extern int dir_rx_set(mode_t mode);
+extern int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask);
+extern int unix_rename(const char *oldpath, const char *newpath);
+
#endif /* _ATALK_ADOUBLE_H */
/* version 3.1 */
#define AFP_ENUMERATE_EXT2 68
+#define AFP_SPOTLIGHT_PRIVATE 76
#define AFP_SYNCDIR 78
#define AFP_SYNCFORK 79
#define AFP_ZZZ 122
#define AFP_GETACL 73
#define AFP_SETACL 74
#define AFP_ACCESS 75
-#define AFP_SPOTLIGHT_PRIVATE 76
#endif
--- /dev/null
+/*
+ * $Id: directory.h,v 1.1 2009-10-02 09:32:40 franklahm Exp $
+ *
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ * Research Systems Unix Group
+ * The University of Michigan
+ * c/o Mike Clark
+ * 535 W. William Street
+ * Ann Arbor, Michigan
+ * +1-313-763-0525
+ * netatalk@itd.umich.edu
+ */
+
+#ifndef ATALK_DIRECTORY_H
+#define ATALK_DIRECTORY_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+#include <dirent.h>
+
+#include <atalk/cnid.h>
+#include <atalk/directory.h>
+
+/* setgid directories */
+#ifndef DIRBITS
+# ifdef AFS
+# define DIRBITS 0
+# else /* AFS */
+# define DIRBITS S_ISGID
+# endif /* AFS */
+#endif /* DIRBITS */
+
+/* the did tree is now a red-black tree while the parent/child
+ * tree is a circular doubly-linked list. how exciting. */
+struct dir {
+ struct dir *d_left, *d_right, *d_back; /* for red-black tree */
+ int d_color;
+ struct dir *d_parent, *d_child; /* parent-child */
+ struct dir *d_prev, *d_next; /* siblings */
+ void *d_ofork; /* oforks using this directory. */
+ u_int32_t d_did;
+ int d_flags;
+
+ time_t ctime; /* inode ctime */
+ u_int32_t offcnt; /* offspring count */
+
+ char *d_m_name; /* mac name */
+ char *d_u_name; /* unix name */
+ ucs2_t *d_m_name_ucs2; /* mac name as UCS2 */
+};
+
+struct path {
+ int m_type; /* mac name type (long name, unicode */
+ char *m_name; /* mac name */
+ char *u_name; /* unix name */
+ cnid_t id; /* file id (only for getmetadata) */
+ struct dir *d_dir; /* */
+ int st_valid; /* does st_errno and st set */
+ int st_errno;
+ struct stat st;
+};
+
+static inline int path_isadir(struct path *o_path)
+{
+ return o_path->d_dir != NULL;
+#if 0
+ return o_path->m_name == '\0' || /* we are in a it */
+ !o_path->st_valid || /* in cache but we can't chdir in it */
+ (!o_path->st_errno && S_ISDIR(o_path->st.st_mode)); /* not in cache an can't chdir */
+#endif
+}
+
+
+#endif /* ATALK_DIRECTORY_H */
--- /dev/null
+/*
+ $Id: ea.h,v 1.1 2009-10-02 09:32:40 franklahm Exp $
+ 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.
+*/
+
+#ifndef ATALK_EA_H
+#define ATALK_EA_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*
+ * This seems to be the current limit fo HFS+, we arbitrarily force that
+ * which also safes us from buffer overflows
+ */
+#define MAX_EA_SIZE 3802
+
+/*
+ * At time of writing the 10.5.6 client adds 8 bytes to the
+ * length of the EA that we send him
+*/
+#define MAX_REPLY_EXTRA_BYTES 8
+
+/*
+ * Library user must provide a static buffer of size ATTRNAMEBUFSIZ.
+ * It's used when listing EAs as intermediate buffer. For afpd it's
+ * defined in extattrs.c.
+ */
+#define ATTRNAMEBUFSIZ 4096
+
+enum {
+ kXAttrNoFollow = 0x1,
+ kXAttrCreate = 0x2,
+ kXAttrReplace = 0x4
+};
+
+
+#define EA_MAGIC 0x61644541 /* "adEA" */
+#define EA_VERSION1 0x01
+#define EA_VERSION EA_VERSION1
+
+typedef enum {
+ /* ea_open flags */
+ EA_CREATE = (1<<1), /* create if not existing on ea_open */
+ EA_RDONLY = (1<<2), /* open read only */
+ EA_RDWR = (1<<3), /* open read/write */
+ /* ea_open internal flags */
+ EA_DIR = (1<<4) /* ea header file is for a dir, ea_open adds it as appropiate */
+} eaflags_t;
+
+#define EA_MAGIC_OFF 0
+#define EA_MAGIC_LEN 4
+#define EA_VERSION_OFF (EA_MAGIC_OFF + EA_MAGIC_LEN)
+#define EA_VERSION_LEN 2
+#define EA_COUNT_OFF (EA_VERSION_OFF + EA_VERSION_LEN)
+#define EA_COUNT_LEN 2
+#define EA_HEADER_SIZE (EA_MAGIC_LEN + EA_VERSION_LEN + EA_COUNT_LEN)
+
+/*
+ * structs describing the layout of the Extended Attributes bookkeeping file.
+ * This isn't really an AppleDouble structure, it's just a binary blob that
+ * lives in our .AppleDouble directory too.
+ */
+
+struct ea_entry {
+ size_t ea_namelen; /* len of ea_name without terminating 0 ie. strlen(ea_name)*/
+ size_t ea_size; /* size of EA*/
+ char *ea_name; /* name of the EA */
+};
+
+/* We read the on-disk data into *ea_data and parse it into this*/
+struct ea {
+ const struct vol *vol; /* vol handle, ea_close needs it */
+ char *filename; /* name of file, needed by ea_close too */
+ unsigned int ea_count; /* number of EAs in ea_entries array */
+ struct ea_entry (*ea_entries)[]; /* malloced and realloced as needed by ea_count*/
+ int ea_fd; /* open fd for ea_data */
+ eaflags_t ea_flags; /* flags */
+ size_t ea_size; /* size of header file = size of ea_data buffer */
+ char *ea_data; /* pointer to buffer into that we actually *
+ * read the disc file into */
+};
+
+/* On-disk format, just for reference ! */
+#if 0
+struct ea_entry_ondisk {
+ uint32_t ea_size;
+ char ea_name[]; /* zero terminated string */
+};
+
+struct ea_ondisk {
+ u_int32_t ea_magic;
+ u_int16_t ea_version;
+ u_int16_t ea_count;
+ struct ea_entry_ondisk ea_entries[ea_count];
+};
+#endif /* 0 */
+
+#endif /* ATALK_EA_H */
--- /dev/null
+/*
+ * Hash Table Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: hash.h,v 1.1 2009-10-02 09:32:40 franklahm Exp $
+ * $Name: $
+ */
+
+#ifndef ATALK_HASH_H
+#define ATALK_HASH_H
+
+#include <limits.h>
+
+typedef unsigned long hashcount_t;
+#define HASHCOUNT_T_MAX ULONG_MAX
+
+typedef unsigned long hash_val_t;
+#define HASH_VAL_T_MAX ULONG_MAX
+
+extern int hash_val_t_bit;
+
+#ifndef HASH_VAL_T_BIT
+#define HASH_VAL_T_BIT ((int) hash_val_t_bit)
+#endif
+
+/*
+ * Hash chain node structure.
+ * Notes:
+ * 1. This preprocessing directive is for debugging purposes. The effect is
+ * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the
+ * inclusion of this header, then the structure shall be declared as having
+ * the single member int __OPAQUE__. This way, any attempts by the
+ * client code to violate the principles of information hiding (by accessing
+ * the structure directly) can be diagnosed at translation time. However,
+ * note the resulting compiled unit is not suitable for linking.
+ * 2. This is a pointer to the next node in the chain. In the last node of a
+ * chain, this pointer is null.
+ * 3. The key is a pointer to some user supplied data that contains a unique
+ * identifier for each hash node in a given table. The interpretation of
+ * the data is up to the user. When creating or initializing a hash table,
+ * the user must supply a pointer to a function for comparing two keys,
+ * and a pointer to a function for hashing a key into a numeric value.
+ * 4. The value is a user-supplied pointer to void which may refer to
+ * any data object. It is not interpreted in any way by the hashing
+ * module.
+ * 5. The hashed key is stored in each node so that we don't have to rehash
+ * each key when the table must grow or shrink.
+ */
+
+typedef struct hnode_t {
+ #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */
+ struct hnode_t *hash_next; /* 2 */
+ const void *hash_key; /* 3 */
+ void *hash_data; /* 4 */
+ hash_val_t hash_hkey; /* 5 */
+ #else
+ int hash_dummy;
+ #endif
+} hnode_t;
+
+/*
+ * The comparison function pointer type. A comparison function takes two keys
+ * and produces a value of -1 if the left key is less than the right key, a
+ * value of 0 if the keys are equal, and a value of 1 if the left key is
+ * greater than the right key.
+ */
+
+typedef int (*hash_comp_t)(const void *, const void *);
+
+/*
+ * The hashing function performs some computation on a key and produces an
+ * integral value of type hash_val_t based on that key. For best results, the
+ * function should have a good randomness properties in *all* significant bits
+ * over the set of keys that are being inserted into a given hash table. In
+ * particular, the most significant bits of hash_val_t are most significant to
+ * the hash module. Only as the hash table expands are less significant bits
+ * examined. Thus a function that has good distribution in its upper bits but
+ * not lower is preferrable to one that has poor distribution in the upper bits
+ * but not the lower ones.
+ */
+
+typedef hash_val_t (*hash_fun_t)(const void *);
+
+/*
+ * allocator functions
+ */
+
+typedef hnode_t *(*hnode_alloc_t)(void *);
+typedef void (*hnode_free_t)(hnode_t *, void *);
+
+/*
+ * This is the hash table control structure. It keeps track of information
+ * about a hash table, as well as the hash table itself.
+ * Notes:
+ * 1. Pointer to the hash table proper. The table is an array of pointers to
+ * hash nodes (of type hnode_t). If the table is empty, every element of
+ * this table is a null pointer. A non-null entry points to the first
+ * element of a chain of nodes.
+ * 2. This member keeps track of the size of the hash table---that is, the
+ * number of chain pointers.
+ * 3. The count member maintains the number of elements that are presently
+ * in the hash table.
+ * 4. The maximum count is the greatest number of nodes that can populate this
+ * table. If the table contains this many nodes, no more can be inserted,
+ * and the hash_isfull() function returns true.
+ * 5. The high mark is a population threshold, measured as a number of nodes,
+ * which, if exceeded, will trigger a table expansion. Only dynamic hash
+ * tables are subject to this expansion.
+ * 6. The low mark is a minimum population threshold, measured as a number of
+ * nodes. If the table population drops below this value, a table shrinkage
+ * will occur. Only dynamic tables are subject to this reduction. No table
+ * will shrink beneath a certain absolute minimum number of nodes.
+ * 7. This is the a pointer to the hash table's comparison function. The
+ * function is set once at initialization or creation time.
+ * 8. Pointer to the table's hashing function, set once at creation or
+ * initialization time.
+ * 9. The current hash table mask. If the size of the hash table is 2^N,
+ * this value has its low N bits set to 1, and the others clear. It is used
+ * to select bits from the result of the hashing function to compute an
+ * index into the table.
+ * 10. A flag which indicates whether the table is to be dynamically resized. It
+ * is set to 1 in dynamically allocated tables, 0 in tables that are
+ * statically allocated.
+ */
+
+typedef struct hash_t {
+ #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ struct hnode_t **hash_table; /* 1 */
+ hashcount_t hash_nchains; /* 2 */
+ hashcount_t hash_nodecount; /* 3 */
+ hashcount_t hash_maxcount; /* 4 */
+ hashcount_t hash_highmark; /* 5 */
+ hashcount_t hash_lowmark; /* 6 */
+ hash_comp_t hash_compare; /* 7 */
+ hash_fun_t hash_function; /* 8 */
+ hnode_alloc_t hash_allocnode;
+ hnode_free_t hash_freenode;
+ void *hash_context;
+ hash_val_t hash_mask; /* 9 */
+ int hash_dynamic; /* 10 */
+ #else
+ int hash_dummy;
+ #endif
+} hash_t;
+
+/*
+ * Hash scanner structure, used for traversals of the data structure.
+ * Notes:
+ * 1. Pointer to the hash table that is being traversed.
+ * 2. Reference to the current chain in the table being traversed (the chain
+ * that contains the next node that shall be retrieved).
+ * 3. Pointer to the node that will be retrieved by the subsequent call to
+ * hash_scan_next().
+ */
+
+typedef struct hscan_t {
+ #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ hash_t *hash_table; /* 1 */
+ hash_val_t hash_chain; /* 2 */
+ hnode_t *hash_next; /* 3 */
+ #else
+ int hash_dummy;
+ #endif
+} hscan_t;
+
+
+#endif /* ATALK_HASH_H */
/*
- * $Id: util.h,v 1.9 2009-04-28 13:01:24 franklahm Exp $
+ * $Id: util.h,v 1.10 2009-10-02 09:32:40 franklahm Exp $
*/
#ifndef _ATALK_UTIL_H
extern int vol_load_charsets __P(( struct volinfo *vol));
#endif /* 0 */
+/*
+ * Function: lock_reg
+ *
+ * Purpose: lock a file with fctnl
+ *
+ * Arguments:
+ *
+ * fd (r) File descriptor
+ * cmd (r) cmd to fcntl, only F_SETLK is usable here
+ * type (r) F_RDLCK, F_WRLCK, F_UNLCK
+ * offset (r) byte offset relative to l_whence
+ * whence (r) SEEK_SET, SEEK_CUR, SEEK_END
+ * len (r) no. of bytes (0 means to EOF)
+ *
+ * Returns: 0 on success, -1 on failure
+ * fcntl return value and errno
+ *
+ * Effects:
+ *
+ * Function called by macros to ease locking.
+ */
+extern int lock_reg(int fd, int cmd, int type, off_t offest, int whence, off_t len);
+
+/*
+ * Macros: read_lock, write_lock, un_lock
+ *
+ * Purpose: lock and unlock files
+ *
+ * Arguments:
+ *
+ * fd (r) File descriptor
+ * offset (r) byte offset relative to l_whence
+ * whence (r) SEEK_SET, SEEK_CUR, SEEK_END
+ * len (r) no. of bytes (0 means to EOF)
+ *
+ * Returns: 0 on success, -1 on failure
+ * fcntl return value and errno
+ *
+ * Effects:
+ *
+ * Nice locking macros.
+ */
+
+#define read_lock(fd, offset, whence, len) \
+ lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))
+#define write_lock(fd, offset, whence, len) \
+ lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
+#define unlock(fd, offset, whence, len) \
+ lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))
+
#endif
--- /dev/null
+/*
+ Copyright (c) 2004 Didier Gautheron
+
+ 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.
+
+ vfs layer for afp
+*/
+
+#ifndef ATALK_VFS_H
+#define ATALK_VFS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_NFSv4_ACLS
+#include <sys/acl.h>
+#endif
+
+#include <atalk/adouble.h>
+#include <atalk/volume.h>
+
+struct vfs_ops {
+ /* low level adouble fn */
+ char *(*ad_path)(const char *, int);
+
+ /* */
+ int (*validupath)(const struct vol *, const char *);
+ int (*rf_chown)(const struct vol *, const char *path, uid_t owner, gid_t group);
+ int (*rf_renamedir)(const struct vol *, const char *oldpath, const char *newpath);
+ int (*rf_deletecurdir)(const struct vol *);
+ int (*rf_setfilmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
+ int (*rf_setdirmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
+ int (*rf_setdirunixmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
+
+ int (*rf_setdirowner)(const struct vol *, const char *path, uid_t owner, gid_t group);
+
+ int (*rf_deletefile)(const struct vol *, const char * );
+ int (*rf_renamefile)(const struct vol *, const char *oldpath, const char *newpath);
+#ifdef HAVE_NFSv4_ACLS
+ int (*rf_acl)(const struct vol *, const char *path, int cmd, int count, ace_t *aces);
+ int (*rf_remove_acl)(const struct vol *, const char *path, int dir);
+#endif
+
+ /* Extended Attributes */
+ int (*get_easize)(const struct vol * restrict, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname);
+ int (*get_eacontent)(const struct vol * restrict , char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname, int maxreply);
+ int (*list_eas)(const struct vol * restrict , char * restrict attrnamebuf, int * restrict buflen, const char * restrict uname, int oflag);
+ int (*set_ea)(const struct vol * restrict , const char * restrict uname, const char * restrict attruname, const char * restrict ibuf, size_t attrsize, int oflag);
+ int (*remove_ea)(const struct vol * restrict , const char * restrict uname, const char * restrict attruname, int oflag);
+};
+
+extern void initvol_vfs(struct vol * restrict vol);
+
+/* VFS inderected funcs ... : */
+/* ...default adouble EAs */
+extern int get_easize(const struct vol * restrict vol, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname);
+extern int get_eacontent(const struct vol * restrict vol, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname, int maxreply);
+extern int list_eas(const struct vol * restrict vol, char * restrict attrnamebuf, int * restrict buflen, const char * restrict uname, int oflag);
+extern int set_ea(const struct vol * restrict vol, const char * restrict uname, const char * restrict attruname, const char * restrict ibuf, size_t attrsize, int oflag);
+extern int remove_ea(const struct vol * restrict vol, const char * restrict uname, const char * restrict attruname, int oflag);
+
+/* ... Solaris native EAs */
+#ifdef HAVE_SOLARIS_EAS
+extern int sol_get_easize(const struct vol * restrict vol, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname);
+extern int sol_get_eacontent(const struct vol * restrict vol, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, char * restrict attruname, int maxreply);
+extern int sol_list_eas(const struct vol * restrict vol, char * restrict attrnamebuf, int * restrict buflen, const char * restrict uname, int oflag);
+extern int sol_set_ea(const struct vol * restrict vol, const char * restrict uname, const char * restrict attruname, const char * restrict ibuf,size_t attrsize, int oflag);
+extern int sol_remove_ea(const struct vol * restrict vol, const char * restrict uname, const char * restrict attruname, int oflag);
+#endif /* HAVE_SOLARIS_EAS */
+
+/* Solaris NFSv4 ACL stuff */
+#ifdef HAVE_NFSv4_ACLS
+extern int get_nfsv4_acl(const char *name, ace_t **retAces);
+extern int remove_acl(const char *name);
+#endif /* HAVE_NFSv4_ACLS */
+
+#endif /* ATALK_VFS_H */
/*
- * $Id: volinfo.h,v 1.7 2009-09-11 09:14:16 franklahm Exp $
+ * $Id: volinfo.h,v 1.8 2009-10-02 09:32:41 franklahm Exp $
*/
#ifndef _ATALK_VOLINFO_H
a valid SHIFT-JIS char, is encoded as U\217 -*/
#define AFPVOL_CACHE (1 << 21) /* Use adouble v2 CNID caching, default don't use it */
#define AFPVOL_INV_DOTS (1 << 22) /* dots files are invisible */
-#define AFPVOL_EXT_ATTRS (1 << 23) /* Volume supports Extended Attributes */
#define AFPVOL_TM (1 << 24) /* Supports TimeMachine */
#define AFPVOL_ACLS (1 << 25) /* Volume supports ACLS */
--- /dev/null
+/*
+ * $Id: volume.h,v 1.1 2009-10-02 09:32:41 franklahm Exp $
+ *
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef ATALK_VOLUME_H
+#define ATALK_VOLUME_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+#include <atalk/unicode.h>
+#include <atalk/cnid.h>
+#include <atalk/hash.h>
+
+#define AFPVOL_U8MNAMELEN 255 /* AFP3 sepc */
+#define AFPVOL_MACNAMELEN 27 /* AFP2 spec */
+
+struct vol {
+ struct vol *v_next;
+ char *v_localname; /* as defined in AppleVolumes.default */
+ ucs2_t *v_u8mname; /* converted to utf8-mac in ucs2 */
+ ucs2_t *v_macname; /* mangled to legacy longname in ucs2 */
+ ucs2_t *v_name; /* either v_u8mname or v_macname */
+ char *v_path;
+
+ struct dir *v_dir, *v_root;
+ hash_t *v_hash;
+ int v_flags;
+#ifdef __svr4__
+ int v_qfd;
+#endif /*__svr4__*/
+ char *v_gvs;
+ time_t v_mtime;
+ time_t v_ctime; /* volume creation date, not unix ctime */
+
+ u_int16_t v_vid;
+ void *v_nfsclient;
+ int v_nfs;
+
+ int v_casefold;
+ size_t max_filename;
+
+ char *v_password;
+ char *v_veto;
+
+ char *v_cnidscheme;
+ char *v_dbpath;
+ dev_t v_dev; /* Unix volume device */
+ struct _cnid_db *v_cdb;
+ char v_stamp[ADEDLEN_PRIVSYN];
+ mode_t v_umask;
+ mode_t v_perm; /* default permission value OR with requested perm*/
+ mode_t v_dperm; /* default directories permission value OR with requested perm*/
+ mode_t v_fperm; /* default files permission value OR with requested perm*/
+
+#ifdef FORCE_UIDGID
+ char *v_forceuid;
+ char *v_forcegid;
+#endif
+
+ char *v_volcodepage;
+ charset_t v_volcharset;
+ struct charset_functions *v_vol;
+ char *v_maccodepage;
+ charset_t v_maccharset;
+ struct charset_functions *v_mac;
+
+ int v_deleted; /* volume open but deleted in new config file */
+ int v_hide; /* new volume wait until old volume is closed */
+ int v_new; /* volume deleted but there's a new one with the same name */
+ int v_adouble; /* default adouble format */
+ int v_ad_options; /* adouble option NODEV, NOCACHE, etc.. */
+
+ char *v_root_preexec;
+ char *v_preexec;
+
+ char *v_root_postexec;
+ char *v_postexec;
+
+ int v_root_preexec_close;
+ int v_preexec_close;
+
+ /* adouble indirection */
+ struct vfs_ops *vfs;
+ int v_vfs_ea; /* The AFPVOL_EA_xx flag */
+ int (*validupath)(const struct vol *, const char *);
+ char *(*ad_path)(const char *, int);
+};
+
+#ifdef NO_LARGE_VOL_SUPPORT
+typedef u_int32_t VolSpace;
+#else /* NO_LARGE_VOL_SUPPORT */
+typedef u_int64_t VolSpace;
+#endif /* NO_LARGE_VOL_SUPPORT */
+
+#define AFPVOL_OPEN (1<<0)
+
+/* flags for AFS and quota 0xxx0 */
+#define AFPVOL_GVSMASK (7<<2)
+#define AFPVOL_NONE (0<<2)
+#define AFPVOL_AFSGVS (1<<2)
+#define AFPVOL_USTATFS (2<<2)
+#define AFPVOL_UQUOTA (4<<2)
+
+/*
+ Flags that alter volume behaviour.
+ Keep in sync with include/atalk/volinfo.h and libatalk/util/volinfo.c
+*/
+#define AFPVOL_A2VOL (1 << 5) /* prodos volume */
+#define AFPVOL_CRLF (1 << 6) /* cr/lf translation */
+#define AFPVOL_NOADOUBLE (1 << 7) /* don't create .AppleDouble by default */
+#define AFPVOL_RO (1 << 8) /* read-only volume */
+#define AFPVOL_MSWINDOWS (1 << 9) /* deal with ms-windows yuckiness.
+this is going away. */
+#define AFPVOL_NOHEX (1 << 10) /* don't do :hex translation */
+#define AFPVOL_USEDOTS (1 << 11) /* use real dots */
+#define AFPVOL_LIMITSIZE (1 << 12) /* limit size for older macs */
+#define AFPVOL_MAPASCII (1 << 13) /* map the ascii range as well */
+#define AFPVOL_DROPBOX (1 << 14) /* dropkludge dropbox support */
+#define AFPVOL_NOFILEID (1 << 15) /* don't advertise createid resolveid and deleteid calls */
+#define AFPVOL_NOSTAT (1 << 16) /* advertise the volume even if we can't stat() it
+ * maybe because it will be mounted later in preexec */
+#define AFPVOL_UNIX_PRIV (1 << 17) /* support unix privileges */
+#define AFPVOL_NODEV (1 << 18) /* always use 0 for device number in cnid calls
+ * help if device number is notconsistent across reboot
+ * NOTE symlink to a different device will return an ACCESS error
+ */
+#define AFPVOL_CASEINSEN (1 << 19) /* volume is case insensitive */
+#define AFPVOL_EILSEQ (1 << 20) /* encode illegal sequence 'asis' UCS2, ex "\217-", which is not
+ a valid SHIFT-JIS char, is encoded as U\217 -*/
+
+#define AFPVOL_CACHE (1 << 21) /* Use adouble v2 CNID caching. Default: yes */
+#define AFPVOL_INV_DOTS (1 << 22) /* dots files are invisible */
+#define AFPVOL_TM (1 << 24) /* Supports TimeMachine */
+#define AFPVOL_ACLS (1 << 25) /* Volume supports ACLS */
+
+/* Extended Attributes vfs indirection */
+#define AFPVOL_EA_AD 0 /* Store them in adouble files. Default. */
+#define AFPVOL_EA_SOLARIS 1 /* Store them in native Solaris EAs */
+
+/* FPGetSrvrParms options */
+#define AFPSRVR_CONFIGINFO (1 << 0)
+#define AFPSRVR_PASSWD (1 << 7)
+
+/* handle casefolding */
+#define AFPVOL_MTOUUPPER (1 << 0)
+#define AFPVOL_MTOULOWER (1 << 1)
+#define AFPVOL_UTOMUPPER (1 << 2)
+#define AFPVOL_UTOMLOWER (1 << 3)
+#define AFPVOL_UMLOWER (AFPVOL_MTOULOWER | AFPVOL_UTOMLOWER)
+#define AFPVOL_UMUPPER (AFPVOL_MTOUUPPER | AFPVOL_UTOMUPPER)
+#define AFPVOL_UUPPERMLOWER (AFPVOL_MTOUUPPER | AFPVOL_UTOMLOWER)
+#define AFPVOL_ULOWERMUPPER (AFPVOL_MTOULOWER | AFPVOL_UTOMUPPER)
+
+#define MSWINDOWS_BADCHARS ":\t\\/<>*?|\""
+
+int wincheck(const struct vol *vol, const char *path);
+
+#define AFPVOLSIG_FLAT 0x0001 /* flat fs */
+#define AFPVOLSIG_FIX 0x0002 /* fixed ids */
+#define AFPVOLSIG_VAR 0x0003 /* variable ids */
+#define AFPVOLSIG_DEFAULT AFPVOLSIG_FIX
+
+/* volume attributes */
+#define VOLPBIT_ATTR_RO (1 << 0)
+#define VOLPBIT_ATTR_PASSWD (1 << 1)
+#define VOLPBIT_ATTR_FILEID (1 << 2)
+#define VOLPBIT_ATTR_CATSEARCH (1 << 3)
+#define VOLPBIT_ATTR_BLANKACCESS (1 << 4)
+#define VOLPBIT_ATTR_UNIXPRIV (1 << 5)
+#define VOLPBIT_ATTR_UTF8 (1 << 6)
+#define VOLPBIT_ATTR_NONETUID (1 << 7)
+#define VOLPBIT_ATTR_EXT_ATTRS (1 << 10)
+#define VOLPBIT_ATTR_ACLS (1 << 11)
+#define VOLPBIT_ATTR_TM (1 << 13)
+
+#define VOLPBIT_ATTR 0
+#define VOLPBIT_SIG 1
+#define VOLPBIT_CDATE 2
+#define VOLPBIT_MDATE 3
+#define VOLPBIT_BDATE 4
+#define VOLPBIT_VID 5
+#define VOLPBIT_BFREE 6
+#define VOLPBIT_BTOTAL 7
+#define VOLPBIT_NAME 8
+/* handle > 4GB volumes */
+#define VOLPBIT_XBFREE 9
+#define VOLPBIT_XBTOTAL 10
+#define VOLPBIT_BSIZE 11 /* block size */
+
+
+#define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? \
+ ADFLAGS_NOADOUBLE : 0)
+#ifdef AFP3x
+#define utf8_encoding() (afp_version >= 30)
+#else
+#define utf8_encoding() (0)
+#endif
+
+#define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
+#define vol_unix_priv(vol) (afp_version >= 30 && ((vol)->v_flags & AFPVOL_UNIX_PRIV))
+#define vol_inv_dots(vol) (((vol)->v_flags & AFPVOL_INV_DOTS) ? 1 : 0)
+
+
+#endif
# Makefile.am for libatalk/
-SUBDIRS = adouble asp atp compat cnid dsi nbp netddp util tdb unicode acl
+SUBDIRS = acl adouble asp atp compat cnid dsi nbp netddp tdb util unicode vfs
lib_LTLIBRARIES = libatalk.la
nbp/libnbp.la \
netddp/libnetddp.la \
util/libutil.la \
- tdb/libtdb.la \
- unicode/libunicode.la @LIBATALK_ACLS@
+ tdb/libtdb.la \
+ unicode/libunicode.la \
+ vfs/libvfs.la @LIBATALK_ACLS@
libatalk_la_DEPENDENCIES = \
adouble/libadouble.la \
nbp/libnbp.la \
netddp/libnetddp.la \
util/libutil.la \
- tdb/libtdb.la \
- unicode/libunicode.la @LIBATALK_ACLS@
+ tdb/libtdb.la \
+ unicode/libunicode.la \
+ vfs/libvfs.la @LIBATALK_ACLS@
libatalk_la_LDFLAGS = -static
noinst_LTLIBRARIES = libadouble.la
-libadouble_la_SOURCES = ad_open.c ad_flush.c ad_read.c ad_write.c ad_size.c ad_mmap.c ad_lock.c ad_date.c ad_attr.c ad_sendfile.c
+libadouble_la_SOURCES = ad_open.c ad_flush.c ad_read.c ad_write.c ad_size.c \
+ ad_mmap.c ad_lock.c ad_date.c ad_attr.c ad_sendfile.c
noinst_HEADERS = ad_private.h
/*
- * $Id: ad_open.c,v 1.47 2009-09-14 00:02:21 didg Exp $
+ * $Id: ad_open.c,v 1.48 2009-10-02 09:32:41 franklahm Exp $
*
* Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
* Copyright (c) 1990,1991 Regents of The University of Michigan.
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;
strdicasecmp.c \
strlcpy.c \
fault.c \
- volinfo.c
+ volinfo.c \
+ locking.c
# util_unicode.c
--- /dev/null
+/*
+ $Id: locking.c,v 1.1 2009-10-02 09:32:41 franklahm Exp $
+ 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.
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+
+/*
+ * Function: lock_reg
+ *
+ * Purpose: lock a file with fctnl
+ *
+ * Arguments:
+ *
+ * fd (r) File descriptor
+ * cmd (r) cmd to fcntl, only F_SETLK is usable here
+ * type (r) F_RDLCK, F_WRLCK, F_UNLCK
+ * offset (r) byte offset relative to l_whence
+ * whence (r) SEEK_SET, SEEK_CUR, SEEK_END
+ * len (r) no. of bytes (0 means to EOF)
+ *
+ * Returns: fcntl return value
+ *
+ * Effects:
+ *
+ * Function called by macros {read|write|un]_lock to ease locking.
+ */
+int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
+{
+ struct flock lock;
+
+ lock.l_type = type;
+ lock.l_start = offset;
+ lock.l_whence = whence;
+ lock.l_len = len;
+
+ return (fcntl(fd, cmd, &lock));
+}
{AFPVOL_EILSEQ, "ILLEGALSEQ"}, /* encode illegal sequence */
{AFPVOL_CACHE, "CACHEID"}, /* Use adouble v2 CNID caching, default don't use it */
{AFPVOL_INV_DOTS, "INVISIBLEDOTS"},
- {AFPVOL_EXT_ATTRS, "EXT_ATTRS"}, /* Vol supports Extened Attributes */
{AFPVOL_ACLS, "ACLS"}, /* Vol supports ACLs */
{AFPVOL_TM, "TM"}, /* Set "kSupportsTMLockSteal" is volume attributes */
{0, NULL}
--- /dev/null
+Makefile
+Makefile.in
+*.lo
+*.la
+*.loT
+.deps
+.libs
--- /dev/null
+# Makefile.am for libatalk/adouble/
+
+noinst_LTLIBRARIES = libvfs.la
+
+libvfs_la_SOURCES = vfs.c unix.c ea.c
+
+if USE_NFSv4_ACLS
+libvfs_la_SOURCES += acl.c
+endif
+
--- /dev/null
+/*
+ $Id: acl.c,v 1.1 2009-10-02 09:32:41 franklahm Exp $
+ 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 <errno.h>
+#include <sys/acl.h>
+
+#include <atalk/util.h>
+#include <atalk/logger.h>
+
+/* Get ACL. Allocates storage as needed. Caller must free.
+ * Returns no of ACEs or -1 on error. */
+int get_nfsv4_acl(const char *name, ace_t **retAces)
+{
+ int ace_count = -1;
+ ace_t *aces;
+
+ *retAces = NULL;
+ ace_count = acl(name, ACE_GETACLCNT, 0, NULL);
+ if (ace_count <= 0) {
+ LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACLCNT) error");
+ return -1;
+ }
+
+ aces = malloc(ace_count * sizeof(ace_t));
+ if (aces == NULL) {
+ LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
+ return -1;
+ }
+
+ if ( (acl(name, ACE_GETACL, ace_count, aces)) == -1 ) {
+ LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
+ free(aces);
+ return -1;
+ }
+
+ LOG(log_debug9, logtype_afpd, "get_nfsv4_acl: file: %s -> No. of ACEs: %d", name, ace_count);
+ *retAces = aces;
+
+ return ace_count;
+}
+
+/* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
+int remove_acl(const char *name)
+{
+ int ret,i, ace_count, trivial_aces, new_aces_count;
+ ace_t *old_aces = NULL;
+ ace_t *new_aces = NULL;
+
+ LOG(log_debug9, logtype_afpd, "remove_acl: BEGIN");
+
+ /* Get existing ACL and count trivial ACEs */
+ if ((ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
+ return AFPERR_MISC;
+ trivial_aces = 0;
+ for ( i=0; i < ace_count; i++) {
+ if (old_aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))
+ trivial_aces++;
+ }
+
+ /* malloc buffer for new ACL */
+ if ((new_aces = malloc(trivial_aces * sizeof(ace_t))) == NULL) {
+ LOG(log_error, logtype_afpd, "remove_acl: malloc %s", strerror(errno));
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ /* Now copy the trivial ACEs */
+ new_aces_count = 0;
+ for (i=0; i < ace_count; i++) {
+ if (old_aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
+ memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
+ new_aces_count++;
+ }
+ }
+
+ if ( (acl(name, ACE_SETACL, trivial_aces, new_aces)) == 0)
+ ret = AFP_OK;
+ else {
+ LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
+ if (errno == (EACCES | EPERM))
+ ret = AFPERR_ACCESS;
+ else if (errno == ENOENT)
+ ret = AFPERR_NOITEM;
+ else
+ ret = AFPERR_MISC;
+ }
+
+exit:
+ free(old_aces);
+ free(new_aces);
+
+ LOG(log_debug9, logtype_afpd, "remove_acl: END");
+ return ret;
+}
--- /dev/null
+/*
+ $Id: ea.c,v 1.1 2009-10-02 09:32:41 franklahm Exp $
+ 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.
+*/
+
+/* According to man fsattr.5 we must define _ATFILE_SOURCE */
+#ifdef HAVE_SOLARIS_EAS
+#define _ATFILE_SOURCE
+#endif
+
+#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>
+
+/*
+ * 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"
+ */
+
+/*
+ * 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, 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)
+{
+ 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_path
+ *
+ * Purpose: return name of ea header filename
+ *
+ * Arguments:
+ *
+ * ea (r) ea handle
+ * eaname (r) name of EA or NULL
+ *
+ * Returns: pointer to name in static buffer
+ *
+ * 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"
+ */
+static char * ea_path(const struct ea * restrict ea,
+ const char * restrict eaname)
+{
+ char *adname;
+ static char pathbuf[MAXPATHLEN + 1];
+
+ /* get name of a adouble file from uname */
+ adname = ea->vol->vfs->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);
+ strlcat(pathbuf, eaname, MAXPATHLEN + 1);
+ }
+
+ return pathbuf;
+}
+
+/*
+ * Function: ea_addentry
+ *
+ * Purpose: add one EA into ea->ea_entries[]
+ *
+ * Arguments:
+ *
+ * ea (rw) pointer to struct ea
+ * uname (r) name of file
+ * attruname (r) name of EA
+ * attrsize (r) size of ea
+ *
+ * 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 uname,
+ const char * restrict attruname,
+ size_t attrsize)
+{
+ void *tmprealloc;
+
+ 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 {
+ 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: ea_delentry
+ *
+ * Purpose: delete one EA from ea->ea_entries[]
+ *
+ * Arguments:
+ *
+ * ea (rw) pointer to struct ea
+ * uname (r) name of EA
+ * attruname (r) size of ea
+ *
+ * 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 uname,
+ const char * restrict attruname)
+{
+ int ret = 0, count = 0;
+
+ if (ea->ea_count == 0) {
+ 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: 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;
+
+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;
+
+ eaname = ea_path(ea, attruname);
+ LOG(log_maxdebug, logtype_afpd, "write_ea: ea_apth: %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)) != attrsize) {
+ LOG(log_error, logtype_afpd, "write_ea: short write: %s", eaname);
+ ret = -1;
+ goto exit;
+ }
+
+exit:
+ if (fd != -1)
+ close(fd); /* and unlock */
+ 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;
+
+ eafile = ea_path(ea, eaname);
+
+ /* 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;
+}
+
+/*
+ * 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 error
+ *
+ * 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.
+ */
+static 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;
+ }
+
+ if ((stat(uname, &st)) != 0) {
+ LOG(log_debug, logtype_afpd, "ea_open: cant stat: %s", 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;
+ if (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);
+ 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 = -1;
+ 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 */
+ 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: error on open for header: %s", eaname);
+ 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)) != 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:
+ if (ret != 0) {
+ if (ea->ea_data) {
+ free(ea->ea_data);
+ ea->ea_data = NULL;
+ }
+ if (ea->ea_fd) {
+ close(ea->ea_fd);
+ ea->ea_fd = -1;
+ }
+ }
+ 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
+ */
+static int ea_close(struct ea * restrict ea)
+{
+ int ret = 0, count = 0;
+ char *eaname;
+ struct stat st;
+
+ LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
+
+ /* 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);
+ if ((stat(eaname, &st)) == 0) {
+ if ((unlink(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) != 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(const struct vol * restrict vol,
+ char * restrict rbuf,
+ int * restrict rbuflen,
+ const char * restrict uname,
+ int oflag,
+ const char * restrict attruname)
+{
+ int ret = AFPERR_MISC, 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) {
+ LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
+ return AFPERR_MISC;
+ }
+
+ 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(const struct vol * restrict vol,
+ char * restrict rbuf,
+ int * restrict rbuflen,
+ const char * restrict uname,
+ int oflag,
+ const char * restrict attruname,
+ int maxreply)
+{
+ int ret = AFPERR_MISC, count = 0, fd = -1;
+ uint32_t uint32;
+ size_t toread;
+ struct ea ea;
+
+ LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
+
+ if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
+ LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
+ return AFPERR_MISC;
+ }
+
+ while (count < ea.ea_count) {
+ if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
+ if ((fd = open(ea_path(&ea, attruname), O_RDONLY)) == -1) {
+ 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)) != toread) {
+ LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
+ 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(const struct vol * restrict vol,
+ char * restrict attrnamebuf,
+ int * restrict buflen,
+ const char * restrict uname,
+ int oflag)
+{
+ int count = 0, 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(const struct vol * restrict vol,
+ const char * restrict uname,
+ const char * restrict attruname,
+ const char * restrict ibuf,
+ size_t attrsize,
+ int oflag)
+{
+ 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, uname, attruname, attrsize)) == -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(const struct vol * restrict vol,
+ const char * restrict uname,
+ const char * restrict attruname,
+ int oflag)
+{
+ 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, uname, 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;
+}
+
+/**********************************************************************************
+ * Solaris EA VFS funcs
+ **********************************************************************************/
+
+/*
+ * Function: sol_get_easize
+ *
+ * Purpose: get size of an EA on Solaris native 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.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_get_easize(const struct vol * restrict vol,
+ char * restrict rbuf,
+ int * restrict rbuflen,
+ const char * restrict uname,
+ int oflag,
+ cons char * restrict attruname)
+{
+ int ret, attrdirfd;
+ uint32_t attrsize;
+ struct stat st;
+
+ LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
+
+ if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
+ if (errno == ELOOP) {
+ /* its a symlink and client requested O_NOFOLLOW */
+ LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
+
+ memset(rbuf, 0, 4);
+ *rbuflen += 4;
+
+ return AFP_OK;
+ }
+ LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
+ return AFPERR_MISC;
+ }
+
+ if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
+ LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
+
+ /* Start building reply packet */
+
+ LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
+
+ /* length of attribute data */
+ attrsize = htonl(attrsize);
+ memcpy(rbuf, &attrsize, 4);
+ *rbuflen += 4;
+
+ ret = AFP_OK;
+
+exit:
+ close(attrdirfd);
+ return ret;
+}
+#endif /* HAVE_SOLARIS_EAS */
+
+/*
+ * Function: sol_get_eacontent
+ *
+ * Purpose: copy Solaris native 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.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_get_eacontent(const struct vol * restrict vol,
+ char * restrict rbuf,
+ int * restrict rbuflen,
+ const char * restrict uname,
+ int oflag,
+ char * restrict attruname,
+ int maxreply)
+{
+ int ret, attrdirfd;
+ size_t toread, okread = 0, len;
+ char *datalength;
+ struct stat st;
+
+ if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
+ if (errno == ELOOP) {
+ /* its a symlink and client requested O_NOFOLLOW */
+ LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
+
+ memset(rbuf, 0, 4);
+ *rbuflen += 4;
+
+ return AFP_OK;
+ }
+ LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
+ return AFPERR_MISC;
+ }
+
+ if ( -1 == (fstat(attrdirfd, &st))) {
+ LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ /* Start building reply packet */
+
+ maxreply -= MAX_REPLY_EXTRA_BYTES;
+ if (maxreply > MAX_EA_SIZE)
+ maxreply = MAX_EA_SIZE;
+
+ /* But never send more than the client requested */
+ toread = (maxreply < st.st_size) ? maxreply : st.st_size;
+
+ LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
+
+ /* remember where we must store length of attribute data in rbuf */
+ datalength = rbuf;
+ rbuf += 4;
+ *rbuflen += 4;
+
+ while (1) {
+ len = read(attrdirfd, rbuf, toread);
+ if (len == -1) {
+ LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ okread += len;
+ rbuf += len;
+ *rbuflen += len;
+ if ((len == 0) || (okread == toread))
+ break;
+ }
+
+ okread = htonl((uint32_t)okread);
+ memcpy(datalength, &okread, 4);
+
+ ret = AFP_OK;
+
+exit:
+ close(attrdirfd);
+ return ret;
+}
+#endif /* HAVE_SOLARIS_EAS */
+
+/*
+ * Function: sol_list_eas
+ *
+ * Purpose: copy names of Solaris native EA 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 *rbuflen accordingly.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_list_eas(const struct vol * restrict vol,
+ char * restrict attrnamebuf,
+ int * restrict buflen,
+ const char * restrict uname,
+ int oflag)
+{
+ int ret, attrbuflen = *buflen, len, attrdirfd = 0;
+ struct dirent *dp;
+ DIR *dirp = NULL;
+
+ /* Now list file attribute dir */
+ if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
+ if (errno == ELOOP) {
+ /* its a symlink and client requested O_NOFOLLOW */
+ ret = AFPERR_BADTYPE;
+ goto exit;
+ }
+ LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ if (NULL == (dirp = fdopendir(attrdirfd))) {
+ LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ while ((dp = readdir(dirp))) {
+ /* check if its "." or ".." */
+ if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
+ (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
+ continue;
+
+ len = strlen(dp->d_name);
+
+ /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
+ if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ if (len == 255)
+ /* convert_string didn't 0-terminate */
+ attrnamebuf[attrbuflen + 255] = 0;
+
+ LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_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, "sol_list_extattr(%s): running out of buffer for EA names", uname);
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ }
+
+ ret = AFP_OK;
+
+exit:
+ if (dirp)
+ closedir(dirp);
+
+ if (attrdirfd > 0)
+ close(attrdirfd);
+
+ *buflen = attrbuflen;
+ return ret;
+}
+#endif /* HAVE_SOLARIS_EAS */
+
+/*
+ * Function: sol_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.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_set_ea(const struct vol * restrict vol,
+ const char * restrict u_name,
+ const char * restrict attruname,
+ const char * restrict ibuf,
+ size_t attrsize,
+ int oflag)
+{
+ int attrdirfd;
+
+ if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) {
+ if (errno == ELOOP) {
+ /* its a symlink and client requested O_NOFOLLOW */
+ LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
+ return AFP_OK;
+ }
+ LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
+ return AFPERR_MISC;
+ }
+
+ if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
+ LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
+ return AFPERR_MISC;
+ }
+
+ return AFP_OK;
+}
+#endif /* HAVE_SOLARIS_EAS */
+
+/*
+ * Function: sol_remove_ea
+ *
+ * Purpose: remove a Solaris native EA
+ *
+ * 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.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_remove_ea(const struct vol * restrict vol,
+ const char * restrict uname,
+ const char * restrict attruname,
+ int oflag)
+{
+ int attrdirfd;
+
+ if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
+ switch (errno) {
+ case ELOOP:
+ /* its a symlink and client requested O_NOFOLLOW */
+ LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
+ return AFP_OK;
+ case EACCES:
+ LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
+ return AFPERR_ACCESS;
+ default:
+ LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
+ return AFPERR_MISC;
+ }
+ }
+
+ if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
+ if (errno == EACCES) {
+ LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
+ return AFPERR_ACCESS;
+ }
+ LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
+ return AFPERR_MISC;
+ }
+
+}
+#endif /* HAVE_SOLARIS_EAS */
--- /dev/null
+/*
+ * $Id: unix.c,v 1.1 2009-10-02 09:32:41 franklahm Exp $
+ *
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/directory.h>
+#include <atalk/volume.h>
+#include <atalk/logger.h>
+
+/* -----------------------------
+ a dropbox is a folder where w is set but not r eg:
+ rwx-wx-wx or rwx-wx--
+ rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
+*/
+int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask)
+{
+ int retval = 0;
+
+#ifdef DROPKLUDGE
+ /* Turn on the sticky bit if this is a drop box, also turn off the setgid bit */
+ if ((dropbox & AFPVOL_DROPBOX)) {
+ int uid;
+
+ if ( ( (mode & S_IWOTH) && !(mode & S_IROTH)) ||
+ ( (mode & S_IWGRP) && !(mode & S_IRGRP)) )
+ {
+ uid=geteuid();
+ if ( seteuid(0) < 0) {
+ LOG(log_error, logtype_afpd, "stickydirmode: unable to seteuid root: %s", strerror(errno));
+ }
+ if ( (retval=chmod( name, ( (DIRBITS | mode | S_ISVTX) & ~v_umask) )) < 0) {
+ LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
+ } else {
+ LOG(log_debug, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(retval) );
+ }
+ seteuid(uid);
+ return retval;
+ }
+ }
+#endif /* DROPKLUDGE */
+
+ /*
+ * Ignore EPERM errors: We may be dealing with a directory that is
+ * group writable, in which case chmod will fail.
+ */
+ if ( (chmod( name, (DIRBITS | mode) & ~v_umask ) < 0) && errno != EPERM &&
+ !(errno == ENOENT && (dropbox & AFPVOL_NOADOUBLE)) )
+ {
+ LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
+ retval = -1;
+ }
+
+ return retval;
+}
+
+/* ------------------------- */
+int dir_rx_set(mode_t mode)
+{
+ return (mode & (S_IXUSR | S_IRUSR)) == (S_IXUSR | S_IRUSR);
+}
+
+/* --------------------- */
+int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+{
+struct stat sb;
+mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO; /* rwx for owner group and other, by default */
+
+ if (!st) {
+ if (stat(name, &sb) != 0)
+ return -1;
+ st = &sb;
+ }
+
+ mode |= st->st_mode & ~mask; /* keep other bits from previous mode */
+ if ( chmod( name, mode & ~v_umask ) < 0 && errno != EPERM ) {
+ return -1;
+ }
+ return 0;
+}
+
+/* -------------------
+ system rmdir with afp error code.
+ ENOENT is not an error.
+ */
+int netatalk_rmdir(const char *name)
+{
+ if (rmdir(name) < 0) {
+ switch ( errno ) {
+ case ENOENT :
+ break;
+ case ENOTEMPTY :
+ return AFPERR_DIRNEMPT;
+ case EPERM:
+ case EACCES :
+ return AFPERR_ACCESS;
+ case EROFS:
+ return AFPERR_VLOCK;
+ default :
+ return AFPERR_PARAM;
+ }
+ }
+ return AFP_OK;
+}
+
+/* -------------------
+ system unlink with afp error code.
+ ENOENT is not an error.
+ */
+int netatalk_unlink(const char *name)
+{
+ if (unlink(name) < 0) {
+ switch (errno) {
+ case ENOENT :
+ break;
+ case EROFS:
+ return AFPERR_VLOCK;
+ case EPERM:
+ case EACCES :
+ return AFPERR_ACCESS;
+ default :
+ return AFPERR_PARAM;
+ }
+ }
+ return AFP_OK;
+}
+
+char *fullpathname(const char *name)
+{
+ static char wd[ MAXPATHLEN + 1];
+
+ if ( getcwd( wd , MAXPATHLEN) ) {
+ strlcat(wd, "/", MAXPATHLEN);
+ strlcat(wd, name, MAXPATHLEN);
+ }
+ else {
+ strlcpy(wd, name, MAXPATHLEN);
+ }
+ return wd;
+}
--- /dev/null
+/*
+ Copyright (c) 2004 Didier Gautheron
+
+ 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.
+
+*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+
+#if 0
+#include "extattrs.h"
+#endif
+
+#ifdef HAVE_NFSv4_ACLS
+extern int remove_acl(const char *name);
+#endif
+
+struct perm {
+ uid_t uid;
+ gid_t gid;
+};
+
+typedef int (*rf_loop)(struct dirent *, char *, void *, int , mode_t );
+
+/* ----------------------------- */
+static int
+for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag, mode_t v_umask)
+{
+ char buf[ MAXPATHLEN + 1];
+ char *m;
+ DIR *dp;
+ struct dirent *de;
+ int ret;
+
+
+ if (NULL == ( dp = opendir( name)) ) {
+ if (!flag) {
+ LOG(log_error, logtype_afpd, "%s: opendir %s: %s", from, fullpathname(name),strerror(errno) );
+ return -1;
+ }
+ return 0;
+ }
+ strlcpy( buf, name, sizeof(buf));
+ strlcat( buf, "/", sizeof(buf) );
+ m = strchr( buf, '\0' );
+ ret = 0;
+ while ((de = readdir(dp))) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+ continue;
+ }
+
+ strlcat(buf, de->d_name, sizeof(buf));
+ if (fn && (ret = fn(de, buf, data, flag, v_umask))) {
+ closedir(dp);
+ return ret;
+ }
+ *m = 0;
+ }
+ closedir(dp);
+ return ret;
+}
+
+/*******************************************************************************
+ * classic adouble format
+ *******************************************************************************/
+
+static int netatalk_name(const char *name)
+{
+ return strcasecmp(name,".AppleDB") &&
+ strcasecmp(name,".AppleDouble") &&
+ strcasecmp(name,".AppleDesktop");
+}
+
+static int validupath_adouble(const struct vol *vol, const char *name)
+{
+ return (vol->v_flags & AFPVOL_USEDOTS) ?
+ netatalk_name(name) && strcasecmp(name,".Parent"): name[0] != '.';
+}
+
+/* ----------------- */
+static int RF_chown_adouble(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
+
+{
+ struct stat st;
+ char *ad_p;
+
+ ad_p = vol->vfs->ad_path(path, ADFLAGS_HF );
+
+ if ( stat( ad_p, &st ) < 0 )
+ return 0; /* ignore */
+
+ return chown( ad_p, uid, gid );
+}
+
+/* ----------------- */
+int RF_renamedir_adouble(const struct vol *vol _U_, const char *oldpath _U_, const char *newpath _U_)
+{
+ return 0;
+}
+
+/* ----------------- */
+static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask)
+{
+ struct stat st;
+ int err;
+
+ /* 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;
+
+ if ((err = netatalk_unlink(name)))
+ return err;
+
+ return 0;
+}
+
+static int RF_deletecurdir_adouble(const struct vol *vol)
+{
+ int err;
+
+ /* delete stray .AppleDouble files. this happens to get .Parent files
+ as well. */
+ if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1, vol->v_umask)))
+ return err;
+ return netatalk_rmdir( ".AppleDouble" );
+}
+
+/* ----------------- */
+static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+{
+ return setfilmode(name, ad_hf_mode(mode), st, v_umask);
+}
+
+static int RF_setfilmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+ return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_HF ), mode, st, vol->v_umask);
+}
+
+/* ----------------- */
+static int RF_setdirunixmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+ char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+ int dropbox = vol->v_flags;
+
+ if (dir_rx_set(mode)) {
+ if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0 )
+ return -1;
+ }
+
+ if (adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st, vol->v_umask) < 0)
+ return -1;
+
+ if (!dir_rx_set(mode)) {
+ if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0 )
+ return -1 ;
+ }
+ return 0;
+}
+
+/* ----------------- */
+static int setdirmode_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask)
+{
+ mode_t hf_mode = *(mode_t *)data;
+ struct stat st;
+
+ if ( stat( name, &st ) < 0 ) {
+ if (flag)
+ return 0;
+ LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
+ }
+ else if (!S_ISDIR(st.st_mode)) {
+ if (setfilmode(name, hf_mode , &st, v_umask) < 0) {
+ /* FIXME what do we do then? */
+ }
+ }
+ return 0;
+}
+
+static int RF_setdirmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st _U_)
+{
+ int dropbox = vol->v_flags;
+ mode_t hf_mode = ad_hf_mode(mode);
+ char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+ char *adouble_p = ad_dir(adouble);
+
+ if (dir_rx_set(mode)) {
+ if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0)
+ return -1;
+ }
+
+ if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, vol_noadouble(vol), vol->v_umask))
+ return -1;
+
+ if (!dir_rx_set(mode)) {
+ if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0)
+ return -1 ;
+ }
+ return 0;
+}
+
+/* ----------------- */
+static int setdirowner_adouble_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 && 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 RF_setdirowner_adouble(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
+
+{
+ int noadouble = vol_noadouble(vol);
+ char *adouble_p;
+ struct stat st;
+ struct perm owner;
+
+ owner.uid = uid;
+ owner.gid = gid;
+
+ adouble_p = ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR ));
+
+ if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, &owner, noadouble, vol->v_umask))
+ 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_adouble(const struct vol *vol, const char *file )
+{
+ return netatalk_unlink(vol->vfs->ad_path( file, ADFLAGS_HF));
+}
+
+/* ----------------- */
+int RF_renamefile_adouble(const struct vol *vol, const char *src, const char *dst)
+{
+ char adsrc[ MAXPATHLEN + 1];
+ int err = 0;
+
+ strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
+ if (unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) < 0) {
+ struct stat st;
+
+ err = errno;
+ if (errno == ENOENT) {
+ struct adouble ad;
+
+ if (stat(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);
+ if (!unix_rename( adsrc, vol->vfs->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;
+}
+
+#ifdef HAVE_NFSv4_ACLS
+static int RF_acl(const struct vol *vol, const char *path, int cmd, int count, ace_t *aces)
+{
+ static char buf[ MAXPATHLEN + 1];
+ struct stat st;
+
+ if ((stat(path, &st)) != 0)
+ return -1;
+ if (S_ISDIR(st.st_mode)) {
+ if ((snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path)) < 0)
+ return -1;
+ /* set acl on .AppleDouble dir first */
+ if ((acl(buf, cmd, count, aces)) != 0)
+ return -1;
+ /* now set ACL on ressource fork */
+ if ((acl(vol->vfs->ad_path(path, ADFLAGS_DIR), cmd, count, aces)) != 0)
+ return -1;
+ } else
+ /* set ACL on ressource fork */
+ if ((acl(vol->vfs->ad_path(path, ADFLAGS_HF), cmd, count, aces)) != 0)
+ return -1;
+
+ return 0;
+}
+
+static int RF_remove_acl(const struct vol *vol, const char *path, int dir)
+{
+ int ret;
+ static char buf[ MAXPATHLEN + 1];
+
+ if (dir) {
+ if ((snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path)) < 0)
+ return AFPERR_MISC;
+ /* remove ACL from .AppleDouble/.Parent first */
+ if ((ret = remove_acl(vol->vfs->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
+ return ret;
+ /* now remove from .AppleDouble dir */
+ if ((ret = remove_acl(buf)) != AFP_OK)
+ return ret;
+ } else
+ /* remove ACL from ressource fork */
+ if ((ret = remove_acl(vol->vfs->ad_path(path, ADFLAGS_HF))) != AFP_OK)
+ return ret;
+
+ return AFP_OK;
+}
+#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(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
+
+{
+ struct stat st;
+ char *ad_p;
+ struct perm owner;
+
+ owner.uid = uid;
+ owner.gid = gid;
+
+
+ ad_p = ad_dir(vol->vfs->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(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(const struct vol *vol _U_)
+{
+ 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( ".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)
+{
+ struct set_mode *param = data;
+
+ return setfilmode(name, param->mode, param->st, v_umask);
+}
+
+static int ads_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+{
+ 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(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+ return ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_HF )), mode, st, vol->v_umask);
+}
+
+/* ------------------- */
+static int RF_setdirunixmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+ char *adouble = vol->vfs->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->vfs->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)
+{
+
+ 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(const struct vol *vol, const char * name, mode_t mode, struct stat *st _U_)
+{
+ char *adouble = vol->vfs->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_)
+{
+ 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_)
+{
+ 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;
+}
+
+static int RF_setdirowner_ads(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
+{
+ 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->vfs->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(const struct vol *vol, const char *file )
+{
+ char *ad_p = ad_dir(vol->vfs->ad_path(file, ADFLAGS_HF ));
+
+ return ads_delete_rf(ad_p);
+}
+
+/* --------------------------- */
+int RF_renamefile_ads(const struct vol *vol, const char *src, const char *dst)
+{
+ char adsrc[ MAXPATHLEN + 1];
+ int err = 0;
+
+ strcpy( adsrc, ad_dir(vol->vfs->ad_path( src, 0 )));
+ if (unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) < 0) {
+ struct stat st;
+
+ err = errno;
+ if (errno == ENOENT) {
+ struct adouble ad;
+
+ if (stat(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, dst );
+ if (!unix_rename( adsrc, ad_dir(vol->vfs->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;
+}
+
+/*************************************************************************
+ * osx adouble format
+ ************************************************************************/
+static int validupath_osx(const struct vol *vol, const char *name)
+{
+ return strncmp(name,"._", 2) && (
+ (vol->v_flags & AFPVOL_USEDOTS) ? netatalk_name(name): name[0] != '.');
+}
+
+/* ---------------- */
+int RF_renamedir_osx(const struct vol *vol, const char *oldpath, const char *newpath)
+{
+ /* We simply move the corresponding ad file as well */
+ char tempbuf[258]="._";
+ return rename(vol->vfs->ad_path(oldpath,0),strcat(tempbuf,newpath));
+}
+
+/* ---------------- */
+int RF_deletecurdir_osx(const struct vol *vol)
+{
+ return netatalk_unlink( vol->vfs->ad_path(".",0) );
+}
+
+/* ---------------- */
+static int RF_setdirunixmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+ return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st, vol->v_umask);
+}
+
+/* ---------------- */
+static int
+RF_setdirmode_osx(const struct vol *vol _U_, const char *name _U_, mode_t mode _U_, struct stat *st _U_)
+{
+ return 0;
+}
+
+/* ---------------- */
+static int
+RF_setdirowner_osx(const struct vol *vol _U_, const char *path _U_, uid_t uid _U_, gid_t gid _U_)
+{
+ return 0;
+}
+
+/* ---------------- */
+int RF_renamefile_osx(const struct vol *vol, const char *src, const char *dst)
+{
+ char adsrc[ MAXPATHLEN + 1];
+ int err = 0;
+
+ strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
+
+ if (unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) < 0) {
+ struct stat st;
+
+ err = errno;
+ if (errno == ENOENT && stat(adsrc, &st)) /* source has no ressource fork, */
+ return 0;
+ errno = err;
+ return -1;
+ }
+ return 0;
+}
+
+struct vfs_ops netatalk_adouble = {
+ /* ad_path: */ ad_path,
+ /* validupath: */ validupath_adouble,
+ /* rf_chown: */ RF_chown_adouble,
+ /* rf_renamedir: */ RF_renamedir_adouble,
+ /* rf_deletecurdir: */ RF_deletecurdir_adouble,
+ /* rf_setfilmode: */ RF_setfilmode_adouble,
+ /* rf_setdirmode: */ RF_setdirmode_adouble,
+ /* rf_setdirunixmode: */ RF_setdirunixmode_adouble,
+ /* rf_setdirowner: */ RF_setdirowner_adouble,
+ /* rf_deletefile: */ RF_deletefile_adouble,
+ /* rf_renamefile: */ RF_renamefile_adouble,
+#ifdef HAVE_NFSv4_ACLS
+ /* rf_acl: */ RF_acl,
+ /* rf_remove_acl */ RF_remove_acl
+#endif
+};
+
+struct vfs_ops netatalk_adouble_osx = {
+ /* ad_path: */ ad_path_osx,
+ /* validupath: */ validupath_osx,
+ /* rf_chown: */ RF_chown_adouble,
+ /* rf_renamedir: */ RF_renamedir_osx,
+ /* rf_deletecurdir: */ RF_deletecurdir_osx,
+ /* rf_setfilmode: */ RF_setfilmode_adouble,
+ /* rf_setdirmode: */ RF_setdirmode_osx,
+ /* rf_setdirunixmode:*/ RF_setdirunixmode_osx,
+ /* rf_setdirowner: */ RF_setdirowner_osx,
+ /* rf_deletefile: */ RF_deletefile_adouble,
+ /* rf_renamefile: */ RF_renamefile_osx,
+};
+
+/* samba sfm format. ad_path shouldn't be set her */
+struct vfs_ops netatalk_adouble_sfm = {
+ /* ad_path: */ ad_path_sfm,
+ /* validupath: */ validupath_adouble,
+ /* rf_chown: */ RF_chown_ads,
+ /* rf_renamedir: */ RF_renamedir_adouble,
+ /* rf_deletecurdir: */ RF_deletecurdir_ads,
+ /* rf_setfilmode: */ RF_setfilmode_ads,
+ /* rf_setdirmode: */ RF_setdirmode_ads,
+ /* rf_setdirunixmode:*/ RF_setdirunixmode_ads,
+ /* rf_setdirowner: */ RF_setdirowner_ads,
+ /* rf_deletefile: */ RF_deletefile_ads,
+ /* rf_renamefile: */ RF_renamefile_ads,
+};
+
+/* ---------------- */
+void initvol_vfs(struct vol *vol)
+{
+ /* adouble stuff */
+ if (vol->v_adouble == AD_VERSION2_OSX) {
+ vol->vfs = &netatalk_adouble_osx;
+ }
+ else if (vol->v_adouble == AD_VERSION1_SFM) {
+ vol->vfs = &netatalk_adouble_sfm;
+ }
+ else {
+ vol->vfs = &netatalk_adouble;
+ }
+
+ /* Extended Attributes */
+ if (vol->v_vfs_ea == AFPVOL_EA_SOLARIS) {
+
+#ifdef HAVE_SOLARIS_EAS
+ LOG(log_debug, logtype_afpd, "initvol_vfs: Enabling EA support with Solaris native EAs.");
+
+ netatalk_adouble.list_eas = sol_list_eas;
+ netatalk_adouble.get_easize = sol_get_easize;
+ netatalk_adouble.get_eacontent = sol_get_eacontent;
+ netatalk_adouble.set_ea = sol_set_ea;
+ netatalk_adouble.remove_ea = sol_remove_ea;
+#else
+ LOG(log_error, logtype_afpd, "initvol_vfs: Can't enable Solaris EA support.");
+ goto enable_adea;
+#endif
+ } else {
+ enable_adea:
+ /* default: AFPVOL_EA_AD */
+ LOG(log_debug, logtype_afpd, "initvol_vfs: Enabling EA support with adouble files.");
+
+ netatalk_adouble.set_ea = set_ea;
+ netatalk_adouble.list_eas = list_eas;
+ netatalk_adouble.get_easize = get_easize;
+ netatalk_adouble.get_eacontent = get_eacontent;
+ netatalk_adouble.remove_ea = remove_ea;
+ }
+}
+
-dnl $Id: summary.m4,v 1.5 2009-02-16 13:49:20 franklahm Exp $
+dnl $Id: summary.m4,v 1.6 2009-10-02 09:32:41 franklahm Exp $
dnl Autoconf macros, display configure summary
AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
if test "x$afp3" = "xyes"; then
AC_MSG_RESULT([ Large file support (>2GB) for AFP3: $wx_largefile])
fi
+ AC_MSG_RESULT([ Extended Attributes: $neta_cv_eas])
AC_MSG_RESULT([ DDP enabled: $netatalk_cv_ddp_enabled])
AC_MSG_RESULT([ CNID:])
AC_MSG_RESULT([ backends: $compiled_backends])
AC_MSG_RESULT([ force volume uid/gid: $netatalk_cv_force_uidgid])
AC_MSG_RESULT([ Apple 2 boot support: $compile_a2boot])
AC_MSG_RESULT([ ACL support: $neta_cv_nfsv4acl])
- AC_MSG_RESULT([ Extended Attributes: $neta_cv_extattrs])
if test x"$use_pam_so" = x"yes" -a x"$netatalk_cv_install_pam" = x"no"; then
AC_MSG_RESULT([])
AC_MSG_WARN([ PAM support was configured for your system, but the netatalk PAM configuration file])
-.TH AppleVolumes.default 5 "06 September 2004" 2.0.0 Netatalk
-.SH NAME
-AppleVolumes.default \- Configuration file used by afpd(8) to determine the shares made available through Appletalk
-.SH DESCRIPTION
-\fB:ETCDIR:/AppleVolumes.default\fR is the
-configuration file used by afpd to determine what
-portions of the file system will be shared via Apple Filing Protocol, as
-well as their behaviour. Any line not prefixed with # is interpreted. The
-configuration lines are composed like:
-.PP
-\fBpath\fR \fI[ volume name ] [ options
-]\fR
-.PP
-The path name must be a fully qualified path name, or a path name
-using either the ~ shell shorthand or any of the substitution variables,
-which are listed below.
-.PP
-The volume name is the name that appears in the Chooser ot the
-"connect to server" dialog on Macintoshes to represent the appropriate
-share. If there are spaces in the name, it should be in quotes (i.e. "File
-Share"). The volume name may not exceed 27 characters in length, and
-cannot contain the \fB':'\fR character.
-.RS
+'\" t
+.\" Title: AppleVolumes.default
+.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.74.3 <http://docbook.sf.net/>
+.\" Date: 02 Octobre 2009
+.\" Manual: Netatalk 2.1
+.\" Source: Netatalk 2.1
+.\" Language: English
+.\"
+.TH "APPLEVOLUMES\&.DEFAU" "5" "02 Octobre 2009" "Netatalk 2.1" "Netatalk 2.1"
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+AppleVolumes.default \- Configuration file used by \fBafpd\fR(8) to determine the shares made available through Appletalk
+.SH "DESCRIPTION"
+.PP
+:ETCDIR:/AppleVolumes\&.default
+is the configuration file used by
+\fBafpd\fR
+to determine what portions of the file system will be shared via Apple Filing Protocol, as well as their behaviour\&. Any line not prefixed with # is interpreted\&. Newline escaping is supported\&. The configuration lines are composed like:
+.PP
+path
+\fI[ volume name ] [ options ]\fR
+.PP
+The path name must be a fully qualified path name, or a path name using either the ~ shell shorthand or any of the substitution variables, which are listed below\&.
+.PP
+The volume name is the name that appears in the Chooser ot the "connect to server" dialog on Macintoshes to represent the appropriate share\&. If there are spaces in the name, it should be in quotes (i\&.e\&. "File Share")\&. The volume name may not exceed 27 characters in length, and cannot contain the
+\':\'
+character\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
\fBNote\fR
+.ps -1
+.br
.PP
-Each volume has to be configured on a \fBsingle\fR line.
+Each volume has to be configured on a
+\fBsingle\fR
+line\&. Though newline escaping is supported\&.
+.sp .5v
.RE
.PP
The possible options and their meanings are:
-.TP
+.PP
adouble:\fI[v1|v2|osx]\fR
-specify the format of the metadata files, which are used for
-saving Mac resource fork as well. Earlier versions used AppleDouble
-V1, the new default format is V2. Starting with Netatalk 2.0, the
-scheme MacOS X uses currently (10.3.x), is also supported
-.RS
+.RS 4
+Specify the format of the metadata files, which are used for saving Mac resource fork as well\&. Earlier versions used AppleDouble V1, the new default format is V2\&. Starting with Netatalk 2\&.0, the scheme MacOS X uses currently (10\&.3\&.x), is also supported
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
\fBNote\fR
-
-Using \fBadouble:osx\fR is \fBnot\fR recommended for production use. Its
-only aim is to temporarely share eg. FAT32 formatted FireWire
-harddrives written on a Macintosh with afpd. Apple's metadata
-scheme lacks several essential features, so using it on the
-server's side will break both CNIDs and MacOS 9
-compatibility
-.RE
-.TP
+.ps -1
+.br
+Using
+\fBadouble:osx\fR
+is
+\fBnot\fR
+recommended for production use\&. Its only aim is to temporarely share eg\&. FAT32 formatted FireWire harddrives written on a Macintosh with afpd\&. Apple\'s metadata scheme lacks several essential features, so using it on the server\'s side will break both CNIDs and MacOS 9 compatibility
+.sp .5v
+.RE
+.RE
+.PP
allow:\fI[users/groups]\fR
-The allow option allows the users and groups that access a
-share to be specified. Users and groups are specified, delimited by
-commas. Groups are designated by a @ prefix. Example:
-allow:user1,user2,@group
-.TP
+.RS 4
+The allow option allows the users and groups that access a share to be specified\&. Users and groups are specified, delimited by commas\&. Groups are designated by a @ prefix\&. Example: allow:user1,user2,@group
+.RE
+.PP
deny:\fI[users/groups]\fR
-The deny option specifies users and groups who are not allowed
-access to the share. It follows the same format as the allow
-option.
-.TP
+.RS 4
+The deny option specifies users and groups who are not allowed access to the share\&. It follows the same format as the allow option\&.
+.RE
+.PP
+allowed_hosts:\fI[IPv4 host address/IPv4 netmask bits[, \&.\&.\&. ]]\fR
+.RS 4
+Only listed hosts and networks are allowed, all others are rejected\&. Example: allowed_hosts:10\&.1\&.0\&.0/16,10\&.2\&.1\&.100
+.RE
+.PP
+denied_hosts:\fI[IPv4 host address/IPv4 netmask bits[, \&.\&.\&.]]\fR
+.RS 4
+Listed hosts and nets are rejected, all others are allowed\&. Example: denied_hosts: 192\&.168\&.100/24,10\&.1\&.1\&.1
+.RE
+.PP
cnidscheme:\fI[backend]\fR
-set the CNID backend to be used for the volume, default is
-[:DEFAULT_CNID_SCHEME:] available schemes:
-[:COMPILED_BACKENDS:]
-.TP
+.RS 4
+set the CNID backend to be used for the volume, default is [:DEFAULT_CNID_SCHEME:] available schemes: [:COMPILED_BACKENDS:]
+.RE
+.PP
dbpath:\fI[path]\fR
-Sets the database information to be stored in path. You have
-to specifiy a writable location, even if the volume is read
-only.
-.TP
+.RS 4
+Sets the database information to be stored in path\&. You have to specifiy a writable location, even if the volume is read only\&.
+.RE
+.PP
+ea:\fI[ad|solaris]\fR
+.RS 4
+Specify the format of the metadata files which are used for saving Extended Attributes\&.
+\fBad\fR
+is the default and stores the EAs inside the
+\&.AppleDouble
+directories\&.
+\fBsolaris\fR
+uses native
+\fI[Open]Solaris\fR
+EAs if available\&.
+.RE
+.PP
maccharset:\fI[charset]\fR
-specifies the mac client codepage for this Volume, e.g.
-"MAC_ROMAN", "MAC_CYRILLIC". If not specified the setting from
-\fBafpd.conf\fR is inherited. This setting is only
-required if you need volumes, where the mac codepage differs from
-the one globally set in \fBafpd.conf\fR.
-.TP
+.RS 4
+specifies the mac client codepage for this Volume, e\&.g\&. "MAC_ROMAN", "MAC_CYRILLIC"\&. If not specified the setting from
+afpd\&.conf
+is inherited\&. This setting is only required if you need volumes, where the mac codepage differs from the one globally set in
+afpd\&.conf\&.
+.RE
+.PP
options:\fI[option]\fR
-This allows multiple options to be specified in a comma
-delimited format. The available options are:
-.RS
-.TP
+.RS 4
+This allows multiple options to be specified in a comma delimited format\&. The available options are:
+.PP
+acls
+.RS 4
+Enable ACLs on this volume\&. Requires a
+\fINFSv4 ACLs\fR
+compatible filesystem (e\&.g\&. ZFS) and an ACL API compatible to *Solaris\&. In other words: this requires Solaris, Opensolaris or a derived distribution\&.
+.RE
+.PP
+tm
+.RS 4
+Enable Time Machine suport for this volume\&.
+.RE
+.PP
+invisibledots
+.RS 4
+Use with
+\fBusedots\fR: make dot files invisible\&.
+.RE
+.PP
limitsize
-Limit disk size reporting to 2GB. This can be used for
-older Macintoshes using newer Appleshare clients.
-.TP
+.RS 4
+Limit disk size reporting to 2GB\&. This can be used for older Macintoshes using newer Appleshare clients\&.
+.RE
+.PP
+preexec_close
+.RS 4
+a non\-zero return code from preexec close the volume being immediately, preventing clients to mount/see the volume in question\&.
+.RE
+.PP
ro
-Specifies the share as being read only for all users.
-The .AppleDB directory has to be writeable, you can use the
-\fB\-dbpath\fR option to relocate it.
-.TP
-usedots
-Don't do :hex translation for dot files. note: when this
-option gets set, certain file names become illegal. These are
-\&.Parent and anything that starts with .Apple. Also, dot files
-created on the unix side are marked invisible.
-.TP
+.RS 4
+Specifies the share as being read only for all users\&. The \&.AppleDB directory has to be writeable, you can use the
+\fB\-dbpath\fR
+option to relocate it\&.
+.RE
+.PP
root_preexec_close
-a non\-zero return code from root_preexec closes the
-volume immediately, preventing clients to mount/see the volume
-in question.
-.TP
-preexec_close
-a non\-zero return code from preexec close the volume
-being immediately, preventing clients to mount/see the volume
-in question.
+.RS 4
+a non\-zero return code from root_preexec closes the volume immediately, preventing clients to mount/see the volume in question\&.
+.RE
+.PP
+upriv
+.RS 4
+use AFP3 unix privileges\&. Become familiar with the new "unix privileges" AFP permissions concepts in MacOS X before using this option\&. See also:
+\fBperm|fperm|dperm\fR\&.
+.RE
+.PP
+usedots
+.RS 4
+Don\'t do :hex translation for dot files\&. note: when this option gets set, certain file names become illegal\&. These are \&.Parent and anything that starts with \&.Apple\&. See also
+\fBinvisibledots\fR\&.
+.RE
.RE
-.TP
+.PP
password:\fI[password]\fR
-This option allows you to set a volume password, which can be
-a maximum of 8 characters long (using ASCII strongly recommended at
-the time of this writing).
-.TP
+.RS 4
+This option allows you to set a volume password, which can be a maximum of 8 characters long (using ASCII strongly recommended at the time of this writing)\&.
+.RE
+.PP
+perm|fperm|dperm:[mode]
+.RS 4
+Add(or) with the client requested permissions:
+\fBperm\fR
+affects files and directories,
+\fBfperm\fR
+is for files only,
+\fBdperm\fR
+is for directories only\&. Use with
+\fBoptions:upriv\fR\&.
+.PP
+\fBExample.\ \&Volume for a collaborative workgroup\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+/path/to/volume "Workgroup" options:upriv dperm:0770 fperm:0660
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.PP
preexec:\fI[command]\fR
-command to be run when the volume is mounted, ignored for user
-defined volumes
-.TP
+.RS 4
+command to be run when the volume is mounted, ignored for user defined volumes
+.RE
+.PP
postexec:\fI[command]\fR
-command to be run when the volume is closed, ignored for user
-defined volumes
-.TP
+.RS 4
+command to be run when the volume is closed, ignored for user defined volumes
+.RE
+.PP
root_preexec:\fI[command]\fR
-command to be run as root when the volume is mounted, ignored
-for user defined volumes
-.TP
+.RS 4
+command to be run as root when the volume is mounted, ignored for user defined volumes
+.RE
+.PP
root_postexec:\fI[command]\fR
-command to be run as root when the volume is closed, ignored
-for user defined volumes
-.TP
+.RS 4
+command to be run as root when the volume is closed, ignored for user defined volumes
+.RE
+.PP
rolist:[\fBusers/groups\fR]
-Allows certain users and groups to have read\-only access to a
-share. This follows the allow option format.
-.TP
+.RS 4
+Allows certain users and groups to have read\-only access to a share\&. This follows the allow option format\&.
+.RE
+.PP
rwlist:\fI[users/groups]\fR
-Allows certain users and groups to have read/write access to a
-share. This follows the allow option format.
-.TP
+.RS 4
+Allows certain users and groups to have read/write access to a share\&. This follows the allow option format\&.
+.RE
+.PP
veto:\fI[vetoed name]\fR
-hide files and directories,where the path matches one of the
-\&'/' delimited vetoed names. Matches are partial, e.g. path is
-\fB/abc/def/file\fR and veto:/abc/ will hide the
-file.
-.TP
+.RS 4
+hide files and directories,where the path matches one of the \'/\' delimited vetoed names\&. Matches are partial, e\&.g\&. path is
+/abc/def/file
+and veto:/abc/ will hide the file\&.
+.RE
+.PP
volcharset:\fI[charset]\fR
-specifies the volume codepage, e.g. "UTF8", "UTF8\-MAC",
-"ISO\-8859\-15". Defaults to "UTF8".
+.RS 4
+specifies the volume codepage, e\&.g\&. "UTF8", "UTF8\-MAC", "ISO\-8859\-15"\&. Defaults to "UTF8"\&.
+.RE
.SH "VARIABLE SUBSTITUTIONS"
-You can use variables in both volume path and volume name.
-.TP
-1.
-if you specify an unknown variable, it will not get
-converted.
-.TP
-2.
-if you specify a known variable, but that variable doesn't have
-a value, it will get ignored.
+.PP
+You can use variables in both volume path and volume name\&.
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 1.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP " 1." 4.2
+.\}
+if you specify an unknown variable, it will not get converted\&.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 2.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP " 2." 4.2
+.\}
+if you specify a known variable, but that variable doesn\'t have a value, it will get ignored\&.
+.RE
.PP
The variables which can be used for substitutions are:
-.TP
+.PP
$b
+.RS 4
basename
-.TP
+.RE
+.PP
$c
-client's ip or appletalk address
-.TP
+.RS 4
+client\'s ip or appletalk address
+.RE
+.PP
$d
+.RS 4
volume pathname on server
-.TP
+.RE
+.PP
$f
-full name (contents of the gecos field in the passwd
-file)
-.TP
+.RS 4
+full name (contents of the gecos field in the passwd file)
+.RE
+.PP
$g
+.RS 4
group name
-.TP
+.RE
+.PP
$h
+.RS 4
hostname
-.TP
+.RE
+.PP
$i
-client's ip, without port
-.TP
+.RS 4
+client\'s ip, without port
+.RE
+.PP
$s
+.RS 4
server name (this can be the hostname)
-.TP
+.RE
+.PP
$u
-user name (if guest, it is the user that guest is running
-as)
-.TP
+.RS 4
+user name (if guest, it is the user that guest is running as)
+.RE
+.PP
$v
+.RS 4
volume name (either ADEID_NAME or basename of path)
-.TP
+.RE
+.PP
$z
+.RS 4
appletalk zone (may not exist)
-.TP
+.RE
+.PP
$$
+.RS 4
prints dollar sign ($)
+.RE
.PP
-When using variable substitution in the volume name, always keep in
-mind, not to exceed the 27 characters limit
+When using variable substitution in the volume name, always keep in mind, not to exceed the 27 characters limit
.PP
-\fBUsing variable substitution when defining volumes\fR
+\fBExample.\ \&Using variable substitution when defining volumes\fR
.PP
+.if n \{\
+.RS 4
+.\}
.nf
/home/groups/$g "Groupdir for $g"
~ "$f is the best one"
.fi
-
-We define "groupdirs" for each primary
-group and use a personalized server name for homedir shares.
+.if n \{\
+.RE
+.\}
+.sp
+We define "groupdirs" for each primary group and use a personalized server name for homedir shares\&.
.SH "CNID BACKENDS"
-The AFP protocol mostly refers to files and directories by ID and
-not by name. Netatalk needs a way to store these ID's in a persistent way,
-to achieve this several different CNID backends are available. The CNID
-Databases are by default located in the \fB.AppleDB\fR
-folder in the volume root.
-.TP
+.PP
+The AFP protocol mostly refers to files and directories by ID and not by name\&. Netatalk needs a way to store these ID\'s in a persistent way, to achieve this several different CNID backends are available\&. The CNID Databases are by default located in the
+\&.AppleDB
+folder in the volume root\&.
+.PP
cdb
-"Concurrent database", backend is based on Sleepycat's Berkely
-DB. With this backend several afpd deamons access
-the CNID database directly. Berkeley DB locking is used to
-synchronize access, if more than one afpd process
-is active for a volume. The drawback is, that the crash of a single
-afpd process might corrupt the database.
-.TP
+.RS 4
+"Concurrent database", backend is based on Sleepycat\'s Berkely DB\&. With this backend several
+\fBafpd\fR
+deamons access the CNID database directly\&. Berkeley DB locking is used to synchronize access, if more than one
+\fBafpd\fR
+process is active for a volume\&. The drawback is, that the crash of a single
+\fBafpd\fR
+process might corrupt the database\&.
+.RE
+.PP
dbd
+.RS 4
Access to the CNID database is restricted to the
-cnid_metad daemon process.
-afpd processes communicate with the daemon for
-database reads and updates. If built with Berkeley DB transactions
-the probability for database corruption is practically zero, but
-performance can be slower than with \fBcdb\fR
-.TP
+\fBcnid_metad\fR
+daemon process\&.
+\fBafpd\fR
+processes communicate with the daemon for database reads and updates\&. If built with Berkeley DB transactions the probability for database corruption is practically zero, but performance can be slower than with
+\fBcdb\fR
+.RE
+.PP
last
-This backend is an exception, in terms of ID persistency. ID's
-are only valid for the current session. This is basically what
-afpd did in the 1.5 (and 1.6) versions. This
-backend is still available, as it is useful for e.g. sharing
-cdroms.
-
+.RS 4
+This backend is an exception, in terms of ID persistency\&. ID\'s are only valid for the current session\&. This is basically what
+\fBafpd\fR
+did in the 1\&.5 (and 1\&.6) versions\&. This backend is still available, as it is useful for e\&.g\&. sharing cdroms\&.
+.sp
\fBWarning\fR: It is
-\fINOT\fR recommended to use this backend for volumes
-anymore, as afpd now relies heavily on a
-persistent ID database. Aliases will likely not work and filename
-mangling is not supported.
-.PP
-Even though ./configure \-\-help might show that
-there are other CNID backends available, be warned those are likely broken
-or mainly used for testing. Don't use them unless you know what you're
-doing, they may be removed without further notice from future
-versions.
+\fINOT\fR
+recommended to use this backend for volumes anymore, as
+\fBafpd\fR
+now relies heavily on a persistent ID database\&. Aliases will likely not work and filename mangling is not supported\&.
+.RE
+.PP
+Even though
+\fB\&./configure \-\-help\fR
+might show that there are other CNID backends available, be warned those are likely broken or mainly used for testing\&. Don\'t use them unless you know what you\'re doing, they may be removed without further notice from future versions\&.
.SH "CHARSET OPTIONS"
-With OS X Apple introduced the AFP3 protocol. One of the most
-important changes was that AFP3 uses unicode names encoded as UTF\-8
-decomposed. Previous AFP/OS versions used codepages, like MacRoman,
-MacCentralEurope, etc.
-.PP
-afpd needs a way to preserve extended macintosh
-characters, or characters illegal in unix filenames, when saving files on
-a unix filesystem. Earlier versions used the the so called CAP encoding.
-An extended character (>0x7F) would be converted to a :xx sequence,
-e.g. the Apple Logo (MacRoman: 0XF0) was saved as \fB:f0\fR.
-Some special characters will be converted as to :xx notation as well.
-\&'\fB/\fR' will be encoded to \fB:2f\fR, if
-\fB\-usedots\fR is not specified, a leading dot
-\&'\fB.\fR' will be encoded as \fB:2e\fR.
-.PP
-This version now uses UTF\-8 as the default encoding for names.
-Special characters, like '\fB/\fR' and a leading
-\&'\fB.\fR' will still be CAP style encoded .
-.PP
-The \fB\-volcharset\fR option will allow you to select
-another volume encoding. E.g. for western users another useful setting
-could be \-volcharset ISO\-8859\-15. apfd will accept any
-\fBiconv\fR(1) provided charset. If a character cannot be converted
-from the mac codepage to the selected volcharset, afpd will save it as a
-CAP encoded character. For AFP3 clients, afpd will
-convert the UTF\-8 character to \fB\-maccharset\fR first. If this
-conversion fails, you'll receive a \-50 error on the mac.
-.PP
-\fINote\fR: Whenever you can, please stick with the
-default UTF\-8 volume format.
+.PP
+With OS X Apple introduced the AFP3 protocol\&. One of the most important changes was that AFP3 uses unicode names encoded as UTF\-8 decomposed\&. Previous AFP/OS versions used codepages, like MacRoman, MacCentralEurope, etc\&.
+.PP
+\fBafpd\fR
+needs a way to preserve extended macintosh characters, or characters illegal in unix filenames, when saving files on a unix filesystem\&. Earlier versions used the the so called CAP encoding\&. An extended character (>0x7F) would be converted to a :xx sequence, e\&.g\&. the Apple Logo (MacRoman: 0XF0) was saved as
+:f0\&. Some special characters will be converted as to :xx notation as well\&. \'/\' will be encoded to
+:2f, if
+\fB\-usedots\fR
+is not specified, a leading dot \'\&.\' will be encoded as
+:2e\&.
+.PP
+This version now uses UTF\-8 as the default encoding for names\&. Special characters, like \'/\' and a leading \'\&.\' will still be CAP style encoded \&.
+.PP
+The
+\fB\-volcharset\fR
+option will allow you to select another volume encoding\&. E\&.g\&. for western users another useful setting could be \-volcharset ISO\-8859\-15\&.
+\fBapfd\fR
+will accept any
+\fBiconv\fR(1)
+provided charset\&. If a character cannot be converted from the mac codepage to the selected volcharset, afpd will save it as a CAP encoded character\&. For AFP3 clients,
+\fBafpd\fR
+will convert the UTF\-8
+character to
+\fB\-maccharset\fR
+first\&. If this conversion fails, you\'ll receive a \-50 error on the mac\&.
+.PP
+\fINote\fR: Whenever you can, please stick with the default UTF\-8 volume format\&.
.SH "COMPATIBILITY WITH EARLIER VERSIONS"
-To use a volume created with an earlier afpd
-version, you'll have to specify the following options:
.PP
-\fBuse a 1.x style volume\fR
+To use a volume created with an earlier
+\fBafpd\fR
+version, you\'ll have to specify the following options:
.PP
+\fBExample.\ \&use a 1.x style volume\fR
+.sp
+.if n \{\
+.RS 4
+.\}
.nf
/path/to/volume "Volname" adouble:v1 volcharset:ASCII
.fi
+.if n \{\
+.RE
+.\}
.PP
-In case you used an NLS you could try using a compatible iconv
-charset for \fB\-volcharset\fR.
-.PP
-\fBuse a 1.x style volume, created with maccode.iso8859\-1\fR
+In case you used an NLS you could try using a compatible iconv charset for
+\fB\-volcharset\fR\&.
.PP
+\fBExample.\ \&use a 1.x style volume, created with maccode.iso8859-1\fR
+.sp
+.if n \{\
+.RS 4
+.\}
.nf
/path/to/volume "Volname" adouble:v1 volcharset:ISO\-8859\-1
.fi
+.if n \{\
+.RE
+.\}
.PP
-You should consider converting old style volumes to the new
-UTF\-8/AD2 format. The safest way to do this, is to create a new volume
-with the default options and copy the files between this volumes with a
-mac.
+You should consider converting old style volumes to the new UTF\-8/AD2 format\&. The safest way to do this, is to create a new volume with the default options and copy the files between this volumes with a mac\&.
.PP
-\fINote\fR: Using above example options will allow
-you to downgrade to 1.x netatalk again.
+\fINote\fR: Using above example options will allow you to downgrade to 1\&.x netatalk again\&.
.PP
-\fINote\fR: Some 1.x NLS files used non standard
-mappings, e.g. \fBmaccode.iso8859\-1.adapted\fR. This is not
-supported anymore. You'll have to copy the contents of those volumes files
-to a Mac and then back to the netatalk server, preferably to an UTF\-8
-volume.
+\fINote\fR: Some 1\&.x NLS files used non standard mappings, e\&.g\&.
+maccode\&.iso8859\-1\&.adapted\&. Three 1\&.x CAP double\-byte maccharsets are incompatible to netatalk 2\&.x; "MAC_CHINESE_TRAD", "MAC_JAPANESE" and "MAC_KOREAN"\&. These are not supported anymore\&. You\'ll have to copy the contents of those volumes files to a Mac and then back to the netatalk server, preferably to an UTF\-8 volume\&.
.SH "ADVANCED OPTIONS"
-The following options should only be used after serious
-consideration. Be sure you fully understood the, sometimes complex,
-consequences, before using them.
-.TP
+.PP
+The following options should only be used after serious consideration\&. Be sure you fully understood the, sometimes complex, consequences, before using them\&.
+.PP
casefold:\fB[option]\fR
-The casefold option handles, if the case of filenames should
-be changed. The available options are:
-
-\fBtolower\fR \- Lowercases names in both
-directions.
-
-\fBtoupper\fR \- Uppercases names in both
-directions.
-
-\fBxlatelower\fR \- Client sees lowercase, server
-sees uppercase.
-
-\fBxlateupper\fR \- Client sees uppercase, server
-sees lowercase.
-.TP
+.RS 4
+The casefold option handles, if the case of filenames should be changed\&. The available options are:
+.sp
+\fBtolower\fR
+\- Lowercases names in both directions\&.
+.sp
+\fBtoupper\fR
+\- Uppercases names in both directions\&.
+.sp
+\fBxlatelower\fR
+\- Client sees lowercase, server sees uppercase\&.
+.sp
+\fBxlateupper\fR
+\- Client sees uppercase, server sees lowercase\&.
+.RE
+.PP
options:[\fBoption\fR]
-This allows multiple options to be specified in a comma
-delimited format. The available options are:
-.RS
-.TP
+.RS 4
+This allows multiple options to be specified in a comma delimited format\&. The available options are:
+.PP
+nocnidcache
+.RS 4
+If set
+\fBafpd\fR
+doesn\'t store the ID information in AppleDouble V2 header files\&. As these IDs are used for caching and as a database backup, this option normally shouldn\'t be set\&.
+.RE
+.PP
crlf
-Enables crlf translation for TEXT files, automatically
-converting macintosh line breaks into Unix ones. Use of this
-option might be dangerous since some older programs store
-binary data files as type "TEXT" when saving and switch the
-filetype in a second step. Afpd will
-potentially destroy such files when "erroneously" changing
-bytes in order to do line break translation.
-.TP
+.RS 4
+Enables crlf translation for TEXT files, automatically converting macintosh line breaks into Unix ones\&. Use of this option might be dangerous since some older programs store binary data files as type "TEXT" when saving and switch the filetype in a second step\&.
+\fBAfpd\fR
+will potentially destroy such files when "erroneously" changing bytes in order to do line break translation\&.
+.RE
+.PP
dropbox
-Allows a volume to be declared as being a "dropbox."
-Note that netatalk must be compiled with dropkludge support
-for this to function. \fIWarning\fR: This
-option is deprecated and might not work as expected.
-.TP
+.RS 4
+Allows a volume to be declared as being a "dropbox\&." Note that netatalk must be compiled with dropkludge support for this to function\&.
+\fIWarning\fR: This option is deprecated and might not work as expected\&.
+.RE
+.PP
mswindows
-Forces filename restrictions imposed by MS WinXX.
-\fIWarning\fR: This is \fINOT\fR
-recommened for volumes mainly used by Macs. Please make sure
-you fully understand this option before using it.
-.TP
+.RS 4
+Forces filename restrictions imposed by MS WinXX\&.
+\fIWarning\fR: This is
+\fINOT\fR
+recommened for volumes mainly used by Macs\&. Please make sure you fully understand this option before using it\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBWarning\fR
+.ps -1
+.br
+This option breaks direct saving to netatalk volumes from some applications, i\&.e\&. OfficeX\&.
+.sp .5v
+.RE
+.RE
+.PP
noadouble
-Forces afpd to not create
-\&.AppleDouble directories unless macintosh metadata needs to be
-written. This option is only useful if you want to share files
-mostly used NOT by macs, causing afpd to
-not automatically create .AppleDouble subdirs containing AD
-header files in every directory it enters (which will it do by
-default).
-
-In case, you save or change files from mac clients, AD
-metadata files have to be written even in case you set this
-option. So you can't avoid the creation of .AppleDouble
-directories and its contents when you give macs write access
-to a share and they make use of it.
-
-Try to avoid \fBnoadouble\fR whenever
-possible.
-.TP
+.RS 4
+Forces
+\fBafpd\fR
+to not create \&.AppleDouble directories unless macintosh metadata needs to be written\&. This option is only useful if you want to share files mostly used NOT by macs, causing
+\fBafpd\fR
+to not automatically create \&.AppleDouble subdirs containing AD header files in every directory it enters (which will it do by default)\&.
+.sp
+In case, you save or change files from mac clients, AD metadata files have to be written even in case you set this option\&. So you can\'t avoid the creation of \&.AppleDouble directories and its contents when you give macs write access to a share and they make use of it\&.
+.sp
+Try to avoid
+\fBnoadouble\fR
+whenever possible\&.
+.RE
+.PP
nodev
-always use 0 for device number, helps when the device
-number is not constant across a reboot, cluster, ...
-.TP
+.RS 4
+always use 0 for device number, helps when the device number is not constant across a reboot, cluster, \&.\&.\&.
+.RE
+.PP
nofileid
-don't advertise createfileid, resolveid, deleteid
-calls.
-.TP
+.RS 4
+don\'t advertise createfileid, resolveid, deleteid calls\&.
+.RE
+.PP
nohex
-Disables :hex translations for anything except dot
-files. This option makes the \fB'/\fR' character
-illegal.
-.TP
-prodos
-Provides compatibility with Apple II clients.
-.TP
+.RS 4
+Disables :hex translations for anything except dot files\&. This option makes the
+\'/\' character illegal\&.
+.RE
+.PP
nostat
-don't stat volume path when enumerating volumes list,
-useful for automounting or volumes created by a preexec
-script.
-.TP
-upriv
-use AFP3 unix privileges. Become familiar with the new
-"unix privileges" AFP permissions concepts in MacOS X before
-using this option.
+.RS 4
+don\'t stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script\&.
+.RE
+.PP
+prodos
+.RS 4
+Provides compatibility with Apple II clients\&.
+.RE
.RE
.SH "SEE ALSO"
-\fBafpd.conf\fR(5), \fBafpd\fR(8)
-
+.PP
+\fBafpd.conf\fR(5),
+\fBafpd\fR(8)