+Changes in 2.2
+==============
+
+* NEW: afpd: new volume option "nonetids"
+* NEW: afpd: ACL access check caching
* NEW: afpd: FCE event notifications
* NEW: afpd: new option "-mimicmodel" for specifying Bonjour model registration
+* UPD: Support for Berkeley DB 5.1
+* UPD: case-conversion is based on Unicode 6.0.0
+* UPD: cnid_metad: allow up to 4096 volumes
+* UPD: afpd: only forward SIGTERM and SIGUSR1 from parent to childs
+* UPD: afpd: use internal function instead of popening du -sh in order to
+ calculate the used size of a volume for option "volsizelimit"
+* UPD: afpd: Add negative UUID caching, enhance local UUID handling
+* FIX: afpd: configuration reload with SIGHUP
+* FIX: afpd: crashes in the dircache
+* FIX: afpd: Correct afp logout vs dsi eof behaviour
+* FIX: afpd: new catsearch was broken
+* FIX: afpd: only use volume UUIDs in master afpd
+* FIX: dbd: Multiple fixes, reliable locking
+* FIX: ad file suite: fix an error that resulted in CNID database inconsistencies
Changes in 2.2beta4
===================
-2.2-beta4
\ No newline at end of file
+2.2-rc1
\ No newline at end of file
#ifndef AD_H
#define AD_H
-#define _XOPEN_SOURCE 600
+#if !defined(__FreeBSD__)
+# define _XOPEN_SOURCE 600
+#endif
#include <sys/types.h>
#include <sys/stat.h>
islink = !fdval && S_ISLNK(fs->st_mode);
mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
+#if defined(__FreeBSD__)
+ TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
+#else
TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
+#endif
+
if (utimes(to.p_path, tv)) {
SLOG("utimes: %s", to.p_path);
rval = 1;
ret = getnamefromuuid( uuid, &name, &type);
if (ret == 0) {
switch (type) {
- case UUID_LOCAL:
- printf("local UUID: %s\n", optarg);
- break;
case UUID_USER:
printf("UUID: %s ==> User: %s\n", optarg, name);
break;
case UUID_GROUP:
printf("UUID: %s ==> Group: %s\n", optarg, name);
break;
+ default:
+ printf("???: %s\n", optarg);
+ break;
}
free(name);
} else {
acl_perm_t perm;
return acl_get_perm_np(permset_d, perm);
],
- [samba_cv_HAVE_ACL_GET_PERM_NP=yes],
- [samba_cv_HAVE_ACL_GET_PERM_NP=no])
+ [netatalk_cv_HAVE_ACL_GET_PERM_NP=yes],
+ [netatalk_cv_HAVE_ACL_GET_PERM_NP=no])
LIBS=$acl_LIBS
])
if test x"netatalk_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
fi
+
+ AC_CACHE_CHECK([for acl_from_mode], netatalk_cv_HAVE_ACL_FROM_MODE,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS $ACL_LIBS"
+ AC_CHECK_FUNCS(acl_from_mode,
+ [netatalk_cv_HAVE_ACL_FROM_MODE=yes],
+ [netatalk_cv_HAVE_ACL_FROM_MODE=no])
+ LIBS=$acl_LIBS
+ ])
+ if test x"netatalk_cv_HAVE_ACL_FROM_MODE" = x"yes"; then
+ AC_DEFINE(HAVE_ACL_FROM_MODE,1,[Whether acl_from_mode() is available])
+ fi
+
+
else
AC_MSG_NOTICE(ACL support is not avaliable)
AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
fi
if test x"$with_acl_support" = x"yes" ; then
+ AC_CHECK_HEADERS([acl/libacl.h])
AC_DEFINE(HAVE_ACLS,1,[Whether ACLs support is available])
AC_SUBST(ACL_LIBS)
fi
printf (CSOURCE "http\:\/\/www.unicode.org\/Public\/UNIDATA\/UnicodeData.txt\n");
printf (CSOURCE "\*\/\n");
printf (CSOURCE "\n");
-printf (CSOURCE "\#include \<netatalk\/endian.h\>\n");
+printf (CSOURCE "\#include \<stdint.h\>\n");
printf (CSOURCE "\#include \<atalk\/unicode.h\>\n");
printf (CSOURCE "\#include \"%s\"\n", $ARGV[1]);
printf (CSOURCE "\n");
$char_start = ($block_start -1)* 64;
$char_end = ($block_end * 64) -1;
- printf(CHEADER "static const u\_int16\_t %s\_table\_%d\[%d\] \= \{\n",
+ printf(CHEADER "static const uint16\_t %s\_table\_%d\[%d\] \= \{\n",
$_[0], $table_no, $char_end - $char_start +1);
for ($char = $char_start ; $char <= $char_end ; $char++) {
printf(CSOURCE "\/*******************************************************************\n");
printf(CSOURCE " Convert a surrogate pair to %s case.\n", $_[0]);
printf(CSOURCE "*******************************************************************\/\n");
- printf(CSOURCE "u\_int32\_t to%s\_sp\(u\_int32\_t val\)\n", $_[0]);
+ printf(CSOURCE "uint32\_t to%s\_sp\(uint32\_t val\)\n", $_[0]);
printf(CSOURCE "{\n");
$table_no = 1;
$char_start = ($block_start -1)* 64;
$char_end = ($block_end * 64) -1;
- printf(CHEADER "static const u\_int32\_t %s\_table\_sp\_%d\[%d\] \= \{\n",
+ printf(CHEADER "static const uint32\_t %s\_table\_sp\_%d\[%d\] \= \{\n",
$_[0], $table_no, $char_end - $char_start +1);
for ($char = $char_start ; $char <= $char_end ; $char++) {
endif
-noinst_HEADERS = auth.h afp_config.h desktop.h directory.h fce_api_internal.h file.h \
- filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \
+noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
+ filedir.h fork.h icon.h mangle.h misc.h status.h switch.h \
uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h \
dircache.h afp_zeroconf.h afp_avahi.h
#endif
#ifdef HAVE_POSIX_ACLS
#include <sys/acl.h>
+#endif
+#ifdef HAVE_ACL_LIBACL_H
#include <acl/libacl.h>
#endif
/* uid/gid first */
EC_ZERO(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
switch (uuidtype) {
- case UUID_LOCAL:
- free(name);
- name = NULL;
- darwin_aces++;
- continue;
case UUID_USER:
EC_NULL_LOG(pwd = getpwnam(name));
nfsv4_aces->a_who = pwd->pw_uid;
nfsv4_aces->a_who = (uid_t)(grp->gr_gid);
nfsv4_ace_flags |= ACE_IDENTIFIER_GROUP;
break;
+ default:
+ LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: unkown uuidtype");
+ EC_FAIL;
}
free(name);
name = NULL;
nfsv4_ace_rights |= darwin_to_nfsv4_rights[i].to;
}
- LOG(log_debug9, logtype_afpd, "map_aces_darwin_to_solaris: ACE flags: Darwin:%08x -> NFSv4:%04x", darwin_ace_flags, nfsv4_ace_flags);
- LOG(log_debug9, logtype_afpd, "map_aces_darwin_to_solaris: ACE rights: Darwin:%08x -> NFSv4:%08x", darwin_ace_rights, nfsv4_ace_rights);
+ LOG(log_debug9, logtype_afpd,
+ "map_aces_darwin_to_solaris: ACE flags: Darwin:%08x -> NFSv4:%04x",
+ darwin_ace_flags, nfsv4_ace_flags);
+ LOG(log_debug9, logtype_afpd,
+ "map_aces_darwin_to_solaris: ACE rights: Darwin:%08x -> NFSv4:%08x",
+ darwin_ace_rights, nfsv4_ace_rights);
nfsv4_aces->a_flags = nfsv4_ace_flags;
nfsv4_aces->a_access_mask = nfsv4_ace_rights;
EC_ZERO_LOG(acl_get_permset(e, &permset));
+#ifdef HAVE_ACL_GET_PERM_NP
+ if (acl_get_perm_np(permset, ACL_READ))
+#else
if (acl_get_perm(permset, ACL_READ))
+#endif
rights = DARWIN_ACE_READ_DATA
| DARWIN_ACE_READ_EXTATTRIBUTES
| DARWIN_ACE_READ_ATTRIBUTES
| DARWIN_ACE_READ_SECURITY;
+#ifdef HAVE_ACL_GET_PERM_NP
+ if (acl_get_perm_np(permset, ACL_WRITE)) {
+#else
if (acl_get_perm(permset, ACL_WRITE)) {
+#endif
rights |= DARWIN_ACE_WRITE_DATA
| DARWIN_ACE_APPEND_DATA
| DARWIN_ACE_WRITE_EXTATTRIBUTES
if (is_dir)
rights |= DARWIN_ACE_DELETE_CHILD;
}
+#ifdef HAVE_ACL_GET_PERM_NP
+ if (acl_get_perm_np(permset, ACL_EXECUTE))
+#else
if (acl_get_perm(permset, ACL_EXECUTE))
+#endif
rights |= DARWIN_ACE_EXECUTE;
EC_CLEANUP:
/* uid/gid */
EC_ZERO_LOG(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
switch (uuidtype) {
- case UUID_LOCAL:
- free(name);
- name = NULL;
- continue;
case UUID_USER:
EC_NULL_LOG(pwd = getpwnam(name));
tag = ACL_USER;
id = (uid_t)(grp->gr_gid);
LOG(log_debug, logtype_afpd, "map_ace: name: %s, gid: %u", name, id);
break;
+ default:
+ continue;
}
free(name);
name = NULL;
/* for files def_acl will be NULL */
/* create access acl from mode */
+#ifdef HAVE_ACL_FROM_MODE
EC_NULL_LOG_ERR(acc_acl = acl_from_mode(st.st_mode), AFPERR_MISC);
-
+#else
+#error "Missing acl_from_mode() replacement"
+#endif
/* adds the clients aces */
EC_ZERO_ERR(map_aces_darwin_to_posix(daces, &def_acl, &acc_acl, ace_count), AFPERR_MISC);
* Note: this gets called frequently and is a good place for optimizations !
*
* @param vol (r) volume
- * @param dir (r) directory
+ * @param dir (rw) directory
* @param path (r) path to filesystem object
* @param uuid (r) UUID of user
* @param requested_rights (r) requested Darwin ACE
* @returns AFP result code
*/
static int check_acl_access(const struct vol *vol,
- const struct dir *dir,
+ struct dir *dir,
const char *path,
const uuidp_t uuid,
uint32_t requested_rights)
LOG(log_warning, logtype_afpd, "check_access: afp_access not supported for groups");
EC_STATUS(AFPERR_MISC);
goto EC_CLEANUP;
-
- case UUID_LOCAL:
- LOG(log_warning, logtype_afpd, "check_access: local UUID");
+ default:
EC_STATUS(AFPERR_MISC);
goto EC_CLEANUP;
}
+ if ((strcmp(path, ".") == 0) && (dir->d_rights_cache != 0xffffffff)) {
+ /* its a dir and the cache value is valid */
+ allowed_rights = dir->d_rights_cache;
+ LOG(log_debug, logtype_afpd, "allowed rights from dircache: 0x%08x", allowed_rights);
+ } else {
#ifdef HAVE_SOLARIS_ACLS
- EC_ZERO_LOG(solaris_acl_rights(path, &st, &allowed_rights));
+ EC_ZERO_LOG(solaris_acl_rights(path, &st, &allowed_rights));
#endif
#ifdef HAVE_POSIX_ACLS
- EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
+ EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
#endif
+ /*
+ * The DARWIN_ACE_DELETE right might implicitly result from write acces to the parent
+ * directory. As it seems the 10.6 AFP client is puzzled when this right is not
+ * allowed where a delete would succeed because the parent dir gives write perms.
+ * So we check the parent dir for write access and set the right accordingly.
+ * Currentyl acl2ownermode calls us with dir = NULL, because it doesn't make sense
+ * there to do this extra check -- afaict.
+ */
+ if (vol && dir && (requested_rights & DARWIN_ACE_DELETE)) {
+ int i;
+ uint32_t parent_rights = 0;
+
+ if (dir->d_did == DIRDID_ROOT_PARENT) {
+ /* use volume path */
+ EC_NULL_LOG_ERR(parent = bfromcstr(vol->v_path), AFPERR_MISC);
+ } else {
+ /* build path for parent */
+ EC_NULL_LOG_ERR(parent = bstrcpy(dir->d_fullpath), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(bconchar(parent, '/'), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(bcatcstr(parent, path), AFPERR_MISC);
+ EC_NEG1_LOG_ERR(i = bstrrchr(parent, '/'), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(binsertch(parent, i, 1, 0), AFPERR_MISC);
+ }
- LOG(log_debug, logtype_afpd, "allowed rights: 0x%08x", allowed_rights);
-
- /*
- * The DARWIN_ACE_DELETE right might implicitly result from write acces to the parent
- * directory. As it seems the 10.6 AFP client is puzzled when this right is not
- * allowed where a delete would succeed because the parent dir gives write perms.
- * So we check the parent dir for write access and set the right accordingly.
- * Currentyl acl2ownermode calls us with dir = NULL, because it doesn't make sense
- * there to do this extra check -- afaict.
- */
- if (vol && dir && (requested_rights & DARWIN_ACE_DELETE)) {
- int i;
- uint32_t parent_rights = 0;
-
- if (dir->d_did == DIRDID_ROOT_PARENT) {
- /* use volume path */
- EC_NULL_LOG_ERR(parent = bfromcstr(vol->v_path), AFPERR_MISC);
- } else {
- /* build path for parent */
- EC_NULL_LOG_ERR(parent = bstrcpy(dir->d_fullpath), AFPERR_MISC);
- EC_ZERO_LOG_ERR(bconchar(parent, '/'), AFPERR_MISC);
- EC_ZERO_LOG_ERR(bcatcstr(parent, path), AFPERR_MISC);
- EC_NEG1_LOG_ERR(i = bstrrchr(parent, '/'), AFPERR_MISC);
- EC_ZERO_LOG_ERR(binsertch(parent, i, 1, 0), AFPERR_MISC);
- }
-
- LOG(log_debug, logtype_afpd,"parent: %s", cfrombstr(parent));
- EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
+ LOG(log_debug, logtype_afpd,"parent: %s", cfrombstr(parent));
+ EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
#ifdef HAVE_SOLARIS_ACLS
- EC_ZERO_LOG(solaris_acl_rights(cfrombstr(parent), &st, &parent_rights));
+ EC_ZERO_LOG(solaris_acl_rights(cfrombstr(parent), &st, &parent_rights));
#endif
#ifdef HAVE_POSIX_ACLS
- EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
+ EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
#endif
- if (parent_rights & (DARWIN_ACE_WRITE_DATA | DARWIN_ACE_DELETE_CHILD))
- allowed_rights |= DARWIN_ACE_DELETE; /* man, that was a lot of work! */
+ if (parent_rights & (DARWIN_ACE_WRITE_DATA | DARWIN_ACE_DELETE_CHILD))
+ allowed_rights |= DARWIN_ACE_DELETE; /* man, that was a lot of work! */
+ }
+ LOG(log_debug, logtype_afpd, "allowed rights: 0x%08x", allowed_rights);
+ dir->d_rights_cache = allowed_rights;
}
if ((requested_rights & allowed_rights) != requested_rights) {
#include <atalk/ldapconfig.h>
#endif
-#include "globals.h"
+#include <atalk/globals.h>
#include "afp_config.h"
#include "uam_auth.h"
#include "status.h"
#include <atalk/server_child.h>
#include <atalk/atp.h>
-#include "globals.h"
+#include <atalk/globals.h>
typedef struct AFPConfig {
AFPObj obj;
#endif /* HAVE_SYS_STAT_H */
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <atalk/logger.h>
#include <setjmp.h>
+#include <time.h>
+#include <atalk/logger.h>
#include <atalk/dsi.h>
#include <atalk/compat.h>
#include <atalk/util.h>
+#include <atalk/uuid.h>
+#include <atalk/paths.h>
+#include <atalk/server_ipc.h>
+#include <atalk/fce_api.h>
-#include "globals.h"
+#include <atalk/globals.h>
#include "switch.h"
#include "auth.h"
#include "fork.h"
}
}
+/* SIGQUIT handler */
+static void ipc_reconnect_handler(int sig _U_)
+{
+ DSI *dsi = (DSI *)AFPobj->handle;
+
+ if (reconnect_ipc(AFPobj) != 0) {
+ LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC reconnect");
+ afp_dsi_close(AFPobj);
+ exit(EXITERR_SYS);
+ }
+
+ if (ipc_child_write(AFPobj->ipc_fd, IPC_GETSESSION, AFPobj->sinfo.clientid_len, AFPobj->sinfo.clientid) != 0) {
+ LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC ID resend");
+ afp_dsi_close(AFPobj);
+ exit(EXITERR_SYS);
+ }
+ LOG(log_note, logtype_afpd, "ipc_reconnect_handler: IPC reconnect done");
+}
+
/* SIGURG handler (primary reconnect) */
static void afp_dsi_transfer_session(int sig _U_)
{
}
if (dsi->flags & DSI_DISCONNECTED) {
+ if (geteuid() == 0) {
+ LOG(log_note, logtype_afpd, "afp_alarm: unauthenticated user, connection problem");
+ afp_dsi_die(EXITERR_CLNT);
+ }
if (dsi->tickle > AFPobj->options.disconnected) {
LOG(log_error, logtype_afpd, "afp_alarm: reconnect timer expired, goodbye");
afp_dsi_die(EXITERR_CLNT);
/* if we're in the midst of processing something, don't die. */
if ( !(dsi->flags & DSI_RUNNING) && (dsi->tickle >= AFPobj->options.timeout)) {
LOG(log_error, logtype_afpd, "afp_alarm: child timed out, entering disconnected state");
- dsi->proto_close(dsi);
- dsi->flags |= DSI_DISCONNECTED;
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
return;
}
LOG(log_debug, logtype_afpd, "afp_alarm: sending DSI tickle");
err = dsi_tickle(AFPobj->handle);
if (err <= 0) {
+ if (geteuid() == 0) {
+ LOG(log_note, logtype_afpd, "afp_alarm: unauthenticated user, connection problem");
+ afp_dsi_die(EXITERR_CLNT);
+ }
LOG(log_error, logtype_afpd, "afp_alarm: connection problem, entering disconnected state");
- dsi->proto_close(dsi);
- dsi->flags |= DSI_DISCONNECTED;
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
}
}
struct sigaction action;
AFPobj = obj;
+ dsi->AFPobj = obj;
obj->exit = afp_dsi_die;
obj->reply = (int (*)()) dsi_cmdreply;
obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
dsi->tickle = 0;
memset(&action, 0, sizeof(action));
+ sigfillset(&action.sa_mask);
+ action.sa_flags = SA_RESTART;
/* install SIGHUP */
action.sa_handler = afp_dsi_reload;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGHUP, &action, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
/* install SIGURG */
action.sa_handler = afp_dsi_transfer_session;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGURG, &action, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
/* install SIGTERM */
action.sa_handler = afp_dsi_die;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGHUP);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGTERM, &action, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
}
- /* Added for server message support */
+ /* install SIGQUIT */
+ action.sa_handler = ipc_reconnect_handler;
+ if ( sigaction(SIGQUIT, &action, NULL ) < 0 ) {
+ LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
+ afp_dsi_die(EXITERR_SYS);
+ }
+
+ /* SIGUSR2 - server message support */
action.sa_handler = afp_dsi_getmesg;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGHUP);
- sigaddset(&action.sa_mask, SIGINT);
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR2, &action, NULL) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
/* SIGUSR1 - set down in 5 minutes */
action.sa_handler = afp_dsi_timedown;
- sigemptyset( &action.sa_mask );
- sigaddset(&action.sa_mask, SIGALRM);
- sigaddset(&action.sa_mask, SIGHUP);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
action.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &action, NULL) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
/* SIGINT - enable max_debug LOGging to /tmp/afpd.PID.XXXXXX */
action.sa_handler = afp_dsi_debug;
- sigfillset( &action.sa_mask );
- action.sa_flags = SA_RESTART;
if ( sigaction( SIGINT, &action, NULL) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
}
#ifndef DEBUGGING
- /* tickle handler */
+ /* SIGALRM - tickle handler */
action.sa_handler = alarm_handler;
- sigemptyset(&action.sa_mask);
- sigaddset(&action.sa_mask, SIGHUP);
- sigaddset(&action.sa_mask, SIGTERM);
- sigaddset(&action.sa_mask, SIGUSR1);
- sigaddset(&action.sa_mask, SIGINT);
- sigaddset(&action.sa_mask, SIGUSR2);
- action.sa_flags = SA_RESTART;
if ((sigaction(SIGALRM, &action, NULL) < 0) ||
(setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) {
afp_dsi_die(EXITERR_SYS);
dsi->flags &= ~DSI_RECONSOCKET;
continue;
}
- /* Some error on the client connection, enter disconnected state */
- dsi->flags |= DSI_DISCONNECTED;
/* the client sometimes logs out (afp_logout) but doesn't close the DSI session */
if (dsi->flags & DSI_AFP_LOGGED_OUT) {
+ LOG(log_note, logtype_afpd, "afp_over_dsi: client logged out, terminating DSI session");
afp_dsi_close(obj);
exit(0);
}
+ /* got ECONNRESET in read from client => exit*/
+ if (dsi->flags & DSI_GOT_ECONNRESET) {
+ LOG(log_note, logtype_afpd, "afp_over_dsi: client connection reset");
+ afp_dsi_close(obj);
+ exit(0);
+ }
+
+ /* Some error on the client connection, enter disconnected state */
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
+
pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
continue; /* continue receiving until disconnect timer expires
* or a primary reconnect succeeds */
if (reload_request) {
reload_request = 0;
load_volumes(AFPobj);
- dircache_dump();
- log_dircache_stat();
}
/* The first SIGINT enables debugging, the next restores the config */
static int debugging = 0;
debug_request = 0;
+ dircache_dump();
+ uuidcache_dump();
+
if (debugging) {
if (obj->options.logconfig)
setuplog(obj->options.logconfig);
if (!dsi_cmdreply(dsi, err)) {
LOG(log_error, logtype_afpd, "dsi_cmdreply(%d): %s", dsi->socket, strerror(errno) );
- dsi->flags |= DSI_DISCONNECTED;
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
}
break;
if (!dsi_wrtreply(dsi, err)) {
LOG(log_error, logtype_afpd, "dsi_wrtreply: %s", strerror(errno) );
- dsi->flags |= DSI_DISCONNECTED;
+ if (dsi_disconnect(dsi) != 0)
+ afp_dsi_die(EXITERR_CLNT);
}
break;
break;
}
pending_request(dsi);
+
+ vol_fce_tm_event();
}
/* error */
#include <atalk/paths.h>
#include <atalk/util.h>
#include <atalk/compat.h>
+#include <atalk/globals.h>
#include <atalk/fce_api.h>
-#include "globals.h"
#include "status.h"
#include "auth.h"
#include "dircache.h"
while (NULL != (c = strstr(c, "-setuplog"))) {
char *optstr;
if ((optstr = getoption(c, "-setuplog"))) {
+ /* hokey2: options->logconfig must be converted to store an array of logstrings */
+ if (options->logconfig)
+ free(options->logconfig);
+ options->logconfig = strdup(optstr);
setuplog(optstr);
- options->logconfig = optstr; /* at least store the last (possibly only) one */
c += sizeof("-setuplog");
}
}
#endif /* HAVE_CONFIG_H */
#include <atalk/afp.h>
-#include "globals.h"
+#include <atalk/globals.h>
const char *AfpNum2name(int num)
{
#include <atalk/afp.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/globals.h>
#include "volume.h"
-#include "globals.h"
#include "directory.h"
#include "file.h"
#include "desktop.h"
#include <atalk/logger.h>
#include <atalk/server_ipc.h>
#include <atalk/uuid.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "auth.h"
#include "uam_auth.h"
#include "switch.h"
if (ibuflen < idlen || idlen > (90-10)) {
return AFPERR_PARAM;
}
- ipc_child_write(obj->ipc_fd, IPC_GETSESSION, idlen+8, p);
+ if (!obj->sinfo.clientid) {
+ obj->sinfo.clientid = malloc(idlen + 8);
+ memcpy(obj->sinfo.clientid, p, idlen + 8);
+ obj->sinfo.clientid_len = idlen + 8;
+ }
+ if (ipc_child_write(obj->ipc_fd, IPC_GETSESSION, idlen+8, p) != 0)
+ return AFPERR_MISC;
tklen = obj->sinfo.sessiontoken_len;
token = obj->sinfo.sessiontoken;
}
setitimer(ITIMER_REAL, &none, NULL);
/* check for old session, possibly transfering session from here to there */
- if (ipc_child_write(obj->ipc_fd, IPC_DISCOLDSESSION, tklen, &token) == -1)
+ if (ipc_child_write(obj->ipc_fd, IPC_DISCOLDSESSION, tklen, &token) != 0)
goto exit;
/* write uint16_t DSI request ID */
if (writet(obj->ipc_fd, &dsi->header.dsi_requestID, 2, 0, 2) != 2) {
#include <sys/cdefs.h>
#endif /* HAVE_SYS_CDEFS_H */
-#include "globals.h"
+#include <atalk/globals.h>
struct afp_versions {
char *av_name;
#include <atalk/util.h>
#include <atalk/bstradd.h>
#include <atalk/unicode.h>
+#include <atalk/globals.h>
#include "desktop.h"
#include "directory.h"
#include "dircache.h"
#include "file.h"
#include "volume.h"
-#include "globals.h"
#include "filedir.h"
#include "fork.h"
path.d_dir = dircache_search_by_name(vol,
curdir,
path.u_name,
- unlen,
- path.st.st_ctime);
+ unlen);
if (path.d_dir == NULL) {
/* path.m_name is set by adddir */
if ((path.d_dir = dir_add(vol,
#include <atalk/afp.h>
#include <atalk/util.h>
#include <atalk/logger.h>
+#include <atalk/globals.h>
#include "volume.h"
#include "directory.h"
#include "fork.h"
-#include "globals.h"
#include "desktop.h"
#include "mangle.h"
#define AFPD_DESKTOP_H 1
#include <sys/cdefs.h>
-#include "globals.h"
+#include <atalk/globals.h>
#include "volume.h"
struct savedt {
#include <atalk/queue.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/globals.h>
#include "dircache.h"
#include "directory.h"
#include "hash.h"
-#include "globals.h"
+
/*
* Directory Cache
* a struct dir is initialized, the fullpath to the directory is stored there.
*
* In order to speed up the CNID query for files too, which eg happens when a directory is enumerated,
- * files are stored too in the dircache. In order to differentiate between files and dirs, we re-use
- * the element fullpath, which for files is always NULL.
+ * files are stored too in the dircache. In order to differentiate between files and dirs, we set
+ * the flag DIRF_ISFILE in struct dir.d_flags for files.
*
* The most frequent codepatch that leads to caching is directory enumeration (cf enumerate.c):
* - if a element is a directory:
* Debugging
* =========
*
- * Sending SIGHUP to a afpd child causes it to dump the dircache to a file "/tmp/dircache.PID".
+ * Sending SIGINT to a afpd child causes it to dump the dircache to a file "/tmp/dircache.PID".
*/
/********************************************************
* This func builds on the fact, that all our code only ever needs to and does search
* the dircache by CNID expecting directories to be returned, but not files.
* Thus
- * (1) if we find a file (d_fullpath == NULL) for a given CNID we
+ * (1) if we find a file for a given CNID we
* (1a) remove it from the cache
* (1b) return NULL indicating nothing found
* (2) we can then use d_fullpath to stat the directory
cdir = hnode_get(hn);
if (cdir) {
- if (cdir->d_fullpath == NULL) { /* (1) */
+ if (cdir->d_flags & DIRF_ISFILE) { /* (1) */
LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {not a directory:\"%s\"}",
ntohl(cnid), cfrombstr(cdir->d_u_name));
(void)dir_remove(vol, cdir); /* (1a) */
dircache_stat.expunged++;
return NULL;
}
- if (cdir->ctime_dircache != st.st_ctime) {
+ if ((cdir->dcache_ctime != st.st_ctime) || (cdir->dcache_ino != st.st_ino)) {
LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {modified:\"%s\"}",
ntohl(cnid), cfrombstr(cdir->d_u_name));
(void)dir_remove(vol, cdir);
* @param dir (r) directory
* @param name (r) name (server side encoding)
* @parma len (r) strlen of name
- * @param ctime (r) current st_ctime from stat
*
* @returns pointer to struct dir if found in cache, else NULL
*/
struct dir *dircache_search_by_name(const struct vol *vol,
const struct dir *dir,
char *name,
- int len,
- time_t ctime)
+ int len)
{
struct dir *cdir = NULL;
struct dir key;
+ struct stat st;
hnode_t *hn;
static_bstring uname = {-1, len, (unsigned char *)name};
}
if (cdir) {
- if (cdir->ctime_dircache != ctime) {
+ if (lstat(cfrombstr(cdir->d_fullpath), &st) != 0) {
+ LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {missing:\"%s\"}",
+ ntohl(dir->d_did), name, cfrombstr(cdir->d_fullpath));
+ (void)dir_remove(vol, cdir);
+ dircache_stat.expunged++;
+ return NULL;
+ }
+
+ /* Remove modified directories and files */
+ if ((cdir->dcache_ctime != st.st_ctime) || (cdir->dcache_ino != st.st_ino)) {
LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {modified}",
ntohl(dir->d_did), name);
(void)dir_remove(vol, cdir);
ntohs(dir->d_vid),
ntohl(dir->d_pdid),
ntohl(dir->d_did),
- dir->d_fullpath ? "d" : "f",
- cfrombstr(dir->d_u_name));
+ dir->d_flags & DIRF_ISFILE ? "f" : "d",
+ cfrombstr(dir->d_fullpath));
}
fprintf(dump, "\nSecondary DID/name index:\n");
ntohs(dir->d_vid),
ntohl(dir->d_pdid),
ntohl(dir->d_did),
- dir->d_fullpath ? "d" : "f",
- cfrombstr(dir->d_u_name));
+ dir->d_flags & DIRF_ISFILE ? "f" : "d",
+ cfrombstr(dir->d_fullpath));
}
fprintf(dump, "\nLRU Queue:\n");
ntohs(dir->d_vid),
ntohl(dir->d_pdid),
ntohl(dir->d_did),
- dir->d_fullpath ? "d" : "f",
- cfrombstr(dir->d_u_name));
+ dir->d_flags & DIRF_ISFILE ? "f" : "d",
+ cfrombstr(dir->d_fullpath));
n = n->next;
}
extern int dircache_add(const struct vol *, struct dir *);
extern void dircache_remove(const struct vol *, struct dir *, int flag);
extern struct dir *dircache_search_by_did(const struct vol *vol, cnid_t did);
-extern struct dir *dircache_search_by_name(const struct vol *, const struct dir *dir, char *name, int len, time_t ctime);
+extern struct dir *dircache_search_by_name(const struct vol *, const struct dir *dir, char *name, int len);
extern void dircache_dump(void);
extern void log_dircache_stat(void);
#endif /* DIRCACHE_H */
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
#include <atalk/errchk.h>
+#include <atalk/globals.h>
#include <atalk/fce_api.h>
#include "directory.h"
#include "fork.h"
#include "file.h"
#include "filedir.h"
-#include "globals.h"
#include "unix.h"
#include "mangle.h"
#include "hash.h"
*/
static int diroffcnt(struct dir *dir, struct stat *st)
{
- return st->st_ctime == dir->ctime;
+ return st->st_ctime == dir->d_ctime;
}
/* --------------------- */
l = bsplit(rpath, '/');
for (int i = 0; i < l->qty ; i++) { /* 3. */
did = cnid;
+ EC_ZERO(bcatcstr(statpath, "/"));
EC_ZERO(bconcat(statpath, l->entry[i]));
EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
"lstat(rpath: %s, elem: %s): %s: %s",
if ((dir = dircache_search_by_name(vol, /* 5. */
dir,
cfrombstr(l->entry[i]),
- blength(l->entry[i]),
- st.st_ctime)) == NULL) {
+ blength(l->entry[i]))) == NULL) {
if ((cnid = cnid_add(vol->v_cdb, /* 6. */
&st,
did,
if ((dir = dirlookup(vol, cnid)) == NULL) /* 7. */
EC_FAIL;
}
-
- EC_ZERO(bcatcstr(statpath, "/"));
}
EC_CLEANUP:
* Resolve a DID, allocate a struct dir for it
* 1. Check for special CNIDs 0 (invalid), 1 and 2.
* 2a. Check if the DID is in the cache.
- * 2b. Check if it's really a dir (d_fullpath != NULL) because we cache files too.
+ * 2b. Check if it's really a dir because we cache files too.
* 3. If it's not in the cache resolve it via the database.
* 4. Build complete server-side path to the dir.
* 5. Check if it exists and is a directory.
int utf8;
int err = 0;
- LOG(log_debug, logtype_afpd, "dirlookup(did: %u)", ntohl(did));
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u): START", ntohl(did));
/* check for did 0, 1 and 2 */
if (did == 0 || vol == NULL) { /* 1 */
/* Search the cache */
if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2a */
- if (ret->d_fullpath == NULL) { /* 2b */
+ if (ret->d_flags & DIRF_ISFILE) { /* 2b */
afp_errno = AFPERR_BADTYPE;
ret = NULL;
goto exit;
}
if (lstat(cfrombstr(ret->d_fullpath), &st) != 0) {
- LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {lstat: %s}", ntohl(did), strerror(errno));
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u, path: \"%s\"): lstat: %s",
+ ntohl(did), cfrombstr(ret->d_fullpath), strerror(errno));
switch (errno) {
case ENOENT:
case ENOTDIR:
/* It's not there anymore, so remove it */
- LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {calling dir_remove()}", ntohl(did));
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u): calling dir_remove", ntohl(did));
dir_remove(vol, ret);
afp_errno = AFPERR_NOOBJ;
ret = NULL;
/* Get it from the database */
cnid = did;
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u): querying CNID database", ntohl(did));
if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL) {
afp_errno = AFPERR_NOOBJ;
err = 1;
* - DIRDID_ROOT is hit
* - a cached entry is found
*/
- LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {recursion for did: %u}", ntohl(pdid));
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u): recursion for did: %u",
+ ntohl(did), ntohl(pdid));
if ((pdir = dirlookup(vol, pdid)) == NULL) {
err = 1;
goto exit;
}
/* stat it and check if it's a dir */
- LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstr(fullpath));
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u): stating \"%s\"",
+ ntohl(did), cfrombstr(fullpath));
- if (stat(cfrombstr(fullpath), &st) != 0) { /* 5a */
+ if (lstat(cfrombstr(fullpath), &st) != 0) { /* 5a */
switch (errno) {
case ENOENT:
afp_errno = AFPERR_NOOBJ;
}
/* Create struct dir */
- if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, st.st_ctime)) == NULL) { /* 6 */
+ if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, &st)) == NULL) { /* 6 */
LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno));
err = 1;
goto exit;
}
}
if (ret)
- LOG(log_debug, logtype_afpd, "dirlookup(did: %u): pdid: %u, \"%s\"",
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u): RESULT: pdid: %u, path: \"%s\"",
ntohl(ret->d_did), ntohl(ret->d_pdid), cfrombstr(ret->d_fullpath));
return ret;
* @param vol (r) pointer to struct vol
* @param pdid (r) Parent CNID
* @param did (r) CNID
- * @param path (r) Full unix path to dir or NULL for files
- * @param ctime (r) st_ctime from stat
+ * @param path (r) Full unix path to object
+ * @param st (r) struct stat of object
*
* @returns pointer to new struct dir or NULL on error
*
cnid_t pdid,
cnid_t did,
bstring path,
- time_t ctime)
+ struct stat *st)
{
struct dir *dir;
dir->d_pdid = pdid;
dir->d_vid = vol->v_vid;
dir->d_fullpath = path;
- dir->ctime_dircache = ctime;
+ dir->dcache_ctime = st->st_ctime;
+ dir->dcache_ino = st->st_ino;
+ if (!S_ISDIR(st->st_mode))
+ dir->d_flags = DIRF_ISFILE;
+ dir->d_rights_cache = 0xffffffff;
return dir;
}
AFP_ASSERT(path);
AFP_ASSERT(len > 0);
- if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name), path->st.st_ctime)) != NULL) {
+ if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name))) != NULL) {
/* there's a stray entry in the dircache */
LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {stray cache entry: did:%u,'%s', removing}",
ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name,
}
/* Allocate and initialize struct dir */
- if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath, path->st.st_ctime)) == NULL) { /* 3 */
+ if ((cdir = dir_new(path->m_name,
+ path->u_name,
+ vol,
+ dir->d_did,
+ id,
+ fullpath,
+ &path->st)) == NULL) { /* 3 */
err = 4;
goto exit;
}
/* Search the cache */
int unamelen = strlen(ret.u_name);
- cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen, ret.st.st_ctime); /* 14 */
+ cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen); /* 14 */
if (cdir == NULL) {
/* Not in cache, create one */
if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */
/* --------------------- */
void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count)
{
- dir->offcnt = count;
- dir->ctime = st->st_ctime;
+ dir->d_offcnt = count;
+ dir->d_ctime = st->st_ctime;
dir->d_flags &= ~DIRF_CNID;
}
*/
int dirreenumerate(struct dir *dir, struct stat *st)
{
- return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
+ return st->st_ctime == dir->d_ctime && (dir->d_flags & DIRF_CNID);
}
/* ------------------------------
isad = 1;
if (ad.ad_md->adf_flags & O_CREAT) {
/* We just created it */
+ if (s_path->m_name == NULL) {
+ if ((s_path->m_name = utompath(vol,
+ upath,
+ dir->d_did,
+ utf8_encoding())) == NULL) {
+ LOG(log_error, logtype_afpd,
+ "getdirparams(\"%s\"): can't assign macname",
+ cfrombstr(dir->d_fullpath));
+ return AFPERR_MISC;
+ }
+ }
ad_setname(&ad, s_path->m_name);
ad_setid( &ad,
s_path->st.st_dev,
ashort = 0;
/* this needs to handle current directory access rights */
if (diroffcnt(dir, st)) {
- ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
+ ashort = (dir->d_offcnt > 0xffff)?0xffff:dir->d_offcnt;
}
else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
setdiroffcnt(dir, st, ret);
- ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
+ ashort = (dir->d_offcnt > 0xffff)?0xffff:dir->d_offcnt;
}
ashort = htons( ashort );
memcpy( data, &ashort, sizeof( ashort ));
return AFPERR_MISC;
}
- curdir->offcnt++;
+ curdir->d_offcnt++;
if ((dir = dir_add(vol, curdir, s_path, strlen(s_path->u_name))) == NULL) {
return AFPERR_MISC;
rbuf += sizeof( id );
*rbuflen = 2 * sizeof( id );
break;
- case UUID_LOCAL:
- free(name);
- return (AFPERR_NOITEM);
default:
return AFPERR_MISC;
}
#endif
#include <atalk/directory.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "volume.h"
-#define DIRF_FSMASK (3<<0)
-#define DIRF_NOFS (0<<0)
-#define DIRF_AFS (1<<0)
-#define DIRF_UFS (2<<0)
-
-#define DIRF_OFFCNT (1<<4) /* offsprings count is valid */
-#define DIRF_CNID (1<<5) /* renumerate id */
-
-#define AFPDIR_READ (1<<0)
-
/* directory bits */
#define DIRPBIT_ATTR 0
#define DIRPBIT_PDID 1
extern void dir_free_invalid_q(void);
extern struct dir *dir_new(const char *mname, const char *uname, const struct vol *,
- cnid_t pdid, cnid_t did, bstring fullpath, time_t ctime);
+ cnid_t pdid, cnid_t did, bstring fullpath, struct stat *);
extern void dir_free (struct dir *);
extern struct dir *dir_add(struct vol *, const struct dir *, struct path *, int);
extern int dir_modify(const struct vol *vol, struct dir *dir, cnid_t pdid, cnid_t did,
#include <atalk/util.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/globals.h>
#include "desktop.h"
#include "directory.h"
#include "dircache.h"
#include "volume.h"
-#include "globals.h"
#include "file.h"
#include "fork.h"
#include "filedir.h"
*/
*sd.sd_last = 0;
sd.sd_last += len + 1;
- curdir->offcnt--; /* a little lie */
+ curdir->d_offcnt--; /* a little lie */
continue;
}
continue;
}
int len = strlen(s_path.u_name);
- if ((dir = dircache_search_by_name(vol, curdir, s_path.u_name, len, s_path.st.st_ctime)) == NULL) {
+ if ((dir = dircache_search_by_name(vol, curdir, s_path.u_name, len)) == NULL) {
if ((dir = dir_add(vol, curdir, &s_path, len)) == NULL) {
LOG(log_error, logtype_afpd, "enumerate(vid:%u, did:%u, name:'%s'): error adding dir: '%s'",
ntohs(vid), ntohl(did), o_path->u_name, s_path.u_name);
#include <atalk/afp.h>
#include <atalk/logger.h>
#include <atalk/ea.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "volume.h"
#include "desktop.h"
#include "directory.h"
#include <atalk/cnid.h>\r
#include <atalk/unix.h>\r
#include <atalk/fce_api.h>\r
+#include <atalk/globals.h>\r
\r
#include "fork.h"\r
#include "file.h"\r
-#include "globals.h"\r
#include "directory.h"\r
#include "desktop.h"\r
#include "volume.h"\r
#include <atalk/cnid.h>\r
#include <atalk/unix.h>\r
#include <atalk/fce_api.h>\r
+#include <atalk/globals.h>\r
\r
#include "fork.h"\r
#include "file.h"\r
-#include "globals.h"\r
#include "directory.h"\r
#include "desktop.h"\r
#include "volume.h"\r
#include <atalk/util.h>
#include <atalk/cnid.h>
#include <atalk/unix.h>
+#include <atalk/globals.h>
#include <atalk/fce_api.h>
#include "directory.h"
#include "fork.h"
#include "file.h"
#include "filedir.h"
-#include "globals.h"
#include "unix.h"
/* the format for the finderinfo fields (from IM: Toolbox Essentials):
|| (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
|| (bitmap & (1 << FILPBIT_FNUM))) {
if (!path->id) {
+ bstring fullpath;
struct dir *cachedfile;
int len = strlen(upath);
- if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL)
+ if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
id = cachedfile->d_did;
else {
id = get_id(vol, adp, st, dir->d_did, upath, len);
if (path->m_name == NULL) {
if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
LOG(log_error, logtype_afpd, "getmetadata: utompath error");
- exit(EXITERR_SYS);
+ return AFPERR_MISC;
}
}
- if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
+ /* Build fullpath */
+ if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
+ || (bconchar(fullpath, '/') != BSTR_OK)
+ || (bcatcstr(fullpath, upath)) != BSTR_OK) {
+ LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
+ return AFPERR_MISC;
+ }
+
+ if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
- exit(EXITERR_SYS);
+ return AFPERR_MISC;
}
if ((dircache_add(vol, cachedfile)) != 0) {
LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
- exit(EXITERR_SYS);
+ return AFPERR_MISC;
}
}
} else {
ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
createfile_done:
- curdir->offcnt++;
+ curdir->d_offcnt++;
#ifdef DROPKLUDGE
if (vol->v_flags & AFPVOL_DROPBOX) {
retvalue = err;
goto copy_exit;
}
- curdir->offcnt++;
+ curdir->d_offcnt++;
#ifdef DROPKLUDGE
if (vol->v_flags & AFPVOL_DROPBOX) {
if (dirreenumerate(dir, &st)) {
/* we already did it once and the dir haven't been modified */
- return dir->offcnt;
+ return dir->d_offcnt;
}
data.vol = vol;
#include <sys/cdefs.h>
#include <netatalk/endian.h>
#include <atalk/adouble.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "volume.h"
#include "directory.h"
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
#include <atalk/acl.h>
+#include <atalk/globals.h>
#include <atalk/fce_api.h>
#include "directory.h"
#include "volume.h"
#include "fork.h"
#include "file.h"
-#include "globals.h"
#include "filedir.h"
#include "unix.h"
if (s_path->st_valid && s_path->st_errno == ENOENT) {
rc = AFPERR_NOOBJ;
} else {
- if ((rc = deletefile(vol, -1, upath, 1)) == AFP_OK)
+ if ((rc = deletefile(vol, -1, upath, 1)) == AFP_OK) {
fce_register_delete_file( s_path );
-
+ if (vol->v_tm_used < s_path->st.st_size)
+ vol->v_tm_used = 0;
+ else
+ vol->v_tm_used -= s_path->st.st_size;
+ }
struct dir *cachedfile;
- if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath), s_path->st.st_ctime))) {
+ if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath)))) {
dircache_remove(vol, cachedfile, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
dir_free(cachedfile);
}
}
}
if ( rc == AFP_OK ) {
- curdir->offcnt--;
+ curdir->d_offcnt--;
setvoltime(obj, vol );
}
rc = AFPERR_PARAM;
goto exit;
}
- curdir->offcnt++;
- sdir->offcnt--;
+ curdir->d_offcnt++;
+ sdir->d_offcnt--;
#ifdef DROPKLUDGE
if (vol->v_flags & AFPVOL_DROPBOX) {
/* FIXME did is not always the source id */
#include <sys/cdefs.h>
#include <sys/stat.h>
-#include "globals.h"
+#include <atalk/globals.h>
#include "volume.h"
extern struct afp_options default_options;
#include <atalk/atp.h>
#include <atalk/asp.h>
#include <atalk/afp.h>
-
#include <atalk/util.h>
#include <atalk/cnid.h>
+#include <atalk/globals.h>
#include "fork.h"
#include "file.h"
-#include "globals.h"
#include "directory.h"
#include "desktop.h"
#include "volume.h"
/* we have modified any fork, remember until close_fork */
ofork->of_flags |= AFPFORK_MODIFIED;
+ /* update write count */
+ ofork->of_vol->v_written += reqcount;
+
*rbuflen = set_off_t (offset, rbuf, is64);
return( AFP_OK );
#include <ctype.h>
#include <pwd.h>
-#include "globals.h"
+#include <atalk/globals.h>
static char *l_curr;
static char *l_end;
+++ /dev/null
-/*
- * Copyright (c) 1990,1993 Regents of The University of Michigan.
- * All Rights Reserved. See COPYRIGHT.
- */
-
-#ifndef AFPD_GLOBALS_H
-#define AFPD_GLOBALS_H 1
-
-#include <sys/param.h>
-#include <sys/cdefs.h>
-
-#ifdef ADMIN_GRP
-#include <grp.h>
-#include <sys/types.h>
-#endif /* ADMIN_GRP */
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h> /* this isn't header-protected under ultrix */
-#endif /* HAVE_NETDB_H */
-
-#include <netatalk/at.h>
-#include <atalk/afp.h>
-#include <atalk/compat.h>
-#include <atalk/unicode.h>
-#include <atalk/uam.h>
-
-/* #define DOSFILELEN 12 */ /* Type1, DOS-compat*/
-#define MACFILELEN 31 /* Type2, HFS-compat */
-#define UTF8FILELEN_EARLY 255 /* Type3, early Mac OS X 10.0-10.4.? */
-/* #define UTF8FILELEN_NAME_MAX 765 */ /* Type3, 10.4.?- , getconf NAME_MAX */
-/* #define UTF8FILELEN_SPEC 0xFFFF */ /* Type3, spec on document */
-/* #define HFSPLUSFILELEN 510 */ /* HFS+ spec, 510byte = 255codepoint */
-
-#define MAXUSERLEN 256
-
-#define OPTION_DEBUG (1 << 0)
-#define OPTION_USERVOLFIRST (1 << 1)
-#define OPTION_NOUSERVOL (1 << 2)
-#define OPTION_PROXY (1 << 3)
-#define OPTION_CUSTOMICON (1 << 4)
-#define OPTION_NOSLP (1 << 5)
-#define OPTION_ANNOUNCESSH (1 << 6)
-#define OPTION_UUID (1 << 7)
-#define OPTION_ACL2MACCESS (1 << 8)
-#define OPTION_NOZEROCONF (1 << 9)
-
-#ifdef FORCE_UIDGID
-/* set up a structure for this */
-typedef struct uidgidset_t {
- uid_t uid;
- gid_t gid;
-} uidgidset;
-#endif /* FORCE_UIDGID */
-
-/* a couple of these options could get stuck in unions to save
- * space. */
-struct afp_volume_name {
- time_t mtime;
- char *name;
- char *full_name;
- int loaded;
-};
-
-struct afp_options {
- int connections, transports, tickleval, timeout, server_notif, flags, dircachesize;
- int sleep; /* Maximum time allowed to sleep (in tickles) */
- int disconnected; /* Maximum time in disconnected state (in tickles) */
- unsigned int tcp_sndbuf, tcp_rcvbuf;
- unsigned char passwdbits, passwdminlen, loginmaxfail;
- u_int32_t server_quantum;
- int dsireadbuf; /* scale factor for sizefof(dsi->buffer) = server_quantum * dsireadbuf */
- char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile;
- struct at_addr ddpaddr;
- char *uampath, *fqdn;
- char *pidfile;
- char *sigconffile;
- char *uuidconf;
- struct afp_volume_name defaultvol, systemvol, uservol;
- int closevol;
-
- char *guest, *loginmesg, *keyfile, *passwdfile;
- char *uamlist;
- char *authprintdir;
- char *signatureopt;
- unsigned char signature[16];
- char *k5service, *k5realm, *k5keytab;
- char *unixcodepage,*maccodepage;
- charset_t maccharset, unixcharset;
- mode_t umask;
- mode_t save_mask;
-#ifdef ADMIN_GRP
- gid_t admingid;
-#endif /* ADMIN_GRP */
- int volnamelen;
-
- /* default value for winbind authentication */
- char *ntdomain, *ntseparator;
- char *logconfig;
-
- char *mimicmodel;
-};
-
-#define AFPOBJ_TMPSIZ (MAXPATHLEN)
-typedef struct _AFPObj {
- int proto;
- unsigned long servernum;
- void *handle; /* either (DSI *) or (ASP *) */
- void *config;
- struct afp_options options;
- char *Obj, *Type, *Zone;
- char username[MAXUSERLEN];
- void (*logout)(void), (*exit)(int);
- int (*reply)(void *, int);
- int (*attention)(void *, AFPUserBytes);
- /* to prevent confusion, only use these in afp_* calls */
- char oldtmp[AFPOBJ_TMPSIZ + 1], newtmp[AFPOBJ_TMPSIZ + 1];
- void *uam_cookie; /* cookie for uams */
- struct session_info sinfo;
- uid_t uid; /* client running user id */
- int ipc_fd; /* anonymous PF_UNIX socket for IPC with afpd parent */
-#ifdef FORCE_UIDGID
- int force_uid;
- uidgidset uidgid;
-#endif
-} AFPObj;
-
-/* typedef for AFP functions handlers */
-typedef int (*AFPCmd)(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen);
-
-/* afp_dsi.c */
-extern AFPObj *AFPobj;
-
-extern int afp_version;
-extern int afp_errno;
-extern unsigned char nologin;
-extern struct dir *curdir;
-extern char getwdbuf[];
-
-/* FIXME CNID */
-extern const char *Cnid_srv;
-extern const char *Cnid_port;
-
-extern int get_afp_errno (const int param);
-extern void afp_options_init (struct afp_options *);
-extern int afp_options_parse (int, char **, struct afp_options *);
-extern int afp_options_parseline (char *, struct afp_options *);
-extern void afp_options_free (struct afp_options *,
- const struct afp_options *);
-extern void setmessage (const char *);
-extern void readmessage (AFPObj *);
-
-/* gettok.c */
-extern void initline (int, char *);
-extern int parseline (int, char *);
-
-/* afp_util.c */
-extern const char *AfpNum2name (int );
-extern const char *AfpErr2name(int err);
-
-/* directory.c */
-extern struct dir rootParent;
-
-#ifndef NO_DDP
-extern void afp_over_asp (AFPObj *);
-#endif /* NO_DDP */
-extern void afp_over_dsi (AFPObj *);
-
-#endif /* globals.h */
#define AFPD_ICON_H 1
#include <sys/cdefs.h>
-#include "globals.h"
+#include <atalk/globals.h>
static const unsigned char apple_atalk_icon[] = { /* default appletalk icon */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#include <sys/poll.h>
#include <errno.h>
#include <sys/wait.h>
+#include <sys/resource.h>
#include <atalk/adouble.h>
#include <atalk/util.h>
#include <atalk/server_child.h>
#include <atalk/server_ipc.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "afp_config.h"
#include "status.h"
#include "fork.h"
static struct polldata *polldata;
static int fdset_size; /* current allocated size */
static int fdset_used; /* number of used elements */
-
+static int disasociated_ipc_fd; /* disasociated sessions uses this fd for IPC */
#ifdef TRU64
void afp_get_cmdline( int *ac, char ***av)
continue;
fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd, LISTEN_FD, config);
}
+ fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, disasociated_ipc_fd, DISASOCIATED_IPC_FD, NULL);
}
static void fd_reset_listening_sockets(void)
continue;
fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd);
}
- fd_set_listening_sockets();
+ fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, disasociated_ipc_fd);
}
/* ------------------ */
static void afp_goaway(int sig)
{
+ AFPConfig *config;
#ifndef NO_DDP
asp_kill(sig);
#endif /* ! NO_DDP */
- if (server_children)
- server_child_kill(server_children, CHILD_DSIFORK, sig);
-
switch( sig ) {
- case SIGTERM :
- LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGTERM");
- AFPConfig *config;
+ case SIGTERM:
+ case SIGQUIT:
+ switch (sig) {
+ case SIGTERM:
+ LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGTERM");
+ break;
+ case SIGQUIT:
+ LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGQUIT, NOT disconnecting clients");
+ break;
+ }
+ if (server_children)
+ server_child_kill(server_children, CHILD_DSIFORK, sig);
+
for (config = configs; config; config = config->next)
if (config->server_cleanup)
config->server_cleanup(config);
nologin++;
auth_unload();
LOG(log_info, logtype_afpd, "disallowing logins");
+
+ if (server_children)
+ server_child_kill(server_children, CHILD_DSIFORK, sig);
break;
case SIGHUP :
}
}
+static int setlimits(void)
+{
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno));
+ exit(1);
+ }
+ if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < 65535) {
+ rlim.rlim_cur = 65535;
+ if (rlim.rlim_max != RLIM_INFINITY && rlim.rlim_max < 65535)
+ rlim.rlim_max = 65535;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno));
+ exit(1);
+ }
+ }
+ return 0;
+}
+
int main(int ac, char **av)
{
AFPConfig *config;
sigaddset(&sv.sa_mask, SIGHUP);
sigaddset(&sv.sa_mask, SIGTERM);
sigaddset(&sv.sa_mask, SIGUSR1);
-
+ sigaddset(&sv.sa_mask, SIGQUIT);
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
sigaddset(&sv.sa_mask, SIGTERM);
sigaddset(&sv.sa_mask, SIGHUP);
sigaddset(&sv.sa_mask, SIGCHLD);
+ sigaddset(&sv.sa_mask, SIGQUIT);
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
sigaddset(&sv.sa_mask, SIGTERM);
sigaddset(&sv.sa_mask, SIGUSR1);
sigaddset(&sv.sa_mask, SIGCHLD);
+ sigaddset(&sv.sa_mask, SIGQUIT);
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGHUP, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
sigaddset(&sv.sa_mask, SIGHUP);
sigaddset(&sv.sa_mask, SIGUSR1);
sigaddset(&sv.sa_mask, SIGCHLD);
+ sigaddset(&sv.sa_mask, SIGQUIT);
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
exit(EXITERR_SYS);
}
+ sigemptyset( &sv.sa_mask );
+ sigaddset(&sv.sa_mask, SIGALRM);
+ sigaddset(&sv.sa_mask, SIGHUP);
+ sigaddset(&sv.sa_mask, SIGUSR1);
+ sigaddset(&sv.sa_mask, SIGCHLD);
+ sigaddset(&sv.sa_mask, SIGTERM);
+ sv.sa_flags = SA_RESTART;
+ if (sigaction(SIGQUIT, &sv, NULL ) < 0 ) {
+ LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
+ exit(EXITERR_SYS);
+ }
+
/* afpd.conf: not in config file: lockfile, connections, configfile
* preference: command-line provides defaults.
* config file over-writes defaults.
cnid_init();
/* watch atp, dsi sockets and ipc parent/child file descriptor. */
+ disasociated_ipc_fd = ipc_server_uds(_PATH_AFP_IPC);
fd_set_listening_sockets();
+ /* set limits */
+ (void)setlimits();
+
afp_child_t *child;
+ int fd[2]; /* we only use one, but server_child_add expects [2] */
+ pid_t pid;
/* wait for an appleshare connection. parent remains in the loop
* while the children get handled by afp_over_{asp,dsi}. this is
if (reloadconfig) {
nologin++;
auth_unload();
+ fd_reset_listening_sockets();
LOG(log_info, logtype_afpd, "re-reading configuration file");
for (config = configs; config; config = config->next)
LOG(log_error, logtype_afpd, "config re-read: no servers configured");
exit(EXITERR_CONF);
}
- fd_reset_listening_sockets();
+
+ fd_set_listening_sockets();
+
nologin = 0;
reloadconfig = 0;
errno = saveerrno;
+ continue;
}
if (ret == 0)
}
for (int i = 0; i < fdset_used; i++) {
- if (fdset[i].revents & POLLIN) {
+ if (fdset[i].revents & (POLLIN | POLLERR | POLLHUP)) {
switch (polldata[i].fdtype) {
+
case LISTEN_FD:
config = (AFPConfig *)polldata[i].data;
/* config->server_start is afp_config.c:dsi_start() for DSI */
fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0], IPC_FD, child);
}
break;
+
case IPC_FD:
child = (afp_child_t *)polldata[i].data;
LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->pid);
+
if ((ret = ipc_server_read(server_children, child->ipc_fds[0])) == 0) {
fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0]);
close(child->ipc_fds[0]);
child->ipc_fds[0] = -1;
+ if (child->disasociated) {
+ LOG(log_note, logtype_afpd, "main: removing reattached child[%u]", child->pid);
+ server_child_remove(server_children, CHILD_DSIFORK, child->pid);
+ }
}
break;
+
+ case DISASOCIATED_IPC_FD:
+ LOG(log_debug, logtype_afpd, "main: IPC reconnect request");
+ if ((fd[0] = accept(disasociated_ipc_fd, NULL, NULL)) == -1) {
+ LOG(log_error, logtype_afpd, "main: accept: %s", strerror(errno));
+ break;
+ }
+ if (readt(fd[0], &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
+ LOG(log_error, logtype_afpd, "main: readt: %s", strerror(errno));
+ close(fd[0]);
+ }
+ LOG(log_note, logtype_afpd, "main: IPC reconnect from [%u]", pid);
+ if ((child = server_child_add(server_children, CHILD_DSIFORK, pid, fd)) == NULL) {
+ LOG(log_error, logtype_afpd, "main: server_child_add");
+ close(fd[0]);
+ break;
+ }
+ child->disasociated = 1;
+ fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd[0], IPC_FD, child);
+ break;
+
default:
LOG(log_debug, logtype_afpd, "main: IPC request for unknown type");
break;
#include <atalk/adouble.h>
#include <atalk/cnid.h>
#include <atalk/logger.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "volume.h"
#include "directory.h"
#include <atalk/dsi.h>
#include <atalk/util.h>
#include <atalk/logger.h>
-#include "globals.h"
+#include <atalk/globals.h>
+
#include "misc.h"
#define AFPD_MISC_H 1
#include <sys/cdefs.h>
-#include "globals.h"
+#include <atalk/globals.h>
/* FP functions */
/* messages.c */
#include <atalk/util.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/globals.h>
#include <atalk/fce_api.h>
-#include "globals.h"
#include "volume.h"
#include "directory.h"
#include "fork.h"
-/* we need to have a hashed list of oforks (by dev inode). just hash
- * by first letter. */
+/* we need to have a hashed list of oforks (by dev inode) */
#define OFORK_HASHSIZE 64
-static struct ofork *ofork_table[OFORK_HASHSIZE];
-
-static struct ofork **oforks = NULL;
+static struct ofork *ofork_table[OFORK_HASHSIZE]; /* forks hashed by dev/inode */
+static struct ofork **oforks = NULL; /* point to allocated table of open forks pointers */
static int nforks = 0;
static u_short lastrefnum = 0;
/* OR some of each character for the hash*/
static unsigned long hashfn(const struct file_key *key)
{
-#if 0
- unsigned long i = 0;
- while (*name) {
- i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++;
- }
-#endif
return key->inode & (OFORK_HASHSIZE - 1);
}
#include <atalk/nbp.h>
#include <atalk/unicode.h>
#include <atalk/util.h>
+#include <atalk/globals.h>
-#include "globals.h" /* includes <netdb.h> */
#include "status.h"
#include "afp_config.h"
#include "icon.h"
#include <sys/cdefs.h>
#include <atalk/dsi.h>
#include <atalk/asp.h>
-#include "globals.h"
+#include <atalk/globals.h>
+
#include "afp_config.h"
/* we use these to prevent whacky alignment problems */
#include <atalk/afp.h>
#include <atalk/uam.h>
-
-#include "globals.h"
+#include <atalk/globals.h>
/* grab the FP functions */
#include "auth.h"
#include <atalk/dsi.h>
#include <atalk/afp.h>
#include <atalk/util.h>
+#include <atalk/globals.h>
-#include "globals.h"
#include "afp_config.h"
#include "auth.h"
#include "uam_auth.h"
#include <pwd.h>
#include <atalk/uam.h>
-#include "globals.h"
+#include <atalk/globals.h>
struct uam_mod {
void *uam_module;
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <inttypes.h>
+#include <time.h>
#include <atalk/asp.h>
#include <atalk/dsi.h>
#include <atalk/uuid.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/ftw.h>
+#include <atalk/globals.h>
+#include <atalk/fce_api.h>
#ifdef CNID_DB
#include <atalk/cnid.h>
#endif /* CNID_DB*/
-#include "globals.h"
#include "directory.h"
#include "file.h"
#include "volume.h"
options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
else if (strcasecmp(p, "searchdb") == 0)
options[VOLOPT_FLAGS].i_value |= AFPVOL_SEARCHDB;
-/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
-#if 0
- else if (strcasecmp(p, "cdrom") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_CDROM | AFPVOL_RO;
-#endif
+ else if (strcasecmp(p, "nonetids") == 0)
+ options[VOLOPT_FLAGS].i_value |= AFPVOL_NONETIDS;
p = strtok(NULL, ",");
}
if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
flags = CONV_FORCE;
- tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_MACNAMELEN - suffixlen, &flags);
+ tmpvlen = convert_charset(obj->options.unixcharset,
+ obj->options.maccharset,
+ 0,
+ name,
+ vlen,
+ tmpname,
+ AFPVOL_MACNAMELEN - suffixlen,
+ &flags);
tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
}
strcat(tmpname, suffix);
}
/* Secondly convert name from maccharset to UCS2 */
- if ( 0 >= ( macvlen = convert_string(obj->options.maccharset, CH_UCS2, tmpname, tmpvlen, mactmpname, AFPVOL_U8MNAMELEN*2)) )
+ if ( 0 >= ( macvlen = convert_string(obj->options.maccharset,
+ CH_UCS2,
+ tmpname,
+ tmpvlen,
+ mactmpname,
+ AFPVOL_U8MNAMELEN*2)) )
return -1;
LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> Longname: '%s'", name, tmpname);
/* check duplicate */
for ( volume = Volumes; volume; volume = volume->v_next ) {
- if (( strcasecmp_w( volume->v_u8mname, u8mtmpname ) == 0 ) || ( strcasecmp_w( volume->v_macname, mactmpname ) == 0 )){
- LOG (log_error, logtype_afpd, "ERROR: Volume name is duplicated. Check AppleVolumes files.");
+ if ((utf8_encoding() && (strcasecmp_w(volume->v_u8mname, u8mtmpname) == 0))
+ ||
+ (!utf8_encoding() && (strcasecmp_w(volume->v_macname, mactmpname) == 0))) {
+ LOG (log_error, logtype_afpd,
+ "Duplicate volume name, check AppleVolumes files: previous: \"%s\", new: \"%s\"",
+ volume->v_localname, name);
if (volume->v_deleted) {
volume->v_new = hide = 1;
}
check_ea_sys_support(volume);
initvol_vfs(volume);
- /* get/store uuid from file */
- if (volume->v_flags & AFPVOL_TM) {
+ /* get/store uuid from file in afpd master*/
+ if ((parent_or_child == 0) && (volume->v_flags & AFPVOL_TM)) {
char *uuid = get_vol_uuid(obj, volume->v_localname);
if (!uuid) {
LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
p1->mtime = st.st_mtime;
}
- if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) {
- LOG(log_error, logtype_afpd, "readvolfile: can't lock volume file \"%s\"", path);
- if ( fclose( fp ) != 0 ) {
- LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) );
+ /* try putting a read lock on the volume file twice, sleep 1 second if first attempt fails */
+ int retries = 2;
+ while (1) {
+ if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) {
+ retries--;
+ if (!retries) {
+ LOG(log_error, logtype_afpd, "readvolfile: can't lock volume file \"%s\"", path);
+ if ( fclose( fp ) != 0 ) {
+ LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) );
+ }
+ return -1;
+ }
+ sleep(1);
+ continue;
}
- return -1;
+ break;
}
memset(save_options, 0, sizeof(save_options));
}
}
+static off_t getused_size; /* result of getused() */
+
+/*!
+ nftw callback for getused()
+ */
+static int getused_stat(const char *path,
+ const struct stat *statp,
+ int tflag,
+ struct FTW *ftw)
+{
+ off_t low, high;
+
+ if (tflag == FTW_F || tflag == FTW_D) {
+ getused_size += statp->st_blocks * 512;
+ }
+
+ return 0;
+}
+
+#define GETUSED_CACHETIME 5
+/*!
+ * Calculate used size of a volume with nftw
+ *
+ * The result is cached, we're try to avoid frequently calling nftw()
+ *
+ * 1) Call nftw an refresh if:
+ * 1a) - we're called the first time
+ * 1b) - volume modification date is not yet set and the last time we've been called is
+ * longer then 30 sec ago
+ * 1c) - the last volume modification is less then 30 sec old
+ *
+ * @param vol (rw) volume to calculate
+ */
+static int getused(struct vol *vol)
+{
+ static time_t vol_mtime = 0;
+ int ret = 0;
+ time_t now = time(NULL);
+
+ if (!vol_mtime
+ || (!vol->v_mtime && ((vol_mtime + GETUSED_CACHETIME) < now))
+ || (vol->v_mtime && ((vol_mtime + GETUSED_CACHETIME) < vol->v_mtime))
+ ) {
+ vol_mtime = now;
+ getused_size = 0;
+ vol->v_written = 0;
+ ret = nftw(vol->v_path, getused_stat, NULL, 20, FTW_PHYS); /* 2 */
+ LOG(log_debug, logtype_afpd, "volparams: from nftw: %" PRIu64 " bytes",
+ getused_size);
+ } else {
+ getused_size += vol->v_written;
+ vol->v_written = 0;
+ LOG(log_debug, logtype_afpd, "volparams: cached used: %" PRIu64 " bytes",
+ getused_size);
+ }
+
+ return ret;
+}
+
static int getvolspace(struct vol *vol,
u_int32_t *bfree, u_int32_t *btotal,
VolSpace *xbfree, VolSpace *xbtotal, u_int32_t *bsize)
{
int spaceflag, rc;
u_int32_t maxsize;
+ VolSpace used;
#ifndef NO_QUOTA_SUPPORT
VolSpace qfree, qtotal;
#endif
getvolspace_done:
if (vol->v_limitsize) {
- bstring cmdstr;
- if ((cmdstr = bformat("du -sh \"%s\" 2> /dev/null | cut -f1", vol->v_path)) == NULL)
- return AFPERR_MISC;
-
- FILE *cmd = popen(cfrombstr(cmdstr), "r");
- bdestroy(cmdstr);
- if (cmd == NULL)
+ if (getused(vol) != 0)
return AFPERR_MISC;
-
- char buf[100];
- fgets(buf, 100, cmd);
-
- if (pclose(cmd) == -1)
- return AFPERR_MISC;
-
- size_t multi = 0;
- if (buf[strlen(buf) - 2] == 'G' || buf[strlen(buf) - 2] == 'g')
- /* GB */
- multi = 1024 * 1024 * 1024;
- else if (buf[strlen(buf) - 2] == 'M' || buf[strlen(buf) - 2] == 'm')
- /* MB */
- multi = 1024 * 1024;
- else if (buf[strlen(buf) - 2] == 'K' || buf[strlen(buf) - 2] == 'k')
- /* MB */
- multi = 1024;
-
- char *p;
- if (p = strchr(buf, ','))
- /* ignore fraction */
- *p = 0;
- else
- /* remove G|M|K char */
- buf[strlen(buf) - 2] = 0;
- /* now buf contains only digits */
- long long used = atoll(buf) * multi;
- LOG(log_debug, logtype_afpd, "volparams: used on volume: %llu bytes", used);
+ LOG(log_debug, logtype_afpd, "volparams: used on volume: %" PRIu64 " bytes",
+ getused_size);
+ vol->v_tm_used = getused_size;
*xbtotal = min(*xbtotal, (vol->v_limitsize * 1024 * 1024));
- *xbfree = min(*xbfree, *xbtotal < used ? 0 : *xbtotal - used);
+ *xbfree = min(*xbfree, *xbtotal < getused_size ? 0 : *xbtotal - getused_size);
}
*bfree = min( *xbfree, maxsize);
return( AFP_OK );
}
+#define FCE_TM_DELTA 10 /* send notification every 10 seconds */
+void vol_fce_tm_event(void)
+{
+ static time_t last;
+ time_t now = time(NULL);
+ struct vol *vol = Volumes;
+
+ if ((last + FCE_TM_DELTA) < now) {
+ last = now;
+ for ( ; vol; vol = vol->v_next ) {
+ if (vol->v_flags & AFPVOL_TM)
+ (void)fce_register_tm_size(vol->v_path, vol->v_tm_used + vol->v_written);
+ }
+ }
+}
+
/* -----------------------
* set volume creation date
* avoid duplicate, well at least it tries
ashort |= VOLPBIT_ATTR_UNIXPRIV;
if (vol->v_flags & AFPVOL_TM)
ashort |= VOLPBIT_ATTR_TM;
-
+ if (vol->v_flags & AFPVOL_NONETIDS)
+ ashort |= VOLPBIT_ATTR_NONETIDS;
if (afp_version >= 32) {
if (vol->v_vfs_ea)
ashort |= VOLPBIT_ATTR_EXT_ATTRS;
DIRDID_ROOT_PARENT,
DIRDID_ROOT,
bfromcstr(volume->v_path),
- st.st_ctime)
+ &st)
) == NULL) {
free(vol_mname);
LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
{
struct timeval tv;
- /* just looking at vol->v_mtime is broken seriously since updates
- * from other users afpd processes never are seen.
- * This is not the most elegant solution (a shared memory between
- * the afpd processes would come closer)
- * [RS] */
-
if ( gettimeofday( &tv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "setvoltime(%s): gettimeofday: %s", vol->v_path, strerror(errno) );
return;
#include <atalk/volume.h>
#include <atalk/cnid.h>
#include <atalk/unicode.h>
-
-#include "globals.h"
-#if 0
-#include "hash.h"
-#endif
+#include <atalk/globals.h>
extern struct vol *getvolbyvid (const u_int16_t);
extern int ustatfs_getvolspace (const struct vol *,
extern void load_volumes (AFPObj *obj);
extern const struct vol *getvolumes(void);
extern void unload_volumes_and_extmap(void);
+extern void vol_fce_tm_event(void);
/* FP functions */
int afp_openvol (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen);
#include <atalk/logger.h>
#include <atalk/cnid_dbd_private.h>
#include <atalk/volinfo.h>
+#include <atalk/util.h>
+
#include "cmd_dbd.h"
#include "dbd.h"
#include "dbif.h"
#include "db_param.h"
-#define LOCKFILENAME "lock"
#define DBOPTIONS (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN)
int nocniddb = 0; /* Dont open CNID database, only scan filesystem */
-volatile sig_atomic_t alarmed;
struct volinfo volinfo; /* needed by pack.c:idxname() */
+volatile sig_atomic_t alarmed; /* flags for signals */
+int db_locked; /* have we got the fcntl lock on lockfile ? */
static DBD *dbd;
static int verbose; /* Logging flag */
DEFAULT_MAXLOCKOBJS, /* maxlockobjs */
0, /* flush_interval */
0, /* flush_frequency */
- 1000, /* txn_frequency */
0, /* usock_file */
-1, /* fd_table_size */
-1, /* idle_timeout */
}
}
-static int get_lock(const char *dbpath)
-{
- int lockfd;
- char lockpath[PATH_MAX];
- struct flock lock;
- struct stat st;
-
- if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) {
- dbd_log( LOGSTD, ".AppleDB pathname too long");
- exit(EXIT_FAILURE);
- }
- strncpy(lockpath, dbpath, PATH_MAX - 1);
- strcat(lockpath, "/");
- strcat(lockpath, LOCKFILENAME);
-
- if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
- dbd_log( LOGSTD, "Error opening lockfile: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- if ((stat(dbpath, &st)) != 0) {
- dbd_log( LOGSTD, "Error statting lockfile: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) {
- dbd_log( LOGSTD, "Error inheriting lockfile permissions: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- lock.l_start = 0;
- lock.l_whence = SEEK_SET;
- lock.l_len = 0;
- lock.l_type = F_WRLCK;
-
- if (fcntl(lockfd, F_SETLK, &lock) < 0) {
- if (errno == EACCES || errno == EAGAIN) {
- if (exclusive) {
- dbd_log( LOGSTD, "Database is in use and exlusive was requested", strerror(errno));
- exit(EXIT_FAILURE);
- };
- } else {
- dbd_log( LOGSTD, "Error getting fcntl F_WRLCK on lockfile: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
- }
-
- return lockfd;
-}
-
-static void free_lock(int lockfd)
-{
- struct flock lock;
-
- lock.l_start = 0;
- lock.l_whence = SEEK_SET;
- lock.l_len = 0;
- lock.l_type = F_UNLCK;
- fcntl(lockfd, F_SETLK, &lock);
- close(lockfd);
-}
-
static void usage (void)
{
printf("Usage: dbd [-e|-t|-v|-x] -d [-i] | -s [-c|-n]| -r [-c|-f] | -u <path to netatalk volume>\n"
close(dbdirfd);
}
- /*
- Before we do anything else, check if there is an instance of cnid_dbd
- running already and silently exit if yes.
- */
- lockfd = get_lock(dbpath);
+ /* Get db lock */
+ if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1)
+ goto exit_failure;
+ if (db_locked != LOCK_EXCL) {
+ /* Couldn't get exclusive lock, try shared lock if -e wasn't requested */
+ if (exclusive) {
+ dbd_log(LOGSTD, "Database is in use and exlusive was requested");
+ goto exit_failure;
+ }
+ if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD)
+ goto exit_failure;
+ }
/* Prepare upgrade ? */
if (prep_upgrade) {
/* Check if -f is requested and wipe db if yes */
if ((flags & DBD_FLAGS_FORCE) && rebuild && (volinfo.v_flags & AFPVOL_CACHE)) {
char cmd[8 + MAXPATHLEN];
- close(lockfd);
+ if ((db_locked = get_lock(LOCK_FREE, NULL)) != 0)
+ goto exit_failure;
+
snprintf(cmd, 8 + MAXPATHLEN, "rm -rf \"%s\"", dbpath);
dbd_log( LOGDEBUG, "Removing old database of volume: '%s'", volpath);
system(cmd);
exit(EXIT_FAILURE);
}
dbd_log( LOGDEBUG, "Removed old database.");
- lockfd = get_lock(dbpath);
+ if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1)
+ goto exit_failure;
}
/*
if ((dbd = dbif_init(dbpath, "cnid2.db")) == NULL)
goto exit_failure;
- if (dbif_env_open(dbd, &db_param, exclusive ? (DBOPTIONS | DB_RECOVER) : DBOPTIONS) < 0) {
+ if (dbif_env_open(dbd,
+ &db_param,
+ (db_locked == LOCK_EXCL) ? (DBOPTIONS | DB_RECOVER) : DBOPTIONS) < 0) {
dbd_log( LOGSTD, "error opening database!");
goto exit_failure;
}
- if (exclusive)
+ if (db_locked == LOCK_EXCL)
dbd_log( LOGDEBUG, "Finished recovery.");
if (dbif_open(dbd, NULL, rebuildindexes) < 0) {
}
}
+ /* Downgrade db lock if not running exclusive */
+ if (!exclusive && (db_locked == LOCK_EXCL)) {
+ if (get_lock(LOCK_UNLOCK, NULL) != 0)
+ goto exit_failure;
+ if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD)
+ goto exit_failure;
+ }
+
/* Now execute given command scan|rebuild|dump */
if (dump && ! nocniddb) {
if (dbif_dump(dbd, dumpindexes) < 0) {
ret = 0;
exit_failure:
- free_lock(lockfd);
+ get_lock(0, NULL);
if ((fchdir(cdir)) < 0)
dbd_log(LOGSTD, "fchdir: %s", strerror(errno));
(strcmp(a,c) b 0)
extern int nocniddb; /* Dont open CNID database, only scan filesystem */
+extern int db_locked; /* have we got the fcntl lock on lockfd ? */
extern volatile sig_atomic_t alarmed;
extern void dbd_log(enum logtype lt, char *fmt, ...);
struct cnid_dbd_rqst rqst;
struct cnid_dbd_rply rply;
- dbd->db_param.txn_frequency = 0;
-
/* jump over rootinfo key */
if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
return;
db_param.cachesize = 64 * 1024; /* 64 MB */
db_param.maxlocks = DEFAULT_MAXLOCKS;
db_param.maxlockobjs = DEFAULT_MAXLOCKOBJS;
- db_param.txn_frequency = 1000; /* close txn every 1000 objects */
db_param.logfile_autoremove = 1;
/* Make it accessible for all funcs */
return -1;
}
- /* temporary rebuild db, used with -re rebuild to delete unused CNIDs, not used with -f */
- if (! nocniddb && !(flags & DBD_FLAGS_FORCE)) {
- /* Get volume stamp */
- dbd_getstamp(dbd, &rqst, &rply);
- if (rply.result != CNID_DBD_RES_OK)
- goto exit;
- memcpy(stamp, rply.name, CNID_DEV_LEN);
+ /* Get volume stamp */
+ dbd_getstamp(dbd, &rqst, &rply);
+ if (rply.result != CNID_DBD_RES_OK) {
+ ret = -1;
+ goto exit;
+ }
+ memcpy(stamp, rply.name, CNID_DEV_LEN);
- /* open/create rebuild dbd, copy rootinfo key */
+ /* temporary rebuild db, used with -re rebuild to delete unused CNIDs, not used with -f */
+ if (! nocniddb && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE)) {
tmpdb_path = get_tmpdb_path();
if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) {
ret = -1;
exit:
if (! nocniddb) {
- if (dbif_txn_close(dbd, 2) != 0)
+ if (dbif_txn_close(dbd, ret == 0 ? 1 : 0) != 0)
ret = -1;
if (dbd_rebuild)
- if (dbif_txn_close(dbd_rebuild, 2) != 0)
+ if (dbif_txn_close(dbd_rebuild, ret == 0 ? 1 : 0) != 0)
ret = -1;
if ((ret == 0) && dbd_rebuild && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE))
/* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
static int srvfd;
static int rqstfd;
static volatile sig_atomic_t sigchild = 0;
+static uint maxvol;
#define MAXSPAWN 3 /* Max times respawned in.. */
#define TESTTIME 42 /* this much seconds apfd client tries to *
* to reconnect every 5 secondes, catch it */
-#define MAXVOLS 512
+#define MAXVOLS 4096
#define DEFAULTHOST "localhost"
#define DEFAULTPORT "4700"
static struct server *test_usockfn(struct volinfo *volinfo)
{
int i;
- for (i = 0; i < MAXVOLS; i++) {
+ for (i = 0; i < maxvol; i++) {
if ((srv[i].volinfo) && (strcmp(srv[i].volinfo->v_path, volinfo->v_path) == 0)) {
return &srv[i];
}
time(&t);
if (!up) {
- /* find an empty slot */
- for (i = 0; i < MAXVOLS; i++) {
- if (srv[i].volinfo == NULL) {
+ /* find an empty slot (i < maxvol) or the first free slot (i == maxvol)*/
+ for (i = 0; i <= maxvol; i++) {
+ if (srv[i].volinfo == NULL && i < MAXVOLS) {
up = &srv[i];
up->volinfo = volinfo;
retainvolinfo(volinfo);
up->tm = t;
up->count = 0;
+ if (i == maxvol)
+ maxvol++;
break;
}
}
sigprocmask(SIG_BLOCK, &set, NULL);
}
+static int setlimits(void)
+{
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno));
+ exit(1);
+ }
+ if (rlim.rlim_cur != RLIM_INFINITY && rlim.rlim_cur < 65535) {
+ rlim.rlim_cur = 65535;
+ if (rlim.rlim_max != RLIM_INFINITY && rlim.rlim_max < 65535)
+ rlim.rlim_max = 65535;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
+ LOG(log_error, logtype_afpd, "setlimits: %s", strerror(errno));
+ exit(1);
+ }
+ }
+ return 0;
+}
+
/* ------------------ */
int main(int argc, char *argv[])
{
daemon_exit(1);
}
+ (void)setlimits();
+
/* Check PID lockfile and become a daemon */
switch(server_lock("cnid_metad", _PATH_CNID_METAD_LOCK, debug)) {
case -1: /* error */
rqstfd = usockfd_check(srvfd, &set);
/* Collect zombie processes and log what happened to them */
if (sigchild) while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
- for (i = 0; i < MAXVOLS; i++) {
+ for (i = 0; i < maxvol; i++) {
if (srv[i].pid == pid) {
srv[i].pid = 0;
close(srv[i].control_fd);
int maxlockobjs;
int flush_interval;
int flush_frequency;
- int txn_frequency;
char usock_file[MAXPATHLEN + 1];
int fd_table_size;
int idle_timeout;
return ret;
}
+/*!
+ * Get lock on db lock file
+ *
+ * @args cmd (r) lock command:
+ * LOCK_FREE: close lockfd
+ * LOCK_UNLOCK: unlock lockm keep lockfd open
+ * LOCK_EXCL: F_WRLCK on lockfd
+ * LOCK_SHRD: F_RDLCK on lockfd
+ * @args dbpath (r) path to lockfile, only used on first call,
+ * later the stored fd is used
+ * @returns LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error
+ * LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on
+ * success, 0 if the lock couldn't be acquired, -1 on other errors
+ */
+int get_lock(int cmd, const char *dbpath)
+{
+ static int lockfd = -1;
+ int ret;
+ char lockpath[PATH_MAX];
+ struct stat st;
+
+ switch (cmd) {
+ case LOCK_FREE:
+ if (lockfd == -1)
+ return -1;
+ close(lockfd);
+ lockfd = -1;
+ return 0;
+
+ case LOCK_UNLOCK:
+ if (lockfd == -1)
+ return -1;
+ return unlock(lockfd, 0, SEEK_SET, 0);
+
+ case LOCK_EXCL:
+ case LOCK_SHRD:
+ if (lockfd == -1) {
+ if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) {
+ LOG(log_error, logtype_cnid, ".AppleDB pathname too long");
+ return -1;
+ }
+ strncpy(lockpath, dbpath, PATH_MAX - 1);
+ strcat(lockpath, "/");
+ strcat(lockpath, LOCKFILENAME);
+
+ if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
+ LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno));
+ return -1;
+ }
+
+ if ((stat(dbpath, &st)) != 0) {
+ LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno));
+ return -1;
+ }
+
+ if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) {
+ LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s",
+ strerror(errno));
+ return -1;
+ }
+ }
+
+ if (cmd == LOCK_EXCL)
+ ret = write_lock(lockfd, 0, SEEK_SET, 0);
+ else
+ ret = read_lock(lockfd, 0, SEEK_SET, 0);
+
+ if (ret != 0) {
+ if (cmd == LOCK_SHRD)
+ LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again...");
+ return 0;
+ }
+
+ LOG(log_debug, logtype_cnid, "get_lock: got %s lock",
+ cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD");
+ return cmd;
+
+ default:
+ return -1;
+ } /* switch(cmd) */
+
+ /* deadc0de, never get here */
+ return -1;
+}
+
/* --------------- */
DBD *dbif_init(const char *envhome, const char *filename)
{
}
/*
- ret = 2 -> commit txn regardless of db_param.txn_frequency
ret = 1 -> commit txn if db_param.txn_frequency
ret = 0 -> abort txn db_param.txn_frequency -> exit!
anything else -> exit!
- db_param of the db environment might specify txn_frequency > 1 in which case
- we only close a txn every txn_frequency time. the `dbd` command uses this for the
- temp rebuild db, cnid_dbd keeps it at 0. For increasing cnid_dbd throughput this
- should be tuned and testes as well.
-
@returns 0 on success (abort or commit), -1 on error
*/
int dbif_txn_close(DBD *dbd, int ret)
LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
return -1;
}
- } else if (ret == 1 || ret == 2) {
- static uint count;
- if (ret != 2 && dbd->db_param.txn_frequency > 1) {
- count++;
- if ((count % dbd->db_param.txn_frequency) != 0)
- return 0;
- }
+ } else if (ret == 1) {
ret = dbif_txn_commit(dbd);
if ( ret < 0) {
LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
#define DBIF_IDX_DIDNAME 2
#define DBIF_IDX_NAME 3
+/* get_lock cmd and return value */
+#define LOCKFILENAME "lock"
+#define LOCK_FREE 0
+#define LOCK_UNLOCK 1
+#define LOCK_EXCL 2
+#define LOCK_SHRD 3
+
/* Structures */
typedef struct {
char *name;
} DBD;
/* Functions */
+extern int get_lock(int cmd, const char *dbpath);
+
extern DBD *dbif_init(const char *envhome, const char *dbname);
extern int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags);
extern int dbif_open(DBD *dbd, struct db_param *dbp, int reindex);
#include "dbd.h"
#include "comm.h"
-#define LOCKFILENAME "lock"
-
/*
Note: DB_INIT_LOCK is here so we can run the db_* utilities while netatalk is running.
It's a likey performance hit, but it might we worth it.
*/
-#define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
+#define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN)
/* Global, needed by pack.c:idxname() */
struct volinfo volinfo;
static DBD *dbd;
static int exit_sig = 0;
+static int db_locked;
static void sig_exit(int signo)
{
}
}
-/* ------------------------ */
-static int get_lock(void)
-{
- int lockfd;
- struct flock lock;
-
- if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
- LOG(log_error, logtype_cnid, "main: error opening lockfile: %s", strerror(errno));
- exit(1);
- }
-
- lock.l_start = 0;
- lock.l_whence = SEEK_SET;
- lock.l_len = 0;
- lock.l_type = F_WRLCK;
-
- if (fcntl(lockfd, F_SETLK, &lock) < 0) {
- if (errno == EACCES || errno == EAGAIN) {
- LOG(log_error, logtype_cnid, "get_lock: locked");
- exit(0);
- } else {
- LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
- exit(1);
- }
- }
-
- return lockfd;
-}
/* ----------------------- */
static void set_signal(void)
}
}
-/* ----------------------- */
-static void free_lock(int lockfd)
-{
- struct flock lock;
-
- lock.l_start = 0;
- lock.l_whence = SEEK_SET;
- lock.l_len = 0;
- lock.l_type = F_UNLCK;
- fcntl(lockfd, F_SETLK, &lock);
- close(lockfd);
-}
-
/* ------------------------ */
int main(int argc, char *argv[])
{
struct db_param *dbp;
int err = 0;
- int lockfd, ctrlfd, clntfd;
+ int ctrlfd, clntfd;
char *logconfig;
set_processname("cnid_dbd");
switch_to_user(dbpath);
- /* Before we do anything else, check if there is an instance of cnid_dbd
- running already and silently exit if yes. */
- lockfd = get_lock();
+ /* Get db lock */
+ if ((db_locked = get_lock(LOCK_EXCL, dbpath)) == -1) {
+ LOG(log_error, logtype_cnid, "main: fatal db lock error");
+ exit(1);
+ }
+ if (db_locked != LOCK_EXCL) {
+ /* Couldn't get exclusive lock, try shared lock */
+ if ((db_locked = get_lock(LOCK_SHRD, NULL)) != LOCK_SHRD) {
+ LOG(log_error, logtype_cnid, "main: fatal db lock error");
+ exit(1);
+ }
+ }
set_signal();
if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
exit(2);
- if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
+ /* Only recover if we got the lock */
+ if (dbif_env_open(dbd,
+ dbp,
+ (db_locked == LOCK_EXCL) ? DBOPTIONS | DB_RECOVER : DBOPTIONS) < 0)
exit(2); /* FIXME: same exit code as failure for dbif_open() */
LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
}
LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
+ /* Downgrade db lock */
+ if (db_locked == LOCK_EXCL) {
+ if (get_lock(LOCK_UNLOCK, NULL) != 0) {
+ dbif_close(dbd);
+ exit(2);
+ }
+ if (get_lock(LOCK_SHRD, NULL) != LOCK_SHRD) {
+ dbif_close(dbd);
+ exit(2);
+ }
+ }
+
+
if (comm_init(dbp, ctrlfd, clntfd) < 0) {
dbif_close(dbd);
exit(3);
if (dbif_env_remove(dbpath) < 0)
err++;
- free_lock(lockfd);
-
if (err)
exit(4);
else if (exit_sig)
#include <atalk/afp.h>
#include <atalk/uam.h>
-#include "../afpd/globals.h"
+#include <atalk/globals.h>
/* Number of bits for p which we generate. Everybode out there uses 512, so we beet them */
#define PRIMEBITS 1024
server_ipc.h tdb.h uam.h unicode.h util.h uuid.h volinfo.h \
zip.h ea.h acl.h unix.h directory.h hash.h volume.h
-noinst_HEADERS = cnid_dbd_private.h cnid_private.h bstradd.h bstrlib.h errchk.h ftw.h fce_api.h
+noinst_HEADERS = cnid_dbd_private.h cnid_private.h bstradd.h bstrlib.h errchk.h ftw.h globals.h fce_api.h
#define DIRDID_ROOT_PARENT htonl(1) /* parent directory of root */
#define DIRDID_ROOT htonl(2) /* root directory */
+/* struct dir.d_flags */
+#define DIRF_FSMASK (3<<0)
+#define DIRF_NOFS (0<<0)
+#define DIRF_AFS (1<<0)
+#define DIRF_UFS (1<<1)
+#define DIRF_ISFILE (1<<3) /* it's cached file, not a directory */
+#define DIRF_OFFCNT (1<<4) /* offsprings count is valid */
+#define DIRF_CNID (1<<5) /* renumerate id */
+
struct dir {
- bstring d_fullpath; /* complete unix path to dir */
+ bstring d_fullpath; /* complete unix path to dir (or file) */
bstring d_m_name; /* mac name */
bstring d_u_name; /* unix name */
/* be careful here! if d_m_name == d_u_name, d_u_name */
/* will just point to the same storage as d_m_name !! */
ucs2_t *d_m_name_ucs2; /* mac name as UCS2 */
qnode_t *qidx_node; /* pointer to position in queue index */
- time_t ctime; /* inode ctime, used and modified by reenumeration */
- time_t ctime_dircache; /* inode ctime, used and modified by dircache */
+ time_t d_ctime; /* inode ctime, used and modified by reenumeration */
+
int d_flags; /* directory flags */
cnid_t d_pdid; /* CNID of parent directory */
cnid_t d_did; /* CNID of directory */
- uint32_t offcnt; /* offspring count */
+ uint32_t d_offcnt; /* offspring count */
uint16_t d_vid; /* only needed in the dircache, because
we put all directories in one cache. */
+ uint32_t d_rights_cache; /* cached rights combinded from mode and possible ACL */
+
+ /* Stuff used in the dircache */
+ time_t dcache_ctime; /* inode ctime, used and modified by dircache */
+ ino_t dcache_ino; /* inode number, used to detect changes in the dircache */
};
struct path {
#include <netinet/in.h>
#include <atalk/afp.h>
#include <atalk/server_child.h>
+#include <atalk/globals.h>
#include <netatalk/endian.h>
#ifdef __OpenBSD__
/* child and parent processes might interpret a couple of these
* differently. */
typedef struct DSI {
+ AFPObj *AFPobj;
dsi_proto protocol;
struct dsi_block header;
struct sockaddr_storage server, client;
#define DSI_RECONSOCKET (1 << 7) /* we have a new socket from primary reconnect */
#define DSI_RECONINPROG (1 << 8) /* used in the new session in reconnect */
#define DSI_AFP_LOGGED_OUT (1 << 9) /* client called afp_logout, quit on next EOF from socket */
+#define DSI_GOT_ECONNRESET (1 << 10) /* got ECONNRESET from client => exit */
/* basic initialization: dsi_init.c */
extern DSI *dsi_init (const dsi_proto /*protocol*/,
extern size_t dsi_stream_read (DSI *, void *, const size_t);
extern int dsi_stream_send (DSI *, void *, size_t);
extern int dsi_stream_receive (DSI *, void *, const size_t, size_t *);
+extern int dsi_disconnect(DSI *dsi);
#ifdef WITH_SENDFILE
extern ssize_t dsi_stream_read_file(DSI *, int, off_t off, const size_t len);
--- /dev/null
+/*
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved. See COPYRIGHT.
+ */
+
+#ifndef AFPD_GLOBALS_H
+#define AFPD_GLOBALS_H 1
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+
+#ifdef ADMIN_GRP
+#include <grp.h>
+#include <sys/types.h>
+#endif /* ADMIN_GRP */
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h> /* this isn't header-protected under ultrix */
+#endif /* HAVE_NETDB_H */
+
+#include <netatalk/at.h>
+#include <atalk/afp.h>
+#include <atalk/compat.h>
+#include <atalk/unicode.h>
+#include <atalk/uam.h>
+
+/* #define DOSFILELEN 12 */ /* Type1, DOS-compat*/
+#define MACFILELEN 31 /* Type2, HFS-compat */
+#define UTF8FILELEN_EARLY 255 /* Type3, early Mac OS X 10.0-10.4.? */
+/* #define UTF8FILELEN_NAME_MAX 765 */ /* Type3, 10.4.?- , getconf NAME_MAX */
+/* #define UTF8FILELEN_SPEC 0xFFFF */ /* Type3, spec on document */
+/* #define HFSPLUSFILELEN 510 */ /* HFS+ spec, 510byte = 255codepoint */
+
+#define MAXUSERLEN 256
+
+#define OPTION_DEBUG (1 << 0)
+#define OPTION_USERVOLFIRST (1 << 1)
+#define OPTION_NOUSERVOL (1 << 2)
+#define OPTION_PROXY (1 << 3)
+#define OPTION_CUSTOMICON (1 << 4)
+#define OPTION_NOSLP (1 << 5)
+#define OPTION_ANNOUNCESSH (1 << 6)
+#define OPTION_UUID (1 << 7)
+#define OPTION_ACL2MACCESS (1 << 8)
+#define OPTION_NOZEROCONF (1 << 9)
+
+#ifdef FORCE_UIDGID
+/* set up a structure for this */
+typedef struct uidgidset_t {
+ uid_t uid;
+ gid_t gid;
+} uidgidset;
+#endif /* FORCE_UIDGID */
+
+/* a couple of these options could get stuck in unions to save
+ * space. */
+struct afp_volume_name {
+ time_t mtime;
+ char *name;
+ char *full_name;
+ int loaded;
+};
+
+struct afp_options {
+ int connections, transports, tickleval, timeout, server_notif, flags, dircachesize;
+ int sleep; /* Maximum time allowed to sleep (in tickles) */
+ int disconnected; /* Maximum time in disconnected state (in tickles) */
+ unsigned int tcp_sndbuf, tcp_rcvbuf;
+ unsigned char passwdbits, passwdminlen, loginmaxfail;
+ u_int32_t server_quantum;
+ int dsireadbuf; /* scale factor for sizefof(dsi->buffer) = server_quantum * dsireadbuf */
+ char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile;
+ struct at_addr ddpaddr;
+ char *uampath, *fqdn;
+ char *pidfile;
+ char *sigconffile;
+ char *uuidconf;
+ struct afp_volume_name defaultvol, systemvol, uservol;
+ int closevol;
+
+ char *guest, *loginmesg, *keyfile, *passwdfile;
+ char *uamlist;
+ char *authprintdir;
+ char *signatureopt;
+ unsigned char signature[16];
+ char *k5service, *k5realm, *k5keytab;
+ char *unixcodepage,*maccodepage;
+ charset_t maccharset, unixcharset;
+ mode_t umask;
+ mode_t save_mask;
+#ifdef ADMIN_GRP
+ gid_t admingid;
+#endif /* ADMIN_GRP */
+ int volnamelen;
+
+ /* default value for winbind authentication */
+ char *ntdomain, *ntseparator;
+ char *logconfig;
+
+ char *mimicmodel;
+};
+
+#define AFPOBJ_TMPSIZ (MAXPATHLEN)
+typedef struct _AFPObj {
+ int proto;
+ unsigned long servernum;
+ void *handle; /* either (DSI *) or (ASP *) */
+ void *config;
+ struct afp_options options;
+ char *Obj, *Type, *Zone;
+ char username[MAXUSERLEN];
+ void (*logout)(void), (*exit)(int);
+ int (*reply)(void *, int);
+ int (*attention)(void *, AFPUserBytes);
+ /* to prevent confusion, only use these in afp_* calls */
+ char oldtmp[AFPOBJ_TMPSIZ + 1], newtmp[AFPOBJ_TMPSIZ + 1];
+ void *uam_cookie; /* cookie for uams */
+ struct session_info sinfo;
+ uid_t uid; /* client running user id */
+ int ipc_fd; /* anonymous PF_UNIX socket for IPC with afpd parent */
+#ifdef FORCE_UIDGID
+ int force_uid;
+ uidgidset uidgid;
+#endif
+} AFPObj;
+
+/* typedef for AFP functions handlers */
+typedef int (*AFPCmd)(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen);
+
+/* afp_dsi.c */
+extern AFPObj *AFPobj;
+
+extern int afp_version;
+extern int afp_errno;
+extern unsigned char nologin;
+extern struct dir *curdir;
+extern char getwdbuf[];
+
+/* FIXME CNID */
+extern const char *Cnid_srv;
+extern const char *Cnid_port;
+
+extern int get_afp_errno (const int param);
+extern void afp_options_init (struct afp_options *);
+extern int afp_options_parse (int, char **, struct afp_options *);
+extern int afp_options_parseline (char *, struct afp_options *);
+extern void afp_options_free (struct afp_options *,
+ const struct afp_options *);
+extern void setmessage (const char *);
+extern void readmessage (AFPObj *);
+
+/* gettok.c */
+extern void initline (int, char *);
+extern int parseline (int, char *);
+
+/* afp_util.c */
+extern const char *AfpNum2name (int );
+extern const char *AfpErr2name(int err);
+
+/* directory.c */
+extern struct dir rootParent;
+
+#ifndef NO_DDP
+extern void afp_over_asp (AFPObj *);
+#endif /* NO_DDP */
+extern void afp_over_dsi (AFPObj *);
+
+#endif /* globals.h */
# define _PATH_AFPDLOCK ATALKPATHCAT(_PATH_LOCKDIR,"afpd.pid")
#else
# define _PATH_AFPDLOCK ATALKPATHCAT(_PATH_LOCKDIR,"afpd")
+#define _PATH_AFP_IPC ATALKPATHCAT(_PATH_LOCKDIR,"afpd_ipc")
#endif
/*
pid_t pid; /* afpd worker process pid (from the worker afpd process )*/
uid_t uid; /* user id of connected client (from the worker afpd process) */
int valid; /* 1 if we have a clientid */
- uint32_t time; /* client boot time (from the mac client) */
int killed; /* 1 if we already tried to kill the client */
+ int disasociated; /* 1 if this is not a child, but a child from a previous afpd master */
+ uint32_t time; /* client boot time (from the mac client) */
uint32_t idlen; /* clientid len (from the Mac client) */
char *clientid; /* clientid (from the Mac client) */
int ipc_fds[2]; /* socketpair for IPC bw */
#define ATALK_SERVER_IPC_H
#include <atalk/server_child.h>
+#include <atalk/globals.h>
#define IPC_DISCOLDSESSION 0
#define IPC_GETSESSION 1
-int ipc_server_read(server_child *children, int fd);
-int ipc_child_write(int fd, uint16_t command, int len, void *token);
+extern int ipc_server_uds(const char *name);
+extern int ipc_client_uds(const char *name);
+extern int reconnect_ipc(AFPObj *);
+extern int ipc_server_read(server_child *children, int fd);
+extern int ipc_child_write(int fd, uint16_t command, int len, void *token);
#endif /* IPC_GETSESSION_LOGIN */
size_t cryptedkey_len;
void *sessiontoken; /* session token sent to the client on FPGetSessionToken*/
size_t sessiontoken_len;
+ void *clientid; /* whole buffer cotaining eg idlen, id and boottime */
+ size_t clientid_len;
};
/* register and unregister uams with these functions */
extern int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2);
/* Structures and functions dealing with dynamic pollfd arrays */
-enum fdtype {IPC_FD, LISTEN_FD};
+enum fdtype {IPC_FD, LISTEN_FD, DISASOCIATED_IPC_FD};
struct polldata {
enum fdtype fdtype; /* IPC fd or listening socket fd */
void *data; /* pointer to AFPconfig for listening socket and *
typedef unsigned char *uuidp_t;
typedef unsigned char atalk_uuid_t[UUID_BINSIZE];
-typedef enum {UUID_USER = 1, UUID_GROUP, UUID_LOCAL} uuidtype_t;
+typedef enum {UUID_USER = 0,
+ UUID_GROUP = 1,
+ UUID_ENOENT = 4} /* used as bit flag */
+ uuidtype_t;
+#define UUIDTYPESTR_MASK 3
extern char *uuidtype[];
/* afp_options.c needs these. defined in libatalk/ldap.c */
extern void localuuid_from_id(unsigned char *buf, uuidtype_t type, unsigned int id);
extern const char *uuid_bin2string(unsigned char *uuid);
extern void uuid_string2bin( const char *uuidstring, uuidp_t uuid);
-
+extern void uuidcache_dump(void);
#endif /* AFP_UUID_H */
char *v_gvs;
void *v_nfsclient;
int v_nfs;
-
+ uintmax_t v_tm_used; /* used bytes on a TM volume */
+ uintmax_t v_written; /* amount of data written in afp_write, reset every time a FCE_TM_SIZE event is sent */
+
/* only when opening/closing volumes or in error */
int v_casefold;
char *v_localname; /* as defined in AppleVolumes.default */
#define AFPVOL_TM (1 << 23) /* Supports TimeMachine */
#define AFPVOL_ACLS (1 << 24) /* Volume supports ACLS */
#define AFPVOL_SEARCHDB (1 << 25) /* Use fast CNID db search instead of filesystem */
-/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
-#if 0
-#define AFPVOL_CDROM (1 << XX) /* Ejectable media eg CD -> in memory CNID db */
-#endif
+#define AFPVOL_NONETIDS (1 << 26) /* signal the client it shall do privelege mapping */
/* Extended Attributes vfs indirection */
#define AFPVOL_EA_NONE 0 /* No EAs */
#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_NONETIDS (1 << 7)
#define VOLPBIT_ATTR_EXT_ATTRS (1 << 10)
#define VOLPBIT_ATTR_ACLS (1 << 11)
#define VOLPBIT_ATTR_TM (1 << 13)
* helper function
********************************************************/
-static int dumpcache() {
+void uuidcache_dump(void) {
int i;
int ret = 0;
cacheduser_t *entry;
continue;
if (strftime(timestr, 200, "%c", tmp) == 0)
continue;
- LOG(log_debug9, logtype_default, "namecache{%d}: name:%s, uuid:%s, type: %s, cached: %s",
- i, entry->name, uuid_bin2string(entry->uuid), uuidtype[entry->type], timestr);
+ LOG(log_debug, logtype_default,
+ "namecache{%d}: name:%s, uuid:%s, type%s: %s, cached: %s",
+ i,
+ entry->name,
+ uuid_bin2string(entry->uuid),
+ (entry->type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
+ uuidtype[entry->type & UUIDTYPESTR_MASK],
+ timestr);
} while ((entry = entry->next) != NULL);
}
}
continue;
if (strftime(timestr, 200, "%c", tmp) == 0)
continue;
- LOG(log_debug9, logtype_default, "uuidcache{%d}: uuid:%s, name:%s, type: %s, cached: %s",
- i, uuid_bin2string(entry->uuid), entry->name, uuidtype[entry->type], timestr);
+ LOG(log_debug, logtype_default,
+ "uuidcache{%d}: uuid:%s, name:%s, type%s: %s, cached: %s",
+ i,
+ uuid_bin2string(entry->uuid),
+ entry->name,
+ (entry->type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
+ uuidtype[entry->type & UUIDTYPESTR_MASK],
+ timestr);
} while ((entry = entry->next) != NULL);
}
}
-
- return ret;
}
/* hash string it into unsigned char */
cacheduser_t *cacheduser = NULL;
unsigned char hash;
-#ifdef DEBUG
- dumpcache();
-#endif
-
/* allocate mem and copy values */
name = malloc(strlen(inname)+1);
if (!name) {
free(cacheduser);
}
-#ifdef DEBUG
- dumpcache();
-#endif
-
return ret;
}
-/*
- * Caller provides buffer uuid for result
+/*!
+ * Search cache by name and uuid type
+ *
+ * @args name (r) name to search
+ * @args type (rw) type (user or group) of name, returns found type here which might
+ * mark it as a negative entry
+ * @args uuid (w) found uuid is returned here
+ * @returns 0 on sucess, entry found
+ * -1 no entry found
*/
-int search_cachebyname( const char *name, uuidtype_t type, uuidp_t uuid) {
+int search_cachebyname(const char *name, uuidtype_t *type, uuidp_t uuid) {
int ret;
unsigned char hash;
cacheduser_t *entry;
time_t tim;
-#ifdef DEBUG
- dumpcache();
-#endif
-
hash = hashstring((unsigned char *)name);
if (namecache[hash] == NULL)
entry = namecache[hash];
while (entry) {
ret = strcmp(entry->name, name);
- if (ret == 0 && type == entry->type) {
+ if (ret == 0 && *type == (entry->type & UUIDTYPESTR_MASK)) {
/* found, now check if expired */
tim = time(NULL);
if ((tim - entry->creationtime) > CACHESECONDS) {
- LOG(log_debug, logtype_default, "search_cachebyname: expired: name:\'%s\' in queue {%d}", entry->name, hash);
+ LOG(log_debug, logtype_default, "search_cachebyname: expired: name:\"%s\"", entry->name);
/* remove item */
if (entry->prev) {
/* 2nd to last in queue */
free(entry->name);
free(entry->uuid);
free(entry);
-#ifdef DEBUG
- dumpcache();
-#endif
return -1;
} else {
memcpy(uuid, entry->uuid, UUID_BINSIZE);
-#ifdef DEBUG
- dumpcache();
-#endif
+ *type = entry->type;
return 0;
}
}
entry = entry->next;
}
-#ifdef DEBUG
- dumpcache();
-#endif
+
return -1;
}
cacheduser_t *entry;
time_t tim;
-#ifdef DEBUG
- dumpcache();
-#endif
-
hash = hashuuid(uuidp);
if (! uuidcache[hash])
free(entry->name);
free(entry->uuid);
free(entry);
-#ifdef DEBUG
- dumpcache();
-#endif
return -1;
} else {
*name = malloc(strlen(entry->name)+1);
strcpy(*name, entry->name);
*type = entry->type;
-#ifdef DEBUG
- dumpcache();
-#endif
return 0;
}
}
entry = entry->next;
}
-#ifdef DEBUG
- dumpcache();
-#endif
-
return -1;
}
cacheduser_t *entry;
unsigned char hash;
-#ifdef DEBUG
- dumpcache();
-#endif
-
/* allocate mem and copy values */
name = malloc(strlen(inname)+1);
if (!name) {
free(cacheduser);
}
-#ifdef DEBUG
- dumpcache();
-#endif
-
return ret;
}
* Interface
********************************************************/
-/*
- * name: search for this name
- * type: of type USER or GROUP
- * uuid: if found copies uuid into this buffer
- * returns 0 on success, !=0 if not found or on errors
- */
-extern int search_cachebyname( const char *name, uuidtype_t type, uuidp_t uuid);
-
-/*
- * inname: name
- * inuuid: uuid
- * type: USER or GROUP
- * (uid: unused)
- * returns 0 on success, !=0 on memory errors
- */
+extern int search_cachebyname( const char *name, uuidtype_t *type, uuidp_t uuid);
extern int add_cachebyname( const char *inname, const uuidp_t inuuid, const uuidtype_t type, const unsigned long uid);
-
-/* same as above but for the uuid cache */
extern int search_cachebyuuid( uuidp_t uuidp, char **name, uuidtype_t *type);
extern int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const unsigned long uid);
#include "aclldap.h"
#include "cache.h"
-char *uuidtype[] = {"NULL","USER", "GROUP", "LOCAL"};
+char *uuidtype[] = {"USER", "GROUP", "LOCAL"};
/********************************************************
* Public helper function
static unsigned char local_group_uuid[] = {0xab, 0xcd, 0xef,
0xab, 0xcd, 0xef,
- 0xab, 0xcd, 0xef,
+ 0xab, 0xcd, 0xef,
0xab, 0xcd, 0xef};
static unsigned char local_user_uuid[] = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd,
return;
}
-/*
+/*
* convert ascii string that can include dashes to binary uuid.
* caller must provide a buffer.
*/
}
-/*!
+/*!
* Convert 16 byte binary uuid to neat ascii represantation including dashes.
- *
+ *
* Returns pointer to static buffer.
*/
const char *uuid_bin2string(unsigned char *uuid) {
* type: and type (UUID_USER or UUID_GROUP)
* uuid: pointer to uuid_t storage that the caller must provide
* returns 0 on success !=0 on errror
- */
+ */
int getuuidfromname( const char *name, uuidtype_t type, uuidp_t uuid) {
int ret = 0;
+ uuidtype_t mytype = type;
+ char nulluuid[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
#ifdef HAVE_LDAP
char *uuid_string = NULL;
#endif
- ret = search_cachebyname( name, type, uuid);
+
+ ret = search_cachebyname(name, &mytype, uuid);
+
if (ret == 0) {
/* found in cache */
- LOG(log_debug, logtype_afpd, "getuuidfromname{cache}: name: %s, type: %s -> UUID: %s",
- name, uuidtype[type], uuid_bin2string(uuid));
+ LOG(log_debug, logtype_afpd,
+ "getuuidfromname{cache}: name: %s, type%s: %s -> UUID: %s",
+ name,
+ (mytype & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
+ uuidtype[type & UUIDTYPESTR_MASK],
+ uuid_bin2string(uuid));
+ if ((mytype & UUID_ENOENT) == UUID_ENOENT)
+ return -1;
} else {
/* if not found in cache */
#ifdef HAVE_LDAP
if ((ret = ldap_getuuidfromname( name, type, &uuid_string)) == 0) {
uuid_string2bin( uuid_string, uuid);
- LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
- name, uuidtype[type], uuid_bin2string(uuid));
+ LOG(log_debug, logtype_afpd, "getuuidfromname{LDAP}: name: %s, type: %s -> UUID: %s",
+ name, uuidtype[type & UUIDTYPESTR_MASK], uuid_bin2string(uuid));
} else {
LOG(log_debug, logtype_afpd, "getuuidfromname(\"%s\",t:%u): no result from ldap search",
name, type);
struct passwd *pwd;
if ((pwd = getpwnam(name)) == NULL) {
LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
- name, uuidtype[type]);
- goto cleanup;
+ name, uuidtype[type & UUIDTYPESTR_MASK]);
+ mytype |= UUID_ENOENT;
+ memcpy(uuid, nulluuid, 16);
+ } else {
+ localuuid_from_id(uuid, UUID_USER, pwd->pw_uid);
+ ret = 0;
+ LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
+ name, uuidtype[type & UUIDTYPESTR_MASK], uuid_bin2string(uuid));
}
- localuuid_from_id(uuid, UUID_USER, pwd->pw_uid);
} else {
struct group *grp;
if ((grp = getgrnam(name)) == NULL) {
LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
- name, uuidtype[type]);
- goto cleanup;
+ name, uuidtype[type & UUIDTYPESTR_MASK]);
+ mytype |= UUID_ENOENT;
+ memcpy(uuid, nulluuid, 16);
+ } else {
+ localuuid_from_id(uuid, UUID_GROUP, grp->gr_gid);
+ ret = 0;
+ LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
+ name, uuidtype[type & UUIDTYPESTR_MASK], uuid_bin2string(uuid));
}
- localuuid_from_id(uuid, UUID_GROUP, grp->gr_gid);
}
- LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
- name, uuidtype[type], uuid_bin2string(uuid));
}
- ret = 0;
- add_cachebyname( name, uuid, type, 0);
+ add_cachebyname(name, uuid, mytype, 0);
}
cleanup:
* Caller must free name appropiately.
*/
int getnamefromuuid(const uuidp_t uuidp, char **name, uuidtype_t *type) {
- int ret;
+ int ret = 0;
+ uid_t uid;
+ gid_t gid;
+ struct passwd *pwd;
+ struct group *grp;
- ret = search_cachebyuuid( uuidp, name, type);
- if (ret == 0) {
+ if (search_cachebyuuid(uuidp, name, type) == 0) {
/* found in cache */
- LOG(log_debug9, logtype_afpd, "getnamefromuuid{cache}: UUID: %s -> name: %s, type:%s",
- uuid_bin2string(uuidp), *name, uuidtype[*type]);
- } else {
- /* not found in cache */
-
- /* Check if UUID is a client local one */
- if (memcmp(uuidp, local_user_uuid, 12) == 0
- || memcmp(uuidp, local_group_uuid, 12) == 0) {
- LOG(log_debug, logtype_afpd, "getnamefromuuid: local UUID: %" PRIu32 "",
- ntohl(*(uint32_t *)(uuidp + 12)));
- *type = UUID_LOCAL;
- *name = strdup("UUID_LOCAL");
- return 0;
+ LOG(log_debug, logtype_afpd,
+ "getnamefromuuid{cache}: UUID: %s -> name: %s, type%s: %s",
+ uuid_bin2string(uuidp),
+ *name,
+ (*type & UUID_ENOENT) == UUID_ENOENT ? "[negative]" : "",
+ uuidtype[(*type) & UUIDTYPESTR_MASK]);
+ if ((*type & UUID_ENOENT) == UUID_ENOENT)
+ return -1;
+ return 0;
+ }
+
+ /* not found in cache */
+
+ /* Check if UUID is a client local one */
+ if (memcmp(uuidp, local_user_uuid, 12) == 0) {
+ *type = UUID_USER;
+ uid = ntohl(*(uint32_t *)(uuidp + 12));
+ if ((pwd = getpwuid(uid)) == NULL) {
+ /* not found, add negative entry to cache */
+ add_cachebyuuid(uuidp, "UUID_ENOENT", UUID_ENOENT, 0);
+ ret = -1;
+ } else {
+ *name = strdup(pwd->pw_name);
+ add_cachebyuuid(uuidp, *name, *type, 0);
+ ret = 0;
}
+ LOG(log_debug, logtype_afpd,
+ "getnamefromuuid{local}: UUID: %s -> name: %s, type:%s",
+ uuid_bin2string(uuidp), *name, uuidtype[(*type) & UUIDTYPESTR_MASK]);
+ return ret;
+ } else if (memcmp(uuidp, local_group_uuid, 12) == 0) {
+ *type = UUID_GROUP;
+ gid = ntohl(*(uint32_t *)(uuidp + 12));
+ if ((grp = getgrgid(gid)) == NULL) {
+ /* not found, add negative entry to cache */
+ add_cachebyuuid(uuidp, "UUID_ENOENT", UUID_ENOENT, 0);
+ ret = -1;
+ } else {
+ *name = strdup(grp->gr_name);
+ add_cachebyuuid(uuidp, *name, *type, 0);
+ ret = 0;
+ }
+ return ret;
+ }
#ifdef HAVE_LDAP
- ret = ldap_getnamefromuuid(uuid_bin2string(uuidp), name, type);
- if (ret != 0) {
- LOG(log_warning, logtype_afpd, "getnamefromuuid(%s): no result from ldap_getnamefromuuid",
- uuid_bin2string(uuidp));
- goto cleanup;
- }
- add_cachebyuuid( uuidp, *name, *type, 0);
- LOG(log_debug, logtype_afpd, "getnamefromuuid{LDAP}: UUID: %s -> name: %s, type:%s",
- uuid_bin2string(uuidp), *name, uuidtype[*type]);
-#endif
+ ret = ldap_getnamefromuuid(uuid_bin2string(uuidp), name, type);
+ if (ret != 0) {
+ LOG(log_warning, logtype_afpd, "getnamefromuuid(%s): no result from ldap_getnamefromuuid",
+ uuid_bin2string(uuidp));
+ add_cachebyuuid(uuidp, "UUID_ENOENT", UUID_ENOENT, 0);
+ return -1;
}
+#endif
-cleanup:
- return ret;
+ add_cachebyuuid(uuidp, *name, *type, 0);
+
+ LOG(log_debug, logtype_afpd, "getnamefromuuid{LDAP}: UUID: %s -> name: %s, type:%s",
+ uuid_bin2string(uuidp), *name, uuidtype[(*type) & UUIDTYPESTR_MASK]);
+
+ return 0;
}
ret = readt(db->fd, rply, sizeof(struct cnid_dbd_rply), 0, ONE_DELAY);
if (ret != sizeof(struct cnid_dbd_rply)) {
- LOG(log_error, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s",
+ LOG(log_debug, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s",
db->db_dir, ret == -1 ? strerror(errno) : "closed");
rply->name = nametmp;
return -1;
default: /* parent */
/* using SIGQUIT is hokey, but the child might not have
* re-established its signal handler for SIGTERM yet. */
- if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds)) < 0) {
+ if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds)) == NULL) {
LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
dsi->header.dsi_flags = DSIFL_REPLY;
dsi->header.dsi_code = DSIERR_SERVBUSY;
return 0;
}
+/*
+ * Return all bytes up to count from dsi->buffer if there are any buffered there
+ */
+static size_t from_buf(DSI *dsi, u_int8_t *buf, size_t count)
+{
+ size_t nbe = 0;
+
+ if (dsi->buffer == NULL)
+ /* afpd master has no DSI buffering */
+ return 0;
+
+ LOG(log_maxdebug, logtype_dsi, "from_buf: %u bytes", count);
+
+ nbe = dsi->eof - dsi->start;
+
+ if (nbe > 0) {
+ nbe = min((size_t)nbe, count);
+ memcpy(buf, dsi->start, nbe);
+ dsi->start += nbe;
+
+ if (dsi->eof == dsi->start)
+ dsi->start = dsi->eof = dsi->buffer;
+ }
+
+ LOG(log_debug, logtype_dsi, "from_buf(read: %u, unread:%u , space left: %u): returning %u",
+ dsi->start - dsi->buffer, dsi->eof - dsi->start, dsi->end - dsi->eof, nbe);
+
+ return nbe;
+}
+
+/*
+ * Get bytes from buffer dsi->buffer or read from socket
+ *
+ * 1. Check if there are bytes in the the dsi->buffer buffer.
+ * 2. Return bytes from (1) if yes.
+ * Note: this may return fewer bytes then requested in count !!
+ * 3. If the buffer was empty, read from the socket.
+ */
+static ssize_t buf_read(DSI *dsi, u_int8_t *buf, size_t count)
+{
+ ssize_t len;
+
+ LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes)", count);
+
+ if (!count)
+ return 0;
+
+ len = from_buf(dsi, buf, count); /* 1. */
+ if (len)
+ return len; /* 2. */
+
+ len = readt(dsi->socket, buf, count, 0, 1); /* 3. */
+
+ LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes): got: %d", count, len);
+
+ return len;
+}
+
+/*
+ * Get "length" bytes from buffer and/or socket. In order to avoid frequent small reads
+ * this tries to read larger chunks (8192 bytes) into a buffer.
+ */
+static size_t dsi_buffered_stream_read(DSI *dsi, u_int8_t *data, const size_t length)
+{
+ size_t len;
+ size_t buflen;
+
+ LOG(log_maxdebug, logtype_dsi, "dsi_buffered_stream_read: %u bytes", length);
+
+ len = from_buf(dsi, data, length); /* read from buffer dsi->buffer */
+ dsi->read_count += len;
+ if (len == length) { /* got enough bytes from there ? */
+ return len; /* yes */
+ }
+
+ /* fill the buffer with 8192 bytes or until buffer is full */
+ buflen = min(8192, dsi->end - dsi->eof);
+ if (buflen > 0) {
+ ssize_t ret;
+ ret = read(dsi->socket, dsi->eof, buflen);
+ if (ret > 0)
+ dsi->eof += ret;
+ }
+
+ /* now get the remaining data */
+ if ((buflen = dsi_stream_read(dsi, data + len, length - len)) != length - len)
+ return 0;
+ len += buflen;
+
+ return len;
+}
+
+/* ---------------------------------------
+*/
+static void block_sig(DSI *dsi)
+{
+ dsi->in_write++;
+}
+
+/* ---------------------------------------
+*/
+static void unblock_sig(DSI *dsi)
+{
+ dsi->in_write--;
+}
+
+/*********************************************************************************
+ * Public functions
+ *********************************************************************************/
+
+/*!
+ * Communication error with the client, enter disconnected state
+ *
+ * 1. close the socket
+ * 2. set the DSI_DISCONNECTED flag
+ *
+ * @returns 0 if successfully entered disconnected state
+ * -1 if ppid is 1 which means afpd master died
+ * or euid == 0 ie where still running as root (unauthenticated session)
+ */
+int dsi_disconnect(DSI *dsi)
+{
+ dsi->proto_close(dsi); /* 1 */
+ dsi->flags |= DSI_DISCONNECTED; /* 2 */
+ if (geteuid() == 0)
+ return -1;
+ return 0;
+}
+
/* ------------------------------
* write raw data. return actual bytes read. checks against EINTR
* aren't necessary if all of the signals have SA_RESTART
LOG(log_maxdebug, logtype_dsi, "dsi_stream_write: sending %u bytes", length);
+ if (dsi->flags & DSI_DISCONNECTED)
+ return -1;
+
while (written < length) {
len = send(dsi->socket, (u_int8_t *) data + written, length - written, flags);
if (len >= 0) {
LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: sending %u bytes", length);
+ if (dsi->flags & DSI_DISCONNECTED)
+ return -1;
+
dsi->in_write++;
written = 0;
}
#endif
-/*
- * Return all bytes up to count from dsi->buffer if there are any buffered there
- */
-static size_t from_buf(DSI *dsi, u_int8_t *buf, size_t count)
-{
- size_t nbe = 0;
-
- if (dsi->buffer == NULL)
- /* afpd master has no DSI buffering */
- return 0;
-
- LOG(log_maxdebug, logtype_dsi, "from_buf: %u bytes", count);
-
- nbe = dsi->eof - dsi->start;
-
- if (nbe > 0) {
- nbe = min((size_t)nbe, count);
- memcpy(buf, dsi->start, nbe);
- dsi->start += nbe;
-
- if (dsi->eof == dsi->start)
- dsi->start = dsi->eof = dsi->buffer;
- }
-
- LOG(log_debug, logtype_dsi, "from_buf(read: %u, unread:%u , space left: %u): returning %u",
- dsi->start - dsi->buffer, dsi->eof - dsi->start, dsi->end - dsi->eof, nbe);
-
- return nbe;
-}
-
-/*
- * Get bytes from buffer dsi->buffer or read from socket
- *
- * 1. Check if there are bytes in the the dsi->buffer buffer.
- * 2. Return bytes from (1) if yes.
- * Note: this may return fewer bytes then requested in count !!
- * 3. If the buffer was empty, read from the socket.
- */
-static ssize_t buf_read(DSI *dsi, u_int8_t *buf, size_t count)
-{
- ssize_t len;
-
- LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes)", count);
-
- if (!count)
- return 0;
-
- len = from_buf(dsi, buf, count); /* 1. */
- if (len)
- return len; /* 2. */
-
- len = readt(dsi->socket, buf, count, 0, 1); /* 3. */
-
- LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes): got: %d", count, len);
-
- return len;
-}
/*
* Essentially a loop around buf_read() to ensure "length" bytes are read
size_t stored;
ssize_t len;
+ if (dsi->flags & DSI_DISCONNECTED)
+ return 0;
+
LOG(log_maxdebug, logtype_dsi, "dsi_stream_read(%u bytes)", length);
stored = 0;
while (stored < length) {
len = buf_read(dsi, (u_int8_t *) data + stored, length - stored);
if (len == -1 && (errno == EINTR || errno == EAGAIN)) {
- LOG(log_debug, logtype_dsi, "dsi_stream_read: select read loop");
+ LOG(log_maxdebug, logtype_dsi, "dsi_stream_read: select read loop");
continue;
} else if (len > 0) {
stored += len;
} else { /* eof or error */
/* don't log EOF error if it's just after connect (OSX 10.3 probe) */
+ if (errno == ECONNRESET)
+ dsi->flags |= DSI_GOT_ECONNRESET;
if (len || stored || dsi->read_count) {
if (! (dsi->flags & DSI_DISCONNECTED)) {
LOG(log_error, logtype_dsi, "dsi_stream_read: len:%d, %s",
return stored;
}
-/*
- * Get "length" bytes from buffer and/or socket. In order to avoid frequent small reads
- * this tries to read larger chunks (8192 bytes) into a buffer.
- */
-static size_t dsi_buffered_stream_read(DSI *dsi, u_int8_t *data, const size_t length)
-{
- size_t len;
- size_t buflen;
-
- LOG(log_maxdebug, logtype_dsi, "dsi_buffered_stream_read: %u bytes", length);
-
- len = from_buf(dsi, data, length); /* read from buffer dsi->buffer */
- dsi->read_count += len;
- if (len == length) { /* got enough bytes from there ? */
- return len; /* yes */
- }
-
- /* fill the buffer with 8192 bytes or until buffer is full */
- buflen = min(8192, dsi->end - dsi->eof);
- if (buflen > 0) {
- ssize_t ret;
- ret = read(dsi->socket, dsi->eof, buflen);
- if (ret > 0)
- dsi->eof += ret;
- }
-
- /* now get the remaining data */
- len += dsi_stream_read(dsi, data + len, length - len);
- return len;
-}
-
-/* ---------------------------------------
-*/
-static void block_sig(DSI *dsi)
-{
- dsi->in_write++;
-}
-
-/* ---------------------------------------
-*/
-static void unblock_sig(DSI *dsi)
-{
- dsi->in_write--;
-}
-
/* ---------------------------------------
* write data. 0 on failure. this assumes that dsi_len will never
* cause an overflow in the data buffer.
LOG(log_maxdebug, logtype_dsi, "dsi_stream_send: %u bytes",
length ? length : sizeof(block));
+ if (dsi->flags & DSI_DISCONNECTED)
+ return 0;
+
block[0] = dsi->header.dsi_flags;
block[1] = dsi->header.dsi_command;
memcpy(block + 2, &dsi->header.dsi_requestID,
LOG(log_maxdebug, logtype_dsi, "dsi_stream_receive: %u bytes", ilength);
+ if (dsi->flags & DSI_DISCONNECTED)
+ return 0;
+
/* read in the header */
if (dsi_buffered_stream_read(dsi, (u_int8_t *)block, sizeof(block)) != sizeof(block))
return 0;
http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
*/
-#include <netatalk/endian.h>
+#include <stdint.h>
#include <atalk/unicode.h>
#include "utf16_casetable.h"
/*******************************************************************
Convert a surrogate pair to upper case.
*******************************************************************/
-u_int32_t toupper_sp(u_int32_t val)
+uint32_t toupper_sp(uint32_t val)
{
if ( val >= 0xD801DC00 && val <= 0xD801DC7F)
return upper_table_sp_1[val-0xD801DC00];
/*******************************************************************
Convert a surrogate pair to lower case.
*******************************************************************/
-u_int32_t tolower_sp(u_int32_t val)
+uint32_t tolower_sp(uint32_t val)
{
if ( val >= 0xD801DC00 && val <= 0xD801DC3F)
return lower_table_sp_1[val-0xD801DC00];
if (type_configs[logtype].level >= log_debug)
goto log; /* bypass flooding checks */
+ goto log;
/* Prevent flooding: hash the message and check if we got the same one recently */
int hash = hash_message(temp_buffer) + log_src_linenumber;
fork = (server_child_fork *) children->fork + forkid;
if ((child = resolve_child(fork->table, pid)) == NULL) {
LOG(log_note, logtype_default, "Reconnect: no child[%u]", pid);
+ if (kill(pid, 0) == 0) {
+ LOG(log_note, logtype_default, "Reconnect: terminating old session[%u]", pid);
+ kill(pid, SIGTERM);
+ sleep(2);
+ if (kill(pid, 0) == 0) {
+ LOG(log_error, logtype_default, "Reconnect: killing old session[%u]", pid);
+ kill(pid, SIGKILL);
+ sleep(2);
+ }
+ }
return 0;
}
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <errno.h>
#include <signal.h>
+#include <time.h>
#include <atalk/server_child.h>
#include <atalk/server_ipc.h>
#include <atalk/logger.h>
#include <atalk/util.h>
+#include <atalk/errchk.h>
+#include <atalk/paths.h>
+#include <atalk/globals.h>
+#include <atalk/dsi.h>
+
+#define IPC_HEADERLEN 14
+#define IPC_MAXMSGSIZE 90
typedef struct ipc_header {
uint16_t command;
return 0;
}
-#define IPC_HEADERLEN 14
-#define IPC_MAXMSGSIZE 90
+/***********************************************************************************
+ * Public functions
+ ***********************************************************************************/
+
+/*!
+ * Listen on UNIX domain socket "name" for IPC from old sesssion
+ *
+ * @args name (r) file name to use for UNIX domain socket
+ * @returns socket fd, -1 on error
+ */
+int ipc_server_uds(const char *name)
+{
+ EC_INIT;
+ struct sockaddr_un address;
+ socklen_t address_length;
+ int fd = -1;
+
+ EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) );
+ EC_ZERO_LOG( setnonblock(fd, 1) );
+ unlink(name);
+ address.sun_family = AF_UNIX;
+ address_length = sizeof(address.sun_family) + sprintf(address.sun_path, name);
+ EC_ZERO_LOG( bind(fd, (struct sockaddr *)&address, address_length) );
+ EC_ZERO_LOG( listen(fd, 1024) );
+
+EC_CLEANUP:
+ if (ret != 0) {
+ return -1;
+ }
+
+ return fd;
+}
+
+/*!
+ * Connect to UNIX domain socket "name" for IPC with new afpd master
+ *
+ * 1. Connect
+ * 2. send pid, which establishes a child structure for us in the master
+ *
+ * @args name (r) file name to use for UNIX domain socket
+ * @returns socket fd, -1 on error
+ */
+int ipc_client_uds(const char *name)
+{
+ EC_INIT;
+ struct sockaddr_un address;
+ socklen_t address_length;
+ int fd = -1;
+ pid_t pid = getpid();
+
+ EC_NEG1_LOG( fd = socket(PF_UNIX, SOCK_STREAM, 0) );
+ EC_ZERO_LOG( setnonblock(fd, 1) );
+ address.sun_family = AF_UNIX;
+ address_length = sizeof(address.sun_family) + sprintf(address.sun_path, name);
+
+ EC_ZERO_LOG( connect(fd, (struct sockaddr *)&address, address_length) ); /* 1 */
+ LOG(log_debug, logtype_afpd, "ipc_client_uds: connected to master");
+
+ if (writet(fd, &pid, sizeof(pid_t), 0, 1) != sizeof(pid_t)) {
+ LOG(log_error, logtype_afpd, "ipc_client_uds: writet: %s", strerror(errno));
+ EC_FAIL;
+ }
+
+EC_CLEANUP:
+ if (ret != 0) {
+ return -1;
+ }
+ LOG(log_debug, logtype_afpd, "ipc_client_uds: fd: %d", fd);
+ return fd;
+}
+
+int reconnect_ipc(AFPObj *obj)
+{
+ int retrycount = 0;
+
+ LOG(log_debug, logtype_afpd, "reconnect_ipc: start");
+
+ close(obj->ipc_fd);
+ obj->ipc_fd = -1;
+
+ srandom(getpid());
+ sleep((random() % 5) + 5); /* give it enough time to start */
+
+ while (retrycount++ < 10) {
+ if ((obj->ipc_fd = ipc_client_uds(_PATH_AFP_IPC)) == -1) {
+ LOG(log_error, logtype_afpd, "reconnect_ipc: cant reconnect to master");
+ sleep(1);
+ continue;
+ }
+ LOG(log_debug, logtype_afpd, "reconnect_ipc: succesfull IPC reconnect");
+ return 0;
+ }
+ return -1;
+}
/* -----------------
* Ipc format
* pid
* uid
*
-*/
+ */
/*!
* Read a IPC message from a child
char block[IPC_MAXMSGSIZE], *p;
pid_t pid;
uid_t uid;
+ ssize_t ret;
+
p = block;
memset ( p, 0 , IPC_MAXMSGSIZE);
LOG(log_debug, logtype_afpd, "ipc_child_write(%s)", ipc_cmd_str[command]);
- return write(fd, block, len+IPC_HEADERLEN );
+ if ((ret = writet(fd, block, len+IPC_HEADERLEN, 0, 1)) != len + IPC_HEADERLEN) {
+ return -1;
+ }
+
+ return 0;
}
#include "config.h"
#endif /* HAVE_CONFIG_H */
-#ifndef _XOPEN_SOURCE
-# define _XOPEN_SOURCE 600
-#endif
-#ifndef __EXTENSIONS__
-# define __EXTENSIONS__
-#endif
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE
+#if !defined(__FreeBSD__)
+# ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+# endif
+# ifndef __EXTENSIONS__
+# define __EXTENSIONS__
+# endif
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
#endif
#include <unistd.h>
#include <fcntl.h>
goto exit;
default: /* -1 */
- if (errno == EINTR) {
+ switch (errno) {
+ case EINTR:
(void)gettimeofday(&now, NULL);
if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) {
- LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
+ LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
goto exit;
}
if (now.tv_usec > end.tv_usec) {
}
FD_SET(socket, &rfds);
continue;
+ case EBADF:
+ /* possibly entered disconnected state, don't spam log here */
+ LOG(log_debug, logtype_afpd, "select: %s", strerror(errno));
+ stored = -1;
+ goto exit;
+ default:
+ LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
+ stored = -1;
+ goto exit;
}
- LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
- stored = -1;
- goto exit;
}
} /* while (select) */
continue;
* Remove a fd from our pollfd array
*
* 1. Search fd
- * 2. If we remove the last array elemnt, just decrease count
+ * 2a
+ * 2b If we remove the last array elemnt, just decrease count
* 3. If found move all following elements down by one
* 4. Decrease count of used elements in array
*
struct pollfd *fdset = *fdsetp;
struct polldata *polldata = *polldatap;
+ if (*fdset_usedp < 1)
+ return;
+
for (int i = 0; i < *fdset_usedp; i++) {
if (fdset[i].fd == fd) { /* 1 */
- if (i < (*fdset_usedp - 1)) { /* 2 */
+ if (i == 0 && *fdset_usedp == 1) { /* 2a */
+ fdset[i].fd = -1;
+ memset(&polldata[i], 0, sizeof(struct polldata));
+ } else if (i < (*fdset_usedp - 1)) { /* 2b */
memmove(&fdset[i], &fdset[i+1], (*fdset_usedp - 1) * sizeof(struct pollfd)); /* 3 */
memmove(&polldata[i], &polldata[i+1], (*fdset_usedp - 1) * sizeof(struct polldata)); /* 3 */
}
\fBusedots\fR: make dot files invisible\&.
.RE
.PP
+nonetids
+.RS 4
+Try to force ACL unawareness on the client\&.
+.RE
+.PP
limitsize
.RS 4
Limit disk size reporting to 2GB\&. This can be used for older Macintoshes using newer Appleshare clients\&.
.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:
+use AFP3 unix privileges\&. This should be set for OS X clients\&. Starting with Netatalk 2\&.1 it\'s part of the default config :DEFAULT: line\&. See also:
\fBperm|fperm|dperm\fR\&.
.RE
.PP
.RE
.SH "SIGNALS"
.PP
-Signals that are sent to the main
+To shut down a user\'s
+\fBafpd\fR
+process it is recommended that
+\fBSIGKILL (\-9)\fR
+\fINOT\fR
+be used, except as a last resort, as this may leave the CNID database in an inconsistent state\&. The safe way to terminate an
+\fBafpd\fR
+is to send it a
+\fBSIGTERM (\-15)\fR
+signal and wait for it to die on its own\&.
+.PP
+SIGTERM and SIGUSR1 signals that are sent to the main
\fBafpd\fR
process are propagated to the children, so all will be affected\&.
.PP
+SIGTERM
+.RS 4
+Clean exit\&. Propagates from master to childs\&.
+.RE
+.PP
+SIGQUIT
+.RS 4
+Send this to the master
+\fBafpd\fR, it will exit leaving all children running! Can be used to implement AFP service without downtime\&.
+.RE
+.PP
SIGHUP
.RS 4
Sending a
logging for this process\&. The log is sent to fhe file
/tmp/afpd\&.PID\&.XXXXXX\&. Sending another
\fBSIGINT\fR
-will terminate the process\&.
+will revert to the original log settings\&.
.RE
.PP
SIGUSR1
\fBafpd\fR
process will look in the message directory configured at build time for a file named message\&.pid\&. For each one found, a the contents will be sent as a message to the associated AFP client\&. The file is removed after the message is sent\&. This should only be sent to a child
\fBafpd\fR\&.
-.sp
-To shut down a user\'s
-\fBafpd\fR
-process it is recommended that
-\fBSIGKILL (\-9)\fR
-\fINOT\fR
-be used, except as a last resort, as this may leave the CNID database in an inconsistent state\&. The safe way to terminate an
-\fBafpd\fR
-is to send it a
-\fBSIGTERM (\-15)\fR
-signal and wait for it to die on its own\&.
.RE
.SH "FILES"
.PP