# Makefile.am for top level of netatalk package
-SUBDIRS = libatalk bin config etc man contrib distrib include sys doc macros
+SUBDIRS = libatalk bin config etc man contrib distrib include sys doc macros test
-EXTRA_DIST = CONTRIBUTORS COPYRIGHT COPYING NEWS\
- TODO VERSION services.atalk
+EXTRA_DIST = CONTRIBUTORS COPYRIGHT COPYING NEWS VERSION services.atalk
ACLOCAL_AMFLAGS = -I macros
AUTOMAKE_OPTIONS = foreign
+Changes in 2.2beta2
+====================
+
+* NEW: afpd: AFP 3.3
+* UPD: afpd: AFP 3.x can't be disabled
+
+Changes in 2.2beta1
+====================
+
+* FIX: composition of Surrogate Pair
+* UPD: gentoo,suse,cobalt,tru64: inistscript name is "netatalk", not "atalk"
+* UPD: gentoo: rc-update install don't hook in the Makefile
+
+Changes in 2.2alpha5
+====================
+
+* UPD: afpd: new option "searchdb" which enables fast catalog searches
+ using the CNID db.
+* UPD: Case-insensitive fast search with the CNID db
+* UPD: cnid_dbd: afpd now passes the volume path, not the db path when
+ connecting for a volume. cnid_dbd will read the
+ ".AppleDesktop/.volinfo" file of the volume in order to figure
+ out the CNID db path and the volume charset encoding.
+
+Changes in 2.2alpha4
+====================
+
+* NEW: Enhanced CNID "dbd" database for fast name search support.
+ Important: this makes cnidscheme "cdb" incompatible with "dbd".
+* NEW: afpd: support for fast catalog searches
+* NEW: ad utility: ad find
+* UPD: afpd: CNID database versioning check for "cdb" scheme
+* UPD: cnid_dbd: CNID database versioning and upgrading. Additional
+ CNID database index for fast name searches.
+
+Changes in 2.2alpha3
+====================
+
+* FIX: afpd: various fixes
+* FIX: Any daemon did not run if atalkd doesn't exist (redhat/debian)
+
+Changes in 2.2alpha2
+====================
+
+* FIX: afpd: fix compilation error when ACL support is not available
+* FIX: Ensure Appletalk manpages and config files are distributed
+
+Changes in 2.2alpha1
+====================
+
+* NEW: ad utility: ad cp
+* NEW: ad utility: ad rm
+* NEW: ad utility: ad mv
+* NEW: afpd: dynamic directoy and CNID cache (new config option -dircachesize)
+* NEW: afpd: POSIX 1e ACL support
+* NEW: afpd: automagic Zeroconf registration with avahi, registering both
+ the service _afpovertcp._tcp and TimeMachine volumes with _adisk._tcp.
+* UPD: afpd: ACLs usable (though not visible on the client side) without common
+ directory service, by mapping ACLs to UARight
+* UPD: afpd: performance improvements for ACL access calculations
+* UPD: AppleTalk is disabled by default at configuration time. If needed
+ use configure switch --enable-ddp.
+* FIX: afpd: Solaris 10 compatibilty fix: don't use SO_SNDTIMEO/SO_RCVTIMEO,
+ use non-blocking IO and select instead.
+* FIX: cnid_dbd: Solaris 10 compatibilty fix: don't use SO_SNDTIMEO/SO_RCVTIMEO,
+ use non-blocking IO and select instead.
+* REM: afile/achfile/apple_cp/apple_mv/apple_rm: use ad
+
Changes in 2.1.6
================
* FIX: afpd: Better handling of symlinks in combination with ACLs and EAs.
Fixes bug 3074076.
* FIX: dbd: Adding a file with the CNID from it's adouble file did
- not work in case that CNID was alread occupied in the database
+ not work in case that CNID was already occupied in the database
* FIX: macusers: add support for Solaris
* NEW: cnid_metad: use a PID lockfile
* NEW: afpd: prevent log flooding
Changes in 2.1-beta2
====================
+
* NEW: afpd: static generated AFP signature stored in afp_signature.conf,
cf man 5 afp_signature.conf
* NEW: afpd: clustering support: new per volume option "cnidserver".
+++ /dev/null
-The documentation for Netatalk is arranged as follows:
-
-* doc/Netatalk-Manual(.pdf|.txt)
- the Netatalk manual
-* doc/DEVELOPER
- information for developers and additional requirements for compiling
-* doc/FAQ
- FAQ in progress
-* doc/README.AppleTalk
- additional instructions for AppleTalk on various operating systems.
-* doc/README.hidden-items
- documents the various special files created by netatalk in file shares
-
-This should be all you need to get netatalk running.
-
- - The Netatalk Team
+++ /dev/null
-desired features (in no particular order):
- afpd:
- return the right version-specific error codes
- honor the readonly/deleteinhibit/renameinhibit/copyprotect bits.
- via a database (that handles ro media, -db <path>):
- Add afp_fileid capabilities - done
- Add afp_catalogue
- afp_enumerate optimization
- server messages in useful places
- change afp/ddp child handling to be in line w/ generic?
- administrative control program (using asip API?)
- appledouble v2 (gets us persistent DIDs). we'll need a did
- database for non-hfs volumes. i think mmapping a
- bitmap with dids at the root level should work.
- also, v2 gets us much better prodos support and
- provides for shortname support. - done
- various toggles to afpd.conf (-timeout, etc.)
- figure out more ways of optimizing things. current places
- of interest:
- 1) reading/writing. currently there's character
- replacement. we should have a better way of doing
- it that speeds things up the no-replacement case.
- i've already done that for the crlf case.
- 2) use of mmap where appropriate. NOTE: this will only
- be a win if we mmap heavily used files. likely
- candidates include file-based databases. mmapping
- header files is actually pretty slow under linux.
- 3) change lookup algorithms where appropriate.
- ability to interact with hfs desktop databases (either by
- changing hfs or changing afpd).
- utmp/wtmp support
-
- papd:
- DHX authenticated printing logins (does a real Appleshare server
- do this?)
- Change to samba-style config file.
-
- autoconf/automake system:
- Need to separate out the flags and libraries for different
- applications, so that we aren't having them all linked with
- every library, etc.
-
-things to fix:
- cleaner separation of layers.
- AFP <-> Unix permissions. there are a couple cases where they
- don't map perfectly. in particular, gid == 0 means
- ignore group permissions while uid == 0 means anybody
- can fiddle with this file. in addition, we need to be
- able to still change permissions on a directory with u-a
- set. finally, we need to adjust the offspring count
- for directories based upon what permissions they
- have. i.e., search -> can see directories.
- read -> can see files.
- we need to map permissions so that these semantics get
- followed.
- support picking up items from dropboxes without linux-side superuser
- intervention
- support for system-wide messages; send the main afpd process SIGUSR2
- and each of the children should send out the same message.
- replacement of a linefeed with the appropriate Macintosh character in
- server messaging (currently replaces with a space)
-
-added features:
- sped up of_findname and of_dealloc.
- nfs quota support
- solaris beta STREAMS driver added.
- 64-bit cleanup
- cleaner startup/takedown
- added debug messages to dsi_write areas.
- fixed server info unexpected disconnects (due to OT bug).
- afp/ddp and afp/tcp cohabitation. afp/ddp and afp/tcp can
- operate separately now (-T turns off tcp, -D turns off ddp).
- incorporated the netbsd patches
- [source: wrstuden@loki.stanford.edu (Bill Studenmund)]
- casefolding on a per volume basis.
- added "generic" platform support for AFP/tcp.
- :ETCDIR:/afppasswd file for randnum passwds
- AppleVolumes variable substitions
- atalkd: zones on single interfaces and the ability to control
- which interfaces get routes between them.
- papd: cleartext and noauth logins using Print Server Security
- Protocol
- CAP-style authenticated printing
- fixed errors when spooling to lpr 0.46 queue
-2.1.6dev
\ No newline at end of file
+2.2beta2
\ No newline at end of file
# Makefile.am for bin/
-SUBDIRS = adv1tov2 aecho afile afppasswd cnid getzones megatron nbp pap psorder uniconv misc
+SUBDIRS = ad adv1tov2 afppasswd cnid megatron uniconv misc
+
+if USE_APPLETALK
+SUBDIRS += aecho getzones nbp pap psorder
+endif
--- /dev/null
+Makefile
+Makefile.in
+ad
+.deps
+.libs
+*.o
--- /dev/null
+# Makefile.am for bin/ad/
+
+noinst_HEADERS = ad.h
+
+if USE_BDB
+bin_PROGRAMS = ad
+
+ad_SOURCES = \
+ ad.c \
+ ad_find.c \
+ ad_util.c \
+ ad_ls.c \
+ ad_cp.c \
+ ad_mv.c \
+ ad_rm.c
+
+ad_CFLAGS = -D_PATH_AD=\"$(bindir)/ad\"
+
+ad_LDADD = \
+ $(top_builddir)/libatalk/cnid/libcnid.la \
+ $(top_builddir)/libatalk/libatalk.la \
+ @ACL_LIBS@
+
+endif
--- /dev/null
+/*
+ Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+#include <atalk/cnid.h>
+#include <atalk/volinfo.h>
+#include <atalk/logger.h>
+#include <atalk/util.h>
+
+#include "ad.h"
+
+static void usage_main(void)
+{
+ printf("Usage: ad ls|cp|rm|mv|find [file|dir, ...]\n");
+}
+
+int main(int argc, char **argv)
+{
+ setuplog("default log_note /dev/tty");
+
+ if (argc < 2) {
+ usage_main();
+ return 1;
+ }
+
+ if (STRCMP(argv[1], ==, "ls"))
+ return ad_ls(argc - 1, argv + 1);
+ else if (STRCMP(argv[1], ==, "cp"))
+ return ad_cp(argc - 1, argv + 1);
+ else if (STRCMP(argv[1], ==, "rm"))
+ return ad_rm(argc - 1, argv + 1);
+ else if (STRCMP(argv[1], ==, "mv"))
+ return ad_mv(argc, argv);
+ else if (STRCMP(argv[1], ==, "find"))
+ return ad_find(argc, argv);
+ else {
+ usage_main();
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef AD_H
+#define AD_H
+
+#define _XOPEN_SOURCE 600
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include <atalk/ftw.h>
+#include <atalk/volinfo.h>
+#include <atalk/cnid.h>
+
+#define DIR_DOT_OR_DOTDOT(a) \
+ ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
+
+#ifndef TIMESPEC_TO_TIMEVAL
+#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
+ (tv)->tv_sec = (ts)->tv_sec; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+ }
+#endif
+
+enum logtype {STD, DBG};
+
+#define SLOG(...) \
+ _log(STD, __VA_ARGS__)
+
+#define ERROR(...) \
+ do { \
+ _log(STD, __VA_ARGS__); \
+ exit(1); \
+ } while (0)
+
+typedef struct {
+ struct volinfo volinfo;
+ struct vol volume;
+ char db_stamp[ADEDLEN_PRIVSYN];
+} afpvol_t;
+
+extern int log_verbose; /* Logging flag */
+extern void _log(enum logtype lt, char *fmt, ...);
+
+extern int ad_ls(int argc, char **argv);
+extern int ad_cp(int argc, char **argv);
+extern int ad_rm(int argc, char **argv);
+extern int ad_mv(int argc, char **argv);
+
+/* ad_util.c */
+extern int openvol(const char *path, afpvol_t *vol);
+extern void closevol(afpvol_t *vol);
+extern cnid_t cnid_for_path(const afpvol_t *vol, const char *path, cnid_t *did);
+extern cnid_t cnid_for_paths_parent(const afpvol_t *vol, const char *path, cnid_t *did);
+extern char *utompath(const struct volinfo *volinfo, const char *upath);
+extern int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path, size_t buflen);
+
+typedef struct {
+ char *p_end;/* pointer to NULL at end of path */
+ char *target_end;/* pointer to end of target base */
+ char p_path[MAXPATHLEN + 2];/* pointer to the start of a path */
+} PATH_T;
+
+extern PATH_T to;
+extern int fflag, iflag, lflag, nflag, pflag, vflag;
+
+#endif /* AD_H */
--- /dev/null
+/*
+ * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Cp copies source files to target files.
+ *
+ * The global PATH_T structure "to" always contains the path to the
+ * current target file. Since fts(3) does not change directories,
+ * this path can be either absolute or dot-relative.
+ *
+ * The basic algorithm is to initialize "to" and use fts(3) to traverse
+ * the file hierarchy rooted in the argument list. A trivial case is the
+ * case of 'cp file1 file2'. The more interesting case is the case of
+ * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
+ * path (relative to the root of the traversal) is appended to dir (stored
+ * in "to") to form the final target path.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#include <atalk/ftw.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/util.h>
+#include <atalk/unix.h>
+#include <atalk/volume.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/queue.h>
+
+#include "ad.h"
+
+#define STRIP_TRAILING_SLASH(p) { \
+ while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
+ *--(p).p_end = 0; \
+ }
+
+static char emptystring[] = "";
+
+PATH_T to = { to.p_path, emptystring, "" };
+enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
+
+int fflag, iflag, nflag, pflag, vflag;
+mode_t mask;
+
+cnid_t ppdid, pdid, did; /* current dir CNID and parent did*/
+
+static afpvol_t svolume, dvolume;
+static enum op type;
+static int Rflag;
+static volatile sig_atomic_t alarmed;
+static int badcp, rval;
+static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL;
+
+static char *netatalk_dirs[] = {
+ ".AppleDouble",
+ ".AppleDB",
+ ".AppleDesktop",
+ NULL
+};
+
+/* Forward declarations */
+static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
+static int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int);
+static int ftw_copy_link(const struct FTW *, const char *, const struct stat *, int);
+static int setfile(const struct stat *, int);
+static int preserve_dir_acls(const struct stat *, char *, char *);
+static int preserve_fd_acls(int, int);
+
+/*
+ Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
+ Returns pointer to name or NULL.
+*/
+static const char *check_netatalk_dirs(const char *name)
+{
+ int c;
+
+ for (c=0; netatalk_dirs[c]; c++) {
+ if ((strcmp(name, netatalk_dirs[c])) == 0)
+ return netatalk_dirs[c];
+ }
+ return NULL;
+}
+
+static void upfunc(void)
+{
+ did = pdid;
+ pdid = ppdid;
+}
+
+/*
+ SIGNAL handling:
+ catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+ alarmed = 1;
+ return;
+}
+
+static void set_signal(void)
+{
+ struct sigaction sv;
+
+ sv.sa_handler = sig_handler;
+ sv.sa_flags = SA_RESTART;
+ sigemptyset(&sv.sa_mask);
+ if (sigaction(SIGTERM, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+ if (sigaction(SIGINT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+ memset(&sv, 0, sizeof(struct sigaction));
+ sv.sa_handler = SIG_IGN;
+ sigemptyset(&sv.sa_mask);
+
+ if (sigaction(SIGABRT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+ if (sigaction(SIGHUP, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+ if (sigaction(SIGQUIT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+static void usage_cp(void)
+{
+ printf(
+ "Usage: ad cp [-R] [-aipvf] <source_file> <target_file>\n"
+ " ad cp [-R] [-aipvfx] <source_file [source_file ...]> <target_directory>\n\n"
+ "In the first synopsis form, the cp utility copies the contents of the source_file to the\n"
+ "target_file. In the second synopsis form, the contents of each named source_file is copied to the\n"
+ "destination target_directory. The names of the files themselves are not changed. If cp detects an\n"
+ "attempt to copy a file to itself, the copy will fail.\n\n"
+ "Netatalk AFP volumes are detected by means of their \".AppleDesktop\" directory\n"
+ "which is located in their volume root. When a copy targetting an AFP volume\n"
+ "is detected, its CNID database daemon is connected and all copies will also\n"
+ "go through the CNID database.\n"
+ "AppleDouble files are also copied and created as needed when the target is\n"
+ "an AFP volume.\n\n"
+ "The following options are available:\n\n"
+ " -a Archive mode. Same as -Rp.\n\n"
+ " -f For each existing destination pathname, remove it and create a new\n"
+ " file, without prompting for confirmation regardless of its permis-\n"
+ " sions. (The -f option overrides any previous -i or -n options.)\n\n"
+ " -i Cause cp to write a prompt to the standard error output before\n"
+ " copying a file that would overwrite an existing file. If the\n"
+ " response from the standard input begins with the character 'y' or\n"
+ " 'Y', the file copy is attempted. (The -i option overrides any pre-\n"
+ " vious -f or -n options.)\n\n"
+ " -n Do not overwrite an existing file. (The -n option overrides any\n"
+ " previous -f or -i options.)\n\n"
+ " -p Cause cp to preserve the following attributes of each source file\n"
+ " in the copy: modification time, access time, file flags, file mode,\n"
+ " user ID, and group ID, as allowed by permissions.\n"
+ " If the user ID and group ID cannot be preserved, no error message\n"
+ " is displayed and the exit value is not altered.\n\n"
+ " -R If source_file designates a directory, cp copies the directory and\n"
+ " the entire subtree connected at that point.If the source_file\n"
+ " ends in a /, the contents of the directory are copied rather than\n"
+ " the directory itself.\n\n"
+ " -v Cause cp to be verbose, showing files as they are copied.\n\n"
+ " -x File system mount points are not traversed.\n\n"
+ );
+ exit(EXIT_FAILURE);
+}
+
+int ad_cp(int argc, char *argv[])
+{
+ struct stat to_stat, tmp_stat;
+ int r, ch, have_trailing_slash;
+ char *target;
+#if 0
+ afpvol_t srcvol;
+ afpvol_t dstvol;
+#endif
+
+ ppdid = pdid = htonl(1);
+ did = htonl(2);
+
+ while ((ch = getopt(argc, argv, "afinpRvx")) != -1)
+ switch (ch) {
+ case 'a':
+ pflag = 1;
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ iflag = nflag = 0;
+ break;
+ case 'i':
+ iflag = 1;
+ fflag = nflag = 0;
+ break;
+ case 'n':
+ nflag = 1;
+ fflag = iflag = 0;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case 'x':
+ ftw_options |= FTW_MOUNT;
+ break;
+ default:
+ usage_cp();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2)
+ usage_cp();
+
+ set_signal();
+ cnid_init();
+
+ /* Save the target base in "to". */
+ target = argv[--argc];
+ if ((strlcpy(to.p_path, target, PATH_MAX)) >= PATH_MAX)
+ ERROR("%s: name too long", target);
+
+ to.p_end = to.p_path + strlen(to.p_path);
+ if (to.p_path == to.p_end) {
+ *to.p_end++ = '.';
+ *to.p_end = 0;
+ }
+ have_trailing_slash = (to.p_end[-1] == '/');
+ if (have_trailing_slash)
+ STRIP_TRAILING_SLASH(to);
+ to.target_end = to.p_end;
+
+ /* Set end of argument list */
+ argv[argc] = NULL;
+
+ /*
+ * Cp has two distinct cases:
+ *
+ * cp [-R] source target
+ * cp [-R] source1 ... sourceN directory
+ *
+ * In both cases, source can be either a file or a directory.
+ *
+ * In (1), the target becomes a copy of the source. That is, if the
+ * source is a file, the target will be a file, and likewise for
+ * directories.
+ *
+ * In (2), the real target is not directory, but "directory/source".
+ */
+ r = stat(to.p_path, &to_stat);
+ if (r == -1 && errno != ENOENT)
+ ERROR("%s", to.p_path);
+ if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
+ /*
+ * Case (1). Target is not a directory.
+ */
+ if (argc > 1)
+ ERROR("%s is not a directory", to.p_path);
+
+ /*
+ * Need to detect the case:
+ *cp -R dir foo
+ * Where dir is a directory and foo does not exist, where
+ * we want pathname concatenations turned on but not for
+ * the initial mkdir().
+ */
+ if (r == -1) {
+ lstat(*argv, &tmp_stat);
+
+ if (S_ISDIR(tmp_stat.st_mode) && Rflag)
+ type = DIR_TO_DNE;
+ else
+ type = FILE_TO_FILE;
+ } else
+ type = FILE_TO_FILE;
+
+ if (have_trailing_slash && type == FILE_TO_FILE) {
+ if (r == -1)
+ ERROR("directory %s does not exist", to.p_path);
+ else
+ ERROR("%s is not a directory", to.p_path);
+ }
+ } else
+ /*
+ * Case (2). Target is a directory.
+ */
+ type = FILE_TO_DIR;
+
+ /*
+ * Keep an inverted copy of the umask, for use in correcting
+ * permissions on created directories when not using -p.
+ */
+ mask = ~umask(0777);
+ umask(~mask);
+
+#if 0
+ /* Inhereting perms in ad_mkdir etc requires this */
+ ad_setfuid(0);
+#endif
+
+ /* Load .volinfo file for destination*/
+ openvol(to.p_path, &dvolume);
+
+ for (int i = 0; argv[i] != NULL; i++) {
+ /* Load .volinfo file for source */
+ openvol(argv[i], &svolume);
+
+ if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
+ if (alarmed) {
+ SLOG("...break");
+ } else {
+ SLOG("Error: %s: %s", argv[i], strerror(errno));
+ }
+ closevol(&svolume);
+ closevol(&dvolume);
+ }
+ }
+ return rval;
+}
+
+static int copy(const char *path,
+ const struct stat *statp,
+ int tflag,
+ struct FTW *ftw)
+{
+ static int base = 0;
+
+ struct stat to_stat;
+ int dne;
+ size_t nlen;
+ const char *p;
+ char *target_mid;
+
+ if (alarmed)
+ return -1;
+
+ const char *dir = strrchr(path, '/');
+ if (dir == NULL)
+ dir = path;
+ else
+ dir++;
+ if (check_netatalk_dirs(dir) != NULL)
+ return FTW_SKIP_SUBTREE;
+
+ /*
+ * If we are in case (2) above, we need to append the
+ * source name to the target name.
+ */
+ if (type != FILE_TO_FILE) {
+ /*
+ * Need to remember the roots of traversals to create
+ * correct pathnames. If there's a directory being
+ * copied to a non-existent directory, e.g.
+ * cp -R a/dir noexist
+ * the resulting path name should be noexist/foo, not
+ * noexist/dir/foo (where foo is a file in dir), which
+ * is the case where the target exists.
+ *
+ * Also, check for "..". This is for correct path
+ * concatenation for paths ending in "..", e.g.
+ * cp -R .. /tmp
+ * Paths ending in ".." are changed to ".". This is
+ * tricky, but seems the easiest way to fix the problem.
+ *
+ * XXX
+ * Since the first level MUST be FTS_ROOTLEVEL, base
+ * is always initialized.
+ */
+ if (ftw->level == 0) {
+ if (type != DIR_TO_DNE) {
+ base = ftw->base;
+
+ if (strcmp(&path[base], "..") == 0)
+ base += 1;
+ } else
+ base = strlen(path);
+ }
+
+ p = &path[base];
+ nlen = strlen(path) - base;
+ target_mid = to.target_end;
+ if (*p != '/' && target_mid[-1] != '/')
+ *target_mid++ = '/';
+ *target_mid = 0;
+ if (target_mid - to.p_path + nlen >= PATH_MAX) {
+ SLOG("%s%s: name too long (not copied)", to.p_path, p);
+ badcp = rval = 1;
+ return 0;
+ }
+ (void)strncat(target_mid, p, nlen);
+ to.p_end = target_mid + nlen;
+ *to.p_end = 0;
+ STRIP_TRAILING_SLASH(to);
+ }
+
+ /* Not an error but need to remember it happened */
+ if (stat(to.p_path, &to_stat) == -1)
+ dne = 1;
+ else {
+ if (to_stat.st_dev == statp->st_dev &&
+ to_stat.st_ino == statp->st_ino) {
+ SLOG("%s and %s are identical (not copied).", to.p_path, path);
+ badcp = rval = 1;
+ if (S_ISDIR(statp->st_mode))
+ /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
+ return FTW_SKIP_SUBTREE;
+ return 0;
+ }
+ if (!S_ISDIR(statp->st_mode) &&
+ S_ISDIR(to_stat.st_mode)) {
+ SLOG("cannot overwrite directory %s with "
+ "non-directory %s",
+ to.p_path, path);
+ badcp = rval = 1;
+ return 0;
+ }
+ dne = 0;
+ }
+
+ /* Convert basename to appropiate volume encoding */
+ if (dvolume.volinfo.v_path) {
+ if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
+ SLOG("Error converting name for %s", to.p_path);
+ badcp = rval = 1;
+ return -1;
+ }
+ }
+
+ switch (statp->st_mode & S_IFMT) {
+ case S_IFLNK:
+ if (ftw_copy_link(ftw, path, statp, !dne))
+ badcp = rval = 1;
+ break;
+ case S_IFDIR:
+ if (!Rflag) {
+ SLOG("%s is a directory", path);
+ badcp = rval = 1;
+ return -1;
+ }
+ /*
+ * If the directory doesn't exist, create the new
+ * one with the from file mode plus owner RWX bits,
+ * modified by the umask. Trade-off between being
+ * able to write the directory (if from directory is
+ * 555) and not causing a permissions race. If the
+ * umask blocks owner writes, we fail..
+ */
+ if (dne) {
+ if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
+ ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
+ } else if (!S_ISDIR(to_stat.st_mode)) {
+ errno = ENOTDIR;
+ ERROR("%s", to.p_path);
+ }
+
+ /* Create ad dir and copy ".Parent" */
+ if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
+
+ /* Create ".AppleDouble" dir */
+ mode_t omask = umask(0);
+ bstring addir = bfromcstr(to.p_path);
+ bcatcstr(addir, "/.AppleDouble");
+ mkdir(cfrombstr(addir), 02777);
+ bdestroy(addir);
+
+ if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
+ /* copy ".Parent" file */
+ if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
+ SLOG("Error copying adouble for %s -> %s", path, to.p_path);
+ badcp = rval = 1;
+ break;
+ }
+ }
+
+ /* Get CNID of Parent and add new childir to CNID database */
+ ppdid = pdid;
+ if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
+ SLOG("Error resolving CNID for %s", to.p_path);
+ badcp = rval = 1;
+ return -1;
+ }
+
+ struct adouble ad;
+ struct stat st;
+ if (lstat(to.p_path, &st) != 0) {
+ badcp = rval = 1;
+ break;
+ }
+ ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
+ if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
+ ERROR("Error opening adouble for: %s", to.p_path);
+ }
+ ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
+ ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
+ ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
+ ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
+ ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
+ ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
+ ad_flush(&ad);
+ ad_close_metadata(&ad);
+
+ umask(omask);
+ }
+
+ if (pflag) {
+ if (setfile(statp, -1))
+ rval = 1;
+#if 0
+ if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
+ rval = 1;
+#endif
+ }
+ break;
+
+ case S_IFBLK:
+ case S_IFCHR:
+ SLOG("%s is a device file (not copied).", path);
+ break;
+ case S_IFSOCK:
+ SLOG("%s is a socket (not copied).", path);
+ break;
+ case S_IFIFO:
+ SLOG("%s is a FIFO (not copied).", path);
+ break;
+ default:
+ if (ftw_copy_file(ftw, path, statp, dne))
+ badcp = rval = 1;
+
+ if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
+
+ mode_t omask = umask(0);
+ if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
+ /* copy ad-file */
+ if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
+ SLOG("Error copying adouble for %s -> %s", path, to.p_path);
+ badcp = rval = 1;
+ break;
+ }
+ }
+
+ /* Get CNID of Parent and add new childir to CNID database */
+ pdid = did;
+ cnid_t cnid;
+ if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
+ SLOG("Error resolving CNID for %s", to.p_path);
+ badcp = rval = 1;
+ return -1;
+ }
+
+ struct adouble ad;
+ struct stat st;
+ if (lstat(to.p_path, &st) != 0) {
+ badcp = rval = 1;
+ break;
+ }
+ ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
+ if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
+ ERROR("Error opening adouble for: %s", to.p_path);
+ }
+ ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
+ ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
+ ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
+ ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
+ ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
+ ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
+ ad_flush(&ad);
+ ad_close_metadata(&ad);
+ umask(omask);
+ }
+ break;
+ }
+ if (vflag && !badcp)
+ (void)printf("%s -> %s\n", path, to.p_path);
+
+ return 0;
+}
+
+/* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
+#define PHYSPAGES_THRESHOLD (32*1024)
+
+/* Maximum buffer size in bytes - do not allow it to grow larger than this */
+#define BUFSIZE_MAX (2*1024*1024)
+
+/* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
+#define MAXPHYS (64 * 1024)
+#define BUFSIZE_SMALL (MAXPHYS)
+
+static int ftw_copy_file(const struct FTW *entp,
+ const char *spath,
+ const struct stat *sp,
+ int dne)
+{
+ static char *buf = NULL;
+ static size_t bufsize;
+ ssize_t wcount;
+ size_t wresid;
+ off_t wtotal;
+ int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
+ char *bufp;
+ char *p;
+
+ if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
+ SLOG("%s: %s", spath, strerror(errno));
+ return (1);
+ }
+
+ /*
+ * If the file exists and we're interactive, verify with the user.
+ * If the file DNE, set the mode to be the from file, minus setuid
+ * bits, modified by the umask; arguably wrong, but it makes copying
+ * executables work right and it's been that way forever. (The
+ * other choice is 666 or'ed with the execute bits on the from file
+ * modified by the umask.)
+ */
+ if (!dne) {
+#define YESNO "(y/n [n]) "
+ if (nflag) {
+ if (vflag)
+ printf("%s not overwritten\n", to.p_path);
+ (void)close(from_fd);
+ return (0);
+ } else if (iflag) {
+ (void)fprintf(stderr, "overwrite %s? %s",
+ to.p_path, YESNO);
+ checkch = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ if (checkch != 'y' && checkch != 'Y') {
+ (void)close(from_fd);
+ (void)fprintf(stderr, "not overwritten\n");
+ return (1);
+ }
+ }
+
+ if (fflag) {
+ /* remove existing destination file name,
+ * create a new file */
+ (void)unlink(to.p_path);
+ (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
+ to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
+ sp->st_mode & ~(S_ISUID | S_ISGID));
+ } else {
+ /* overwrite existing destination file name */
+ to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
+ }
+ } else {
+ to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
+ sp->st_mode & ~(S_ISUID | S_ISGID));
+ }
+
+ if (to_fd == -1) {
+ SLOG("%s: %s", to.p_path, strerror(errno));
+ (void)close(from_fd);
+ return (1);
+ }
+
+ rval = 0;
+
+ /*
+ * Mmap and write if less than 8M (the limit is so we don't totally
+ * trash memory on big files. This is really a minor hack, but it
+ * wins some CPU back.
+ * Some filesystems, such as smbnetfs, don't support mmap,
+ * so this is a best-effort attempt.
+ */
+
+ if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
+ sp->st_size <= 8 * 1024 * 1024 &&
+ (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
+ MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
+ wtotal = 0;
+ for (bufp = p, wresid = sp->st_size; ;
+ bufp += wcount, wresid -= (size_t)wcount) {
+ wcount = write(to_fd, bufp, wresid);
+ if (wcount <= 0)
+ break;
+ wtotal += wcount;
+ if (wcount >= (ssize_t)wresid)
+ break;
+ }
+ if (wcount != (ssize_t)wresid) {
+ SLOG("%s: %s", to.p_path, strerror(errno));
+ rval = 1;
+ }
+ /* Some systems don't unmap on close(2). */
+ if (munmap(p, sp->st_size) < 0) {
+ SLOG("%s: %s", spath, strerror(errno));
+ rval = 1;
+ }
+ } else {
+ if (buf == NULL) {
+ /*
+ * Note that buf and bufsize are static. If
+ * malloc() fails, it will fail at the start
+ * and not copy only some files.
+ */
+ if (sysconf(_SC_PHYS_PAGES) >
+ PHYSPAGES_THRESHOLD)
+ bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
+ else
+ bufsize = BUFSIZE_SMALL;
+ buf = malloc(bufsize);
+ if (buf == NULL)
+ ERROR("Not enough memory");
+
+ }
+ wtotal = 0;
+ while ((rcount = read(from_fd, buf, bufsize)) > 0) {
+ for (bufp = buf, wresid = rcount; ;
+ bufp += wcount, wresid -= wcount) {
+ wcount = write(to_fd, bufp, wresid);
+ if (wcount <= 0)
+ break;
+ wtotal += wcount;
+ if (wcount >= (ssize_t)wresid)
+ break;
+ }
+ if (wcount != (ssize_t)wresid) {
+ SLOG("%s: %s", to.p_path, strerror(errno));
+ rval = 1;
+ break;
+ }
+ }
+ if (rcount < 0) {
+ SLOG("%s: %s", spath, strerror(errno));
+ rval = 1;
+ }
+ }
+
+ /*
+ * Don't remove the target even after an error. The target might
+ * not be a regular file, or its attributes might be important,
+ * or its contents might be irreplaceable. It would only be safe
+ * to remove it if we created it and its length is 0.
+ */
+
+ if (pflag && setfile(sp, to_fd))
+ rval = 1;
+ if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
+ rval = 1;
+ if (close(to_fd)) {
+ SLOG("%s: %s", to.p_path, strerror(errno));
+ rval = 1;
+ }
+
+ (void)close(from_fd);
+
+ return (rval);
+}
+
+static int ftw_copy_link(const struct FTW *p,
+ const char *spath,
+ const struct stat *sstp,
+ int exists)
+{
+ int len;
+ char llink[PATH_MAX];
+
+ if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
+ SLOG("readlink: %s: %s", spath, strerror(errno));
+ return (1);
+ }
+ llink[len] = '\0';
+ if (exists && unlink(to.p_path)) {
+ SLOG("unlink: %s: %s", to.p_path, strerror(errno));
+ return (1);
+ }
+ if (symlink(llink, to.p_path)) {
+ SLOG("symlink: %s: %s", llink, strerror(errno));
+ return (1);
+ }
+ return (pflag ? setfile(sstp, -1) : 0);
+}
+
+static int setfile(const struct stat *fs, int fd)
+{
+ static struct timeval tv[2];
+ struct stat ts;
+ int rval, gotstat, islink, fdval;
+ mode_t mode;
+
+ rval = 0;
+ fdval = fd != -1;
+ islink = !fdval && S_ISLNK(fs->st_mode);
+ mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
+
+ TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
+ if (utimes(to.p_path, tv)) {
+ SLOG("utimes: %s", to.p_path);
+ rval = 1;
+ }
+ if (fdval ? fstat(fd, &ts) :
+ (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
+ gotstat = 0;
+ else {
+ gotstat = 1;
+ ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
+ S_IRWXU | S_IRWXG | S_IRWXO;
+ }
+ /*
+ * Changing the ownership probably won't succeed, unless we're root
+ * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
+ * the mode; current BSD behavior is to remove all setuid bits on
+ * chown. If chown fails, lose setuid/setgid bits.
+ */
+ if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
+ if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
+ (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
+ chown(to.p_path, fs->st_uid, fs->st_gid))) {
+ if (errno != EPERM) {
+ SLOG("chown: %s: %s", to.p_path, strerror(errno));
+ rval = 1;
+ }
+ mode &= ~(S_ISUID | S_ISGID);
+ }
+
+ if (!gotstat || mode != ts.st_mode)
+ if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
+ SLOG("chmod: %s: %s", to.p_path, strerror(errno));
+ rval = 1;
+ }
+
+#ifdef HAVE_ST_FLAGS
+ if (!gotstat || fs->st_flags != ts.st_flags)
+ if (fdval ?
+ fchflags(fd, fs->st_flags) :
+ (islink ? lchflags(to.p_path, fs->st_flags) :
+ chflags(to.p_path, fs->st_flags))) {
+ SLOG("chflags: %s: %s", to.p_path, strerror(errno));
+ rval = 1;
+ }
+#endif
+
+ return (rval);
+}
+
+static int preserve_fd_acls(int source_fd, int dest_fd)
+{
+#if 0
+ acl_t acl;
+ acl_type_t acl_type;
+ int acl_supported = 0, ret, trivial;
+
+ ret = fpathconf(source_fd, _PC_ACL_NFS4);
+ if (ret > 0 ) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_NFS4;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
+ return (1);
+ }
+ if (acl_supported == 0) {
+ ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
+ if (ret > 0 ) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_ACCESS;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
+ to.p_path);
+ return (1);
+ }
+ }
+ if (acl_supported == 0)
+ return (0);
+
+ acl = acl_get_fd_np(source_fd, acl_type);
+ if (acl == NULL) {
+ warn("failed to get acl entries while setting %s", to.p_path);
+ return (1);
+ }
+ if (acl_is_trivial_np(acl, &trivial)) {
+ warn("acl_is_trivial() failed for %s", to.p_path);
+ acl_free(acl);
+ return (1);
+ }
+ if (trivial) {
+ acl_free(acl);
+ return (0);
+ }
+ if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
+ warn("failed to set acl entries for %s", to.p_path);
+ acl_free(acl);
+ return (1);
+ }
+ acl_free(acl);
+#endif
+ return (0);
+}
+
+static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
+{
+#if 0
+ acl_t (*aclgetf)(const char *, acl_type_t);
+ int (*aclsetf)(const char *, acl_type_t, acl_t);
+ struct acl *aclp;
+ acl_t acl;
+ acl_type_t acl_type;
+ int acl_supported = 0, ret, trivial;
+
+ ret = pathconf(source_dir, _PC_ACL_NFS4);
+ if (ret > 0) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_NFS4;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
+ return (1);
+ }
+ if (acl_supported == 0) {
+ ret = pathconf(source_dir, _PC_ACL_EXTENDED);
+ if (ret > 0) {
+ acl_supported = 1;
+ acl_type = ACL_TYPE_ACCESS;
+ } else if (ret < 0 && errno != EINVAL) {
+ warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
+ source_dir);
+ return (1);
+ }
+ }
+ if (acl_supported == 0)
+ return (0);
+
+ /*
+ * If the file is a link we will not follow it
+ */
+ if (S_ISLNK(fs->st_mode)) {
+ aclgetf = acl_get_link_np;
+ aclsetf = acl_set_link_np;
+ } else {
+ aclgetf = acl_get_file;
+ aclsetf = acl_set_file;
+ }
+ if (acl_type == ACL_TYPE_ACCESS) {
+ /*
+ * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
+ * size ACL will be returned. So it is not safe to simply
+ * check the pointer to see if the default ACL is present.
+ */
+ acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
+ if (acl == NULL) {
+ warn("failed to get default acl entries on %s",
+ source_dir);
+ return (1);
+ }
+ aclp = &acl->ats_acl;
+ if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
+ ACL_TYPE_DEFAULT, acl) < 0) {
+ warn("failed to set default acl entries on %s",
+ dest_dir);
+ acl_free(acl);
+ return (1);
+ }
+ acl_free(acl);
+ }
+ acl = aclgetf(source_dir, acl_type);
+ if (acl == NULL) {
+ warn("failed to get acl entries on %s", source_dir);
+ return (1);
+ }
+ if (acl_is_trivial_np(acl, &trivial)) {
+ warn("acl_is_trivial() failed on %s", source_dir);
+ acl_free(acl);
+ return (1);
+ }
+ if (trivial) {
+ acl_free(acl);
+ return (0);
+ }
+ if (aclsetf(dest_dir, acl_type, acl) < 0) {
+ warn("failed to set acl entries on %s", dest_dir);
+ acl_free(acl);
+ return (1);
+ }
+ acl_free(acl);
+#endif
+ return (0);
+}
--- /dev/null
+/*
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+#include <atalk/cnid_dbd_private.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/directory.h>
+#include <atalk/util.h>
+#include <atalk/unicode.h>
+#include "ad.h"
+
+static volatile sig_atomic_t alarmed;
+
+/*
+ SIGNAL handling:
+ catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+ alarmed = 1;
+ return;
+}
+
+static void set_signal(void)
+{
+ struct sigaction sv;
+
+ sv.sa_handler = sig_handler;
+ sv.sa_flags = SA_RESTART;
+ sigemptyset(&sv.sa_mask);
+ if (sigaction(SIGTERM, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+ if (sigaction(SIGINT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+ memset(&sv, 0, sizeof(struct sigaction));
+ sv.sa_handler = SIG_IGN;
+ sigemptyset(&sv.sa_mask);
+
+ if (sigaction(SIGABRT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+ if (sigaction(SIGHUP, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+ if (sigaction(SIGQUIT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+static void usage_find(void)
+{
+ printf(
+ "Usage: ad find [-v VOLUME_PATH] NAME\n"
+ );
+}
+
+int ad_find(int argc, char **argv)
+{
+ int c, ret;
+ afpvol_t vol;
+ const char *srchvol = getcwdpath();
+
+ while ((c = getopt(argc-1, &argv[1], ":v:")) != -1) {
+ switch(c) {
+ case 'v':
+ srchvol = strdup(optarg);
+ break;
+ case ':':
+ case '?':
+ usage_find();
+ return -1;
+ break;
+ }
+
+ }
+ optind++;
+
+ if ((argc - optind) != 1) {
+ usage_find();
+ exit(1);
+ }
+
+ set_signal();
+ cnid_init();
+
+ if (openvol(srchvol, &vol) != 0)
+ ERROR("Cant open volume \"%s\"", srchvol);
+
+ uint16_t flags = CONV_TOLOWER;
+ char namebuf[MAXPATHLEN + 1];
+ if (convert_charset(vol.volinfo.v_volcharset,
+ vol.volinfo.v_volcharset,
+ vol.volinfo.v_maccharset,
+ argv[optind],
+ strlen(argv[optind]),
+ namebuf,
+ MAXPATHLEN,
+ &flags) == (size_t)-1) {
+ ERROR("conversion error");
+ }
+
+ int count;
+ char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)];
+ if ((count = cnid_find(vol.volume.v_cdb,
+ namebuf,
+ strlen(namebuf),
+ resbuf,
+ sizeof(resbuf))) < 1) {
+ ret = 1;
+ } else {
+ ret = 0;
+ cnid_t cnid;
+ char *bufp = resbuf;
+ bstring sep = bfromcstr("/");
+ while (count--) {
+ memcpy(&cnid, bufp, sizeof(cnid_t));
+ bufp += sizeof(cnid_t);
+
+ bstring path = NULL;
+ bstring volpath = bfromcstr(vol.volinfo.v_path);
+ BSTRING_STRIP_SLASH(volpath);
+ char buffer[12 + MAXPATHLEN + 1];
+ int buflen = 12 + MAXPATHLEN + 1;
+ char *name;
+ cnid_t did = cnid;
+ struct bstrList *pathlist = bstrListCreateMin(32);
+
+ while (did != DIRDID_ROOT) {
+ if ((name = cnid_resolve(vol.volume.v_cdb, &did, buffer, buflen)) == NULL)
+ goto next;
+ bstrListPush(pathlist, bfromcstr(name));
+ }
+ bstrListPush(pathlist, volpath);
+ path = bjoinInv(pathlist, sep);
+
+ printf("%s\n", cfrombstr(path));
+
+ next:
+ bstrListDestroy(pathlist);
+ bdestroy(path);
+ }
+ bdestroy(sep);
+ }
+
+ closevol(&vol);
+
+ return ret;
+}
--- /dev/null
+/*
+ Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+#include <atalk/volinfo.h>
+#include "ad.h"
+
+#define ADv2_DIRNAME ".AppleDouble"
+
+#define DIR_DOT_OR_DOTDOT(a) \
+ ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
+
+static volatile sig_atomic_t alarmed;
+
+/* ls options */
+static int ls_a;
+static int ls_l;
+static int ls_R;
+static int ls_d;
+static int ls_u;
+
+/* Used for pretty printing */
+static int first = 1;
+static int recursion;
+
+static char *netatalk_dirs[] = {
+ ADv2_DIRNAME,
+ ".AppleDB",
+ ".AppleDesktop",
+ NULL
+};
+
+static char *labels[] = {
+ "---",
+ "gry",
+ "gre",
+ "vio",
+ "blu",
+ "yel",
+ "red",
+ "ora"
+};
+
+/*
+ SIGNAL handling:
+ catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+ alarmed = 1;
+ return;
+}
+
+static void set_signal(void)
+{
+ struct sigaction sv;
+
+ sv.sa_handler = sig_handler;
+ sv.sa_flags = SA_RESTART;
+ sigemptyset(&sv.sa_mask);
+ if (sigaction(SIGTERM, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+ if (sigaction(SIGINT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+ memset(&sv, 0, sizeof(struct sigaction));
+ sv.sa_handler = SIG_IGN;
+ sigemptyset(&sv.sa_mask);
+
+ if (sigaction(SIGABRT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+ if (sigaction(SIGHUP, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+ if (sigaction(SIGQUIT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+/*
+ Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
+ Returns pointer to name or NULL.
+*/
+static const char *check_netatalk_dirs(const char *name)
+{
+ int c;
+
+ for (c=0; netatalk_dirs[c]; c++) {
+ if ((strcmp(name, netatalk_dirs[c])) == 0)
+ return netatalk_dirs[c];
+ }
+ return NULL;
+}
+
+
+static void usage_ls(void)
+{
+ printf(
+ "Usage: ad ls [-dRl[u]] [file|dir, ...]\n\n"
+ " -l Long Output [-u: unix info]:\n"
+ " <unixinfo ...> <FinderFlags> <AFPAttributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>\n\n"
+ " FinderFlags (valid for (f)ile and/or (d)irectory):\n"
+ " d = On Desktop (f/d)\n"
+ " e = Hidden extension (f/d)\n"
+ " m = Shared (can run multiple times) (f)\n"
+ " n = No INIT resources (f)\n"
+ " i = Inited (f/d)\n"
+ " c = Custom icon (f/d)\n"
+ " t = Stationery (f)\n"
+ " s = Name locked (f/d)\n"
+ " b = Bundle (f/d)\n"
+ " v = Invisible (f/d)\n"
+ " a = Alias file (f/d)\n\n"
+ " AFPAttributes:\n"
+ " y = System (f/d)\n"
+ " w = No write (f)\n"
+ " p = Needs backup (f/d)\n"
+ " r = No rename (f/d)\n"
+ " l = No delete (f/d)\n"
+ " o = No copy (f)\n\n"
+ " Note: any letter appearing in uppercase means the flag is set\n"
+ " but it's a directory for which the flag is not allowed.\n"
+ );
+}
+
+static void print_numlinks(const struct stat *statp)
+{
+ printf("%5ld", (long)statp->st_nlink);
+}
+
+static void print_owner(const struct stat *statp)
+{
+ struct passwd *pwd = getpwuid(statp->st_uid);
+
+ if (pwd == NULL)
+ printf(" %-8ld", (long)statp->st_uid);
+ else
+ printf(" %-8s", pwd->pw_name);
+}
+
+static void print_group(const struct stat *statp)
+{
+ struct group *grp = getgrgid(statp->st_gid);
+
+ if (grp == NULL)
+ printf(" %-8ld", (long)statp->st_gid);
+ else
+ printf(" %-8s", grp->gr_name);
+}
+
+static void print_size(const struct stat *statp)
+{
+ switch (statp->st_mode & S_IFMT) {
+ case S_IFCHR:
+ case S_IFBLK:
+ printf("%4u,%4u", (unsigned)(statp->st_rdev >> 8),
+ (unsigned)(statp->st_rdev & 0xFF));
+ break;
+ default:
+ printf("%9lu", (unsigned long)statp->st_size);
+ }
+}
+
+static void print_date(const struct stat *statp)
+{
+ time_t now;
+ double diff;
+ char buf[100], *fmt;
+
+ if (time(&now) == -1) {
+ printf(" ????????????");
+ return;
+ }
+ diff = difftime(now, statp->st_mtime);
+ if (diff < 0 || diff > 60 * 60 * 24 * 182.5)
+ fmt = "%b %e %Y";
+ else
+ fmt = "%b %e %H:%M";
+ strftime(buf, sizeof(buf), fmt, localtime(&statp->st_mtime));
+ printf(" %s", buf);
+}
+
+static void print_flags(char *path, afpvol_t *vol, const struct stat *st)
+{
+ int adflags = 0;
+ struct adouble ad;
+ char *FinderInfo;
+ uint16_t FinderFlags;
+ uint16_t AFPattributes;
+ char type[5] = "----";
+ char creator[5] = "----";
+ int i;
+ uint32_t cnid;
+
+ if (S_ISDIR(st->st_mode))
+ adflags = ADFLAGS_DIR;
+
+ if (vol->volinfo.v_path == NULL)
+ return;
+
+ ad_init(&ad, vol->volinfo.v_adouble, vol->volinfo.v_ad_options);
+
+ if ( ad_metadata(path, adflags, &ad) < 0 )
+ return;
+
+ FinderInfo = ad_entry(&ad, ADEID_FINDERI);
+
+ memcpy(&FinderFlags, FinderInfo + 8, 2);
+ FinderFlags = ntohs(FinderFlags);
+
+ memcpy(type, FinderInfo, 4);
+ memcpy(creator, FinderInfo + 4, 4);
+
+ ad_getattr(&ad, &AFPattributes);
+ AFPattributes = ntohs(AFPattributes);
+
+ /*
+ Finder flags. Lowercase means valid, uppercase means invalid because
+ object is a dir and flag is only valid for files.
+ */
+ putchar(' ');
+ if (FinderFlags & FINDERINFO_ISONDESK)
+ putchar('d');
+ else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_HIDEEXT)
+ putchar('e');
+ else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_ISHARED) {
+ if (adflags & ADFLAGS_DIR)
+ putchar('M');
+ else
+ putchar('m');
+ } else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_HASNOINITS) {
+ if (adflags & ADFLAGS_DIR)
+ putchar('N');
+ else
+ putchar('n');
+ } else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_HASBEENINITED)
+ putchar('i');
+ else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_HASCUSTOMICON)
+ putchar('c');
+ else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_ISSTATIONNERY) {
+ if (adflags & ADFLAGS_DIR)
+ putchar('T');
+ else
+ putchar('t');
+ } else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_NAMELOCKED)
+ putchar('s');
+ else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_HASBUNDLE)
+ putchar('b');
+ else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_INVISIBLE)
+ putchar('v');
+ else
+ putchar('-');
+
+ if (FinderFlags & FINDERINFO_ISALIAS)
+ putchar('a');
+ else
+ putchar('-');
+
+ putchar(' ');
+
+ /* AFP attributes */
+ if (AFPattributes & ATTRBIT_SYSTEM)
+ putchar('y');
+ else
+ putchar('-');
+
+ if (AFPattributes & ATTRBIT_NOWRITE) {
+ if (adflags & ADFLAGS_DIR)
+ putchar('W');
+ else
+ putchar('w');
+ } else
+ putchar('-');
+
+ if (AFPattributes & ATTRBIT_BACKUP)
+ putchar('p');
+ else
+ putchar('-');
+
+ if (AFPattributes & ATTRBIT_NORENAME)
+ putchar('r');
+ else
+ putchar('-');
+
+ if (AFPattributes & ATTRBIT_NODELETE)
+ putchar('l');
+ else
+ putchar('-');
+
+ if (AFPattributes & ATTRBIT_NOCOPY) {
+ if (adflags & ADFLAGS_DIR)
+ putchar('O');
+ else
+ putchar('o');
+ } else
+ putchar('-');
+
+ /* Color */
+ printf(" %s ", labels[(FinderFlags & FINDERINFO_COLOR) >> 1]);
+
+ /* Type & Creator */
+ for(i=0; i<4; i++) {
+ if (isalnum(type[i]))
+ putchar(type[i]);
+ else
+ putchar('-');
+ }
+ putchar(' ');
+ for(i=0; i<4; i++) {
+ if (isalnum(creator[i]))
+ putchar(creator[i]);
+ else
+ putchar('-');
+ }
+ putchar(' ');
+
+ /* CNID */
+ cnid = ad_forcegetid(&ad);
+ if (cnid)
+ printf(" %10u ", ntohl(cnid));
+ else
+ printf(" !ADVOL_CACHE ");
+
+ ad_close_metadata(&ad);
+}
+
+#define TYPE(b) ((st->st_mode & (S_IFMT)) == (b))
+#define MODE(b) ((st->st_mode & (b)) == (b))
+
+static void print_mode(const struct stat *st)
+{
+ if (TYPE(S_IFBLK))
+ putchar('b');
+ else if (TYPE(S_IFCHR))
+ putchar('c');
+ else if (TYPE(S_IFDIR))
+ putchar('d');
+ else if (TYPE(S_IFIFO))
+ putchar('p');
+ else if (TYPE(S_IFREG))
+ putchar('-');
+ else if (TYPE(S_IFLNK))
+ putchar('l');
+ else if (TYPE(S_IFSOCK))
+ putchar('s');
+ else
+ putchar('?');
+ putchar(MODE(S_IRUSR) ? 'r' : '-');
+ putchar(MODE(S_IWUSR) ? 'w' : '-');
+ if (MODE(S_ISUID)) {
+ if (MODE(S_IXUSR))
+ putchar('s');
+ else
+ putchar('S');
+ }
+ else if (MODE(S_IXUSR))
+ putchar('x');
+ else
+ putchar('-');
+ putchar(MODE(S_IRGRP) ? 'r' : '-');
+ putchar(MODE(S_IWGRP) ? 'w' : '-');
+ if (MODE(S_ISGID)) {
+ if (MODE(S_IXGRP))
+ putchar('s');
+ else
+ putchar('S');
+ }
+ else if (MODE(S_IXGRP))
+ putchar('x');
+ else
+ putchar('-');
+ putchar(MODE(S_IROTH) ? 'r' : '-');
+ putchar(MODE(S_IWOTH) ? 'w' : '-');
+ if (MODE(S_IFDIR) && MODE(S_ISVTX)) {
+ if (MODE(S_IXOTH))
+ putchar('t');
+ else
+ putchar('T');
+ }
+ else if (MODE(S_IXOTH))
+ putchar('x');
+ else
+ putchar('-');
+}
+#undef TYPE
+#undef MODE
+
+static int ad_print(char *path, const struct stat *st, afpvol_t *vol)
+{
+ if ( ! ls_l) {
+ printf("%s ", path);
+ if (ls_d)
+ printf("\n");
+ return 0;
+ }
+
+ /* Long output */
+ if (ls_u) {
+ print_mode(st);
+ print_numlinks(st);
+ print_owner(st);
+ print_group(st);
+ print_size(st);
+ print_date(st);
+ }
+ print_flags(path, vol, st);
+ printf(" %s\n", path);
+
+
+ return 0;
+}
+
+static int ad_ls_r(char *path, afpvol_t *vol)
+{
+ int ret = 0, cwd, dirprinted = 0, dirempty;
+ const char *name;
+ char *tmp;
+ static char cwdpath[MAXPATHLEN+1];
+ DIR *dp;
+ struct dirent *ep;
+ static struct stat st; /* Save some stack space */
+
+ if ( first)
+ cwdpath[0] = 0;
+ else
+ strcat(cwdpath, "/");
+
+ strcat(cwdpath, path);
+ first = 0;
+
+ if (lstat(path, &st) < 0) {
+ perror("Can't stat");
+ return -1;
+ }
+ /* If its a file or a dir with -d option call ad_print and return */
+ if (S_ISREG(st.st_mode) || ls_d)
+ return ad_print(path, &st, vol);
+
+ /* Its a dir: chdir to it remembering where we started */
+ if ((cwd = open(".", O_RDONLY)) == -1) {
+ perror("Cant open .");
+ return -1;
+ }
+ if (chdir(path) != 0) {
+ perror("Cant chdir");
+ close(cwd);
+ return -1;
+ }
+
+ if ((dp = opendir (".")) == NULL) {
+ perror("Couldn't opendir .");
+ return -1;
+ }
+
+ /* First run: print everything */
+ dirempty = 1;
+ while ((ep = readdir (dp))) {
+ if (alarmed) {
+ ret = -1;
+ goto exit;
+ }
+
+ /* Check if its "." or ".." */
+ if (DIR_DOT_OR_DOTDOT(ep->d_name))
+ continue;
+
+ /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
+ if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
+ continue;
+
+ if ((ep->d_name[0] == '.') && ! ls_a)
+ continue;
+
+ dirempty = 0;
+
+ if (recursion && ! dirprinted) {
+ printf("\n%s:\n", cwdpath);
+ dirprinted = 1;
+ }
+
+ if (lstat(ep->d_name, &st) < 0) {
+ perror("Can't stat");
+ return -1;
+ }
+
+ ret = ad_print(ep->d_name, &st, vol);
+ if (ret != 0)
+ goto exit;
+ }
+
+ if (! ls_l && ! dirempty)
+ printf("\n");
+
+ /* Second run: recurse to dirs */
+ if (ls_R) {
+ rewinddir(dp);
+ while ((ep = readdir (dp))) {
+ if (alarmed) {
+ ret = -1;
+ goto exit;
+ }
+
+ /* Check if its "." or ".." */
+ if (DIR_DOT_OR_DOTDOT(ep->d_name))
+ continue;
+
+ /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
+ if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
+ continue;
+
+ if ((ret = lstat(ep->d_name, &st)) < 0) {
+ perror("Can't stat");
+ goto exit;
+ }
+
+ /* Recursion */
+ if (S_ISDIR(st.st_mode)) {
+ recursion = 1;
+ ret = ad_ls_r(ep->d_name, vol);
+ }
+ if (ret != 0)
+ goto exit;
+ }
+ }
+
+exit:
+ closedir(dp);
+ fchdir(cwd);
+ close(cwd);
+
+ tmp = strrchr(cwdpath, '/');
+ if (tmp)
+ *tmp = 0;
+
+ return ret;
+}
+
+int ad_ls(int argc, char **argv)
+{
+ int c, firstarg;
+ afpvol_t vol;
+ struct stat st;
+
+ while ((c = getopt(argc, argv, ":adlRu")) != -1) {
+ switch(c) {
+ case 'a':
+ ls_a = 1;
+ break;
+ case 'd':
+ ls_d = 1;
+ break;
+ case 'l':
+ ls_l = 1;
+ break;
+ case 'R':
+ ls_R = 1;
+ break;
+ case 'u':
+ ls_u = 1;
+ break;
+ case ':':
+ case '?':
+ usage_ls();
+ return -1;
+ break;
+ }
+
+ }
+
+ set_signal();
+ cnid_init();
+
+ if ((argc - optind) == 0) {
+ openvol(".", &vol);
+ ad_ls_r(".", &vol);
+ closevol(&vol);
+ }
+ else {
+ int havefile = 0;
+
+ firstarg = optind;
+
+ /* First run: only print files from argv paths */
+ while(optind < argc) {
+ if (stat(argv[optind], &st) != 0)
+ goto next;
+ if (S_ISDIR(st.st_mode))
+ goto next;
+
+ havefile = 1;
+ first = 1;
+ recursion = 0;
+
+ openvol(argv[optind], &vol);
+ ad_ls_r(argv[optind], &vol);
+ closevol(&vol);
+ next:
+ optind++;
+ }
+ if (havefile && (! ls_l))
+ printf("\n");
+
+ /* Second run: print dirs */
+ optind = firstarg;
+ while(optind < argc) {
+ if (stat(argv[optind], &st) != 0)
+ goto next2;
+ if ( ! S_ISDIR(st.st_mode))
+ goto next2;
+ if ((optind > firstarg) || havefile)
+ printf("\n%s:\n", argv[optind]);
+
+ first = 1;
+ recursion = 0;
+
+ openvol(argv[optind], &vol);
+ ad_ls_r(argv[optind], &vol);
+ closevol(&vol);
+
+ next2:
+ optind++;
+ }
+
+
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#include <atalk/ftw.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/util.h>
+#include <atalk/unix.h>
+#include <atalk/volume.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/queue.h>
+
+#include "ad.h"
+
+#define STRIP_TRAILING_SLASH(p) { \
+ while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
+ *--(p).p_end = 0; \
+ }
+
+static int fflg, iflg, nflg, vflg;
+
+static afpvol_t svolume, dvolume;
+static cnid_t did, pdid;
+static volatile sig_atomic_t alarmed;
+static char *netatalk_dirs[] = {
+ ".AppleDouble",
+ ".AppleDB",
+ ".AppleDesktop",
+ NULL
+};
+
+static int copy(const char *, const char *);
+static int do_move(const char *, const char *);
+static void preserve_fd_acls(int source_fd, int dest_fd, const char *source_path,
+ const char *dest_path);
+/*
+ Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
+ Returns pointer to name or NULL.
+*/
+static const char *check_netatalk_dirs(const char *name)
+{
+ int c;
+
+ for (c=0; netatalk_dirs[c]; c++) {
+ if ((strcmp(name, netatalk_dirs[c])) == 0)
+ return netatalk_dirs[c];
+ }
+ return NULL;
+}
+
+/*
+ SIGNAL handling:
+ catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+ alarmed = 1;
+ return;
+}
+
+static void set_signal(void)
+{
+ struct sigaction sv;
+
+ sv.sa_handler = sig_handler;
+ sv.sa_flags = SA_RESTART;
+ sigemptyset(&sv.sa_mask);
+ if (sigaction(SIGTERM, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+ if (sigaction(SIGINT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+ memset(&sv, 0, sizeof(struct sigaction));
+ sv.sa_handler = SIG_IGN;
+ sigemptyset(&sv.sa_mask);
+
+ if (sigaction(SIGABRT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+ if (sigaction(SIGHUP, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+ if (sigaction(SIGQUIT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+static void usage_mv(void)
+{
+ printf(
+ "Usage: ad mv [-f | -i | -n] [-v] source target\n"
+ " ad mv [-f | -i | -n] [-v] source ... directory\n\n"
+ "Move files around within an AFP volume, updating the CNID\n"
+ "database as needed. If either:\n"
+ " - source or destination is not an AFP volume\n"
+ " - source volume != destinatio volume\n"
+ "the files are copied and removed from the source.\n\n"
+ "The following options are available:\n\n"
+ " -f Do not prompt for confirmation before overwriting the destination\n"
+ " path. (The -f option overrides any previous -i or -n options.)\n"
+ " -i Cause mv to write a prompt to standard error before moving a file\n"
+ " that would overwrite an existing file. If the response from the\n"
+ " standard input begins with the character `y' or `Y', the move is\n"
+ " attempted. (The -i option overrides any previous -f or -n\n"
+ " options.)\n"
+ " -n Do not overwrite an existing file. (The -n option overrides any\n"
+ " previous -f or -i options.)\n"
+ " -v Cause mv to be verbose, showing files after they are moved.\n"
+ );
+ exit(EXIT_FAILURE);
+}
+
+int ad_mv(int argc, char *argv[])
+{
+ size_t baselen, len;
+ int rval;
+ char *p, *endp;
+ struct stat sb;
+ int ch;
+ char path[MAXPATHLEN];
+
+
+ pdid = htonl(1);
+ did = htonl(2);
+
+ argc--;
+ argv++;
+
+ while ((ch = getopt(argc, argv, "finv")) != -1)
+ switch (ch) {
+ case 'i':
+ iflg = 1;
+ fflg = nflg = 0;
+ break;
+ case 'f':
+ fflg = 1;
+ iflg = nflg = 0;
+ break;
+ case 'n':
+ nflg = 1;
+ fflg = iflg = 0;
+ break;
+ case 'v':
+ vflg = 1;
+ break;
+ default:
+ usage_mv();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2)
+ usage_mv();
+
+ set_signal();
+ cnid_init();
+ if (openvol(argv[argc - 1], &dvolume) != 0) {
+ SLOG("Error opening CNID database for %s: ", argv[argc - 1]);
+ return 1;
+ }
+
+ /*
+ * If the stat on the target fails or the target isn't a directory,
+ * try the move. More than 2 arguments is an error in this case.
+ */
+ if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
+ if (argc > 2)
+ usage_mv();
+ if (openvol(argv[0], &svolume) != 0) {
+ SLOG("Error opening CNID database for %s: ", argv[0]);
+ return 1;
+ }
+ rval = do_move(argv[0], argv[1]);
+ closevol(&svolume);
+ closevol(&dvolume);
+ return 1;
+ }
+
+ /* It's a directory, move each file into it. */
+ if (strlen(argv[argc - 1]) > sizeof(path) - 1)
+ ERROR("%s: destination pathname too long", *argv);
+
+ (void)strcpy(path, argv[argc - 1]);
+ baselen = strlen(path);
+ endp = &path[baselen];
+ if (!baselen || *(endp - 1) != '/') {
+ *endp++ = '/';
+ ++baselen;
+ }
+
+ for (rval = 0; --argc; ++argv) {
+ /*
+ * Find the last component of the source pathname. It
+ * may have trailing slashes.
+ */
+ p = *argv + strlen(*argv);
+ while (p != *argv && p[-1] == '/')
+ --p;
+ while (p != *argv && p[-1] != '/')
+ --p;
+
+ if ((baselen + (len = strlen(p))) >= PATH_MAX) {
+ SLOG("%s: destination pathname too long", *argv);
+ rval = 1;
+ } else {
+ memmove(endp, p, (size_t)len + 1);
+ if (openvol(*argv, &svolume) != 0) {
+ SLOG("Error opening CNID database for %s: ", argv[0]);
+ rval = 1;
+ goto exit;
+ }
+ if (do_move(*argv, path))
+ rval = 1;
+ closevol(&svolume);
+ }
+ }
+
+exit:
+ closevol(&dvolume);
+ return rval;
+}
+
+static int do_move(const char *from, const char *to)
+{
+ struct stat sb;
+ int ask, ch, first;
+
+ /*
+ * Check access. If interactive and file exists, ask user if it
+ * should be replaced. Otherwise if file exists but isn't writable
+ * make sure the user wants to clobber it.
+ */
+ if (!fflg && !access(to, F_OK)) {
+
+ /* prompt only if source exist */
+ if (lstat(from, &sb) == -1) {
+ SLOG("%s: %s", from, strerror(errno));
+ return (1);
+ }
+
+ ask = 0;
+ if (nflg) {
+ if (vflg)
+ printf("%s not overwritten\n", to);
+ return (0);
+ } else if (iflg) {
+ (void)fprintf(stderr, "overwrite %s? (y/n [n]) ", to);
+ ask = 1;
+ } else if (access(to, W_OK) && !stat(to, &sb)) {
+ (void)fprintf(stderr, "override for %s? (y/n [n]) ", to);
+ ask = 1;
+ }
+ if (ask) {
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ if (first != 'y' && first != 'Y') {
+ (void)fprintf(stderr, "not overwritten\n");
+ return (0);
+ }
+ }
+ }
+
+ int mustcopy = 0;
+ /*
+ * Besides the usual EXDEV we copy instead of moving if
+ * 1) source AFP volume != dest AFP volume
+ * 2) either source or dest isn't even an AFP volume
+ */
+ if (!svolume.volinfo.v_path
+ || !dvolume.volinfo.v_path
+ || strcmp(svolume.volinfo.v_path, dvolume.volinfo.v_path) != 0)
+ mustcopy = 1;
+
+ cnid_t cnid = 0;
+ if (!mustcopy) {
+ if ((cnid = cnid_for_path(&svolume, from, &did)) == CNID_INVALID) {
+ SLOG("Couldn't resolve CNID for %s", from);
+ return -1;
+ }
+
+ if (stat(from, &sb) != 0) {
+ SLOG("Cant stat %s: %s", to, strerror(errno));
+ return -1;
+ }
+
+ if (rename(from, to) != 0) {
+ if (errno == EXDEV) {
+ mustcopy = 1;
+ char path[MAXPATHLEN];
+
+ /*
+ * If the source is a symbolic link and is on another
+ * filesystem, it can be recreated at the destination.
+ */
+ if (lstat(from, &sb) == -1) {
+ SLOG("%s: %s", from, strerror(errno));
+ return (-1);
+ }
+ if (!S_ISLNK(sb.st_mode)) {
+ /* Can't mv(1) a mount point. */
+ if (realpath(from, path) == NULL) {
+ SLOG("cannot resolve %s: %s: %s", from, path, strerror(errno));
+ return (1);
+ }
+ }
+ } else { /* != EXDEV */
+ SLOG("rename %s to %s: %s", from, to, strerror(errno));
+ return (1);
+ }
+ } /* rename != 0*/
+
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFREG:
+ if (dvolume.volume.vfs->vfs_renamefile(&dvolume.volume, -1, from, to) != 0) {
+ SLOG("Error moving adouble file for %s", from);
+ return -1;
+ }
+ break;
+ case S_IFDIR:
+ break;
+ default:
+ SLOG("Not a file or dir: %s", from);
+ return -1;
+ }
+
+ /* get CNID of new parent dir */
+ cnid_t newpdid, newdid;
+ if ((newdid = cnid_for_paths_parent(&dvolume, to, &newpdid)) == CNID_INVALID) {
+ SLOG("Couldn't resolve CNID for parent of %s", to);
+ return -1;
+ }
+
+ if (stat(to, &sb) != 0) {
+ SLOG("Cant stat %s: %s", to, strerror(errno));
+ return 1;
+ }
+
+ char *p = strdup(to);
+ char *name = basename(p);
+ if (cnid_update(dvolume.volume.v_cdb, cnid, &sb, newdid, name, strlen(name)) != 0) {
+ SLOG("Cant update CNID for: %s", to);
+ return 1;
+ }
+ free(p);
+
+ struct adouble ad;
+ ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
+ if (ad_open_metadata(to, S_ISDIR(sb.st_mode) ? ADFLAGS_DIR : 0, O_RDWR, &ad) != 0) {
+ SLOG("Error opening adouble for: %s", to);
+ return 1;
+ }
+ ad_setid(&ad, sb.st_dev, sb.st_ino, cnid, newdid, dvolume.db_stamp);
+ ad_flush(&ad);
+ ad_close_metadata(&ad);
+
+ if (vflg)
+ printf("%s -> %s\n", from, to);
+ return (0);
+ }
+
+ if (mustcopy)
+ return copy(from, to);
+
+ /* If we get here it's an error */
+ return -1;
+}
+
+static int copy(const char *from, const char *to)
+{
+ struct stat sb;
+ int pid, status;
+
+ if (lstat(to, &sb) == 0) {
+ /* Destination path exists. */
+ if (S_ISDIR(sb.st_mode)) {
+ if (rmdir(to) != 0) {
+ SLOG("rmdir %s: %s", to, strerror(errno));
+ return (1);
+ }
+ } else {
+ if (unlink(to) != 0) {
+ SLOG("unlink %s: %s", to, strerror(errno));
+ return (1);
+ }
+ }
+ } else if (errno != ENOENT) {
+ SLOG("%s: %s", to, strerror(errno));
+ return (1);
+ }
+
+ /* Copy source to destination. */
+ if (!(pid = fork())) {
+ execl(_PATH_AD, "ad", "cp", vflg ? "-Rpv" : "-Rp", from, to, (char *)NULL);
+ _exit(1);
+ }
+ while ((waitpid(pid, &status, 0)) == -1) {
+ if (errno == EINTR)
+ continue;
+ SLOG("%s cp -R %s %s: waitpid: %s", _PATH_AD, from, to, strerror(errno));
+ return (1);
+ }
+ if (!WIFEXITED(status)) {
+ SLOG("%s cp -R %s %s: did not terminate normally", _PATH_AD, from, to);
+ return (1);
+ }
+ switch (WEXITSTATUS(status)) {
+ case 0:
+ break;
+ default:
+ SLOG("%s cp -R %s %s: terminated with %d (non-zero) status",
+ _PATH_AD, from, to, WEXITSTATUS(status));
+ return (1);
+ }
+
+ /* Delete the source. */
+ if (!(pid = fork())) {
+ execl(_PATH_AD, "ad", "rm", "-R", from, (char *)NULL);
+ _exit(1);
+ }
+ while ((waitpid(pid, &status, 0)) == -1) {
+ if (errno == EINTR)
+ continue;
+ SLOG("%s rm -R %s: waitpid: %s", _PATH_AD, from, strerror(errno));
+ return (1);
+ }
+ if (!WIFEXITED(status)) {
+ SLOG("%s rm -R %s: did not terminate normally", _PATH_AD, from);
+ return (1);
+ }
+ switch (WEXITSTATUS(status)) {
+ case 0:
+ break;
+ default:
+ SLOG("%s rm -R %s: terminated with %d (non-zero) status",
+ _PATH_AD, from, WEXITSTATUS(status));
+ return (1);
+ }
+ return 0;
+}
+
+static void
+preserve_fd_acls(int source_fd,
+ int dest_fd,
+ const char *source_path,
+ const char *dest_path)
+{
+ ;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atalk/ftw.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/util.h>
+#include <atalk/unix.h>
+#include <atalk/volume.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/queue.h>
+
+#include "ad.h"
+
+#define STRIP_TRAILING_SLASH(p) { \
+ while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
+ *--(p).p_end = 0; \
+ }
+
+static afpvol_t volume;
+
+static cnid_t did, pdid;
+static int Rflag;
+static volatile sig_atomic_t alarmed;
+static int badrm, rval;
+
+static char *netatalk_dirs[] = {
+ ".AppleDB",
+ ".AppleDesktop",
+ NULL
+};
+
+/* Forward declarations */
+static int rm(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
+
+/*
+ Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
+ Returns pointer to name or NULL.
+*/
+static const char *check_netatalk_dirs(const char *name)
+{
+ int c;
+
+ for (c=0; netatalk_dirs[c]; c++) {
+ if ((strcmp(name, netatalk_dirs[c])) == 0)
+ return netatalk_dirs[c];
+ }
+ return NULL;
+}
+
+static void upfunc(void)
+{
+ did = pdid;
+}
+
+/*
+ SIGNAL handling:
+ catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+ alarmed = 1;
+ return;
+}
+
+static void set_signal(void)
+{
+ struct sigaction sv;
+
+ sv.sa_handler = sig_handler;
+ sv.sa_flags = SA_RESTART;
+ sigemptyset(&sv.sa_mask);
+ if (sigaction(SIGTERM, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+ if (sigaction(SIGINT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+ memset(&sv, 0, sizeof(struct sigaction));
+ sv.sa_handler = SIG_IGN;
+ sigemptyset(&sv.sa_mask);
+
+ if (sigaction(SIGABRT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+ if (sigaction(SIGHUP, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+ if (sigaction(SIGQUIT, &sv, NULL) < 0)
+ ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+static void usage_rm(void)
+{
+ printf(
+ "Usage: ad rm [-vR] <file|dir> [<file|dir> ...]\n\n"
+ "The rm utility attempts to remove the non-directory type files specified\n"
+ "on the command line.\n"
+ "If the files and directories reside on an AFP volume, the corresponding\n"
+ "CNIDs are deleted from the volumes database.\n\n"
+ "The options are as follows:\n\n"
+ " -R Attempt to remove the file hierarchy rooted in each file argument.\n"
+ " -v Be verbose when deleting files, showing them as they are removed.\n"
+ );
+ exit(EXIT_FAILURE);
+}
+
+int ad_rm(int argc, char *argv[])
+{
+ int ch;
+
+ pdid = htonl(1);
+ did = htonl(2);
+
+ while ((ch = getopt(argc, argv, "vR")) != -1)
+ switch (ch) {
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage_rm();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage_rm();
+
+ set_signal();
+ cnid_init();
+
+ /* Set end of argument list */
+ argv[argc] = NULL;
+
+ for (int i = 0; argv[i] != NULL; i++) {
+ /* Load .volinfo file for source */
+ openvol(argv[i], &volume);
+
+ if (nftw(argv[i], rm, upfunc, 20, FTW_DEPTH | FTW_PHYS) == -1) {
+ if (alarmed) {
+ SLOG("...break");
+ } else {
+ SLOG("Error: %s", argv[i]);
+ }
+ closevol(&volume);
+ }
+ }
+ return rval;
+}
+
+static int rm(const char *path,
+ const struct stat *statp,
+ int tflag,
+ struct FTW *ftw)
+{
+ cnid_t cnid;
+
+ if (alarmed)
+ return -1;
+
+ const char *dir = strrchr(path, '/');
+ if (dir == NULL)
+ dir = path;
+ else
+ dir++;
+ if (check_netatalk_dirs(dir) != NULL)
+ return FTW_SKIP_SUBTREE;
+
+ switch (statp->st_mode & S_IFMT) {
+
+ case S_IFLNK:
+ if (volume.volinfo.v_path) {
+ if ((volume.volinfo.v_adouble == AD_VERSION2)
+ && (strstr(path, ".AppleDouble") != NULL)) {
+ /* symlink inside adouble dir */
+ if (unlink(path) != 0)
+ badrm = rval = 1;
+ break;
+ }
+
+ /* Get CNID of Parent and add new childir to CNID database */
+ pdid = did;
+ if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
+ SLOG("Error resolving CNID for %s", path);
+ return -1;
+ }
+ if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
+ SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
+ return -1;
+ }
+ }
+
+ if (unlink(path) != 0) {
+ badrm = rval = 1;
+ break;
+ }
+
+ break;
+
+ case S_IFDIR:
+ if (!Rflag) {
+ SLOG("%s is a directory", path);
+ return FTW_SKIP_SUBTREE;
+ }
+
+ if (volume.volinfo.v_path) {
+ if ((volume.volinfo.v_adouble == AD_VERSION2)
+ && (strstr(path, ".AppleDouble") != NULL)) {
+ /* should be adouble dir itself */
+ if (rmdir(path) != 0) {
+ SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
+ badrm = rval = 1;
+ return -1;
+ }
+ break;
+ }
+
+ /* Get CNID of Parent and add new childir to CNID database */
+ if ((did = cnid_for_path(&volume, path, &pdid)) == CNID_INVALID) {
+ SLOG("Error resolving CNID for %s", path);
+ return -1;
+ }
+ if (cnid_delete(volume.volume.v_cdb, did) != 0) {
+ SLOG("Error removing CNID %u for %s", ntohl(did), path);
+ return -1;
+ }
+ }
+
+ if (rmdir(path) != 0) {
+ SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
+ badrm = rval = 1;
+ return -1;
+ }
+
+ break;
+
+ case S_IFBLK:
+ case S_IFCHR:
+ SLOG("%s is a device file.", path);
+ badrm = rval = 1;
+ break;
+
+ case S_IFSOCK:
+ SLOG("%s is a socket.", path);
+ badrm = rval = 1;
+ break;
+
+ case S_IFIFO:
+ SLOG("%s is a FIFO.", path);
+ badrm = rval = 1;
+ break;
+
+ default:
+ if (volume.volinfo.v_path) {
+ if ((volume.volinfo.v_adouble == AD_VERSION2)
+ && (strstr(path, ".AppleDouble") != NULL)) {
+ /* file in adouble dir */
+ if (unlink(path) != 0)
+ badrm = rval = 1;
+ break;
+ }
+
+ /* Get CNID of Parent and add new childir to CNID database */
+ pdid = did;
+ if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
+ SLOG("Error resolving CNID for %s", path);
+ return -1;
+ }
+ if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
+ SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
+ return -1;
+ }
+
+ /* Ignore errors, because with -R adouble stuff is always alread gone */
+ volume.volume.vfs->vfs_deletefile(&volume.volume, -1, path);
+ }
+
+ if (unlink(path) != 0) {
+ badrm = rval = 1;
+ break;
+ }
+
+ break;
+ }
+
+ if (vflag && !badrm)
+ (void)printf("%s\n", path);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <libgen.h>
+
+#ifdef HAVE_SOLARIS_ACLS
+#include <sys/acl.h>
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+#include <sys/types.h>
+#include <sys/acl.h>
+#endif /* HAVE_POSIX_ACLS */
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/logger.h>
+#include <atalk/errchk.h>
+#include <atalk/unicode.h>
+
+#include "ad.h"
+
+int log_verbose; /* Logging flag */
+
+void _log(enum logtype lt, char *fmt, ...)
+{
+ int len;
+ static char logbuffer[1024];
+ va_list args;
+
+ if ( (lt == STD) || (log_verbose == 1)) {
+ va_start(args, fmt);
+ len = vsnprintf(logbuffer, 1023, fmt, args);
+ va_end(args);
+ logbuffer[1023] = 0;
+
+ printf("%s\n", logbuffer);
+ }
+}
+
+/*!
+ * Load volinfo and initialize struct vol
+ *
+ * Only opens "dbd" volumes !
+ *
+ * @param path (r) path to evaluate
+ * @param vol (rw) structure to initialize
+ *
+ * @returns 0 on success, exits on error
+ */
+int openvol(const char *path, afpvol_t *vol)
+{
+ int flags = 0;
+
+ memset(vol, 0, sizeof(afpvol_t));
+
+ /* try to find a .AppleDesktop/.volinfo */
+ if (loadvolinfo((char *)path, &vol->volinfo) != 0)
+ return -1;
+
+ if (STRCMP(vol->volinfo.v_cnidscheme, != , "dbd"))
+ ERROR("\"%s\" isn't a \"dbd\" CNID volume!", vol->volinfo.v_path);
+
+ if (vol_load_charsets(&vol->volinfo) == -1)
+ ERROR("Error loading charsets!");
+
+ /* Sanity checks to ensure we can touch this volume */
+ if (vol->volinfo.v_adouble != AD_VERSION2)
+ ERROR("Unsupported adouble versions: %u", vol->volinfo.v_adouble);
+
+ if (vol->volinfo.v_vfs_ea != AFPVOL_EA_SYS)
+ ERROR("Unsupported Extended Attributes option: %u", vol->volinfo.v_vfs_ea);
+
+ /* initialize sufficient struct vol for VFS initialisation */
+ vol->volume.v_adouble = AD_VERSION2;
+ vol->volume.v_vfs_ea = AFPVOL_EA_SYS;
+ initvol_vfs(&vol->volume);
+
+ if ((vol->volinfo.v_flags & AFPVOL_NODEV))
+ flags |= CNID_FLAG_NODEV;
+
+ if ((vol->volume.v_cdb = cnid_open(vol->volinfo.v_path,
+ 0000,
+ "dbd",
+ flags,
+ vol->volinfo.v_dbd_host,
+ vol->volinfo.v_dbd_port)) == NULL)
+ ERROR("Cant initialize CNID database connection for %s", vol->volinfo.v_path);
+
+ cnid_getstamp(vol->volume.v_cdb,
+ vol->db_stamp,
+ sizeof(vol->db_stamp));
+
+ return 0;
+}
+
+void closevol(afpvol_t *vol)
+{
+ if (vol->volume.v_cdb)
+ cnid_close(vol->volume.v_cdb);
+
+ memset(vol, 0, sizeof(afpvol_t));
+}
+
+/*
+ Taken form afpd/desktop.c
+*/
+char *utompath(const struct volinfo *volinfo, const char *upath)
+{
+ static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
+ char *m;
+ const char *u;
+ uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX;
+ size_t outlen;
+
+ if (!upath)
+ return NULL;
+
+ m = mpath;
+ u = upath;
+ outlen = strlen(upath);
+
+ if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
+ flags |= CONV_TOUPPER;
+ else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
+ flags |= CONV_TOLOWER;
+
+ if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
+ flags |= CONV__EILSEQ;
+ }
+
+ /* convert charsets */
+ if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
+ CH_UTF8_MAC,
+ volinfo->v_maccharset,
+ u, outlen, mpath, MAXPATHLEN, &flags)) ) {
+ SLOG("Conversion from %s to %s for %s failed.",
+ volinfo->v_volcodepage, volinfo->v_maccodepage, u);
+ return NULL;
+ }
+
+ return(m);
+}
+
+/*!
+ * Build path relativ to volume root
+ *
+ * path might be:
+ * (a) relative:
+ * "dir/subdir" with cwd: "/afp_volume/topdir"
+ * (b) absolute:
+ * "/afp_volume/dir/subdir"
+ *
+ * @param path (r) path relative to cwd() or absolute
+ * @param volpath (r) volume path that path is a subdir of (has been computed in volinfo funcs)
+ *
+ * @returns relative path in new bstring, caller must bdestroy it
+ */
+static bstring rel_path_in_vol(const char *path, const char *volpath)
+{
+ EC_INIT;
+ int cwd = -1;
+ bstring fpath = NULL;
+ char *dname = NULL;
+ struct stat st;
+
+ if (path == NULL || volpath == NULL)
+ return NULL;
+
+ EC_NEG1_LOG(cwd = open(".", O_RDONLY));
+
+ EC_ZERO_LOGSTR(lstat(path, &st), "lstat(%s): %s", path, strerror(errno));
+ switch (S_IFMT & st.st_mode) {
+
+ case S_IFREG:
+ case S_IFLNK:
+ EC_NULL_LOG(dname = strdup(path));
+ EC_ZERO_LOGSTR(chdir(dirname(dname)), "chdir(%s): %s", dirname, strerror(errno));
+ free(dname);
+ dname = NULL;
+ EC_NULL(fpath = bfromcstr(getcwdpath()));
+ BSTRING_STRIP_SLASH(fpath);
+ EC_ZERO(bcatcstr(fpath, "/"));
+ EC_NULL_LOG(dname = strdup(path));
+ EC_ZERO(bcatcstr(fpath, basename(dname)));
+ break;
+
+ case S_IFDIR:
+ EC_ZERO_LOGSTR(chdir(path), "chdir(%s): %s", path, strerror(errno));
+ EC_NULL(fpath = bfromcstr(getcwdpath()));
+ break;
+
+ default:
+ SLOG("special: %s", path);
+ EC_FAIL;
+ }
+
+ BSTRING_STRIP_SLASH(fpath);
+
+ /*
+ * Now we have eg:
+ * fpath: /Volume/netatalk/dir/bla
+ * volpath: /Volume/netatalk/
+ * we want: "dir/bla"
+ */
+ EC_ZERO(bdelete(fpath, 0, strlen(volpath)));
+
+EC_CLEANUP:
+ if (dname) free(dname);
+ if (cwd != -1) {
+ fchdir(cwd);
+ close(cwd);
+ }
+ if (ret != 0)
+ return NULL;
+ return fpath;
+}
+
+/*!
+ * Convert dot encoding of basename _in place_
+ *
+ * path arg can be "[/][dir/ | ...]filename". It will be converted in place
+ * possible encoding ".file" as ":2efile" which means the result will be
+ * longer then the original which means provide a big enough buffer.
+ *
+ * @param svol (r) source volume
+ * @param dvol (r) destinatio volume
+ * @param path (rw) path to convert _in place_
+ * @param buflen (r) size of path buffer (max strlen == buflen -1)
+ *
+ * @returns 0 on sucess, -1 on error
+ */
+int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path, size_t buflen)
+{
+ static charset_t from = (charset_t) -1;
+ static char buf[MAXPATHLEN+2];
+ char *bname = stripped_slashes_basename(path);
+ int pos = bname - path;
+ uint16_t flags = 0;
+
+ if ( ! svol->volinfo.v_path) {
+ /* no source volume: escape special chars (eg ':') */
+ from = dvol->volinfo.v_volcharset; /* src = dst charset */
+ flags |= CONV_ESCAPEHEX;
+ } else {
+ from = svol->volinfo.v_volcharset;
+ }
+
+ if ( (svol->volinfo.v_path)
+ && ! (svol->volinfo.v_flags & AFPVOL_USEDOTS)
+ && (dvol->volinfo.v_flags & AFPVOL_USEDOTS)) {
+ /* source is without dots, destination is with */
+ flags |= CONV_UNESCAPEHEX;
+ } else if (! (dvol->volinfo.v_flags & AFPVOL_USEDOTS)) {
+ flags |= CONV_ESCAPEDOTS;
+ }
+
+ int len = convert_charset(from,
+ dvol->volinfo.v_volcharset,
+ dvol->volinfo.v_maccharset,
+ bname, strlen(bname),
+ buf, MAXPATHLEN,
+ &flags);
+ if (len == -1)
+ return -1;
+
+ if (strlcpy(bname, buf, MAXPATHLEN - pos) > MAXPATHLEN - pos)
+ return -1;
+ return 0;
+}
+
+/*!
+ * ResolvesCNID of a given paths
+ *
+ * path might be:
+ * (a) relative:
+ * "dir/subdir" with cwd: "/afp_volume/topdir"
+ * (b) absolute:
+ * "/afp_volume/dir/subdir"
+ *
+ * path MUST be pointing inside vol, this is usually the case as vol has been build from
+ * path using loadvolinfo and friends.
+ *
+ * @param vol (r) pointer to afpvol_t
+ * @param path (r) path, see above
+ * @param did (rw) parent CNID of returned CNID
+ *
+ * @returns CNID of path
+ */
+cnid_t cnid_for_path(const afpvol_t *vol,
+ const char *path,
+ cnid_t *did)
+{
+ EC_INIT;
+
+ cnid_t cnid;
+ bstring rpath = NULL;
+ bstring statpath = NULL;
+ struct bstrList *l = NULL;
+ struct stat st;
+
+ *did = htonl(1);
+ cnid = htonl(2);
+
+ EC_NULL(rpath = rel_path_in_vol(path, vol->volinfo.v_path));
+ EC_NULL(statpath = bfromcstr(vol->volinfo.v_path));
+
+ l = bsplit(rpath, '/');
+ for (int i = 0; i < l->qty ; i++) {
+ *did = cnid;
+ EC_ZERO(bconcat(statpath, l->entry[i]));
+ EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
+ "lstat(rpath: %s, elem: %s): %s: %s",
+ cfrombstr(rpath), cfrombstr(l->entry[i]),
+ cfrombstr(statpath), strerror(errno));
+
+ if ((cnid = cnid_add(vol->volume.v_cdb,
+ &st,
+ *did,
+ cfrombstr(l->entry[i]),
+ blength(l->entry[i]),
+ 0)) == CNID_INVALID) {
+ EC_FAIL;
+ }
+ EC_ZERO(bcatcstr(statpath, "/"));
+ }
+
+EC_CLEANUP:
+ bdestroy(rpath);
+ bstrListDestroy(l);
+ bdestroy(statpath);
+ if (ret != 0)
+ return CNID_INVALID;
+
+ return cnid;
+}
+
+/*!
+ * Resolves CNID of a given paths parent directory
+ *
+ * path might be:
+ * (a) relative:
+ * "dir/subdir" with cwd: "/afp_volume/topdir"
+ * (b) absolute:
+ * "/afp_volume/dir/subdir"
+ *
+ * path MUST be pointing inside vol, this is usually the case as vol has been build from
+ * path using loadvolinfo and friends.
+ *
+ * @param vol (r) pointer to afpvol_t
+ * @param path (r) path, see above
+ * @param did (rw) parent CNID of returned CNID
+ *
+ * @returns CNID of path
+ */
+cnid_t cnid_for_paths_parent(const afpvol_t *vol,
+ const char *path,
+ cnid_t *did)
+{
+ EC_INIT;
+
+ cnid_t cnid;
+ bstring rpath = NULL;
+ bstring statpath = NULL;
+ struct bstrList *l = NULL;
+ struct stat st;
+
+ *did = htonl(1);
+ cnid = htonl(2);
+
+ EC_NULL(rpath = rel_path_in_vol(path, vol->volinfo.v_path));
+ EC_NULL(statpath = bfromcstr(vol->volinfo.v_path));
+
+ l = bsplit(rpath, '/');
+ if (l->qty == 1)
+ /* only one path element, means parent dir cnid is volume root = 2 */
+ goto EC_CLEANUP;
+ for (int i = 0; i < (l->qty - 1); i++) {
+ *did = cnid;
+ EC_ZERO(bconcat(statpath, l->entry[i]));
+ EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
+ "lstat(rpath: %s, elem: %s): %s: %s",
+ cfrombstr(rpath), cfrombstr(l->entry[i]),
+ cfrombstr(statpath), strerror(errno));
+
+ if ((cnid = cnid_add(vol->volume.v_cdb,
+ &st,
+ *did,
+ cfrombstr(l->entry[i]),
+ blength(l->entry[i]),
+ 0)) == CNID_INVALID) {
+ EC_FAIL;
+ }
+ EC_ZERO(bcatcstr(statpath, "/"));
+ }
+
+EC_CLEANUP:
+ bdestroy(rpath);
+ bstrListDestroy(l);
+ bdestroy(statpath);
+ if (ret != 0)
+ return CNID_INVALID;
+
+ return cnid;
+}
+
+++ /dev/null
-# Makefile.am for bin/afile/
-
-INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/sys
-
-bin_PROGRAMS = afile achfile
-
-afile_SOURCES = afile.c common.c common.h
-achfile_SOURCES = achfile.c common.c common.h
+++ /dev/null
-/*
- * $Id: achfile.c,v 1.7 2009-10-14 01:38:28 didg Exp $
- *
- afile - determine the MacOS creator/type of files
-
- Copyright (C) 2001 Sebastian Rittau.
- All rights reserved.
-
- This file may be distributed and/or modfied under the terms of the
- following license:
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the author nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif /* HAVE_FCNTL_H */
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-
-#include <atalk/adouble.h>
-
-#include "common.h"
-
-/* Global Variables */
-static const char *type = NULL;
-static const char *creator = NULL;
-
-
-/* Print usage information. */
-static void usage(char *prog)
-{
- fprintf(stderr, "Usage: %s [-t TYPE] [-c CREATOR] FILE ...\n", prog);
-}
-
-/* Print extensive help. */
-static void help(char *prog)
-{
- usage(prog);
- fprintf(stderr,
- "\n"
- "Change the MacOS creator/type of FILEs.\n"
- "\n"
- " -t, --type=TYPE choose type\n"
- " -c, --creator=CREATOR choose creator\n"
- " -h, --help show this help and exit\n"
- " -v, --version show version information and exit\n");
-}
-
-/* Print the version. */
-static void version(void)
-{
- fprintf(stderr, "achfile (netatalk " VERSION ")\n");
-}
-
-/* Argument Handling
- * known options: -t, -c, -h, -v
- * known long options: --help, --version
- */
-#define OPTSTRING "t:c:hv-:"
-static const char *get_long_arg(int argc, char *argv[], const char *arg, const char *oa) {
- switch (*oa) {
- case '=':
- return &oa[1];
- case '\0':
- if (optind == argc) {
- fprintf(stderr, "%s: option \'%s\' requires an argument\n", argv[0], arg);
- return NULL;
- }
- return argv[optind++];
- default:
- fprintf(stderr, "%s: unrecognized option \'%s\'\n", argv[0], arg);
- usage(argv[0]);
- return NULL;
- }
-}
-
-static int parse_args(int argc, char *argv[])
-{
- int c;
- const char *longarg;
-
- /* iterate through the command line */
- while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
- switch (c) {
- case 'h':
- help(argv[0]);
- exit(0);
- case 'v':
- version();
- exit(0);
- case 't':
- type = optarg;
- break;
- case 'c':
- creator = optarg;
- break;
- case '-':
- if (strcmp(optarg, "help") == 0) {
- help(argv[0]);
- exit(0);
- }
- if (strcmp(optarg, "version") == 0) {
- version();
- exit(0);
- }
- if (strncmp(optarg, "type", 4) == 0) {
- longarg = get_long_arg(argc, argv, "type", &optarg[4]);
- if (!longarg)
- return -1;
- type = longarg;
- } else if (strncmp(optarg, "creator", 7) == 0) {
- longarg = get_long_arg(argc, argv, "creator", &optarg[7]);
- if (!longarg)
- return -1;
- creator = longarg;
- }
- break;
- default:
- usage(argv[0]);
- return -1;
- }
- }
-
- /* At least one file argument is required. */
- if (argc == optind) {
- usage(argv[0]);
- return -1;
- }
-
- /* Either type or creator is required. */
- if (!type && !creator) {
- fprintf(stderr, "achfile: either type or creator must be specified\n");
- return -1;
- }
-
- /* Type and creator must be exactly four characters long. */
- if ((type && strlen(type) != 4) || (creator && strlen(creator) != 4)) {
- fprintf(stderr, "achfile: type and creator must be four character IDs\n");
- return -1;
- }
-
- return 0;
-}
-
-
-/* Change the owner/creator of each file specified on the command line. */
-static int handle_file(const char *filename)
-{
- int fd;
- struct stat statbuf;
- char *adname;
- ssize_t sz;
- char buf[AD_DATASZ];
- struct adouble *ad = (struct adouble *) &buf;
-
- if (stat(filename, &statbuf) == -1) {
- fprintf(stderr, "achfile:%s: %s\n", filename, strerror(errno));
- return -1;
- }
-
- adname = dataname_to_adname(filename);
- fd = open(adname, O_RDWR, 0);
- if (fd == -1) {
- if (errno == ENOENT)
- fprintf(stderr, "achfile:%s: no resource fork\n", filename);
- else
- fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
- free(adname);
- return -1;
- }
- sz = read(fd, buf, AD_DATASZ);
- if (sz == -1) {
- fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
- free(adname);
- close(fd);
- return -1;
- } else if (sz < AD_DATASZ) {
- fprintf(stderr, "achfile:%s: corrupt resource fork\n", filename);
- free(adname);
- close(fd);
- return -1;
- }
- if ( ntohl(ad->ad_magic) != AD_MAGIC) {
- fprintf(stderr, "achfile:%s: corrupt resource fork\n", filename);
- free(adname);
- close(fd);
- return -1;
- }
-
- /* Change Type */
- if (type) {
- buf[ADEDOFF_FINDERI + 0] = type[0];
- buf[ADEDOFF_FINDERI + 1] = type[1];
- buf[ADEDOFF_FINDERI + 2] = type[2];
- buf[ADEDOFF_FINDERI + 3] = type[3];
- }
-
- /* Change Creator */
- if (creator) {
- buf[ADEDOFF_FINDERI + 4] = creator[0];
- buf[ADEDOFF_FINDERI + 5] = creator[1];
- buf[ADEDOFF_FINDERI + 6] = creator[2];
- buf[ADEDOFF_FINDERI + 7] = creator[3];
- }
-
- /* Write file back to disk. */
- if (lseek(fd, 0, SEEK_SET) == -1) {
- fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
- free(adname);
- close(fd);
- return -1;
- }
- if (write(fd, &buf, AD_DATASZ) < AD_DATASZ) {
- fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
- free(adname);
- close(fd);
- return -1;
- }
-
- /* Clean Up */
- free(adname);
- close(fd);
-
- return 0;
-}
-
-
-int main(int argc, char *argv[])
-{
- /* argument handling */
- if (parse_args(argc, argv) == -1)
- return 1;
-
- while (optind < argc)
- handle_file(argv[optind++]);
-
- return 0;
-}
+++ /dev/null
-/*
- * $Id: afile.c,v 1.7 2009-10-14 01:38:28 didg Exp $
- *
- afile - determine the MacOS creator/type of files
-
- Copyright (C) 2001 Sebastian Rittau.
- All rights reserved.
-
- This file may be distributed and/or modfied under the terms of the
- following license:
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the author nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-
-#include <atalk/adouble.h>
-
-#include "common.h"
-
-/* Possible return types. These are taken from the original afile for
- * compatibility.
- */
-#define RET_OK 0 /* everything went fine */
-#define RET_NO_SUCH_FILE 1 /* file doesn't exist */
-#define RET_UNREADABLE 2 /* file is unreadable */
-#define RET_IS_DIR 3 /* file is a directory */
-#define RET_NO_DF 4 /* there is no corresponding data fork */
-#define RET_INVALID_DF 5 /* data fork exists but can't be read */
-#define RET_NO_AD 6 /* AppleDouble file doesn't exist */
-#define RET_INVALID_AD 7 /* AppleDouble file exists but can't be read */
-#define RET_SHORT_AD 8 /* AppleDouble file is too short */
-#define RET_BAD_MAGIC 9 /* AppleDouble file has a bad magic value */
-#define RET_BAD_ARGS 99 /* bad command line options */
-
-
-/* Global Variables */
-static int showall = 0;
-
-
-/* Print usage information. */
-static void usage(char *prog)
-{
- fprintf(stderr, "Usage: %s [-a] FILE ...\n", prog);
-}
-
-/* Print extensive help. */
-static void help(char *prog)
-{
- usage(prog);
- fprintf(stderr,
- "\n"
- "Determine the MacOS creator/type of FILEs.\n"
- "\n"
- " -a, --show-all also show unknown files and directories\n"
- " -h, --help show this help and exit\n"
- " -v, --version show version information and exit\n");
-}
-
-/* Print the version. */
-static void version(void)
-{
- fprintf(stderr, "afile (netatalk " VERSION ")\n");
-}
-
-/* Argument Handling
- * known options: -a, -h, -v
- * known long options: --show-all, --help, --version
- */
-#define OPTSTRING "ahv-:"
-static int parse_args(int argc, char *argv[])
-{
- int c;
-
- /* iterate through the command line */
- while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
- switch (c) {
- case 'h':
- help(argv[0]);
- exit(0);
- case 'v':
- version();
- exit(0);
- case 'a':
- showall = 1;
- break;
- case '-':
- if (strcmp(optarg, "help") == 0) {
- help(argv[0]);
- exit(0);
- }
- if (strcmp(optarg, "version") == 0) {
- version();
- exit(0);
- }
- if (strcmp(optarg, "show-all") == 0)
- showall = 1;
- break;
- default:
- usage(argv[0]);
- return -1;
- }
- }
-
- /* At least one file argument is required. */
- if (argc == optind) {
- usage(argv[0]);
- return -1;
- }
-
- return 0;
-}
-
-
-/* Print the creator/type as taken from the supplied character stream, which
- * must be a AppleDouble file header.
- */
-static void print_type(const char *filename, const char *creator, const char *type)
-{
- printf("%4.4s %4.4s %s\n", creator, type, filename);
-}
-
-
-static int handle_ad(struct AFile *rfile)
-{
- int fd;
- char *dataname;
-
- dataname = adname_to_dataname(afile_filename(rfile));
-
- /* Try to open data file. */
- fd = open(dataname, O_RDONLY);
- free(dataname);
- if (fd == -1) {
- if (errno == ENOENT)
- return RET_NO_DF; /* There is no data fork. */
- else
- return RET_INVALID_DF; /* The data fork can't be read. */
- }
- /* FIXME: stat */
-
- close(fd);
-
- return RET_OK;
-}
-
-
-static int handle_datafile(struct AFile *datafile)
-{
- int ret;
- char *adname;
- struct AFile *rfile;
-
- /* Try to load AppleDouble file. */
- adname = dataname_to_adname(afile_filename(datafile));
- rfile = afile_new(adname);
- if (!rfile) {
- if (errno == ENOENT) {
- free(adname);
- if (showall)
- printf("unknown %s\n", afile_filename(datafile));
- return RET_NO_AD;
- }
- fprintf(stderr, "afile:%s: %s\n", adname, strerror(errno));
- free(adname);
- return RET_INVALID_AD;
- }
- free(adname);
-
- /* Check if this is really an AppleDouble file. */
- if (afile_is_ad(rfile)) {
- print_type(afile_filename(datafile),
- afile_creator(rfile), afile_type(rfile));
- if (afile_mode(datafile) != afile_mode(rfile))
- fprintf(stderr, "afile:%s: mode does not match", afile_filename(datafile));
- ret = RET_OK;
- } else
- ret = RET_INVALID_AD;
-
- afile_delete(rfile);
-
- return ret;
-}
-
-
-/* Parse a file and its resource fork. Output the file's creator/type. */
-static int parse_file(char *filename)
-{
- int ret;
- struct AFile *afile;
-
- afile = afile_new(filename);
- if (!afile) {
- fprintf(stderr, "afile:%s: %s\n", filename, strerror(errno));
- return errno == ENOENT ? RET_NO_SUCH_FILE : RET_UNREADABLE;
- }
-
- if (afile_is_dir(afile)) {
- if (showall)
- printf("directory %s\n", filename);
- ret = RET_IS_DIR;
- } else if (afile_is_ad(afile))
- ret = handle_ad(afile);
- else
- ret = handle_datafile(afile);
-
- afile_delete(afile);
-
- return ret;
-}
-
-
-int main(int argc, char *argv[])
-{
- int ret = RET_OK;
-
- /* argument handling */
- if (parse_args(argc, argv) == -1)
- return RET_BAD_ARGS;
-
- /* iterate through all files specified as arguments */
- while (optind < argc)
- ret = parse_file(argv[optind++]);
-
- return ret;
-}
+++ /dev/null
-/*
- * $Id: common.c,v 1.3 2001-06-29 14:14:46 rufustfirefly Exp $
- *
- common functions, defines, and structures for afile, achfile, and acleandir
-
- Copyright (C) 2001 Sebastian Rittau.
- All rights reserved.
-
- This file may be distributed and/or modfied under the terms of the
- following license:
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the author nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif /* HAVE_FCNTL_H */
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-
-#include <atalk/adouble.h>
-#include <netatalk/endian.h>
-
-#include "common.h"
-
-
-#define AD_PREFIX ".AppleDouble/"
-#define PARENT_PREFIX "../"
-
-
-char *dataname_to_adname(const char *dataname)
-{
- const char *filepart;
- char *adname;
- size_t adlen = strlen(AD_PREFIX);
- size_t dirlen;
-
- /* Construct the AppleDouble file name from data fork file name. */
- adname = calloc(adlen + strlen(dataname) + 1, sizeof(char));
- filepart = rindex(dataname, '/');
- if (filepart == NULL) {
- /* Filename doesn't contain a path. */
- strcpy(adname, AD_PREFIX);
- strcpy(adname + adlen, dataname);
- } else {
- /* Filename does contain a path. */
- dirlen = (size_t) (filepart - dataname);
- strncpy(adname, dataname, dirlen + 1);
- strcpy(adname + dirlen + 1, AD_PREFIX);
- strcpy(adname + dirlen + adlen + 1, filepart + 1);
- }
-
- return adname;
-}
-
-char *adname_to_dataname(const char *adname)
-{
- const char *filepart;
- char *dataname;
- size_t plen = strlen(PARENT_PREFIX);
- size_t dirlen;
-
- /* Construct the data file name from the AppleDouble file name. */
- dataname = calloc(strlen(adname) + plen + 1, sizeof(char));
- filepart = rindex(adname, '/');
- if (filepart == NULL) {
- strcpy(dataname, PARENT_PREFIX);
- strcpy(dataname + plen, adname);
- } else {
- dirlen = (size_t) (filepart - adname);
- strncpy(dataname, adname, dirlen + 1);
- strcpy(dataname + dirlen + 1, PARENT_PREFIX);
- strcpy(dataname + dirlen + plen + 1, filepart + 1);
- }
-
- return dataname;
-}
-
-
-#define FLAG_NONE 0x0000
-#define FLAG_AD 0x0001
-#define FLAG_DIR 0x0002
-
-
-struct AFile *afile_new(const char *filename)
-{
- struct stat fdstat;
- char adbuf[AD_DATASZ];
- ssize_t sz;
- struct stat statbuf;
-
- struct AFile *afile = (struct AFile *) calloc(sizeof(struct AFile), 1);
- afile->filename = filename;
- afile->fd = open(filename, O_RDONLY);
- afile->flags = FLAG_NONE;
-
- /* Try to open file. */
- if (afile->fd == -1) {
- free(afile);
- return NULL;
- }
-
- fstat(afile->fd, &statbuf);
- afile->mode = statbuf.st_mode;
-
- /* Try to determine if this file is a regular file, a directory, or
- * something else.
- */
- fstat(afile->fd, &fdstat);
- if (S_ISREG(fdstat.st_mode)) { /* it is a regular file */
- /* Check if the opened file is the resource fork. */
- sz = read(afile->fd, adbuf, AD_DATASZ);
- if (sz >= 0) {
- /* If we can't read a whole AppleDouble header, this can't be a valid
- * AppleDouble file.
- */
- if (sz == AD_DATASZ) {
- /* ... but as we could, maybe this is. Now if only the magic number
- * would be correct ...
- */
- if (ntohl(((unsigned int *) adbuf)[0]) == AD_MAGIC)
- /* Great! It obviously is a AppleDouble file. */
- afile->flags |= FLAG_AD;
- afile->creator[0] = adbuf[ADEDOFF_FINDERI + 0];
- afile->creator[1] = adbuf[ADEDOFF_FINDERI + 1];
- afile->creator[2] = adbuf[ADEDOFF_FINDERI + 2];
- afile->creator[3] = adbuf[ADEDOFF_FINDERI + 3];
- afile->type [0] = adbuf[ADEDOFF_FINDERI + 4];
- afile->type [1] = adbuf[ADEDOFF_FINDERI + 5];
- afile->type [2] = adbuf[ADEDOFF_FINDERI + 6];
- afile->type [3] = adbuf[ADEDOFF_FINDERI + 7];
- }
-
- } else {
- afile_delete(afile);
- return NULL;
- }
-
- } else if (S_ISDIR(fdstat.st_mode)) /* it is a directory */
- afile->flags |= FLAG_DIR;
- else { /* it is neither */
- afile_delete(afile);
- return NULL;
- }
-
- return afile;
-}
-
-
-void afile_delete(struct AFile *afile)
-{
- close(afile->fd);
- free(afile);
-}
-
-
-const char *afile_filename(struct AFile *afile)
-{
- return afile->filename;
-}
-
-int afile_is_ad(struct AFile *afile)
-{
- return afile->flags & FLAG_AD;
-}
-
-int afile_is_dir(struct AFile *afile)
-{
- return afile->flags & FLAG_DIR;
-}
-
-mode_t afile_mode(struct AFile *afile)
-{
- return afile->mode;
-}
-
-const char *afile_creator(const struct AFile *afile)
-{
- return afile->creator;
-}
-
-const char *afile_type(const struct AFile *afile)
-{
- return afile->type;
-}
+++ /dev/null
-/*
- * $Id: common.h,v 1.2 2001-06-29 14:14:46 rufustfirefly Exp $
- *
- common functions, defines, and structures for afile, achfile, and acleandir
-
- Copyright (C) 2001 Sebastian Rittau.
- All rights reserved.
-
- This file may be distributed and/or modfied under the terms of the
- following license:
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the author nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-*/
-
-#include <stdlib.h>
-
-
-char *dataname_to_adname(const char *dataname);
-char *adname_to_dataname(const char *adname);
-
-/* Class for handling data and AppleDouble files. Don't access directly.
- * Instead, use the methods provided below.
- */
-struct AFile {
- const char *filename;
- int fd;
- mode_t mode;
- unsigned short flags;
- char creator[4], type[4];
-};
-
-
-/* Constructor */
-struct AFile *afile_new(const char *filename);
-/* Destructor */
-void afile_delete(struct AFile *afile);
-/* Accessor Methods */
-const char *afile_filename(struct AFile *afile);
-int afile_is_ad(struct AFile *afile);
-int afile_is_dir(struct AFile *afile);
-mode_t afile_mode(struct AFile *afile);
-/* The following two methods are only valid if afile_is_ad yields true. */
-const char *afile_creator(const struct AFile *afile);
-const char *afile_type(const struct AFile *afile);
# Makefile.am for bin/cnid/
EXTRA_DIST = cnid2_create.in
-noinst_HEADERS = ad.h
if USE_BDB
-bin_PROGRAMS = ad
bin_SCRIPTS = cnid2_create
-
-ad_SOURCES = ad.c ad_util.c \
- ad_ls.c \
- ad_cp.c
-
-ad_LDADD = $(top_builddir)/libatalk/libatalk.la
endif
+++ /dev/null
-/*
- $Id: ad.c,v 1.2 2009-10-13 22:55:36 didg Exp $
-
- Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <signal.h>
-#include <string.h>
-#include <errno.h>
-
-#include <atalk/cnid.h>
-#include <atalk/volinfo.h>
-#include "ad.h"
-
-static void usage_main(void)
-{
-/*
- printf("Usage: ad ls|rm|cp|mv|set [file|dir, ...]\n");
-*/
- printf("Usage: ad ls [file|dir, ...]\n");
-}
-
-int main(int argc, char **argv)
-{
- if (argc < 2) {
- usage_main();
- return 1;
- }
-
- if (STRCMP(argv[1], ==, "ls"))
- return ad_ls(argc - 1, argv + 1);
- else if (STRCMP(argv[1], ==, "cp"))
- return ad_cp(argc - 1, argv + 1);
- else {
- usage_main();
- return 1;
- }
-
- return 0;
-}
+++ /dev/null
-/*
- $Id: ad.h,v 1.1 2009-09-01 14:28:07 franklahm Exp $
-
- Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-*/
-
-#ifndef AD_H
-#define AD_H
-
-#include <atalk/volinfo.h>
-
-#define STRCMP(a,b,c) (strcmp(a,c) b 0)
-
-#define DIR_DOT_OR_DOTDOT(a) \
- ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
-
-typedef struct {
- struct volinfo volinfo;
-// char *dirname;
-// char *basename;
-// int adflags; /* file:0, dir:ADFLAGS_DIR */
-} afpvol_t;
-
-
-extern int newvol(const char *path, afpvol_t *vol);
-extern void freevol(afpvol_t *vol);
-
-extern int ad_ls(int argc, char **argv);
-extern int ad_cp(int argc, char **argv);
-
-#endif /* AD_H */
+++ /dev/null
-/*
- Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <string.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <grp.h>
-#include <time.h>
-#include <libgen.h>
-
-#include <atalk/adouble.h>
-#include <atalk/cnid.h>
-#include <atalk/volinfo.h>
-#include <atalk/util.h>
-#include "ad.h"
-
-#define ADv2_DIRNAME ".AppleDouble"
-
-/* options */
-static int cp_R;
-static int cp_L;
-static int cp_P;
-static int cp_n;
-static int cp_p;
-static int cp_v;
-
-static char *netatalk_dirs[] = {
- ADv2_DIRNAME,
- ".AppleDB",
- ".AppleDesktop",
- NULL
-};
-
-/*
- Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
- Returns pointer to name or NULL.
-*/
-static const char *check_netatalk_dirs(const char *name)
-{
- int c;
-
- for (c=0; netatalk_dirs[c]; c++) {
- if ((strcmp(name, netatalk_dirs[c])) == 0)
- return netatalk_dirs[c];
- }
- return NULL;
-}
-
-
-static void usage_cp(void)
-{
- printf(
- "Usage: ad cp [-R [-L | -P]] [-pv] <source_file> <target_file>\n"
- "Usage: ad cp [-R [-L | -P]] [-pv] <source_file [source_file ...]> <target_directory>\n"
- );
-}
-
-static int ad_cp_copy(const afpvol_t *srcvol, const afpvol_t *dstvol,
- char *srcfile, char *dstfile, struct stat *st)
-{
- printf("copy: '%s' -> '%s'\n", srcfile, dstfile);
- return 0;
-}
-
-static int ad_cp_r(const afpvol_t *srcvol, const afpvol_t *dstvol, char *srcdir, char *dstdir)
-{
- int ret = 0, dirprinted = 0, dirempty;
- static char srcpath[MAXPATHLEN+1];
- static char dstpath[MAXPATHLEN+1];
- char *tmp;
- DIR *dp = NULL;
- struct dirent *ep;
- static struct stat st; /* Save some stack space */
-
- strlcat(srcpath, srcdir, sizeof(srcpath));
- strlcat(dstpath, dstdir, sizeof(dstpath));
-
- if ((dp = opendir (srcdir)) == NULL) {
- perror("Couldn't opendir .");
- return -1;
- }
-
- /* First run: copy files */
- while ((ep = readdir (dp))) {
- /* Check if its "." or ".." */
- if (DIR_DOT_OR_DOTDOT(ep->d_name))
- continue;
-
- /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
- if ((check_netatalk_dirs(ep->d_name)) != NULL)
- continue;
-
- if (lstat(ep->d_name, &st) < 0) {
- perror("Can't stat");
- return -1;
- }
-
- /* Build paths, copy, strip name */
- strlcat(srcpath, "/", sizeof(srcpath));
- strlcat(dstpath, "/", sizeof(dstpath));
- strlcat(srcpath, ep->d_name, sizeof(srcpath));
- strlcat(dstpath, ep->d_name, sizeof(dstpath));
-
- ret = ad_cp_copy(srcvol, dstvol, srcpath, dstpath, &st);
-
- if ((tmp = strrchr(srcpath, '/')))
- *tmp = 0;
- if ((tmp = strrchr(dstpath, '/')))
- *tmp = 0;
-
- if (ret != 0)
- goto exit;
- }
-
- /* Second run: recurse to dirs */
- rewinddir(dp);
- while ((ep = readdir (dp))) {
- /* Check if its "." or ".." */
- if (DIR_DOT_OR_DOTDOT(ep->d_name))
- continue;
-
- /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
- if (check_netatalk_dirs(ep->d_name) != NULL)
- continue;
-
- if (lstat(ep->d_name, &st) < 0) {
- perror("Can't stat");
- return -1;
- }
-
- /* Recursion */
- if (S_ISDIR(st.st_mode)) {
- strlcat(srcpath, "/", sizeof(srcpath));
- strlcat(dstpath, "/", sizeof(dstpath));
- ret = ad_cp_r(srcvol, dstvol, ep->d_name, ep->d_name);
- }
- if (ret != 0)
- goto exit;
- }
-
-exit:
- if (dp)
- closedir(dp);
-
- if ((tmp = strrchr(srcpath, '/')))
- *tmp = 0;
- if ((tmp = strrchr(dstpath, '/')))
- *tmp = 0;
-
- return ret;
-}
-
-int ad_cp(int argc, char **argv)
-{
- int c, numpaths;
- afpvol_t srcvvol;
- struct stat sst;
- struct stat dst;
- afpvol_t srcvol;
- afpvol_t dstvol;
- char *srcfile = NULL;
- char *srcdir = NULL;
- char *dstfile = NULL;
- char *dstdir = NULL;
- char path[MAXPATHLEN+1];
- char *basenametmp;
-
- while ((c = getopt(argc, argv, ":npvLPR")) != -1) {
- switch(c) {
- case 'n':
- cp_n = 1;
- break;
- case 'p':
- cp_p = 1;
- break;
- case 'v':
- cp_v = 1;
- break;
- case 'L':
- cp_L = 1;
- break;
- case 'P':
- cp_P = 1;
- break;
- case 'R':
- cp_R = 1;
- break;
- case ':':
- case '?':
- usage_cp();
- return -1;
- break;
- }
- }
-
- /* How many pathnames do we have */
- numpaths = argc - optind;
- printf("Number of paths: %u\n", numpaths);
- if (numpaths < 2) {
- usage_cp();
- exit(EXIT_FAILURE);
- }
-
- while ( argv[argc-1][(strlen(argv[argc-1]) - 1)] == '/')
- argv[argc-1][(strlen(argv[argc-1]) - 1)] = 0;
- printf("Destination is: %s\n", argv[argc-1]);
-
- /* Create vol for destination */
- newvol(argv[argc-1], &dstvol);
-
- if (numpaths == 2) {
- /* Case 1: 2 paths */
- if (stat(argv[optind], &sst) != 0)
- goto next;
- if (S_ISREG(sst.st_mode)) {
- /* Either file to file or file to dir copy */
- newvol(argv[optind], &srcvol);
- ad_cp_copy(&srcvol, &dstvol, srcfile, path, &sst);
- freevol(&srcvol);
- freevol(&dstvol);
-
- } else if (S_ISDIR(sst.st_mode)) {
- /* dir to dir copy. Check if -R is requested */
- if (!cp_R) {
- usage_cp();
- exit(EXIT_FAILURE);
- }
- }
-
- } else {
- /* Case 2: >2 paths */
- while (optind < (argc-1)) {
- printf("Source is: %s\n", argv[optind]);
- newvol(argv[optind], &srcvol);
- if (stat(argv[optind], &sst) != 0)
- goto next;
- if (S_ISDIR(sst.st_mode)) {
- /* Source is a directory */
- if (!cp_R) {
- printf("Source %s is a directory\n", argv[optind]);
- goto next;
- }
- srcdir = argv[optind];
- dstdir = argv[argc-1];
-
- ad_cp_r(&srcvol, &dstvol, srcdir, dstdir);
- freevol(&srcvol);
- } else {
- /* Source is a file */
- srcfile = argv[optind];
- if (numpaths != 2 && !dstdir) {
- usage_cp();
- exit(EXIT_FAILURE);
- }
- path[0] = 0;
- strlcat(path, dstdir, sizeof(path));
- basenametmp = strdup(srcfile);
- strlcat(path, basename(basenametmp), sizeof(path));
- free(basenametmp);
- printf("%s %s\n", srcfile, path);
- if (ad_cp_copy(&srcvol, &dstvol, srcfile, path, &sst) != 0) {
- freevol(&srcvol);
- freevol(&dstvol);
- exit(EXIT_FAILURE);
- }
- } /* else */
- next:
- optind++;
- } /* while */
- } /* else (numpath>2) */
-
- freevol(&dstvol);
-
- return 0;
-
-}
+++ /dev/null
-/*
- $Id: ad_ls.c,v 1.4 2009-10-14 01:38:28 didg Exp $
-
- Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <string.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <grp.h>
-#include <time.h>
-
-#include <atalk/adouble.h>
-#include <atalk/cnid.h>
-#include <atalk/volinfo.h>
-#include "ad.h"
-
-#define ADv2_DIRNAME ".AppleDouble"
-
-#define DIR_DOT_OR_DOTDOT(a) \
- ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
-
-/* ls options */
-static int ls_a;
-static int ls_l;
-static int ls_R;
-static int ls_d;
-static int ls_u;
-
-/* Used for pretty printing */
-static int first = 1;
-static int recursion;
-
-static char *netatalk_dirs[] = {
- ADv2_DIRNAME,
- ".AppleDB",
- ".AppleDesktop",
- NULL
-};
-
-static char *labels[] = {
- "---",
- "gry",
- "gre",
- "vio",
- "blu",
- "yel",
- "red",
- "ora"
-};
-
-/*
- Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
- Returns pointer to name or NULL.
-*/
-static const char *check_netatalk_dirs(const char *name)
-{
- int c;
-
- for (c=0; netatalk_dirs[c]; c++) {
- if ((strcmp(name, netatalk_dirs[c])) == 0)
- return netatalk_dirs[c];
- }
- return NULL;
-}
-
-
-static void usage_ls(void)
-{
- printf(
- "Usage: ad ls [-dRl[u]] [file|dir, ...]\n\n"
- " -l Long Output [-u: unix info]:\n"
- " <unixinfo ...> <FinderFlags> <AFPAttributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>\n\n"
- " FinderFlags (valid for (f)ile and/or (d)irectory):\n"
- " d = On Desktop (f/d)\n"
- " e = Hidden extension (f/d)\n"
- " m = Shared (can run multiple times) (f)\n"
- " n = No INIT resources (f)\n"
- " i = Inited (f/d)\n"
- " c = Custom icon (f/d)\n"
- " t = Stationery (f)\n"
- " s = Name locked (f/d)\n"
- " b = Bundle (f/d)\n"
- " v = Invisible (f/d)\n"
- " a = Alias file (f/d)\n\n"
- " AFPAttributes:\n"
- " y = System (f/d)\n"
- " w = No write (f)\n"
- " p = Needs backup (f/d)\n"
- " r = No rename (f/d)\n"
- " l = No delete (f/d)\n"
- " o = No copy (f)\n\n"
- " Note: any letter appearing in uppercase means the flag is set\n"
- " but it's a directory for which the flag is not allowed.\n"
- );
-}
-
-static void print_numlinks(const struct stat *statp)
-{
- printf("%5ld", (long)statp->st_nlink);
-}
-
-static void print_owner(const struct stat *statp)
-{
- struct passwd *pwd = getpwuid(statp->st_uid);
-
- if (pwd == NULL)
- printf(" %-8ld", (long)statp->st_uid);
- else
- printf(" %-8s", pwd->pw_name);
-}
-
-static void print_group(const struct stat *statp)
-{
- struct group *grp = getgrgid(statp->st_gid);
-
- if (grp == NULL)
- printf(" %-8ld", (long)statp->st_gid);
- else
- printf(" %-8s", grp->gr_name);
-}
-
-static void print_size(const struct stat *statp)
-{
- switch (statp->st_mode & S_IFMT) {
- case S_IFCHR:
- case S_IFBLK:
- printf("%4u,%4u", (unsigned)(statp->st_rdev >> 8),
- (unsigned)(statp->st_rdev & 0xFF));
- break;
- default:
- printf("%9lu", (unsigned long)statp->st_size);
- }
-}
-
-static void print_date(const struct stat *statp)
-{
- time_t now;
- double diff;
- char buf[100], *fmt;
-
- if (time(&now) == -1) {
- printf(" ????????????");
- return;
- }
- diff = difftime(now, statp->st_mtime);
- if (diff < 0 || diff > 60 * 60 * 24 * 182.5)
- fmt = "%b %e %Y";
- else
- fmt = "%b %e %H:%M";
- strftime(buf, sizeof(buf), fmt, localtime(&statp->st_mtime));
- printf(" %s", buf);
-}
-
-static void print_flags(char *path, afpvol_t *vol, const struct stat *st)
-{
- int adflags = 0;
- struct adouble ad;
- char *FinderInfo;
- uint16_t FinderFlags;
- uint16_t AFPattributes;
- char type[5] = "----";
- char creator[5] = "----";
- int i;
- uint32_t cnid;
-
- if (S_ISDIR(st->st_mode))
- adflags = ADFLAGS_DIR;
-
- if (vol->volinfo.v_path == NULL)
- return;
-
- ad_init(&ad, vol->volinfo.v_adouble, vol->volinfo.v_ad_options);
-
- if ( ad_metadata(path, adflags, &ad) < 0 )
- return;
-
- FinderInfo = ad_entry(&ad, ADEID_FINDERI);
-
- memcpy(&FinderFlags, FinderInfo + 8, 2);
- FinderFlags = ntohs(FinderFlags);
-
- memcpy(type, FinderInfo, 4);
- memcpy(creator, FinderInfo + 4, 4);
-
- ad_getattr(&ad, &AFPattributes);
- AFPattributes = ntohs(AFPattributes);
-
- /*
- Finder flags. Lowercase means valid, uppercase means invalid because
- object is a dir and flag is only valid for files.
- */
- putchar(' ');
- if (FinderFlags & FINDERINFO_ISONDESK)
- putchar('d');
- else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_HIDEEXT)
- putchar('e');
- else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_ISHARED) {
- if (adflags & ADFLAGS_DIR)
- putchar('M');
- else
- putchar('m');
- } else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_HASNOINITS) {
- if (adflags & ADFLAGS_DIR)
- putchar('N');
- else
- putchar('n');
- } else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_HASBEENINITED)
- putchar('i');
- else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_HASCUSTOMICON)
- putchar('c');
- else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_ISSTATIONNERY) {
- if (adflags & ADFLAGS_DIR)
- putchar('T');
- else
- putchar('t');
- } else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_NAMELOCKED)
- putchar('s');
- else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_HASBUNDLE)
- putchar('b');
- else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_INVISIBLE)
- putchar('v');
- else
- putchar('-');
-
- if (FinderFlags & FINDERINFO_ISALIAS)
- putchar('a');
- else
- putchar('-');
-
- putchar(' ');
-
- /* AFP attributes */
- if (AFPattributes & ATTRBIT_SYSTEM)
- putchar('y');
- else
- putchar('-');
-
- if (AFPattributes & ATTRBIT_NOWRITE) {
- if (adflags & ADFLAGS_DIR)
- putchar('W');
- else
- putchar('w');
- } else
- putchar('-');
-
- if (AFPattributes & ATTRBIT_BACKUP)
- putchar('p');
- else
- putchar('-');
-
- if (AFPattributes & ATTRBIT_NORENAME)
- putchar('r');
- else
- putchar('-');
-
- if (AFPattributes & ATTRBIT_NODELETE)
- putchar('l');
- else
- putchar('-');
-
- if (AFPattributes & ATTRBIT_NOCOPY) {
- if (adflags & ADFLAGS_DIR)
- putchar('O');
- else
- putchar('o');
- } else
- putchar('-');
-
- /* Color */
- printf(" %s ", labels[(FinderFlags & FINDERINFO_COLOR) >> 1]);
-
- /* Type & Creator */
- for(i=0; i<4; i++) {
- if (isalnum(type[i]))
- putchar(type[i]);
- else
- putchar('-');
- }
- putchar(' ');
- for(i=0; i<4; i++) {
- if (isalnum(creator[i]))
- putchar(creator[i]);
- else
- putchar('-');
- }
- putchar(' ');
-
- /* CNID */
- cnid = ad_forcegetid(&ad);
- if (cnid)
- printf(" %10u ", ntohl(cnid));
- else
- printf(" !ADVOL_CACHE ");
-
- ad_close_metadata(&ad);
-}
-
-#define TYPE(b) ((st->st_mode & (S_IFMT)) == (b))
-#define MODE(b) ((st->st_mode & (b)) == (b))
-
-static void print_mode(const struct stat *st)
-{
- if (TYPE(S_IFBLK))
- putchar('b');
- else if (TYPE(S_IFCHR))
- putchar('c');
- else if (TYPE(S_IFDIR))
- putchar('d');
- else if (TYPE(S_IFIFO))
- putchar('p');
- else if (TYPE(S_IFREG))
- putchar('-');
- else if (TYPE(S_IFLNK))
- putchar('l');
- else if (TYPE(S_IFSOCK))
- putchar('s');
- else
- putchar('?');
- putchar(MODE(S_IRUSR) ? 'r' : '-');
- putchar(MODE(S_IWUSR) ? 'w' : '-');
- if (MODE(S_ISUID)) {
- if (MODE(S_IXUSR))
- putchar('s');
- else
- putchar('S');
- }
- else if (MODE(S_IXUSR))
- putchar('x');
- else
- putchar('-');
- putchar(MODE(S_IRGRP) ? 'r' : '-');
- putchar(MODE(S_IWGRP) ? 'w' : '-');
- if (MODE(S_ISGID)) {
- if (MODE(S_IXGRP))
- putchar('s');
- else
- putchar('S');
- }
- else if (MODE(S_IXGRP))
- putchar('x');
- else
- putchar('-');
- putchar(MODE(S_IROTH) ? 'r' : '-');
- putchar(MODE(S_IWOTH) ? 'w' : '-');
- if (MODE(S_IFDIR) && MODE(S_ISVTX)) {
- if (MODE(S_IXOTH))
- putchar('t');
- else
- putchar('T');
- }
- else if (MODE(S_IXOTH))
- putchar('x');
- else
- putchar('-');
-}
-#undef TYPE
-#undef MODE
-
-static int ad_print(char *path, const struct stat *st, afpvol_t *vol)
-{
- if ( ! ls_l) {
- printf("%s ", path);
- if (ls_d)
- printf("\n");
- return 0;
- }
-
- /* Long output */
- if (ls_u) {
- print_mode(st);
- print_numlinks(st);
- print_owner(st);
- print_group(st);
- print_size(st);
- print_date(st);
- }
- print_flags(path, vol, st);
- printf(" %s\n", path);
-
-
- return 0;
-}
-
-static int ad_ls_r(char *path, afpvol_t *vol)
-{
- int ret = 0, cwd, dirprinted = 0, dirempty;
- const char *name;
- char *tmp;
- static char cwdpath[MAXPATHLEN+1];
- DIR *dp;
- struct dirent *ep;
- static struct stat st; /* Save some stack space */
-
- if ( first)
- cwdpath[0] = 0;
- else
- strcat(cwdpath, "/");
-
- strcat(cwdpath, path);
- first = 0;
-
- if (lstat(path, &st) < 0) {
- perror("Can't stat");
- return -1;
- }
- /* If its a file or a dir with -d option call ad_print and return */
- if (S_ISREG(st.st_mode) || ls_d)
- return ad_print(path, &st, vol);
-
- /* Its a dir: chdir to it remembering where we started */
- if ((cwd = open(".", O_RDONLY)) == -1) {
- perror("Cant open .");
- return -1;
- }
- if (chdir(path) != 0) {
- perror("Cant chdir");
- close(cwd);
- return -1;
- }
-
- if ((dp = opendir (".")) == NULL) {
- perror("Couldn't opendir .");
- return -1;
- }
-
- /* First run: print everything */
- dirempty = 1;
- while ((ep = readdir (dp))) {
- /* Check if its "." or ".." */
- if (DIR_DOT_OR_DOTDOT(ep->d_name))
- continue;
-
- /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
- if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
- continue;
-
- if ((ep->d_name[0] == '.') && ! ls_a)
- continue;
-
- dirempty = 0;
-
- if (recursion && ! dirprinted) {
- printf("\n%s:\n", cwdpath);
- dirprinted = 1;
- }
-
- if (lstat(ep->d_name, &st) < 0) {
- perror("Can't stat");
- return -1;
- }
-
- ret = ad_print(ep->d_name, &st, vol);
- if (ret != 0)
- goto exit;
- }
-
- if (! ls_l && ! dirempty)
- printf("\n");
-
- /* Second run: recurse to dirs */
- if (ls_R) {
- rewinddir(dp);
- while ((ep = readdir (dp))) {
- /* Check if its "." or ".." */
- if (DIR_DOT_OR_DOTDOT(ep->d_name))
- continue;
-
- /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
- if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
- continue;
-
- if ((ret = lstat(ep->d_name, &st)) < 0) {
- perror("Can't stat");
- goto exit;
- }
-
- /* Recursion */
- if (S_ISDIR(st.st_mode)) {
- recursion = 1;
- ret = ad_ls_r(ep->d_name, vol);
- }
- if (ret != 0)
- goto exit;
- }
- }
-
-exit:
- closedir(dp);
- fchdir(cwd);
- close(cwd);
-
- tmp = strrchr(cwdpath, '/');
- if (tmp)
- *tmp = 0;
-
- return ret;
-}
-
-int ad_ls(int argc, char **argv)
-{
- int c, firstarg;
- afpvol_t vol;
- struct stat st;
-
- while ((c = getopt(argc, argv, ":adlRu")) != -1) {
- switch(c) {
- case 'a':
- ls_a = 1;
- break;
- case 'd':
- ls_d = 1;
- break;
- case 'l':
- ls_l = 1;
- break;
- case 'R':
- ls_R = 1;
- break;
- case 'u':
- ls_u = 1;
- break;
- case ':':
- case '?':
- usage_ls();
- return -1;
- break;
- }
-
- }
-
- if ((argc - optind) == 0) {
- newvol(".", &vol);
- ad_ls_r(".", &vol);
- freevol(&vol);
- }
- else {
- int havefile = 0;
-
- firstarg = optind;
-
- /* First run: only print files from argv paths */
- while(optind < argc) {
- if (stat(argv[optind], &st) != 0)
- goto next;
- if (S_ISDIR(st.st_mode))
- goto next;
-
- havefile = 1;
- first = 1;
- recursion = 0;
-
- newvol(argv[optind], &vol);
- ad_ls_r(argv[optind], &vol);
- freevol(&vol);
- next:
- optind++;
- }
- if (havefile && (! ls_l))
- printf("\n");
-
- /* Second run: print dirs */
- optind = firstarg;
- while(optind < argc) {
- if (stat(argv[optind], &st) != 0)
- goto next2;
- if ( ! S_ISDIR(st.st_mode))
- goto next2;
- if ((optind > firstarg) || havefile)
- printf("\n%s:\n", argv[optind]);
-
- first = 1;
- recursion = 0;
-
- newvol(argv[optind], &vol);
- ad_ls_r(argv[optind], &vol);
- freevol(&vol);
-
- next2:
- optind++;
- }
-
-
- }
-
- return 0;
-}
+++ /dev/null
-/*
- $Id: ad_util.c,v 1.2 2009-10-14 02:30:42 didg Exp $
-
- Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <libgen.h>
-
-#include <atalk/cnid.h>
-#include <atalk/volinfo.h>
-#include "ad.h"
-
-
-int newvol(const char *path, afpvol_t *vol)
-{
-// char *pathdup;
-
- memset(vol, 0, sizeof(afpvol_t));
-
-// pathdup = strdup(path);
-// vol->dirname = strdup(dirname(pathdup));
-// free(pathdup);
-
-// pathdup = strdup(path);
-// vol->basename = strdup(basename(pathdup));
-// free(pathdup);
-
- loadvolinfo((char *)path, &vol->volinfo);
-
- return 0;
-}
-
-void freevol(afpvol_t *vol)
-{
-#if 0
- if (vol->dirname) {
- free(vol->dirname);
- vol->dirname = NULL;
- }
- if (vol->basename) {
- free(vol->basename);
- vol->basename = NULL;
- }
-#endif
-}
Makefile
Makefile.in
netacnv
+afpldaptest
+logger_test
.deps
.libs
*.o
-afpldaptest
-.gitignore
-netacnv.o
+
+
# Makefile.am for bin/misc
-bin_PROGRAMS = netacnv
+pkgconfdir = @PKGCONFDIR@
+bin_PROGRAMS =
+
+noinst_PROGRAMS = netacnv logger_test
netacnv_SOURCES = netacnv.c
netacnv_LDADD = $(top_builddir)/libatalk/libatalk.la
-pkgconfdir = @PKGCONFDIR@
-if USE_NFSv4_ACLS
-bin_PROGRAMS += afpldaptest
+logger_test_SOURCES = logger_test.c
+logger_test_LDADD = $(top_builddir)/libatalk/libatalk.la
+bin_PROGRAMS += afpldaptest
afpldaptest_SOURCES = uuidtest.c
+afpldaptest_CFLAGS = -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\"
afpldaptest_LDADD = $(top_builddir)/libatalk/libatalk.la
-AM_CFLAGS = -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\"
-endif
--- /dev/null
+#include <stdio.h>
+
+#include <atalk/boolean.h>
+#include <atalk/logger.h>
+
+int main(int argc, char *argv[])
+{
+ set_processname("logger_Test");
+#if 0
+ LOG(log_severe, logtype_logger, "Logging Test starting: this should only log to syslog");
+
+ /* syslog testing */
+ LOG(log_severe, logtype_logger, "Disabling syslog logging.");
+ unsetuplog("Default");
+ LOG(log_error, logtype_default, "This shouldn't log to syslog: LOG(log_error, logtype_default).");
+ LOG(log_error, logtype_logger, "This shouldn't log to syslog: LOG(log_error, logtype_logger).");
+ setuplog("Default LOG_INFO");
+ LOG(log_info, logtype_logger, "Set syslog logging to 'log_info', so this should log again. LOG(log_info, logtype_logger).");
+ LOG(log_error, logtype_logger, "This should log to syslog: LOG(log_error, logtype_logger).");
+ LOG(log_error, logtype_default, "This should log to syslog. LOG(log_error, logtype_default).");
+ LOG(log_debug, logtype_logger, "This shouldn't log to syslog. LOG(log_debug, logtype_logger).");
+ LOG(log_debug, logtype_default, "This shouldn't log to syslog. LOG(log_debug, logtype_default).");
+ LOG(log_severe, logtype_logger, "Disabling syslog logging.");
+ unsetuplog("Default");
+#endif
+ /* filelog testing */
+
+ setuplog("DSI log_maxdebug test.log");
+ LOG(log_info, logtype_dsi, "This should log.");
+ LOG(log_error, logtype_default, "This should not log.");
+
+ setuplog("Default log_debug test.log");
+ LOG(log_debug, logtype_default, "This should log.");
+ LOG(log_maxdebug, logtype_default, "This should not log.");
+
+ LOG(log_maxdebug, logtype_dsi, "This should still log.");
+
+ /* flooding prevention check */
+ LOG(log_debug, logtype_default, "Flooding 3x");
+ for (int i = 0; i < 3; i++) {
+ LOG(log_debug, logtype_default, "Flooding...");
+ }
+ /* wipe the array */
+ LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");
+
+ LOG(log_debug, logtype_default, "-============");
+ LOG(log_debug, logtype_default, "Flooding 5x");
+ for (int i = 0; i < 5; i++) {
+ LOG(log_debug, logtype_default, "Flooding...");
+ }
+ LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");
+
+ LOG(log_debug, logtype_default, "o============");
+ LOG(log_debug, logtype_default, "Flooding 2005x");
+ for (int i = 0; i < 2005; i++) {
+ LOG(log_debug, logtype_default, "Flooding...");
+ }
+ LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");
+
+ LOG(log_debug, logtype_default, "0============");
+ LOG(log_debug, logtype_default, "Flooding 11x1");
+ for (int i = 0; i < 11; i++) {
+ LOG(log_error, logtype_default, "flooding 11x1 1");
+ }
+
+ LOG(log_debug, logtype_default, "1============");
+ LOG(log_debug, logtype_default, "Flooding 11x2");
+ for (int i = 0; i < 11; i++) {
+ LOG(log_error, logtype_default, "flooding 11x2 1");
+ LOG(log_error, logtype_default, "flooding 11x2 2");
+ }
+
+ LOG(log_debug, logtype_default, "2============");
+ LOG(log_debug, logtype_default, "Flooding 11x3");
+ for (int i = 0; i < 11; i++) {
+ LOG(log_error, logtype_default, "flooding 11x3 1");
+ LOG(log_error, logtype_default, "flooding 11x3 2");
+ LOG(log_error, logtype_default, "flooding 11x3 3");
+ }
+
+ LOG(log_debug, logtype_default, "3============");
+ LOG(log_debug, logtype_default, "Flooding 11x4");
+ for (int i = 0; i < 11; i++) {
+ LOG(log_error, logtype_default, "flooding 11x4 1");
+ LOG(log_error, logtype_default, "flooding 11x4 2");
+ LOG(log_error, logtype_default, "flooding 11x4 3");
+ LOG(log_error, logtype_default, "flooding 11x4 4");
+ }
+
+
+ return 0;
+}
+
+
/*
- $Id: uuidtest.c,v 1.3 2009-11-28 12:27:24 franklahm Exp $
Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include "config.h"
#endif /* HAVE_CONFIG_H */
-#ifdef HAVE_NFSv4_ACLS
-
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
+
+#ifdef HAVE_LDAP
+#define LDAP_DEPRECATED 1
#include <ldap.h>
+#endif
#include <atalk/ldapconfig.h>
#include <atalk/uuid.h>
static int inited = 0;
if (! inited) {
+#ifdef HAVE_LDAP
/* Parse afp_ldap.conf */
printf("Start parsing afp_ldap.conf\n");
acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
exit(EXIT_FAILURE);
}
} else {
- printf("afp_ldap.conf is not ok.\n");
- exit(EXIT_FAILURE);
+ printf("afp_ldap.conf is not ok, not using LDAP. Only local UUID testing available.\n");
}
+#else
+ printf("Built without LDAP support, only local UUID testing available.\n");
+#endif
inited = 1;
}
}
int main( int argc, char **argv)
{
- int ret, i, c;
+ int ret, c;
int verbose = 0;
+ atalk_uuid_t uuid;
int logsetup = 0;
- uuid_t uuid;
uuidtype_t type;
- char *uuidstring = NULL;
char *name = NULL;
while ((c = getopt(argc, argv, ":vu:g:i:")) != -1) {
printf("Searching user: %s\n", optarg);
ret = getuuidfromname( optarg, UUID_USER, uuid);
if (ret == 0) {
- uuid_bin2string( uuid, &uuidstring);
- printf("User: %s ==> UUID: %s\n", optarg, uuidstring);
- free(uuidstring);
+ printf("User: %s ==> UUID: %s\n", optarg, uuid_bin2string(uuid));
} else {
printf("User %s not found.\n", optarg);
}
printf("Searching group: %s\n", optarg);
ret = getuuidfromname( optarg, UUID_GROUP, uuid);
if (ret == 0) {
- uuid_bin2string( uuid, &uuidstring);
- printf("Group: %s ==> UUID: %s\n", optarg, uuidstring);
- free(uuidstring);
+ printf("Group: %s ==> UUID: %s\n", optarg, uuid_bin2string(uuid));
} else {
printf("Group %s not found.\n", optarg);
}
uuid_string2bin(optarg, uuid);
ret = getnamefromuuid( uuid, &name, &type);
if (ret == 0) {
- if (type == UUID_USER)
+ switch (type) {
+ case UUID_LOCAL:
+ printf("local UUID: %s\n", optarg);
+ break;
+ case UUID_USER:
printf("UUID: %s ==> User: %s\n", optarg, name);
- else
+ break;
+ case UUID_GROUP:
printf("UUID: %s ==> Group: %s\n", optarg, name);
+ break;
+ }
free(name);
} else {
printf("UUID: %s not found.\n", optarg);
return 0;
}
-#endif /* HAVE_NFSv4_ACLS */
# illegalseq -> encode illegal sequence in filename asis,
# ex "\217-", which is not a valid SHIFT-JIS char,
# is encoded as U\217 -
-# acls -> Enable ACLs on this volume. Requires a NFSv4 ACLs
-# compatible filesystem (e.g. ZFS) and an ACL API
-# compatible to *Solaris. In other words: this requires
-# Solaris, Opensolaris or a derived distribution.
# nocnidcache -> Don't store and read CNID to/from AppleDouble file.
# This should not be used as it also prevents a CNID
# database rebuild with `dbd`!
SUBDIRS = pam
SUFFIXES = .tmpl .
-GENFILES = afpd.conf AppleVolumes.default
TMPLFILES = afpd.conf.tmpl AppleVolumes.default.tmpl
+GENFILES = afpd.conf AppleVolumes.default
+CLEANFILES = $(GENFILES)
+EXTRA_DIST = \
+ AppleVolumes.default.tmpl \
+ AppleVolumes.system \
+ afp_ldap.conf \
+ afpd.conf.tmpl \
+ atalkd.conf \
+ netatalk.conf \
+ papd.conf
+
+OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
if USE_DEBIAN
-CONFFILES = AppleVolumes.system atalkd.conf papd.conf
+CONFFILES = AppleVolumes.system
else
-CONFFILES = AppleVolumes.system atalkd.conf papd.conf netatalk.conf
+CONFFILES = AppleVolumes.system netatalk.conf
endif
-if USE_NFSv4_ACLS
+if HAVE_ACLS
CONFFILES += afp_ldap.conf
endif
-OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
-EXTRA_DIST = $(CONFFILES) $(TMPLFILES) afp_ldap.conf
-CLEANFILES = $(GENFILES)
+if USE_APPLETALK
+CONFFILES += atalkd.conf papd.conf
+endif
pkgconfdir = @PKGCONFDIR@
webminpath = @WEBMIN_PATH@
# string.
# -slp Register this server with the Service Location
# Protocol (if SLP support was compiled in).
+# -nozeroconf Don't register this server with the Multicats
+# DNS Protocol.
# -advertise_ssh Allows Mac OS X clients (10.3.3-10.4) to
# automagically establish a tunneled AFP connection
# through SSH. This option is not so significant
# Netatalk configuration
-# Change this to increase the maximum number of clients that can connect:
-AFPD_MAX_CLIENTS=20
-# Change this to set the machine's atalk name and zone.
-# NOTE: if your zone has spaces in it, you're better off specifying
-# it in afpd.conf
-#ATALK_ZONE=@zone
-ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
+#########################################################################
+# Global configuration
+#########################################################################
-# specify the Mac and unix charsets to be used
-ATALK_MAC_CHARSET='MAC_ROMAN'
+#### machine's AFPserver/AppleTalk name.
+#ATALK_NAME=machinename
+
+#### server (unix) and legacy client (<= Mac OS 9) charsets
ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+#### Don't Edit. export the charsets, read form ENV by apps
+export ATALK_UNIX_CHARSET
+export ATALK_MAC_CHARSET
+
+#########################################################################
+# AFP specific configuration
+#########################################################################
-# specify the UAMs to enable
-# available options: uams_guest.so, uams_clrtxt.so, uams_randnum.so,
-# uams_dhx.so, uams_dhx2.so
-# AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
-
-# Change this to set the id of the guest user
-AFPD_GUEST=nobody
-
-# Set which daemons to run.
-# If you need legacy AppleTalk, run atalkd.
-# papd, timelord and a2boot are dependent upon atalkd.
-# If you use "AFP over TCP" server only, run only cnid_metad and afpd.
-ATALKD_RUN=no
-PAPD_RUN=no
-TIMELORD_RUN=no
-A2BOOT_RUN=no
+#### Set which daemons to run.
+#### If you use AFP file server, run both cnid_metad and afpd.
CNID_METAD_RUN=yes
AFPD_RUN=yes
-# Control whether the daemons are started in the background.
-# If it is dissatisfied that atalkd starts slowly, set "yes".
-ATALK_BGROUND=no
+#### maximum number of clients that can connect:
+#AFPD_MAX_CLIENTS=20
-# export the charsets, read form ENV by apps
-export ATALK_MAC_CHARSET
-export ATALK_UNIX_CHARSET
+#### UAMs (User Authentication Modules)
+#### available options: uams_dhx.so, uams_dhx2.so, uams_guest.so,
+#### uams_clrtxt.so(legacy), uams_randnum.so(legacy)
+#AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+
+#### Set the id of the guest user when using uams_guest.so
+#AFPD_GUEST=nobody
+
+#### config for cnid_metad. Default log config:
+#CNID_CONFIG="-l log_note"
-# config for cnid_metad. Default log config:
-# CNID_CONFIG="-l log_note"
+#########################################################################
+# AppleTalk specific configuration (legacy)
+#########################################################################
+
+#### Set which legacy daemons to run.
+#### If you need AppleTalk, run atalkd.
+#### papd, timelord and a2boot are dependent upon atalkd.
+#ATALKD_RUN=no
+#PAPD_RUN=no
+#TIMELORD_RUN=no
+#A2BOOT_RUN=no
+
+#### Control whether the daemons are started in the background.
+#### If it is dissatisfied that legacy atalkd starts slowly, set "yes".
+#ATALK_BGROUND=no
+
+#### Set the AppleTalk Zone name.
+#### NOTE: if your zone has spaces in it, you're better off specifying
+#### it in afpd.conf
+#ATALK_ZONE=@zone
-dnl $Id: configure.in,v 1.244 2010-04-13 08:05:06 franklahm Exp $
dnl configure.in for netatalk
AC_INIT(etc/afpd/main.c)
AM_PROG_CC_C_O
-dnl *********************************************************************
-dnl FIXME! FIXME! These should be selectable properly, and should produce
-dnl the proper flags and defines...
-dnl *********************************************************************
-
-############################################
-# we need dlopen/dlclose/dlsym/dlerror for PAM, the password database plugins and the plugin loading code
-#AC_SEARCH_LIBS(dlopen, [dl])
-# dlopen/dlclose/dlsym/dlerror will be checked again later and defines will be set then
-
-dnl Checks for libraries.
-dnl Replace `main' with a function in -labs:
-dnl AC_CHECK_LIB(abs, main)
-dnl Replace `main' with a function in -laudit:
-dnl AC_CHECK_LIB(audit, main)
-dnl Replace `main' with a function in -lauth:
-dnl AC_CHECK_LIB(auth, main)
-dnl Replace `main' with a function in -lcmd:
-dnl AC_CHECK_LIB(cmd, main)
-dnl Replace `main' with a function in -lcrypt:
-dnl AC_CHECK_LIB(crypt, main)
-dnl Replace `main' with a function in -ld:
-dnl AC_CHECK_LIB(d, main)
-dnl Replace `main' with a function in -ldl:
-dnl AC_CHECK_LIB(dl, dlopen)
-dnl Replace `main' with a function in -lkauth:
-dnl AC_CHECK_LIB(kauth, main)
-dnl Replace `main' with a function in -lkrb:
-dnl AC_CHECK_LIB(krb, main)
-dnl Replace `main' with a function in -llwp:
-dnl AC_CHECK_LIB(lwp, main)
-dnl Replace `main' with a function in -ln:
-dnl AC_CHECK_LIB(n, main)
-
-dnl not the right stuff but should be enough for now
-AC_CHECK_FUNC(gethostbyname,,[AC_CHECK_LIB(nsl,gethostbyname)])
-AC_CHECK_FUNC(connect,,[AC_CHECK_LIB(socket,connect)])
-
-dnl Replace `main' with a function in -lprot:
-dnl AC_CHECK_LIB(prot, main)
-dnl Replace `main' with a function in -lrx:
-dnl AC_CHECK_LIB(rx, main)
-dnl Replace `main' with a function in -lrxkad:
-dnl AC_CHECK_LIB(rxkad, main)
-dnl Replace `main' with a function in -lsys:
-dnl AC_CHECK_LIB(sys, main)
-dnl Replace `main' with a function in -lubik:
-dnl AC_CHECK_LIB(ubik, main)
-
-
-#
-# Check presence of some functions
-#
-# Check for XPG4 access() function
-# Be sure to test before adding AFS libs in LIBS path as AFS lib
-# has such a function that works only on AFS filesystems.
-AC_CHECK_FUNCS(access)
-#
-AC_CHECK_FUNCS(pread pwrite)
-
dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS(fcntl.h limits.h stdint.h strings.h time.h sys/param.h sys/fcntl.h sys/file.h sys/ioctl.h sys/time.h sys/mnttab.h sys/statvfs.h sys/stat.h sys/vfs.h mntent.h syslog.h unistd.h termios.h sys/termios.h netdb.h sgtty.h ufs/quota.h mount.h statfs.h sys/types.h dlfcn.h errno.h sys/errno.h sys/uio.h)
+AC_CHECK_HEADERS(fcntl.h limits.h stdint.h strings.h time.h sys/param.h sys/fcntl.h sys/file.h sys/ioctl.h sys/time.h sys/mnttab.h sys/statvfs.h sys/stat.h sys/vfs.h mntent.h syslog.h unistd.h termios.h sys/termios.h netdb.h sgtty.h ufs/quota.h mount.h statfs.h sys/types.h dlfcn.h errno.h sys/errno.h sys/uio.h langinfo.h locale.h sys/filio.h)
AC_CHECK_HEADER(sys/cdefs.h,,
AC_MSG_RESULT([enabling generic cdefs.h from tree])
CFLAGS="-I\$(top_srcdir)/sys/generic $CFLAGS"
#endif
])
-AC_CHECK_HEADERS(langinfo.h locale.h sys/filio.h)
-
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_UID_T
AC_DEFINE(DLSYM_PREPEND_UNDERSCORE, 1, [BSD compatibility macro])
fi
-
dnl Checks for library functions.
AC_TYPE_GETGROUPS
AC_PROG_GCC_TRADITIONAL
AC_FUNC_UTIME_NULL
AC_FUNC_WAIT3
AC_CHECK_FUNCS(getcwd gethostname gettimeofday getusershell mkdir rmdir select socket strdup strcasestr strstr strtoul strchr memcpy)
-AC_CHECK_FUNCS(backtrace_symbols setlocale nl_langinfo strlcpy strlcat setlinebuf dirfd pselect)
+AC_CHECK_FUNCS(backtrace_symbols setlocale nl_langinfo strlcpy strlcat setlinebuf dirfd pselect access pread pwrite)
AC_CHECK_FUNCS(waitpid getcwd strdup strndup strnlen strtoul strerror chown fchown chmod fchmod chroot link mknod mknod64)
AC_CHECK_FUNC(renameat, AC_DEFINE([_ATFILE_SOURCE], 1, AT file source))
AC_CHECK_MEMBERS(struct tm.tm_gmtoff,,, [#include <time.h>])
+
+AC_CHECK_FUNC(gethostbyname,,[AC_CHECK_LIB(nsl,gethostbyname)])
+AC_CHECK_FUNC(connect,,[AC_CHECK_LIB(socket,connect)])
+dnl search for necessary libs for libpthread stuff
+AC_SEARCH_LIBS(pthread_sigmask, pthread,,
+ [AC_MSG_ERROR([cannot find pthread_sigmask in libc or libpthread])])
+if test x"$ac_cv_search_pthread_sigmask" != x"none required" ; then
+ PTHREAD_LIBS=$ac_cv_search_pthread_sigmask
+fi
+AC_SUBST(PTHREAD_LIBS)
AC_CACHE_SAVE
dnl Checks for (v)snprintf
dnl --------------------------------------------------------------------------
AC_MSG_CHECKING([whether to check for 64bit libraries])
-dnl Determine libdir name
-case $host in
-*-*-linux*)
- # Test if the compiler is 64bit
- echo 'int i;' > conftest.$ac_ext
- atalk_cv_cc_64bit_output=no
- if AC_TRY_EVAL(ac_compile); then
+# Test if the compiler is in 64bit mode
+echo 'int i;' > conftest.$ac_ext
+atalk_cv_cc_64bit_output=no
+if AC_TRY_EVAL(ac_compile); then
case `/usr/bin/file conftest.$ac_objext` in
*"ELF 64"*)
atalk_cv_cc_64bit_output=yes
;;
esac
- fi
- rm -rf conftest*
- ;;
-esac
-
-dnl
-dnl FIXME: Do we need something like this for Solaris 64bit?
-dnl
+fi
+rm -rf conftest*
case $host_cpu:$atalk_cv_cc_64bit_output in
-powerpc64:yes | s390x:yes | sparc64:yes | x86_64:yes)
- atalk_libname="lib64"
- AC_MSG_RESULT([yes])
- ;;
+powerpc64:yes | s390x:yes | sparc*:yes | x86_64:yes | i386:yes)
+ AC_MSG_RESULT([yes])
+ case $target_os in
+ solaris2*)
+ atalk_libname="lib/64"
+ ;;
+ *)
+ atalk_libname="lib64"
+ ;;
+ esac
+ ;;
*:*)
- atalk_libname="lib"
- AC_MSG_RESULT([no])
- ;;
+ AC_MSG_RESULT([no])
+ atalk_libname="lib"
+ ;;
esac
dnl --------------------------------------------------------------------------
AC_MSG_CHECKING([for cracklib support])
AC_MSG_RESULT([$netatalk_cv_with_cracklib])
-netatalk_cv_ddp_enabled=yes
+netatalk_cv_ddp_enabled=no
AC_MSG_CHECKING([whether to enable DDP])
AC_ARG_ENABLE(ddp,
- [ --disable-ddp disable DDP],[
- if test "$enableval" = "no"; then
- AC_DEFINE(NO_DDP, 1, [Define if DDP should be disabled])
- AC_MSG_RESULT([no])
- netatalk_cv_ddp_enabled=no
+ [ --enable-ddp enable DDP (AppleTalk)],[
+ if test "$enableval" = "yes"; then
+ AC_MSG_RESULT([yes])
+ netatalk_cv_ddp_enabled=yes
else
AC_MSG_RESULT([yes])
+ AC_DEFINE(NO_DDP, 1, [Define if DDP should be disabled])
fi
],[
- AC_MSG_RESULT([yes])
+ AC_MSG_RESULT([no])
+ AC_DEFINE(NO_DDP, 1, [Define if DDP should be disabled])
]
)
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
+ AC_DEFINE(NDEBUG, 1, [Disable assertions])
fi
],[
AC_MSG_RESULT([no])
+ AC_DEFINE(NDEBUG, 1, [Disable assertions])
]
)
]
)
-
-afp3=no
-afp3set=no
-AC_MSG_CHECKING([whether AFP 3.x calls should be enabled])
-AC_ARG_ENABLE(afp3,
- [ --disable-afp3 disable AFP 3.x calls],
- [
- if test x"$enableval" != x"no"; then
- afp3set=yes
- afp3=yes
- AC_MSG_RESULT([yes])
- else
- AC_MSG_RESULT([no])
- fi
- ],[
- AC_MSG_RESULT([yes])
- afp3=yes
- ]
-)
-
-if test x"$afp3" = x"yes"; then
- AC_SYS_LARGEFILE([
- AC_DEFINE(AFP3x, 1, [Define to enable AFP 3.x support])
- ],[
- if test x"$afp3set" = x"yes"; then
- AC_MSG_ERROR([AFP 3.x support requires Large File Support.])
- else
- AC_MSG_WARN([AFP 3.x support requires Large File Support. AFP3.x support disabled])
- afp3=no
- fi
- ])
-fi
-
+AC_SYS_LARGEFILE([], AC_MSG_ERROR([AFP 3.x support requires Large File Support.]))
AC_CHECK_ICONV
dnl ----------- A NOTE ABOUT DROPKLUDGE
dnl Check for optional server location protocol support (used by MacOS X)
NETATALK_SRVLOC
+dnl Check for optional Zeroconf support
+NETATALK_ZEROCONF
+
dnl Check for PAM libs
netatalk_cv_use_pam=no
AC_PATH_PAM([
)
AC_MSG_RESULT([$OVERWRITE_CONFIG])
-dnl --------------------- check for ACL support
-AC_MSG_CHECKING([if NFSv4 ACL Support should be enabled])
-AC_ARG_ENABLE(nfsv4acls,
- [ --enable-nfsv4acls enable NFSv4 ACL Support (auto)],[
- if test x"$enableval" = x"yes"; then
- AC_MSG_RESULT([yes])
- neta_cv_nfsv4acl="yes"
- else
- AC_MSG_RESULT([no])
- neta_cv_nfsv4acl="no"
- fi],[
- AC_MSG_RESULT([auto])
- neta_cv_nfsv4acl="yes"
- ]
-)
+dnl --------------------- check for LDAP support, for client-side ACL visibility
+AC_MSG_CHECKING(for LDAP (necessary for client-side ACL visibility))
+AC_ARG_WITH(ldap,
+ [AS_HELP_STRING([--with-ldap],
+ [LDAP support (default=auto)])],
+ [ case "$withval" in
+ yes|no)
+ with_ldap="$withval"
+ ;;
+ *)
+ with_ldap=auto
+ ;;
+ esac ])
+AC_MSG_RESULT($with_ldap)
+
+if test x"$with_ldap" != x"no" ; then
+ AC_CHECK_HEADER([ldap.h], with_ldap=yes,
+ [ if test x"$with_ldap" = x"yes" ; then
+ AC_MSG_ERROR([Missing LDAP headers])
+ fi
+ with_ldap=no
+ ])
+ AC_CHECK_LIB(ldap, ldap_init, with_ldap=yes,
+ [ if test x"$with_ldap" = x"yes" ; then
+ AC_MSG_ERROR([Missing LDAP library])
+ fi
+ with_ldap=no
+ ])
+fi
-if test x"$this_os" != x"solaris" && test x"$neta_cv_nfsv4acl" = x"yes" ; then
- AC_MSG_NOTICE([NFSv4 ACL Support only available on (Open)Solaris])
- neta_cv_nfsv4acl="no"
+if test x"$with_ldap" = x"yes"; then
+ AC_DEFINE(HAVE_LDAP,1,[Whether LDAP is available])
fi
-if test x$neta_cv_nfsv4acl = xyes; then
- AC_CHECK_HEADER([ldap.h],,[
- AC_MSG_ERROR([ACL Support need the LDAP client headers not found.])
- neta_cv_nfsv4acl=no
- ]
- )
- AC_CHECK_LIB(ldap,ldap_init,neta_cv_nfsv4acl=yes,neta_cv_nfsv4acl=no)
+dnl --------------------- check for ACL support
+AC_MSG_CHECKING(whether to support ACLs)
+AC_ARG_WITH(acls,
+ [AS_HELP_STRING([--with-acls],
+ [Include ACL support (default=auto)])],
+ [ case "$withval" in
+ yes|no)
+ with_acl_support="$withval"
+ ;;
+ *)
+ with_acl_support=auto
+ ;;
+ esac ],
+ [with_acl_support=auto])
+AC_MSG_RESULT($with_acl_support)
+
+if test x"$with_acl_support" = x"no"; then
+ AC_MSG_RESULT(Disabling ACL support)
+ AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support should be built in])
+else
+ with_acl_support=yes
fi
-if test x$neta_cv_nfsv4acl = xyes; then
- AC_CHECK_HEADER([sys/acl.h],[
- AC_DEFINE([HAVE_NFSv4_ACLS], 1, [Enable ACL code])
- AC_MSG_NOTICE([Enabling ACL support])
- ],
- neta_cv_nfsv4acl=no
- )
+
+if test x"$with_acl_support" = x"yes" ; then
+ AC_MSG_NOTICE(checking whether ACL support is available:)
+ case "$host_os" in
+ *sysv5*)
+ AC_MSG_NOTICE(Using UnixWare ACLs)
+ AC_DEFINE(HAVE_UNIXWARE_ACLS,1,[Whether UnixWare ACLs are available])
+ ;;
+ *solaris*)
+ AC_MSG_NOTICE(Using solaris ACLs)
+ AC_DEFINE(HAVE_SOLARIS_ACLS,1,[Whether solaris ACLs are available])
+ ACL_LIBS="$ACL_LIBS -lsec"
+ ;;
+ *hpux*)
+ AC_MSG_NOTICE(Using HPUX ACLs)
+ AC_DEFINE(HAVE_HPUX_ACLS,1,[Whether HPUX ACLs are available])
+ ;;
+ *irix*)
+ AC_MSG_NOTICE(Using IRIX ACLs)
+ AC_DEFINE(HAVE_IRIX_ACLS,1,[Whether IRIX ACLs are available])
+ ;;
+ *aix*)
+ AC_MSG_NOTICE(Using AIX ACLs)
+ AC_DEFINE(HAVE_AIX_ACLS,1,[Whether AIX ACLs are available])
+ ;;
+ *osf*)
+ AC_MSG_NOTICE(Using Tru64 ACLs)
+ AC_DEFINE(HAVE_TRU64_ACLS,1,[Whether Tru64 ACLs are available])
+ ACL_LIBS="$ACL_LIBS -lpacl"
+ ;;
+ *darwin*)
+ AC_MSG_NOTICE(ACLs on Darwin currently not supported)
+ AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+ ;;
+ *)
+ AC_CHECK_LIB(acl,acl_get_file,[ACL_LIBS="$ACL_LIBS -lacl"])
+ case "$host_os" in
+ *linux*)
+ AC_CHECK_LIB(attr,getxattr,[ACL_LIBS="$ACL_LIBS -lattr"])
+ ;;
+ esac
+ AC_CACHE_CHECK([for POSIX ACL support],netatalk_cv_HAVE_POSIX_ACLS,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS $ACL_LIBS"
+ AC_TRY_LINK([
+ #include <sys/types.h>
+ #include <sys/acl.h>
+ ],[
+ acl_t acl;
+ int entry_id;
+ acl_entry_t *entry_p;
+ return acl_get_entry(acl, entry_id, entry_p);
+ ],
+ [netatalk_cv_HAVE_POSIX_ACLS=yes],
+ [netatalk_cv_HAVE_POSIX_ACLS=no
+ with_acl_support=no])
+ LIBS=$acl_LIBS
+ ])
+ if test x"$netatalk_cv_HAVE_POSIX_ACLS" = x"yes"; then
+ AC_MSG_NOTICE(Using POSIX ACLs)
+ AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
+ AC_CACHE_CHECK([for acl_get_perm_np],netatalk_cv_HAVE_ACL_GET_PERM_NP,[
+ acl_LIBS=$LIBS
+ LIBS="$LIBS $ACL_LIBS"
+ AC_TRY_LINK([
+ #include <sys/types.h>
+ #include <sys/acl.h>
+ ],[
+ acl_permset_t permset_d;
+ 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])
+ 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
+ else
+ AC_MSG_NOTICE(ACL support is not avaliable)
+ AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+ fi
+ ;;
+ esac
fi
-if test x$neta_cv_nfsv4acl = xyes; then
- LIBATALK_ACLS="acl/libacl.la"
-else
- LIBATALK_ACLS=""
+
+if test x"$with_acl_support" = x"yes" ; then
+ AC_DEFINE(HAVE_ACLS,1,[Whether ACLs support is available])
+ AC_SUBST(ACL_LIBS)
fi
-AC_SUBST(LIBATALK_ACLS)
dnl --------------------- check for Extended Attributes support
neta_cv_eas="ad"
neta_cv_eas="$neta_cv_eas | sys"
fi
fi
+AC_DEFINE_UNQUOTED(EA_MODULES,["$neta_cv_eas"],[Available Extended Attributes modules])
dnl --------------------- Check if realpath() takes NULL
AC_CACHE_CHECK([if the realpath function allows a NULL argument],
AM_CONDITIONAL(COMPILE_A2BOOT, test x$compile_a2boot = xyes)
AM_CONDITIONAL(HAVE_LIBGCRYPT, test x$neta_cv_have_libgcrypt = xyes)
AM_CONDITIONAL(HAVE_OPENSSL, test x$neta_cv_have_openssl = xyes)
-AM_CONDITIONAL(USE_NFSv4_ACLS, test x$neta_cv_nfsv4acl = xyes)
+AM_CONDITIONAL(HAVE_ACLS, test x"$with_acl_support" = x"yes")
+AM_CONDITIONAL(HAVE_LDAP, test x"$with_ldap" = x"yes")
AM_CONDITIONAL(USE_DHX, test x$neta_cv_compile_dhx = xyes)
AM_CONDITIONAL(USE_DHX2, test x$neta_cv_compile_dhx2 = xyes)
AM_CONDITIONAL(USE_RANDNUM, test x$neta_cv_have_openssl = xyes)
AM_CONDITIONAL(USE_DEBIAN, test x$sysv_style = xdebian)
AM_CONDITIONAL(USE_UNDEF, test x$sysv_style = x)
AM_CONDITIONAL(USE_BDB, test x$bdb_required = xyes)
+AM_CONDITIONAL(USE_APPLETALK, test x$netatalk_cv_ddp_enabled = xyes)
dnl --------------------- generate files
AC_OUTPUT([Makefile
bin/Makefile
+ bin/ad/Makefile
bin/adv1tov2/Makefile
bin/aecho/Makefile
- bin/afile/Makefile
bin/afppasswd/Makefile
bin/cnid/Makefile
bin/cnid/cnid2_create
contrib/printing/Makefile
contrib/shell_utils/Makefile
contrib/shell_utils/afpd-mtab.pl
- contrib/shell_utils/apple_cp
contrib/shell_utils/apple_dump
- contrib/shell_utils/apple_mv
- contrib/shell_utils/apple_rm
contrib/shell_utils/asip-status.pl
contrib/timelord/Makefile
contrib/a2boot/Makefile
libatalk/adouble/Makefile
libatalk/asp/Makefile
libatalk/atp/Makefile
+ libatalk/bstring/Makefile
libatalk/cnid/Makefile
libatalk/cnid/cdb/Makefile
libatalk/cnid/last/Makefile
libatalk/nbp/Makefile
libatalk/netddp/Makefile
libatalk/util/Makefile
- libatalk/util/test/Makefile
libatalk/tdb/Makefile
libatalk/unicode/Makefile
libatalk/unicode/charsets/Makefile
sys/solaris/Makefile
sys/sunos/Makefile
sys/ultrix/Makefile
+ test/Makefile
+ test/afpd/Makefile
],
[chmod a+x distrib/config/netatalk-config contrib/shell_utils/apple_*]
)
+++ /dev/null
-#!perl\r#\r# ICDumpMap\r# --- Dump suffix mappings from your Internet Config extension.\r#\r# iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>\r#\r\ruse Mac::InternetConfig;\r\ropen MAP, ">AppleVolumes";\rprintf MAP "%-9s \"%4s\" \"%4s\" %-30s %-25s %-15s\n\n",\r".", "TEXT", "ttxt", "ASCII Text", "SimpleText", "text/plain";\rprint MAP "\# The following lines are extracted from Internet Config Preference.\n\n";\rfor my $entry (keys %InternetConfigMap) {\r next unless $entry->extension =~ /^\./;\r $_ = sprintf "%-9s \"%4s\" \"%4s\" %-30s %-25s %-15s",\r $entry->extension, $entry->file_type, $entry->file_creator,\r $entry->entry_name, $entry->creator_app_name,\r $entry->MIME_type;\r s/\s*$/\n/;\r print MAP;\r}\rclose MAP;\r
\ No newline at end of file
# Makefile.am for contrib/
+SUBDIRS = macusers shell_utils
+
if COMPILE_TIMELORD
-TIMELORD = timelord
-else
-TIMELORD =
+SUBDIRS += timelord
endif
if COMPILE_A2BOOT
-A2BOOT = a2boot
-else
-A2BOOT =
+SUBDIRS += a2boot
endif
-SUBDIRS = macusers printing shell_utils ${TIMELORD} ${A2BOOT}
-
-EXTRA_DIST = ICDumpSuffixMap
+if USE_APPLETALK
+SUBDIRS += printing
+endif
#!/usr/bin/perl
-
+#
# usage: make-precompose.h.pl UnicodeData.txt > precompose.h
+#
+# (c) 2008-2011 by HAT <hat@fa2.so-net.ne.jp>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
# See
# http://www.unicode.org/Public/UNIDATA/UCD.html
# http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
-# table for binary search --------------------------------------------------
+# temp files for binary search (compose.TEMP, compose_sp.TEMP) -------------
open(UNICODEDATA, "<$ARGV[0]");
-open(PRECOMPOSETEMP, ">precompose.TEMP");
-open( DECOMPOSETEMP, ">decompose.TEMP");
-while (<UNICODEDATA>){
+open(COMPOSE_TEMP, ">compose.TEMP");
+open(COMPOSE_SP_TEMP, ">compose_sp.TEMP");
+
+while (<UNICODEDATA>) {
chop;
(
$code0,
$Simple_Uppercase_Mapping12,
$Simple_Lowercase_Mapping13,
$Simple_Titlecase_Mapping14
- ) = split(/\;/);
+ ) = split(/\;/);
if (($Decomposition_Mapping5 ne "") && ($Decomposition_Mapping5 !~ /\</) && ($Decomposition_Mapping5 =~ / /)) {
($base, $comb) = split(/ /,$Decomposition_Mapping5);
-
+
$leftbracket = " { ";
$rightbracket =" }, ";
- if (hex($code0) > 0xFFFF) { # DELETE THIS LINE IF INTERNAL CODE IS UCS4
- $leftbracket = "\/\*{ "; # DELETE THIS LINE IF INTERNAL CODE IS UCS4
- $rightbracket =" },\*\/ "; # DELETE THIS LINE IF INTERNAL CODE IS UCS4
- } # DELETE THIS LINE IF INTERNAL CODE IS UCS4
-
# AFP 3.x Spec
if ( ((0x2000 <= hex($code0)) && (hex($code0) <= 0x2FFF))
- || ((0xFE30 <= hex($code0)) && (hex($code0) <= 0xFE4F))
- || ((0x2F800 <= hex($code0)) && (hex($code0) <= 0x2FA1F))) {
+ || ((0xFE30 <= hex($code0)) && (hex($code0) <= 0xFE4F))
+ || ((0x2F800 <= hex($code0)) && (hex($code0) <= 0x2FA1F))) {
+ $leftbracket = "\/\*{ ";
+ $rightbracket =" },\*\/ ";
+ }
+
+ if (hex($code0) > 0xFFFF) {
+
+ $code0_sp_hi = 0xD800 - (0x10000 >> 10) + (hex($code0) >> 10);
+ $code0_sp_lo = 0xDC00 + (hex($code0) & 0x3FF);
+
+ $base_sp_hi = 0xD800 - (0x10000 >> 10) + (hex($base) >> 10);
+ $base_sp_lo = 0xDC00 + (hex($base) & 0x3FF);
+
+ $comb_sp_hi = 0xD800 - (0x10000 >> 10) + (hex($comb) >> 10);
+ $comb_sp_lo = 0xDC00 + (hex($comb) & 0x3FF);
+
+ printf(COMPOSE_SP_TEMP "%s0x%04X%04X, 0x%04X%04X, 0x%04X%04X%s\/\* %s \*\/\n",
+ $leftbracket, $code0_sp_hi ,$code0_sp_lo, $base_sp_hi, $base_sp_lo, $comb_sp_hi, $comb_sp_lo, $rightbracket, $Name1);
+
$leftbracket = "\/\*{ ";
$rightbracket =" },\*\/ ";
}
-
- printf(PRECOMPOSETEMP "%s0x%08X, 0x%08X, 0x%08X%s\/\* %s \*\/\n", $leftbracket, hex($code0), hex($base), hex($comb), $rightbracket, $Name1);
- printf( DECOMPOSETEMP "%s0x%08X, 0x%08X, 0x%08X%s\/\* %s \*\/\n", $leftbracket, hex($code0), hex($base), hex($comb), $rightbracket, $Name1);
-
+ printf(COMPOSE_TEMP "%s0x%08X, 0x%08X, 0x%08X%s\/\* %s \*\/\n", $leftbracket, hex($code0), hex($base), hex($comb), $rightbracket, $Name1);
+
+ }
+}
+
+close(UNICODEDATA);
+
+close(COMPOSE_TEMP);
+close(COMPOSE_SP_TEMP);
+
+# macros for BMP (PRECOMP_COUNT, DECOMP_COUNT, MAXCOMBLEN) ----------------
+
+open(COMPOSE_TEMP, "<compose.TEMP");
+
+@comp_table = ();
+$comp_count = 0;
+
+while (<COMPOSE_TEMP>) {
+ if (m/^\/\*/) {
+ next;
+ }
+ $comp_table[$comp_count][0] = substr($_, 4, 10);
+ $comp_table[$comp_count][1] = substr($_, 16, 10);
+ $comp_count++;
+}
+
+$maxcomblen = 2; # Hangul's maxcomblen is already 2. That is, VT.
+
+for ($i = 0 ; $i < $comp_count ; $i++) {
+ $base = $comp_table[$i][1];
+ $comblen = 1;
+ $j = 0;
+ while ($j < $comp_count) {
+ if ($base ne $comp_table[$j][0]) {
+ $j++;
+ next;
+ } else {
+ $comblen++;
+ $base = $comp_table[$j][1];
+ $j = 0;
+ }
+ }
+ $maxcomblen = ($maxcomblen > $comblen) ? $maxcomblen : $comblen;
+}
+
+close(COMPOSE_TEMP);
+
+# macros for SP (PRECOMP_SP_COUNT,DECOMP_SP_COUNT, MAXCOMBSPLEN) -----------
+
+open(COMPOSE_SP_TEMP, "<compose_sp.TEMP");
+
+@comp_sp_table = ();
+$comp_sp_count = 0;
+
+while (<COMPOSE_SP_TEMP>) {
+ if (m/^\/\*/) {
+ next;
+ }
+ $comp_sp_table[$comp_sp_count][0] = substr($_, 4, 10);
+ $comp_sp_table[$comp_sp_count][1] = substr($_, 16, 10);
+ $comp_sp_count++;
+}
+
+$maxcombsplen = 2; # one char have 2 codepoints, like a D8xx DCxx.
+
+for ($i = 0 ; $i < $comp_sp_count ; $i++) {
+ $base_sp = $comp_sp_table[$i][1];
+ $comblen = 2;
+ $j = 0;
+ while ($j < $comp_sp_count) {
+ if ($base_sp ne $comp_sp_table[$j][0]) {
+ $j++;
+ next;
+ } else {
+ $comblen += 2;
+ $base_sp = $comp_sp_table[$j][1];
+ $j = 0;
+ }
}
+ $maxcombsplen = ($maxcombsplen > $comblen) ? $maxcombsplen : $comblen;
}
+close(COMPOSE_SP_TEMP);
+
+# macro for buffer length (COMBBUFLEN) -------------------------------------
+
+$combbuflen = ($maxcomblen > $maxcombsplen) ? $maxcomblen : $maxcombsplen;
+
# sort ---------------------------------------------------------------------
-system("sort -k 3 precompose.TEMP \> precompose.SORT");
-system("sort -k 2 decompose.TEMP \> decompose.SORT");
+system("sort -k 3 compose.TEMP \> precompose.SORT");
+system("sort -k 2 compose.TEMP \> decompose.SORT");
+
+system("sort -k 3 compose_sp.TEMP \> precompose_sp.SORT");
+system("sort -k 2 compose_sp.TEMP \> decompose_sp.SORT");
# print -------------------------------------------------------------------
-printf ("\/\* This file is generated by contrib/misc/make-precompose.h.pl %s \*\/\n", $ARGV[0]);
print ("\/\* DO NOT EDIT BY HAND\!\!\! \*\/\n");
+print ("\/\* This file is generated by \*\/\n");
+printf ("\/\* contrib/misc/make-precompose.h.pl %s \*\/\n", $ARGV[0]);
print ("\n");
printf ("\/\* %s is got from \*\/\n", $ARGV[0]);
print ("\/\* http\:\/\/www.unicode.org\/Public\/UNIDATA\/UnicodeData.txt \*\/\n");
print ("\n");
+print ("\#define SBASE 0xAC00\n");
+print ("\#define LBASE 0x1100\n");
+print ("\#define VBASE 0x1161\n");
+print ("\#define TBASE 0x11A7\n");
+print ("\#define LCOUNT 19\n");
+print ("\#define VCOUNT 21\n");
+print ("\#define TCOUNT 28\n");
+print ("\#define NCOUNT 588 \/\* (VCOUNT \* TCOUNT) \*\/\n");
+print ("\#define SCOUNT 11172 \/\* (LCOUNT \* NCOUNT) \*\/\n");
+print ("\n");
+
+printf ("\#define PRECOMP_COUNT %d\n", $comp_count);
+printf ("\#define DECOMP_COUNT %d\n", $comp_count);
+printf ("\#define MAXCOMBLEN %d\n", $maxcomblen);
+print ("\n");
+printf ("\#define PRECOMP_SP_COUNT %d\n", $comp_sp_count);
+printf ("\#define DECOMP_SP_COUNT %d\n", $comp_sp_count);
+printf ("\#define MAXCOMBSPLEN %d\n", $maxcombsplen);
+print ("\n");
+printf ("\#define COMBBUFLEN %d \/\* max\(MAXCOMBLEN\,MAXCOMBSPLEN\) \*\/\n", $combbuflen);
+print ("\n");
+
print ("static const struct \{\n");
print (" unsigned int replacement\;\n");
print (" unsigned int base\;\n");
print ("\}\;\n");
print ("\n");
+
+
+print ("static const struct \{\n");
+print (" unsigned int replacement_sp\;\n");
+print (" unsigned int base_sp\;\n");
+print (" unsigned int comb_sp\;\n");
+print ("\} precompositions_sp\[\] \= \{\n");
+
+system("cat precompose_sp.SORT");
+
+print ("\}\;\n");
+print ("\n");
+
+print ("static const struct \{\n");
+print (" unsigned int replacement_sp\;\n");
+print (" unsigned int base_sp\;\n");
+print (" unsigned int comb_sp\;\n");
+print ("\} decompositions_sp\[\] \= \{\n");
+
+system("cat decompose_sp.SORT");
+
+print ("\}\;\n");
+print ("\n");
+
print ("\/\* EOF \*\/\n");
# EOF
+++ /dev/null
-This directory contains patches that are under consideration or being
-tested.
+++ /dev/null
-First try for a netatalk vfs layer
-
-current schemes
-adouble=v1,v2 classic adouble format
-adouble=osx ._<filename> OSX resource fork.
-adouble=ads NT like alternate data stream.
-
-Note for ads:
-* cf. patch.vfs for samba ADS vfs layer and patch.samba.xx for samba tree patch.
-
-* It's using Afp_AfpInfo name (MS SFM name) but it's not yet compatible with SFM.
- from cdrecord source code Afp_AfpInfo is the raw HFS data, we are storing an appledouble file.
-
-* Server side copy and Macintosh copy only deal with resource fork, other NT ADS are lost.
- unfixable for Macintosh copy but doable for server side.
-
-* It's ok for rename, delete, chown and chmod.
-
-* Copy from a NT box should work.
-
-* Last but not least : only on a new volume!
-
-TODO
-indirection for metadata, aka stored in EA, a different file, whatever.
-
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/Makefile.am ./etc/afpd/Makefile.am
---- ../src.dev2/etc/afpd/Makefile.am Mon Feb 9 22:45:51 2004
-+++ ./etc/afpd/Makefile.am Fri Jun 18 19:15:47 2004
-@@ -8,14 +8,14 @@
- file.c enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
- mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c \
- afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
-- catsearch.c afprun.c
-+ catsearch.c afprun.c vfs_adouble.c
-
- afpd_LDADD = $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la
- afpd_LDFLAGS = -export-dynamic
-
- noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
- filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \
-- uam_auth.h uid.h unix.h volume.h
-+ uam_auth.h uid.h unix.h volume.h afp_vfs.h
-
- LIBS = @LIBS@ @PAM_LIBS@ @QUOTA_LIBS@ @SLP_LIBS@
-
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/afp_vfs.h ./etc/afpd/afp_vfs.h
---- ../src.dev2/etc/afpd/afp_vfs.h Thu Jan 1 00:00:00 1970
-+++ ./etc/afpd/afp_vfs.h Wed Jun 23 03:56:15 2004
-@@ -0,0 +1,49 @@
-+/*
-+ Copyright (c) 2004 Didier Gautheron
-+
-+ This program is free software; you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published by
-+ the Free Software Foundation; either version 2 of the License, or
-+ (at your option) any later version.
-+
-+ This program is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program; if not, write to the Free Software
-+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+
-+ vfs layer for afp
-+*/
-+
-+#ifndef _AFP_VFS_H
-+#define _AFP_VFS_H
-+
-+#include <atalk/adouble.h>
-+struct vol;
-+
-+struct vfs_ops {
-+ /* low level adouble fn */
-+ char *(*ad_path)(const char *, int);
-+
-+ /* */
-+ int (*validupath)(const struct vol *, const char *);
-+ int (*rf_chown)(const struct vol *, const char *path, uid_t owner, gid_t group);
-+ int (*rf_renamedir)(const struct vol *, const char *oldpath, const char *newpath);
-+ int (*rf_deletecurdir)(const struct vol *);
-+ int (*rf_setfilmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
-+ int (*rf_setdirmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
-+ int (*rf_setdirunixmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
-+
-+ int (*rf_setdirowner)(const struct vol *, const char *path, uid_t owner, gid_t group);
-+
-+ int (*rf_deletefile)(const struct vol *, const char * );
-+ int (*rf_renamefile)(const struct vol *, const char *oldpath, const char *newpath);
-+
-+};
-+
-+void initvol_vfs(struct vol *vol);
-+
-+#endif
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/directory.c ./etc/afpd/directory.c
---- ../src.dev2/etc/afpd/directory.c Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/directory.c Sat Jun 19 14:07:14 2004
-@@ -610,7 +610,7 @@
- system rmdir with afp error code.
- ENOENT is not an error.
- */
--static int netatalk_rmdir(const char *name)
-+int netatalk_rmdir(const char *name)
- {
- if (rmdir(name) < 0) {
- switch ( errno ) {
-@@ -2075,15 +2075,11 @@
- }
- }
-
-- if (vol->v_adouble == AD_VERSION2_OSX) {
-- /* We simply move the corresponding ad file as well */
-- char tempbuf[258]="._";
-- rename(vol->ad_path(src,0),strcat(tempbuf,dst));
-- }
-+ vol->vfs->rf_renamedir(vol, src, dst);
-
- len = strlen( newname );
- /* rename() succeeded so we need to update our tree even if we can't open
-- * .Parent
-+ * metadata
- */
-
- ad_init(&ad, vol->v_adouble);
-@@ -2132,12 +2128,9 @@
- return( AFP_OK );
- }
-
--#define DOT_APPLEDOUBLE_LEN 13
- /* delete an empty directory */
--int deletecurdir( vol, path, pathlen )
-+int deletecurdir( vol)
- const struct vol *vol;
--char *path;
--int pathlen;
- {
- struct dirent *de;
- struct stat st;
-@@ -2162,42 +2155,9 @@
- return AFPERR_OLOCK;
- }
- }
--
-- if (vol->v_adouble == AD_VERSION2_OSX) {
--
-- if ((err = netatalk_unlink(vol->ad_path(".",0) )) ) {
-- return err;
-- }
-- }
-- else {
-- /* delete stray .AppleDouble files. this happens to get .Parent files
-- as well. */
-- if ((dp = opendir(".AppleDouble"))) {
-- strcpy(path, ".AppleDouble/");
-- while ((de = readdir(dp))) {
-- /* skip this and previous directory */
-- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-- continue;
--
-- /* bail if the file exists in the current directory.
-- * note: this will not fail with dangling symlinks */
-- if (stat(de->d_name, &st) == 0) {
-- closedir(dp);
-- return AFPERR_DIRNEMPT;
-- }
--
-- strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
-- if ((err = netatalk_unlink(path))) {
-- closedir(dp);
-- return err;
-- }
-- }
-- closedir(dp);
-- }
--
-- if ( (err = netatalk_rmdir( ".AppleDouble" )) ) {
-- return err;
-- }
-+ err = vol->vfs->rf_deletecurdir(vol);
-+ if (err) {
-+ return err;
- }
-
- /* now get rid of dangling symlinks */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/directory.h ./etc/afpd/directory.h
---- ../src.dev2/etc/afpd/directory.h Mon May 10 18:40:32 2004
-+++ ./etc/afpd/directory.h Sat Jun 19 03:23:18 2004
-@@ -196,7 +196,7 @@
-
- extern struct dir *dirinsert __P((struct vol *, struct dir *));
- extern int movecwd __P((const struct vol *, struct dir *));
--extern int deletecurdir __P((const struct vol *, char *, int));
-+extern int deletecurdir __P((const struct vol *));
- extern struct path *cname __P((const struct vol *, struct dir *,
- char **));
- extern mode_t mtoumode __P((struct maccess *));
-@@ -215,6 +215,7 @@
- extern int check_access __P((char *name , int mode));
- extern int file_access __P((struct path *path, int mode));
-
-+extern int netatalk_rmdir __P((const char *name));
- extern int netatalk_unlink __P((const char *name));
-
- /* from enumerate.c */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/enumerate.c ./etc/afpd/enumerate.c
---- ../src.dev2/etc/afpd/enumerate.c Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/enumerate.c Thu Jun 24 04:26:35 2004
-@@ -166,7 +166,7 @@
- if (!strcmp(name, "..") || !strcmp(name, "."))
- return NULL;
-
-- if (!vol->validupath(vol, name))
-+ if (!vol->vfs->validupath(vol, name))
- return NULL;
-
- /* check for vetoed filenames */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/file.c ./etc/afpd/file.c
---- ../src.dev2/etc/afpd/file.c Tue Jun 15 22:53:54 2004
-+++ ./etc/afpd/file.c Mon Jun 21 00:21:24 2004
-@@ -901,7 +901,7 @@
-
- /* second try with adouble open
- */
-- if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
-+ if ( ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
- O_RDWR|O_CREAT, 0666, adp) < 0) {
- /* for some things, we don't need an adouble header */
- if (f_bitmap & ~(1<<FILPBIT_MDATE)) {
-@@ -1020,7 +1020,6 @@
- char *src, *dst, *newname;
- struct adouble *adp;
- {
-- char adsrc[ MAXPATHLEN + 1];
- int rc;
-
- #ifdef DEBUG
-@@ -1055,38 +1054,10 @@
- }
- }
-
-- strcpy( adsrc, vol->ad_path( src, 0 ));
--
-- if (unix_rename( adsrc, vol->ad_path( dst, 0 )) < 0 ) {
-- struct stat st;
-+ if (vol->vfs->rf_renamefile(vol, src, dst) < 0 ) {
- int err;
-
- err = errno;
-- if (errno == ENOENT) {
-- struct adouble ad;
--
-- if (stat(adsrc, &st)) /* source has no ressource fork, */
-- return AFP_OK;
--
-- /* We are here because :
-- * -there's no dest folder.
-- * -there's no .AppleDouble in the dest folder.
-- * if we use the struct adouble passed in parameter it will not
-- * create .AppleDouble if the file is already opened, so we
-- * use a diff one, it's not a pb,ie it's not the same file, yet.
-- */
-- ad_init(&ad, vol->v_adouble);
-- if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
-- ad_close(&ad, ADFLAGS_HF);
-- if (!unix_rename( adsrc, vol->ad_path( dst, 0 )) )
-- err = 0;
-- else
-- err = errno;
-- }
-- else { /* it's something else, bail out */
-- err = errno;
-- }
-- }
- /* try to undo the data fork rename,
- * we know we are on the same device
- */
-@@ -1436,6 +1407,7 @@
- if (ret_err) {
- deletefile(d_vol, dst, 0);
- }
-+ /* ADS here */
-
- /* set dest modification date to src date */
- if (!stat(src, &st)) {
-@@ -1562,14 +1534,12 @@
- if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
- err = AFPERR_BUSY;
- }
-- else if (!(err = netatalk_unlink( vol->ad_path( file, ADFLAGS_HF)) ) &&
-- !(err = netatalk_unlink( file )) ) {
-+ else if (!(err = vol->vfs->rf_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
- cnid_t id;
- if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file))))
- {
- cnid_delete(vol->v_cdb, id);
- }
--
- }
- if (adp)
- ad_close( &ad, adflags ); /* ad_close removes locks if any */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/filedir.c ./etc/afpd/filedir.c
---- ../src.dev2/etc/afpd/filedir.c Mon May 10 18:40:32 2004
-+++ ./etc/afpd/filedir.c Sat Jun 19 15:09:08 2004
-@@ -73,7 +73,7 @@
- return AFPERR_NOOBJ ;
- }
-
-- adpath = vol->ad_path( upath, ADFLAGS_HF );
-+ adpath = vol->vfs->ad_path( upath, ADFLAGS_HF );
- /* FIXME dirsearch doesn't move cwd to did ! */
- if (( dir = dirlookup( vol, did )) == NULL ) {
- LOG(log_error, logtype_afpd, "matchfile2dirperms: Unable to get directory info.");
-@@ -313,7 +313,7 @@
- if ((vol->v_flags & AFPVOL_NOHEX) && strchr(name, '/'))
- return AFPERR_PARAM;
-
-- if (!vol->validupath(vol, name))
-+ if (!vol->vfs->validupath(vol, name))
- return AFPERR_EXIST;
-
- /* check for vetoed filenames */
-@@ -582,7 +582,7 @@
- rc = AFPERR_ACCESS;
- }
- else {
-- rc = deletecurdir( vol, obj->oldtmp, AFPOBJ_TMPSIZ);
-+ rc = deletecurdir( vol);
- }
- } else if (of_findname(s_path)) {
- rc = AFPERR_BUSY;
-@@ -764,7 +764,7 @@
- int admode = ad_mode("", 0777);
-
- setfilmode(upath, admode, NULL);
-- setfilmode(vol->ad_path( upath, ADFLAGS_HF ), ad_hf_mode(admode), NULL);
-+ vol->vfs->rf_setfilmode(vol, upath, admode, NULL);
- }
- setvoltime(obj, vol );
- }
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/unix.c ./etc/afpd/unix.c
---- ../src.dev2/etc/afpd/unix.c Tue Jun 15 22:53:55 2004
-+++ ./etc/afpd/unix.c Wed Jun 23 04:04:01 2004
-@@ -260,8 +260,8 @@
- rwx-wx-wx or rwx-wx--
- rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
- */
--static int stickydirmode(name, mode, dropbox)
--char * name;
-+int stickydirmode(name, mode, dropbox)
-+const char * name;
- const mode_t mode;
- const int dropbox;
- {
-@@ -405,12 +405,12 @@
- if (setfilmode( path->u_name, mode, &path->st) < 0)
- return -1;
- /* we need to set write perm if read set for resource fork */
-- return setfilmode(vol->ad_path( path->u_name, ADFLAGS_HF ), ad_hf_mode(mode), &path->st);
-+ return vol->vfs->rf_setfilmode(vol, path->u_name, mode, &path->st);
- }
-
- /* --------------------- */
- int setfilmode(name, mode, st)
--char * name;
-+const char * name;
- mode_t mode;
- struct stat *st;
- {
-@@ -436,29 +436,18 @@
- const char *name;
- const mode_t mode;
- {
--char *adouble = vol->ad_path( name, ADFLAGS_DIR );
-
- int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-
- if (dir_rx_set(mode)) {
-- /* extending right? dir first then .AppleDouble */
-+ /* extending right? dir first then .AppleDouble in rf_setdirmode */
- if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
- return -1;
-- if (vol->v_adouble != AD_VERSION2_OSX) {
-- if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
-- return -1 ;
-- }
-- }
- }
-- if (setfilmode(adouble, ad_hf_mode(mode), NULL) < 0 && !vol_noadouble(vol)) {
-+ if (vol->vfs->rf_setdirunixmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
- return -1 ;
- }
- if (!dir_rx_set(mode)) {
-- if (vol->v_adouble != AD_VERSION2_OSX) {
-- if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
-- return -1 ;
-- }
-- }
- if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
- return -1;
- }
-@@ -471,26 +460,17 @@
- const char *name;
- const mode_t mode;
- {
-- char buf[ MAXPATHLEN + 1];
- struct stat st;
-- char *m;
- struct dirent *dirp;
- DIR *dir;
- int osx = vol->v_adouble == AD_VERSION2_OSX;
- int hf_mode = ad_hf_mode(mode);
- int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-- char *adouble = vol->ad_path( name, ADFLAGS_DIR );
-- char *adouble_p = ad_dir(adouble);
-
- if (dir_rx_set(mode)) {
-- /* extending right? dir first then .AppleDouble */
-+ /* extending right? dir first */
- if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
- return -1;
-- if (!osx) {
-- if (stickydirmode(adouble_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
-- return -1 ;
-- }
-- }
- }
-
- if (( dir = opendir( name )) == NULL ) {
-@@ -516,61 +496,13 @@
- return -1;
- }
- }
--#if 0
-- /* Don't change subdir perm */
-- else if (S_ISDIR(st.st_mode)) {
-- if (stickydirmode(dirp->d_name, DIRBITS | mode, dropbox) < 0)
-- return (-1);
-- } else if (stickydirmode(dirp->d_name, mode, dropbox) < 0)
-- return (-1);
-- }
--#endif
- }
- closedir( dir );
-
-- if (osx) {
-- goto setdirmode_noadouble;
-- }
--
-- /* change perm of .AppleDouble's files
-- */
-- if (( dir = opendir( adouble_p )) == NULL ) {
-- if (vol_noadouble(vol))
-- goto setdirmode_noadouble;
-- LOG(log_error, logtype_afpd, "setdirmode: opendir %s: %s", fullpathname(".AppleDouble"),strerror(errno) );
-- return( -1 );
-- }
-- strcpy( buf, adouble_p);
-- strcat( buf, "/" );
-- m = strchr( buf, '\0' );
-- for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
-- if ( strcmp( dirp->d_name, "." ) == 0 ||
-- strcmp( dirp->d_name, ".." ) == 0 ) {
-- continue;
-- }
-- *m = '\0';
-- strcat( buf, dirp->d_name );
--
-- if ( stat( buf, &st ) < 0 ) {
-- LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", buf, strerror(errno) );
-- continue;
-- }
-- if (!S_ISDIR(st.st_mode)) {
-- if (setfilmode(buf, hf_mode , &st) < 0) {
-- /* FIXME what do we do then? */
-- }
-- }
-- } /* end for */
-- closedir( dir );
--
-- if (!dir_rx_set(mode)) {
-- /* XXX: need to preserve special modes */
-- if (stickydirmode(adouble_p, DIRBITS | mode, dropbox) < 0 ) {
-- return -1 ;
-- }
-+ if (vol->vfs->rf_setdirmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
-+ return -1 ;
- }
-
--setdirmode_noadouble:
- if (!dir_rx_set(mode)) {
- if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
- return -1;
-@@ -578,6 +510,7 @@
- return( 0 );
- }
-
-+/* ----------------------------- */
- int setdeskowner( uid, gid )
- const uid_t uid;
- const gid_t gid;
-@@ -648,8 +581,6 @@
- const gid_t gid;
- struct path* path;
- {
-- struct stat st;
-- char *ad_p;
-
- if (!path->st_valid) {
- of_stat(path);
-@@ -665,22 +596,15 @@
- return -1;
- }
-
-- ad_p = vol->ad_path( path->u_name, ADFLAGS_HF );
--
-- if ( stat( ad_p, &st ) < 0 ) {
-- /* ignore */
-- return 0;
-- }
-- if ( chown( ad_p, uid, gid ) < 0 &&
-- errno != EPERM ) {
-- LOG(log_debug, logtype_afpd, "setfilowner: chown %d/%d %s: %s",
-- uid, gid, ad_p, strerror(errno) );
-+ if (vol->vfs->rf_chown(vol, path->u_name, uid, gid ) < 0 && errno != EPERM) {
-+ LOG(log_debug, logtype_afpd, "setfilowner: rf_chown %d/%d %s: %s",
-+ uid, gid, path->u_name, strerror(errno) );
- return -1;
- }
-+
- return 0;
- }
-
--
- /* ---------------------------------
- * uid/gid == 0 need to be handled as special cases. they really mean
- * that user/group should inherit from other, but that doesn't fit
-@@ -692,15 +616,10 @@
- const uid_t uid;
- const gid_t gid;
- {
-- char buf[ MAXPATHLEN + 1];
- struct stat st;
-- char *m;
- struct dirent *dirp;
- DIR *dir;
- int osx = vol->v_adouble == AD_VERSION2_OSX;
-- int noadouble = vol_noadouble(vol);
-- char *adouble;
-- char *adouble_p;
-
- if (( dir = opendir( name )) == NULL ) {
- return( -1 );
-@@ -723,56 +642,15 @@
- }
- }
- closedir( dir );
--
-- if (osx) {
-- goto setdirowner_noadouble;
-- }
-
-- adouble = vol->ad_path( name, ADFLAGS_DIR );
-- adouble_p = ad_dir(adouble);
-- if (( dir = opendir( adouble_p )) == NULL ) {
-- if (noadouble)
-- goto setdirowner_noadouble;
-- return( -1 );
-- }
-- strcpy( buf, adouble_p );
-- strcat( buf, "/" );
-- m = strchr( buf, '\0' );
-- for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
-- if ( strcmp( dirp->d_name, "." ) == 0 ||
-- strcmp( dirp->d_name, ".." ) == 0 ) {
-- continue;
-- }
-- *m = '\0';
-- strcat( buf, dirp->d_name );
-- if ( chown( buf, uid, gid ) < 0 && errno != EPERM ) {
-- LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-- uid, gid, fullpathname(buf), strerror(errno) );
-- /* return ( -1 ); Sometimes this is okay */
-- }
-- }
-- closedir( dir );
--
-- /*
-- * We cheat: we know that chown doesn't do anything.
-- */
-- if ( stat( ".AppleDouble", &st ) < 0 ) {
-- LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
-- return( -1 );
-- }
-- if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 &&
-- errno != EPERM ) {
-- LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-- uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
-- /* return ( -1 ); Sometimes this is okay */
-+ if (vol->vfs->rf_setdirowner(vol, name, uid, gid) < 0) {
-+ return -1;
- }
--
--setdirowner_noadouble:
-+
- if ( stat( ".", &st ) < 0 ) {
- return( -1 );
- }
-- if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 &&
-- errno != EPERM ) {
-+ if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 && errno != EPERM ) {
- LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
- uid, gid, fullpathname("."), strerror(errno) );
- }
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/unix.h ./etc/afpd/unix.h
---- ../src.dev2/etc/afpd/unix.h Mon May 10 18:40:33 2004
-+++ ./etc/afpd/unix.h Wed Jun 23 03:43:28 2004
-@@ -221,11 +221,12 @@
- extern int setdirmode __P((const struct vol *, const char *, const mode_t));
- extern int setdeskowner __P((const uid_t, const gid_t));
- extern int setdirowner __P((const struct vol *, const char *, const uid_t, const gid_t));
--extern int setfilmode __P((char *, mode_t , struct stat *));
-+extern int setfilmode __P((const char *, mode_t , struct stat *));
- extern int setfilunixmode __P((const struct vol *, struct path*, const mode_t));
- extern int setfilowner __P((const struct vol *, const uid_t, const gid_t, struct path*));
- extern int unix_rename __P((const char *oldpath, const char *newpath));
- extern int dir_rx_set __P((mode_t mode));
-+extern int stickydirmode __P((const char * name, const mode_t mode, const int dropbox));
-
- extern void accessmode __P((char *, struct maccess *, struct dir *, struct stat *));
- extern char *fullpathname __P((const char *));
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/vfs_adouble.c ./etc/afpd/vfs_adouble.c
---- ../src.dev2/etc/afpd/vfs_adouble.c Thu Jan 1 00:00:00 1970
-+++ ./etc/afpd/vfs_adouble.c Wed Jun 30 19:31:49 2004
-@@ -0,0 +1,749 @@
-+/*
-+ Copyright (c) 2004 Didier Gautheron
-+
-+ This program is free software; you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published by
-+ the Free Software Foundation; either version 2 of the License, or
-+ (at your option) any later version.
-+
-+ This program is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program; if not, write to the Free Software
-+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+
-+*/
-+#ifdef HAVE_CONFIG_H
-+#include "config.h"
-+#endif /* HAVE_CONFIG_H */
-+
-+#ifdef STDC_HEADERS
-+#include <string.h>
-+#endif
-+
-+#include <stdio.h>
-+
-+#include <atalk/adouble.h>
-+#include <atalk/logger.h>
-+#include <atalk/util.h>
-+
-+#include "directory.h"
-+#include "volume.h"
-+#include "unix.h"
-+
-+struct perm {
-+ uid_t uid;
-+ gid_t gid;
-+};
-+
-+typedef int (*rf_loop)(struct dirent *, char *, void *, int );
-+
-+/* ----------------------------- */
-+static int
-+for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag)
-+{
-+ char buf[ MAXPATHLEN + 1];
-+ char *m;
-+ DIR *dp;
-+ struct dirent *de;
-+ int ret;
-+
-+
-+ if (NULL == ( dp = opendir( name)) ) {
-+ if (!flag) {
-+ LOG(log_error, logtype_afpd, "%s: opendir %s: %s", from, fullpathname(name),strerror(errno) );
-+ return -1;
-+ }
-+ return 0;
-+ }
-+ strlcpy( buf, name, sizeof(buf));
-+ strlcat( buf, "/", sizeof(buf) );
-+ m = strchr( buf, '\0' );
-+ ret = 0;
-+ while ((de = readdir(dp))) {
-+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-+ continue;
-+ }
-+
-+ strlcat(buf, de->d_name, sizeof(buf));
-+ if (fn && (ret = fn(de, buf, data, flag))) {
-+ closedir(dp);
-+ return ret;
-+ }
-+ *m = 0;
-+ }
-+ closedir(dp);
-+ return ret;
-+}
-+
-+/* ------------------------------ */
-+static int ads_chown_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+ struct perm *owner = data;
-+
-+ if (chown( name , owner->uid, owner->gid ) < 0) {
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+static int RF_chown_ads(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
-+
-+{
-+ struct stat st;
-+ char *ad_p;
-+ struct perm owner;
-+
-+ owner.uid = uid;
-+ owner.gid = gid;
-+
-+
-+ ad_p = ad_dir(vol->vfs->ad_path(path, ADFLAGS_HF ));
-+
-+ if ( stat( ad_p, &st ) < 0 ) {
-+ /* ignore */
-+ return 0;
-+ }
-+
-+ if (chown( ad_p, uid, gid ) < 0) {
-+ return -1;
-+ }
-+ return for_each_adouble("chown_ads", ad_p, ads_chown_loop, &owner, 1);
-+}
-+
-+/* --------------------------------- */
-+static int deletecurdir_ads1_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+ return netatalk_unlink(name);
-+}
-+
-+static int ads_delete_rf(char *name)
-+{
-+ int err;
-+
-+ if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1)))
-+ return err;
-+ return netatalk_rmdir(name);
-+}
-+
-+static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+ struct stat st;
-+
-+ /* bail if the file exists in the current directory.
-+ * note: this will not fail with dangling symlinks */
-+
-+ if (stat(de->d_name, &st) == 0) {
-+ return AFPERR_DIRNEMPT;
-+ }
-+ return ads_delete_rf(name);
-+}
-+
-+static int RF_deletecurdir_ads(const struct vol *vol)
-+{
-+ int err;
-+
-+ /* delete stray .AppleDouble files. this happens to get .Parent files as well. */
-+ if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, NULL, 1)))
-+ return err;
-+ return netatalk_rmdir( ".AppleDouble" );
-+}
-+
-+/* ------------------- */
-+struct set_mode {
-+ mode_t mode;
-+ struct stat *st;
-+};
-+
-+static int ads_setfilmode_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+ struct set_mode *param = data;
-+
-+ return setfilmode(name, param->mode, param->st);
-+}
-+
-+static int ads_setfilmode(const char * name, mode_t mode, struct stat *st)
-+{
-+ mode_t dir_mode = mode;
-+ mode_t file_mode = ad_hf_mode(mode);
-+ struct set_mode param;
-+
-+ if ((dir_mode & (S_IRUSR | S_IWUSR )))
-+ dir_mode |= S_IXUSR;
-+ if ((dir_mode & (S_IRGRP | S_IWGRP )))
-+ dir_mode |= S_IXGRP;
-+ if ((dir_mode & (S_IROTH | S_IWOTH )))
-+ dir_mode |= S_IXOTH;
-+
-+ /* change folder */
-+ dir_mode |= DIRBITS;
-+ if (dir_rx_set(dir_mode)) {
-+ if (chmod( name, dir_mode ) < 0)
-+ return -1;
-+ }
-+ param.st = st;
-+ param.mode = file_mode;
-+ if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, ¶m, 0) < 0)
-+ return -1;
-+
-+ if (!dir_rx_set(dir_mode)) {
-+ if (chmod( name, dir_mode ) < 0)
-+ return -1;
-+ }
-+
-+ return 0;
-+}
-+
-+static int RF_setfilmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+ return ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_HF )), mode, st);
-+}
-+
-+/* ------------------- */
-+static int RF_setdirunixmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+ char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+ char ad_p[ MAXPATHLEN + 1];
-+ int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+
-+ strlcpy(ad_p,ad_dir(adouble), MAXPATHLEN + 1);
-+
-+ if (dir_rx_set(mode)) {
-+
-+ /* .AppleDouble */
-+ if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
-+ return -1;
-+
-+ /* .AppleDouble/.Parent */
-+ if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
-+ return -1;
-+ }
-+
-+ if (ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR)), mode, st) < 0)
-+ return -1;
-+
-+ if (!dir_rx_set(mode)) {
-+ if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
-+ return -1 ;
-+ if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+/* ------------------- */
-+struct dir_mode {
-+ mode_t mode;
-+ int dropbox;
-+};
-+
-+static int setdirmode_ads_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+
-+ struct dir_mode *param = data;
-+ int ret = 0; /* 0 ignore error, -1 */
-+
-+ if (dir_rx_set(param->mode)) {
-+ if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
-+ if (flag) {
-+ return 0;
-+ }
-+ return ret;
-+ }
-+ }
-+ if (ads_setfilmode(name, param->mode, NULL) < 0)
-+ return ret;
-+
-+ if (!dir_rx_set(param->mode)) {
-+ if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
-+ if (flag) {
-+ return 0;
-+ }
-+ return ret;
-+ }
-+ }
-+ return 0;
-+}
-+
-+static int RF_setdirmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+ char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+ char ad_p[ MAXPATHLEN + 1];
-+ struct dir_mode param;
-+
-+ param.mode = mode;
-+ param.dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+
-+ strlcpy(ad_p,ad_dir(adouble), sizeof(ad_p));
-+
-+ if (dir_rx_set(mode)) {
-+ /* .AppleDouble */
-+ if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol))
-+ return -1;
-+ }
-+
-+ if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, ¶m, vol_noadouble(vol)))
-+ return -1;
-+
-+ if (!dir_rx_set(mode)) {
-+ if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol))
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+/* ------------------- */
-+static int setdirowner_ads1_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+ struct perm *owner = data;
-+
-+ if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
-+ LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+ owner->uid, owner->gid, fullpathname(name), strerror(errno) );
-+ /* return ( -1 ); Sometimes this is okay */
-+ }
-+ return 0;
-+}
-+
-+static int setdirowner_ads_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+ struct perm *owner = data;
-+
-+ if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, data, flag) < 0)
-+ return -1;
-+
-+ if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
-+ LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+ owner->uid, owner->gid, fullpathname(name), strerror(errno) );
-+ /* return ( -1 ); Sometimes this is okay */
-+ }
-+ return 0;
-+}
-+
-+static int RF_setdirowner_ads(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
-+{
-+ int noadouble = vol_noadouble(vol);
-+ char adouble_p[ MAXPATHLEN + 1];
-+ struct stat st;
-+ struct perm owner;
-+
-+ owner.uid = uid;
-+ owner.gid = gid;
-+
-+ strlcpy(adouble_p, ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR )), sizeof(adouble_p));
-+
-+ if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, &owner, noadouble))
-+ return -1;
-+
-+ /*
-+ * We cheat: we know that chown doesn't do anything.
-+ */
-+ if ( stat( ".AppleDouble", &st ) < 0) {
-+ if (errno == ENOENT && noadouble)
-+ return 0;
-+ LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
-+ return -1;
-+ }
-+ if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
-+ LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+ uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
-+ /* return ( -1 ); Sometimes this is okay */
-+ }
-+ return 0;
-+}
-+
-+/* ------------------- */
-+static int RF_deletefile_ads(const struct vol *vol, const char *file )
-+{
-+ char *ad_p = ad_dir(vol->vfs->ad_path(file, ADFLAGS_HF ));
-+
-+ return ads_delete_rf(ad_p);
-+}
-+
-+/* --------------------------- */
-+int RF_renamefile_ads(const struct vol *vol, const char *src, const char *dst)
-+{
-+ char adsrc[ MAXPATHLEN + 1];
-+ int err = 0;
-+
-+ strcpy( adsrc, ad_dir(vol->vfs->ad_path( src, 0 )));
-+ if (unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) < 0) {
-+ struct stat st;
-+
-+ err = errno;
-+ if (errno == ENOENT) {
-+ struct adouble ad;
-+
-+ if (stat(adsrc, &st)) /* source has no ressource fork, */
-+ return AFP_OK;
-+
-+ /* We are here because :
-+ * -there's no dest folder.
-+ * -there's no .AppleDouble in the dest folder.
-+ * if we use the struct adouble passed in parameter it will not
-+ * create .AppleDouble if the file is already opened, so we
-+ * use a diff one, it's not a pb,ie it's not the same file, yet.
-+ */
-+ ad_init(&ad, vol->v_adouble);
-+ if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
-+ ad_close(&ad, ADFLAGS_HF);
-+
-+ /* We must delete it */
-+ RF_deletefile_ads(vol, dst );
-+ if (!unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) )
-+ err = 0;
-+ else
-+ err = errno;
-+ }
-+ else { /* it's something else, bail out */
-+ err = errno;
-+ }
-+ }
-+ }
-+ if (err) {
-+ errno = err;
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+/* ===================================================
-+ classic adouble format
-+*/
-+
-+static int validupath_adouble(const struct vol *vol, const char *name)
-+{
-+ return (vol->v_flags & AFPVOL_USEDOTS) ? strncasecmp(name,".Apple", 6) && strcasecmp(name, ".Parent")
-+ : name[0] != '.';
-+}
-+
-+/* ----------------- */
-+static int RF_chown_adouble(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
-+
-+{
-+ struct stat st;
-+ char *ad_p;
-+
-+ ad_p = vol->vfs->ad_path(path, ADFLAGS_HF );
-+
-+ if ( stat( ad_p, &st ) < 0 )
-+ return 0; /* ignore */
-+
-+ return chown( ad_p, uid, gid );
-+}
-+
-+/* ----------------- */
-+int RF_renamedir_adouble(const struct vol *vol, const char *oldpath, const char *newpath)
-+{
-+ return 0;
-+}
-+
-+/* ----------------- */
-+static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+ struct stat st;
-+ int err;
-+
-+ /* bail if the file exists in the current directory.
-+ * note: this will not fail with dangling symlinks */
-+
-+ if (stat(de->d_name, &st) == 0)
-+ return AFPERR_DIRNEMPT;
-+
-+ if ((err = netatalk_unlink(name)))
-+ return err;
-+
-+ return 0;
-+}
-+
-+static int RF_deletecurdir_adouble(const struct vol *vol)
-+{
-+ int err;
-+
-+ /* delete stray .AppleDouble files. this happens to get .Parent files
-+ as well. */
-+ if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1)))
-+ return err;
-+ return netatalk_rmdir( ".AppleDouble" );
-+}
-+
-+/* ----------------- */
-+static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st)
-+{
-+ return setfilmode(name, ad_hf_mode(mode), st);
-+}
-+
-+static int RF_setfilmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+ return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_HF ), mode, st);
-+}
-+
-+/* ----------------- */
-+static int RF_setdirunixmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+ char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+ int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+
-+ if (dir_rx_set(mode)) {
-+ if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
-+ return -1;
-+ }
-+
-+ if (adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st) < 0)
-+ return -1;
-+
-+ if (!dir_rx_set(mode)) {
-+ if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
-+ return -1 ;
-+ }
-+ return 0;
-+}
-+
-+/* ----------------- */
-+static int setdirmode_adouble_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+ int hf_mode = *(int *)data;
-+ struct stat st;
-+
-+ if ( stat( name, &st ) < 0 ) {
-+ if (flag)
-+ return 0;
-+ LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
-+ }
-+ else if (!S_ISDIR(st.st_mode)) {
-+ if (setfilmode(name, hf_mode , &st) < 0) {
-+ /* FIXME what do we do then? */
-+ }
-+ }
-+ return 0;
-+}
-+
-+static int RF_setdirmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st1)
-+{
-+ int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+ int hf_mode = ad_hf_mode(mode);
-+ char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+ char *adouble_p = ad_dir(adouble);
-+
-+ if (dir_rx_set(mode)) {
-+ if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
-+ return -1;
-+ }
-+
-+ if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, vol_noadouble(vol)))
-+ return -1;
-+
-+ if (!dir_rx_set(mode)) {
-+ if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
-+ return -1 ;
-+ }
-+ return 0;
-+}
-+
-+/* ----------------- */
-+static int setdirowner_adouble_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+ struct perm *owner = data;
-+
-+ if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
-+ LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+ owner->uid, owner->gid, fullpathname(name), strerror(errno) );
-+ /* return ( -1 ); Sometimes this is okay */
-+ }
-+ return 0;
-+}
-+
-+static int RF_setdirowner_adouble(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
-+
-+{
-+ int noadouble = vol_noadouble(vol);
-+ char *adouble_p;
-+ struct stat st;
-+ struct perm owner;
-+
-+ owner.uid = uid;
-+ owner.gid = gid;
-+
-+ adouble_p = ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR ));
-+
-+ if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, &owner, noadouble))
-+ return -1;
-+
-+ /*
-+ * We cheat: we know that chown doesn't do anything.
-+ */
-+ if ( stat( ".AppleDouble", &st ) < 0) {
-+ if (errno == ENOENT && noadouble)
-+ return 0;
-+ LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
-+ return -1;
-+ }
-+ if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
-+ LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+ uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
-+ /* return ( -1 ); Sometimes this is okay */
-+ }
-+ return 0;
-+}
-+
-+/* ----------------- */
-+static int RF_deletefile_adouble(const struct vol *vol, const char *file )
-+{
-+ return netatalk_unlink(vol->vfs->ad_path( file, ADFLAGS_HF));
-+}
-+
-+/* ----------------- */
-+int RF_renamefile_adouble(const struct vol *vol, const char *src, const char *dst)
-+{
-+ char adsrc[ MAXPATHLEN + 1];
-+ int err = 0;
-+
-+ strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
-+ if (unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) < 0) {
-+ struct stat st;
-+
-+ err = errno;
-+ if (errno == ENOENT) {
-+ struct adouble ad;
-+
-+ if (stat(adsrc, &st)) /* source has no ressource fork, */
-+ return AFP_OK;
-+
-+ /* We are here because :
-+ * -there's no dest folder.
-+ * -there's no .AppleDouble in the dest folder.
-+ * if we use the struct adouble passed in parameter it will not
-+ * create .AppleDouble if the file is already opened, so we
-+ * use a diff one, it's not a pb,ie it's not the same file, yet.
-+ */
-+ ad_init(&ad, vol->v_adouble);
-+ if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
-+ ad_close(&ad, ADFLAGS_HF);
-+ if (!unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) )
-+ err = 0;
-+ else
-+ err = errno;
-+ }
-+ else { /* it's something else, bail out */
-+ err = errno;
-+ }
-+ }
-+ }
-+ if (err) {
-+ errno = err;
-+ return -1;
-+ }
-+ return 0;
-+}
-+
-+struct vfs_ops netatalk_adouble = {
-+ /* ad_path: */ ad_path,
-+ /* validupath: */ validupath_adouble,
-+ /* rf_chown: */ RF_chown_adouble,
-+ /* rf_renamedir: */ RF_renamedir_adouble,
-+ /* rf_deletecurdir: */ RF_deletecurdir_adouble,
-+ /* rf_setfilmode: */ RF_setfilmode_adouble,
-+ /* rf_setdirmode: */ RF_setdirmode_adouble,
-+ /* rf_setdirunixmode: */ RF_setdirunixmode_adouble,
-+ /* rf_setdirowner: */ RF_setdirowner_adouble,
-+ /* rf_deletefile: */ RF_deletefile_adouble,
-+ /* rf_renamefile: */ RF_renamefile_adouble,
-+};
-+
-+/* =======================================
-+ osx adouble format
-+ */
-+static int validupath_osx(const struct vol *vol, const char *name)
-+{
-+ return strncasecmp(name,".Apple", 6) && strncasecmp(name,"._", 2);
-+}
-+
-+/* ---------------- */
-+int RF_renamedir_osx(const struct vol *vol, const char *oldpath, const char *newpath)
-+{
-+ /* We simply move the corresponding ad file as well */
-+ char tempbuf[258]="._";
-+ return rename(vol->vfs->ad_path(oldpath,0),strcat(tempbuf,newpath));
-+}
-+
-+/* ---------------- */
-+int RF_deletecurdir_osx(const struct vol *vol)
-+{
-+ return netatalk_unlink( vol->vfs->ad_path(".",0) );
-+}
-+
-+/* ---------------- */
-+static int RF_setdirunixmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+ return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st);
-+}
-+
-+/* ---------------- */
-+static int RF_setdirmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+ return 0;
-+}
-+
-+/* ---------------- */
-+static int RF_setdirowner_osx(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
-+{
-+ return 0;
-+}
-+
-+/* ---------------- */
-+int RF_renamefile_osx(const struct vol *vol, const char *src, const char *dst)
-+{
-+ char adsrc[ MAXPATHLEN + 1];
-+
-+ strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
-+ return unix_rename( adsrc, vol->vfs->ad_path( dst, 0 ));
-+}
-+
-+struct vfs_ops netatalk_adouble_osx = {
-+ /* ad_path: */ ad_path_osx,
-+ /* validupath: */ validupath_osx,
-+ /* rf_chown: */ RF_chown_adouble,
-+ /* rf_renamedir: */ RF_renamedir_osx,
-+ /* rf_deletecurdir: */ RF_deletecurdir_osx,
-+ /* rf_setfilmode: */ RF_setfilmode_adouble,
-+ /* rf_setdirmode: */ RF_setdirmode_osx,
-+ /* rf_setdirunixmode:*/ RF_setdirunixmode_osx,
-+ /* rf_setdirowner: */ RF_setdirowner_osx,
-+ /* rf_deletefile: */ RF_deletefile_adouble,
-+ /* rf_renamefile: */ RF_renamefile_osx,
-+};
-+
-+/* =======================================
-+ samba ads format
-+ */
-+struct vfs_ops netatalk_adouble_ads = {
-+ /* ad_path: */ ad_path_ads,
-+ /* validupath: */ validupath_adouble,
-+ /* rf_chown: */ RF_chown_ads,
-+ /* rf_renamedir: */ RF_renamedir_adouble,
-+ /* rf_deletecurdir: */ RF_deletecurdir_ads,
-+ /* rf_setfilmode: */ RF_setfilmode_ads,
-+ /* rf_setdirmode: */ RF_setdirmode_ads,
-+ /* rf_setdirunixmode:*/ RF_setdirunixmode_ads,
-+ /* rf_setdirowner: */ RF_setdirowner_ads,
-+ /* rf_deletefile: */ RF_deletefile_ads,
-+ /* rf_renamefile: */ RF_renamefile_ads,
-+};
-+
-+/* ---------------- */
-+void initvol_vfs(struct vol *vol)
-+{
-+ if (vol->v_adouble == AD_VERSION2_OSX) {
-+ vol->vfs = &netatalk_adouble_osx;
-+ }
-+ else if (vol->v_adouble == AD_VERSION1_ADS) {
-+ vol->vfs = &netatalk_adouble_ads;
-+ }
-+ else {
-+ vol->vfs = &netatalk_adouble;
-+ }
-+}
-+
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/volume.c ./etc/afpd/volume.c
---- ../src.dev2/etc/afpd/volume.c Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/volume.c Mon Jul 12 00:29:11 2004
-@@ -427,6 +427,8 @@
- options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
- else if (strcasecmp(val + 1, "osx") == 0)
- options[VOLOPT_ADOUBLE].i_value = AD_VERSION2_OSX;
-+ else if (strcasecmp(val + 1, "ads") == 0)
-+ options[VOLOPT_ADOUBLE].i_value = AD_VERSION1_ADS;
- #endif
- } else if (optionok(tmp, "options:", val)) {
- char *p;
-@@ -523,34 +525,6 @@
- }
- }
-
--/* -----------------
-- * FIXME should be define elsewhere
--*/
--static int validupath_adouble(const struct vol *vol, const char *name)
--{
-- return (vol->v_flags & AFPVOL_USEDOTS) ? strncasecmp(name,".Apple", 6) && strcasecmp(name, ".Parent")
-- : name[0] != '.';
--}
--
--/* ----------------- */
--static int validupath_osx(const struct vol *vol, const char *name)
--{
-- return strncasecmp(name,".Apple", 6) && strncasecmp(name,"._", 2);
--}
--
--/* ---------------- */
--static void initvoladouble(struct vol *vol)
--{
-- if (vol->v_adouble == AD_VERSION2_OSX) {
-- vol->validupath = validupath_osx;
-- vol->ad_path = ad_path_osx;
-- }
-- else {
-- vol->validupath = validupath_adouble;
-- vol->ad_path = ad_path;
-- }
--}
--
- /* ------------------------------- */
- static int creatvol(AFPObj *obj, struct passwd *pwd,
- char *path, char *name,
-@@ -653,7 +627,8 @@
- volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
- else
- volume->v_adouble = AD_VERSION;
-- initvoladouble(volume);
-+
-+ initvol_vfs(volume);
- #ifdef FORCE_UIDGID
- if (options[VOLOPT_FORCEUID].c_value) {
- volume->v_forceuid = strdup(options[VOLOPT_FORCEUID].c_value);
-@@ -2231,6 +2206,9 @@
- break;
- case AD_VERSION2_OSX:
- strlcat(buf, "ADOUBLE_VER:osx\n", sizeof(buf));
-+ break;
-+ case AD_VERSION1_ADS:
-+ strlcat(buf, "ADOUBLE_VER:ads\n", sizeof(buf));
- break;
- }
-
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/volume.h ./etc/afpd/volume.h
---- ../src.dev2/etc/afpd/volume.h Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/volume.h Fri Jun 25 22:01:56 2004
-@@ -14,6 +14,7 @@
-
- #include "atalk/unicode.h"
- #include "globals.h"
-+#include "afp_vfs.h"
-
- #define AFPVOL_NAMELEN 27
-
-@@ -75,6 +76,7 @@
- int v_preexec_close;
-
- /* adouble indirection */
-+ struct vfs_ops *vfs;
- int (*validupath)(const struct vol *, const char *);
- char *(*ad_path)(const char *, int);
- };
-diff -Nur -X .cvsignore -x CVS ../src.dev2/include/atalk/adouble.h ./include/atalk/adouble.h
---- ../src.dev2/include/atalk/adouble.h Tue Jun 15 01:08:28 2004
-+++ ./include/atalk/adouble.h Sun Jun 20 22:33:26 2004
-@@ -82,6 +82,7 @@
- #define AD_VERSION1 0x00010000
- #define AD_VERSION2 0x00020000
- #define AD_VERSION2_OSX 0x00020001
-+#define AD_VERSION1_ADS 0x00010002
- #define AD_VERSION AD_VERSION2
-
- /*
-@@ -252,6 +253,7 @@
- the header parameter size is too small.
- */
- char *(*ad_path)(const char *, int);
-+ int (*ad_mkrf)(char *);
-
- #ifdef USE_MMAPPED_HEADERS
- char *ad_data;
-@@ -364,6 +366,7 @@
- extern char *ad_dir __P((const char *));
- extern char *ad_path __P((const char *, int));
- extern char *ad_path_osx __P((const char *, int));
-+extern char *ad_path_ads __P((const char *, int));
-
- extern int ad_mode __P((const char *, int));
- extern int ad_mkdir __P((const char *, int));
-diff -Nur -X .cvsignore -x CVS ../src.dev2/libatalk/adouble/ad_open.c ./libatalk/adouble/ad_open.c
---- ../src.dev2/libatalk/adouble/ad_open.c Mon Jul 12 02:01:45 2004
-+++ ./libatalk/adouble/ad_open.c Mon Jul 12 02:12:25 2004
-@@ -697,6 +697,25 @@
- return( pathbuf );
- }
-
-+/* -------------------- */
-+static int ad_mkrf(char *path)
-+{
-+ char *slash;
-+ /*
-+ * Probably .AppleDouble doesn't exist, try to mkdir it.
-+ */
-+ if (NULL == ( slash = strrchr( path, '/' )) ) {
-+ return -1;
-+ }
-+ *slash = '\0';
-+ errno = 0;
-+ if ( ad_mkdir( path, 0777 ) < 0 ) {
-+ return -1;
-+ }
-+ *slash = '/';
-+ return 0;
-+}
-+
- /* ---------------------------------------
- * Put the resource fork where it needs to be:
- * ._name
-@@ -729,8 +748,97 @@
- strlcat( pathbuf, slash, MAXPATHLEN +1);
- return pathbuf;
- }
-+/* -------------------- */
-+static int ad_mkrf_osx(char *path)
-+{
-+ return 0;
-+}
-
--/*
-+/* ---------------------------------------
-+ * Put the .AppleDouble where it needs to be:
-+ *
-+ * / a/.AppleDouble/b/Afp_AfpInfo
-+ * a/b
-+ * \ b/.AppleDouble/.Parent/Afp_AfpInfo
-+ *
-+ */
-+char *
-+ad_path_ads( path, adflags )
-+ const char *path;
-+ int adflags;
-+{
-+ static char pathbuf[ MAXPATHLEN + 1];
-+ char c, *slash, buf[MAXPATHLEN + 1];
-+ size_t l;
-+
-+ l = strlcpy(buf, path, MAXPATHLEN +1);
-+ if ( adflags & ADFLAGS_DIR ) {
-+ strcpy( pathbuf, buf);
-+ if ( *buf != '\0' && l < MAXPATHLEN) {
-+ pathbuf[l++] = '/';
-+ pathbuf[l] = 0;
-+ }
-+ slash = ".Parent";
-+ } else {
-+ if (NULL != ( slash = strrchr( buf, '/' )) ) {
-+ c = *++slash;
-+ *slash = '\0';
-+ strcpy( pathbuf, buf);
-+ *slash = c;
-+ } else {
-+ pathbuf[ 0 ] = '\0';
-+ slash = buf;
-+ }
-+ }
-+ strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
-+ strlcat( pathbuf, slash, MAXPATHLEN +1);
-+
-+ strlcat( pathbuf, "/Afp_AfpInfo", MAXPATHLEN +1);
-+
-+#if 0
-+ if ((adflags & ADFLAGS_HF)) {
-+ strlcat( pathbuf, "Afp_AfpInfo", MAXPATHLEN +1);
-+ else {
-+ strlcat( pathbuf, "Afp_Resource", MAXPATHLEN +1);
-+ }
-+#endif
-+ return( pathbuf );
-+}
-+
-+/* -------------------- */
-+static int ad_mkrf_ads(char *path)
-+{
-+ char *slash;
-+ /*
-+ * Probably .AppleDouble doesn't exist, try to mkdir it.
-+ */
-+ if (NULL == ( slash = strrchr( path, '/' )) ) {
-+ return -1;
-+ }
-+ *slash = 0;
-+ errno = 0;
-+ if ( ad_mkdir( path, 0777 ) < 0 ) {
-+ if ( errno == ENOENT ) {
-+ char *slash1;
-+
-+ if (NULL == ( slash1 = strrchr( path, '/' )) )
-+ return -1;
-+ errno = 0;
-+ *slash1 = 0;
-+ if ( ad_mkdir( path, 0777 ) < 0 )
-+ return -1;
-+ *slash1 = '/';
-+ if ( ad_mkdir( path, 0777 ) < 0 )
-+ return -1;
-+ }
-+ else
-+ return -1;
-+ }
-+ *slash = '/';
-+ return 0;
-+}
-+
-+/* -------------------------
- * Support inherited protection modes for AppleDouble files. The supplied
- * mode is ANDed with the parent directory's mask value in lieu of "umask",
- * and that value is returned.
-@@ -914,10 +1022,16 @@
- memset( ad, 0, sizeof( struct adouble ) );
- ad->ad_flags = flags;
- if (flags == AD_VERSION2_OSX) {
-- ad->ad_path = ad_path_osx;
-+ ad->ad_path = ad_path_osx;
-+ ad->ad_mkrf = ad_mkrf_osx;
-+ }
-+ else if (flags == AD_VERSION1_ADS) {
-+ ad->ad_path = ad_path_ads;
-+ ad->ad_mkrf = ad_mkrf_ads;
- }
- else {
-- ad->ad_path = ad_path;
-+ ad->ad_path = ad_path;
-+ ad->ad_mkrf = ad_mkrf;
- }
- }
-
-@@ -931,7 +1045,7 @@
- struct adouble *ad;
- {
- struct stat st;
-- char *slash, *ad_p;
-+ char *ad_p;
- int hoflags, admode;
- int st_invalid;
- int open_df = 0;
-@@ -1031,19 +1145,9 @@
- st_invalid = ad_mode_st(ad_p, &admode, &st);
- admode = ad_hf_mode(admode);
- if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) {
-- /*
-- * Probably .AppleDouble doesn't exist, try to
-- * mkdir it.
-- */
-- if (NULL == ( slash = strrchr( ad_p, '/' )) ) {
-- return ad_error(ad, adflags);
-- }
-- *slash = '\0';
-- errno = 0;
-- if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
-+ if (ad->ad_mkrf( ad_p) < 0) {
- return ad_error(ad, adflags);
-- }
-- *slash = '/';
-+ }
- admode = mode;
- st_invalid = ad_mode_st(ad_p, &admode, &st);
- admode = ad_hf_mode(admode);
+++ /dev/null
-Workaround for Network Trash and system without byte locking (broken nfs/afs)
-mangle OS9 "Network Trash Folder/Trash Can #2" name to
-"Network Trash Folder/Trash Can #2.<client ip>.<tcp port>"
-So multiple clients can share the same volume and have a working trash.
-
-Index: etc/afpd/directory.c
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/directory.c,v
-retrieving revision 1.71.2.4.2.12
-diff -u -r1.71.2.4.2.12 directory.c
---- etc/afpd/directory.c 11 Mar 2004 16:16:40 -0000 1.71.2.4.2.12
-+++ etc/afpd/directory.c 21 Apr 2004 12:42:03 -0000
-@@ -554,6 +554,7 @@
- * attempt to extend the current dir. tree to include path
- * as a side-effect, movecwd to that point and return the new dir
- */
-+
- static struct dir *
- extenddir( vol, dir, path )
- struct vol *vol;
-@@ -563,7 +564,25 @@
- char *save_m_name;
-
- if ( path->u_name == NULL) {
-- path->u_name = mtoupath(vol, path->m_name, dir->d_did, (path->m_type==3) );
-+#ifdef DISABLE_LOCKING
-+ int l = strlen(TRASH_PREFIX);
-+ /* XXX replace mac name with unix name */
-+ if (vol->v_trash_id && vol->v_trash_id == dir->d_did && vol->v_ip &&
-+ !strncmp(TRASH_PREFIX , path->m_name, l ) )
-+ {
-+ static char temp[MAXPATHLEN + 1];
-+ char *u;
-+
-+ strcpy(temp, path->m_name);
-+ u = temp +l;
-+ strcat(temp, ".");
-+ strcat(temp, vol->v_ip);
-+ path->u_name = temp;
-+
-+ }
-+ else
-+#endif
-+ path->u_name = mtoupath(vol, path->m_name, dir->d_did, (path->m_type==3) );
- }
- path->dir = NULL;
-
-Index: etc/afpd/enumerate.c
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/enumerate.c,v
-retrieving revision 1.39.2.2.2.4
-diff -u -r1.39.2.2.2.4 enumerate.c
---- etc/afpd/enumerate.c 11 Mar 2004 02:01:59 -0000 1.39.2.2.2.4
-+++ etc/afpd/enumerate.c 21 Apr 2004 12:42:04 -0000
-@@ -54,9 +54,39 @@
- if (id == 0) {
- return NULL;
- }
-+
-+#ifdef DISABLE_LOCKING
-+ if (!path->m_name) {
-+ int l = strlen(TRASH_PREFIX);
-+ /* XXX */
-+ if (vol->v_trash_id && vol->v_trash_id == dir->d_did && vol->v_ip &&
-+ !strncmp(TRASH_PREFIX , upath, l ) )
-+ {
-+ static char temp[MAXPATHLEN + 1];
-+ char *u;
-+
-+ strcpy(temp, upath);
-+ u = temp +l;
-+
-+ while (*u >= '0' && *u <= '9') {
-+ u++;
-+ }
-+ if (*u == '.') {
-+ *u = '\0';
-+ }
-+ path->m_name = temp;
-+ }
-+
-+ else if(!(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
-+ return NULL;
-+ }
-+ }
-+#else
- if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
-- return NULL;
-+ return NULL;
- }
-+#endif
-+
- name = path->m_name;
- if ((cdir = dirnew(name, upath)) == NULL) {
- LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
-@@ -185,6 +215,32 @@
- return name;
- }
-
-+#ifdef DISABLE_LOCKING
-+/* ----------------------------- */
-+int check_trash(const struct vol *vol, char *name)
-+{
-+int l = strlen(TRASH_PREFIX);
-+char *u;
-+
-+ if (strncmp(TRASH_PREFIX , name, l))
-+ return 0;
-+ /* */
-+ u = name +l;
-+ while (*u >= '0' && *u <= '9') {
-+ u++;
-+ }
-+
-+ if (u == name +l)
-+ return 0;
-+
-+ if (*u == '.' && !strcmp(vol->v_ip, u +1)) {
-+ return 0;
-+ }
-+ /* hide this one */
-+ return 1;
-+}
-+#endif
-+
- /* ----------------------------- */
- int
- for_each_dirent(const struct vol *vol, char *name, dir_loop fn, void *data)
-@@ -193,15 +249,28 @@
- struct dirent *de;
- char *m_name;
- int ret;
-+#ifdef DISABLE_LOCKING
-+ int mangle_trash = 0;
-+#endif
-
- if (NULL == ( dp = opendir( name)) ) {
- return -1;
- }
-+
-+#ifdef DISABLE_LOCKING
-+ if (vol->v_trash_id && vol->v_trash_id == curdir->d_did && !strcmp(name, ".")) {
-+ mangle_trash = 1;
-+ }
-+#endif
- ret = 0;
- for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
- if (!(m_name = check_dirent(vol, de->d_name)))
- continue;
-
-+#ifdef DISABLE_LOCKING
-+ if (mangle_trash && check_trash(vol, de->d_name))
-+ continue;
-+#endif
- ret++;
- if (fn && fn(de,m_name, data) < 0) {
- closedir(dp);
-Index: etc/afpd/volume.c
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/volume.c,v
-retrieving revision 1.51.2.7.2.28
-diff -u -r1.51.2.7.2.28 volume.c
---- etc/afpd/volume.c 6 Apr 2004 23:29:37 -0000 1.51.2.7.2.28
-+++ etc/afpd/volume.c 21 Apr 2004 12:42:05 -0000
-@@ -73,7 +73,11 @@
-
- static struct vol *Volumes = NULL;
- static u_int16_t lastvid = 0;
--static char *Trash = "\02\024Network Trash Folder";
-+
-+/* type, len, name */
-+static char *Trash2 = "\02\024Network Trash Folder";
-+/* type, hint (4 bytes), len (2bytes), name */
-+static char *Trash3 = "\03\0\0\0\0\0\024Network Trash Folder";
-
- static struct extmap *Extmap = NULL, *Defextmap = NULL;
- static int Extmap_cnt;
-@@ -1038,6 +1042,10 @@
- free(vol->v_forceuid);
- free(vol->v_forcegid);
- #endif /* FORCE_UIDGID */
-+
-+#ifdef DISABLE_LOCKING
-+ free(vol->v_ip);
-+#endif
- }
-
- /* ------------------------------- */
-@@ -1730,9 +1738,31 @@
- goto openvol_err;
- }
- }
-- else {
-- p = Trash;
-- cname( volume, volume->v_dir, &p );
-+#ifndef DISABLE_LOCKING
-+ else
-+#endif
-+ {
-+ struct path *s_path;
-+
-+ /* use the right name format */
-+ p = (afp_version>= 30)?Trash3:Trash2;
-+ s_path = cname( volume, volume->v_dir, &p );
-+#ifdef DISABLE_LOCKING
-+ if (s_path && *s_path->m_name == '\0' ) {
-+ /* XXXX should do the same with ASP, could use volxlate but there's ':' in $p */
-+ if (obj->proto == AFPPROTO_DSI) {
-+ DSI *dsi = obj->handle;
-+
-+ /* cname moved into dest folder */
-+ volume->v_trash_id = curdir->d_did;
-+ volume->v_ip = malloc(MAXPATHLEN +1);
-+ if (volume->v_ip) {
-+ sprintf(volume->v_ip, "%s.%u", inet_ntoa(dsi->client.sin_addr),
-+ ntohs(dsi->client.sin_port));
-+ }
-+ }
-+ }
-+#endif
- }
- return( AFP_OK );
- }
-Index: etc/afpd/volume.h
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/volume.h,v
-retrieving revision 1.19.2.5.2.6
-diff -u -r1.19.2.5.2.6 volume.h
---- etc/afpd/volume.h 11 Mar 2004 02:02:04 -0000 1.19.2.5.2.6
-+++ etc/afpd/volume.h 21 Apr 2004 12:42:05 -0000
-@@ -81,6 +81,12 @@
- /* adouble indirection */
- int (*validupath)(const struct vol *, const char *);
- char *(*ad_path)(const char *, int);
-+
-+#ifdef DISABLE_LOCKING
-+ /* for OS 9 trash when there's no working byte locking (afs, nfs) */
-+ cnid_t v_trash_id;
-+ char *v_ip;
-+#endif
- };
-
- #ifdef NO_LARGE_VOL_SUPPORT
-@@ -167,6 +173,8 @@
- #define VOLPBIT_BSIZE 11 /* block size */
-
-
-+#define TRASH_PREFIX "Trash Can #"
-+
- #define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? \
- ADFLAGS_NOADOUBLE : 0)
- #ifdef AFP3x
+++ /dev/null
-Index: source/smbd/nttrans.c
-===================================================================
---- source/smbd/nttrans.c (revision 1473)
-+++ source/smbd/nttrans.c (working copy)
-@@ -661,11 +661,16 @@
- * Check to see if this is a mac fork of some kind.
- */
-
-- if( strchr_m(fname, ':')) {
-- END_PROFILE(SMBntcreateX);
-- return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-- }
--
-+ if( !strchr_m(fname, ':')) {
-+ /* it's not an alternate stream */
-+ END_PROFILE(SMBntcreateX);
-+ return(ERROR_DOS(ERRDOS,ERRbadfid));
-+ }
-+ else if (-1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
-+ /* fs have no support for alternate streams */
-+ END_PROFILE(SMBntcreateX);
-+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-+ }
- /*
- we need to handle the case when we get a
- relative open relative to a file and the
-@@ -673,26 +678,29 @@
- (hint from demyn plantenberg)
- */
-
-- END_PROFILE(SMBntcreateX);
-- return(ERROR_DOS(ERRDOS,ERRbadfid));
-+ /*
-+ * Copy in the base name.
-+ */
-+ pstrcpy( fname, dir_fsp->fsp_name );
-+ dir_name_len = strlen(fname);
- }
-+ else { /* it's a dir */
-+ /*
-+ * Copy in the base directory name.
-+ */
-
-- /*
-- * Copy in the base directory name.
-- */
-+ pstrcpy( fname, dir_fsp->fsp_name );
-+ dir_name_len = strlen(fname);
-
-- pstrcpy( fname, dir_fsp->fsp_name );
-- dir_name_len = strlen(fname);
-+ /*
-+ * Ensure it ends in a '\'.
-+ */
-
-- /*
-- * Ensure it ends in a '\'.
-- */
--
-- if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
-- pstrcat(fname, "/");
-- dir_name_len++;
-- }
--
-+ if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
-+ pstrcat(fname, "/");
-+ dir_name_len++;
-+ }
-+ }
- srvstr_get_path(inbuf, rel_fname, smb_buf(inbuf), sizeof(rel_fname), 0, STR_TERMINATE, &status,False);
- if (!NT_STATUS_IS_OK(status)) {
- END_PROFILE(SMBntcreateX);
-@@ -709,7 +717,6 @@
- /*
- * Check to see if this is a mac fork of some kind.
- */
--
- if( strchr_m(fname, ':')) {
-
- #ifdef HAVE_SYS_QUOTAS
-@@ -725,8 +732,11 @@
- */
- } else {
- #endif
-- END_PROFILE(SMBntcreateX);
-- return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-+ if (-1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
-+ END_PROFILE(SMBntcreateX);
-+ return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-+ }
-+
- #ifdef HAVE_SYS_QUOTAS
- }
- #endif
-@@ -1235,12 +1245,10 @@
- }
-
- /*
-- * Check to see if this is a mac fork of some kind.
-+ * Check to see if this is a mac fork of some kind. FIXME
- */
--
-- if( strchr_m(fname, ':'))
-+ if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
- return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
--
- return ERROR_DOS(ERRDOS,ERRbadfid);
- }
-
-@@ -1278,7 +1286,7 @@
- * Check to see if this is a mac fork of some kind.
- */
-
-- if( strchr_m(fname, ':'))
-+ if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
- return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
- }
-
-Index: source/smbd/vfs.c
-===================================================================
---- source/smbd/vfs.c (revision 1473)
-+++ source/smbd/vfs.c (working copy)
-@@ -142,7 +142,10 @@
- vfswrap_fremovexattr,
- vfswrap_setxattr,
- vfswrap_lsetxattr,
-- vfswrap_fsetxattr
-+ vfswrap_fsetxattr,
-+
-+ /* alternate streams operations. */
-+ vfswrap_listads
- }
- };
-
-Index: source/smbd/vfs-wrap.c
-===================================================================
---- source/smbd/vfs-wrap.c (revision 1473)
-+++ source/smbd/vfs-wrap.c (working copy)
-@@ -1029,3 +1029,14 @@
- {
- return sys_fsetxattr(fd, name, value, size, flags);
- }
-+
-+/****************************************************************
-+ Alternate stream operations.
-+*****************************************************************/
-+
-+ssize_t vfswrap_listads(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+ errno = ENOSYS;
-+ return -1;
-+}
-+
-Index: source/smbd/trans2.c
-===================================================================
---- source/smbd/trans2.c (revision 1473)
-+++ source/smbd/trans2.c (working copy)
-@@ -406,6 +406,159 @@
- }
-
- /****************************************************************************
-+ ****************************************************************************
-+ Return a linked list of the alternate streams Plus the total size
-+****************************************************************************/
-+struct ads_list {
-+ struct ads_list *next, *prev;
-+ struct ads_struct ads;
-+};
-+
-+static struct ads_list *get_ads_list(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *fname, size_t *pads_total_len)
-+{
-+ /* Get a list of all ads with size, lax namesize is 64k. */
-+ size_t ads_namelist_size = 4096;
-+ char *ads_namelist;
-+ char *p;
-+ ssize_t sizeret;
-+ int i;
-+ struct ads_list *ads_list_head = NULL;
-+
-+ *pads_total_len = 0;
-+
-+ DEBUG(10,("get_ads_list\n" ));
-+
-+ for (i = 0, ads_namelist = talloc(mem_ctx, ads_namelist_size); i < 6;
-+ ads_namelist = talloc_realloc(mem_ctx, ads_namelist, ads_namelist_size), i++) {
-+
-+ sizeret = SMB_VFS_LISTADS(conn, fname, ads_namelist, ads_namelist_size);
-+ if (sizeret == -1 && errno == ERANGE) {
-+ ads_namelist_size *= 2;
-+ } else {
-+ break;
-+ }
-+ }
-+
-+ if (sizeret == -1)
-+ return NULL;
-+
-+ DEBUG(10,("get_ads_list: ads_namelist size = %d\n", sizeret ));
-+
-+ if (sizeret) {
-+ for (p = ads_namelist; p - ads_namelist < sizeret; p += strlen(p) +1) {
-+ struct ads_list *listp, *tmp;
-+ SMB_STRUCT_STAT sbuf;
-+ char *t;
-+
-+ listp = talloc(mem_ctx, sizeof(struct ads_list));
-+ if (!listp)
-+ return NULL;
-+
-+ listp->ads.name = talloc_strdup(mem_ctx, p);
-+ if (!listp->ads.name)
-+ return NULL;
-+
-+ listp->ads.size = 0;
-+ listp->ads.allocation_size = 0;
-+
-+ t = talloc_asprintf(mem_ctx, "%s%s", fname, p);
-+ if (!t)
-+ return NULL;
-+ if (!SMB_VFS_STAT(conn, t ,&sbuf)) {
-+ listp->ads.size = get_file_size(sbuf);
-+ listp->ads.allocation_size = get_allocation_size(NULL,&sbuf);
-+ }
-+ /* FIXME get ride of this */
-+ {
-+ fstring dos_ads_name;
-+
-+ push_ascii_fstring(dos_ads_name, listp->ads.name);
-+ *pads_total_len += strlen(dos_ads_name) + 1 + 24;
-+ DEBUG(10,("get_ads_list: total_len = %u, %s, size = %llu\n",
-+ *pads_total_len, dos_ads_name, listp->ads.size ));
-+ }
-+ DLIST_ADD_END(ads_list_head, listp, tmp);
-+ }
-+ }
-+
-+ DEBUG(10,("get_ads_list: total_len = %u\n", *pads_total_len));
-+ return ads_list_head;
-+}
-+
-+/****************************************************************************
-+ Fill a qfilepathinfo buffer with alternate streams.
-+ Returns the length of the buffer that was filled.
-+****************************************************************************/
-+
-+static unsigned int fill_ads_buffer(char *pdata, unsigned int total_data_size,
-+ connection_struct *conn, files_struct *fsp, const char *fname)
-+{
-+ unsigned int ret_data_size = 0;
-+ char *p = pdata;
-+ size_t total_ads_len;
-+ TALLOC_CTX *mem_ctx;
-+ struct ads_list *ads_list;
-+
-+ SMB_ASSERT(total_data_size >= 24);
-+
-+ mem_ctx = talloc_init("fill_ads_buffer");
-+ if (!mem_ctx) {
-+ return 0;
-+ }
-+
-+ ads_list = get_ads_list(mem_ctx, conn, fsp, fname, &total_ads_len);
-+ if (!ads_list) {
-+ talloc_destroy(mem_ctx);
-+ return 0;
-+ }
-+
-+ if (total_ads_len > total_data_size) {
-+ talloc_destroy(mem_ctx);
-+ return 0;
-+ }
-+
-+ for (p = pdata; ads_list; ads_list = ads_list->next) {
-+#if 0
-+ size_t dos_namelen;
-+ fstring dos_ads_name;
-+
-+ push_ascii_fstring(dos_ads_name, ads_list->ads.name);
-+ dos_namelen = strlen(dos_ads_name);
-+ if (dos_namelen > 255 || dos_namelen == 0) {
-+ break;
-+ }
-+ if (dos_namelen + 24 > total_data_size) {
-+ break;
-+ }
-+#endif
-+ /* We know we have room. */
-+ size_t byte_len = dos_PutUniCode(p +24, ads_list->ads.name, -1, False);
-+ size_t off = SMB_ROUNDUP(24 +byte_len, 8);
-+
-+ SIVAL(p,0,0); /* from ethereal next entry offset */
-+ SIVAL(p,4, byte_len); /* Byte length of unicode string :filename:$DATA */
-+ SOFF_T(p,8, ads_list->ads.size);
-+ SOFF_T(p,16, ads_list->ads.allocation_size);
-+ if (ads_list->next) {
-+ SIVAL(p,0, off);
-+ }
-+ else {
-+ /* don't pad the last one */
-+ off = 24 +byte_len;
-+ }
-+
-+ total_data_size -= off;
-+ p += off;
-+ }
-+
-+ ret_data_size = PTR_DIFF(p, pdata);
-+ DEBUG(10,("fill_ads_buffer: data_size = %u, total_ads_len = %u\n",
-+ ret_data_size, total_ads_len ));
-+ talloc_destroy(mem_ctx);
-+ return ret_data_size;
-+}
-+
-+/****************************************************************************
- Send the required number of replies back.
- We assume all fields other than the data fields are
- set correctly for the type of call.
-@@ -2653,7 +2806,7 @@
- data_size = 4;
- break;
-
--#if 0
-+#if 1
- /*
- * NT4 server just returns "invalid query" to this - if we try to answer
- * it then NTws gets a BSOD! (tridge).
-@@ -2663,16 +2816,24 @@
- #endif
- case SMB_FILE_STREAM_INFORMATION:
- DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_STREAM_INFORMATION\n"));
-- if (mode & aDIR) {
-- data_size = 0;
-- } else {
-- size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
-- SIVAL(pdata,0,0); /* ??? */
-- SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
-- SOFF_T(pdata,8,file_size);
-- SIVAL(pdata,16,allocation_size);
-- SIVAL(pdata,20,0); /* ??? */
-- data_size = 24 + byte_len;
-+ {
-+ size_t off;
-+
-+ if (mode & aDIR) {
-+ off = 0;
-+ } else {
-+ size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
-+
-+ off = SMB_ROUNDUP(24 +byte_len, 8); /* FIXME or 8 ? */
-+ SIVAL(pdata,0,0); /* from ethereal next entry offset */
-+ SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
-+ SOFF_T(pdata,8,file_size);
-+ SOFF_T(pdata,16,allocation_size);
-+ }
-+ if ((data_size = fill_ads_buffer(pdata +off, data_size, conn, fsp, fname))) {
-+ SIVAL(pdata,0,off);
-+ }
-+ data_size += off;
- }
- break;
-
-Index: source/include/vfs_macros.h
-===================================================================
---- source/include/vfs_macros.h (revision 1473)
-+++ source/include/vfs_macros.h (working copy)
-@@ -119,6 +119,9 @@
- #define SMB_VFS_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs.ops.lsetxattr((conn)->vfs.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
- #define SMB_VFS_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs.ops.fsetxattr((fsp)->conn->vfs.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
-
-+/* ADS operations. */
-+#define SMB_VFS_LISTADS(conn,path,list,size) ((conn)->vfs.ops.listads((conn)->vfs.handles.listads,(conn),(path),(list),(size)))
-+
- /*******************************************************************
- Don't access conn->vfs_opaque.ops directly!!!
- Use this macros!
-@@ -217,6 +220,9 @@
- #define SMB_VFS_OPAQUE_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs_opaque.ops.lsetxattr((conn)->vfs_opaque.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
- #define SMB_VFS_OPAQUE_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs_opaque.ops.fsetxattr((fsp)->conn->vfs_opaque.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
-
-+/* ADS operations. */
-+#define SMB_VFS_OPAQUE_LISTADS(conn,path,list,size) ((conn)->vfs_opaque.ops.listads((conn)->vfs_opaque.handles.listads,(conn),(path),(list),(size)))
-+
- /*******************************************************************
- Don't access handle->vfs_next.ops.* directly!!!
- Use this macros!
-@@ -315,4 +321,7 @@
- #define SMB_VFS_NEXT_LSETXATTR(handle,conn,path,name,value,size,flags) ((handle)->vfs_next.ops.lsetxattr((handle)->vfs_next.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
- #define SMB_VFS_NEXT_FSETXATTR(handle,fsp,fd,name,value,size,flags) ((handle)->vfs_next.ops.fsetxattr((handle)->vfs_next.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
-
-+/* ADS operations. */
-+#define SMB_VFS_NEXT_LISTADS(handle,conn,path,list,size) ((handle)->vfs_next.ops.listads((handle)->vfs_next.handles.listads,(conn),(path),(list),(size)))
-+
- #endif /* _VFS_MACROS_H */
-Index: source/include/vfs.h
-===================================================================
---- source/include/vfs.h (revision 1473)
-+++ source/include/vfs.h (working copy)
-@@ -55,7 +55,8 @@
- /* Changed to version 8 includes EA calls. JRA. */
- /* Changed to version 9 to include the get_shadow_data call. --metze */
- /* Changed to version 10 to include pread/pwrite calls. */
--#define SMB_VFS_INTERFACE_VERSION 10
-+/* Changed to version 11 to include alternate data streams. */
-+#define SMB_VFS_INTERFACE_VERSION 11
-
-
- /* to bug old modules witch are trying to compile with the old functions */
-@@ -185,6 +186,9 @@
- SMB_VFS_OP_SETXATTR,
- SMB_VFS_OP_LSETXATTR,
- SMB_VFS_OP_FSETXATTR,
-+
-+ /* alternate stream */
-+ SMB_VFS_OP_LISTADS,
-
- /* This should always be last enum value */
-
-@@ -294,6 +298,9 @@
- int (*lsetxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags);
- int (*fsetxattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int filedes, const char *name, const void *value, size_t size, int flags);
-
-+ /* alternate stream operations. */
-+ ssize_t (*listads)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size);
-+
- } ops;
-
- struct vfs_handles_pointers {
-@@ -394,6 +401,8 @@
- struct vfs_handle_struct *lsetxattr;
- struct vfs_handle_struct *fsetxattr;
-
-+ /* alternate stream operations. */
-+ struct vfs_handle_struct *listads;
- } handles;
- };
-
-Index: source/include/smb.h
-===================================================================
---- source/include/smb.h (revision 1473)
-+++ source/include/smb.h (working copy)
-@@ -1703,6 +1703,12 @@
- DATA_BLOB value;
- };
-
-+struct ads_struct {
-+ SMB_BIG_UINT size;
-+ SMB_BIG_UINT allocation_size;
-+ char *name;
-+};
-+
- /* EA names used internally in Samba. KEEP UP TO DATE with prohibited_ea_names in trans2.c !. */
- #define SAMBA_POSIX_INHERITANCE_EA_NAME "user.SAMBA_PAI"
- /* EA to use for DOS attributes */
+++ /dev/null
-diff -ur ../smb3.0a20.orig/source/include/smb.h ./source/include/smb.h
---- ../smb3.0a20.orig/source/include/smb.h Mon Jan 6 18:04:22 2003
-+++ ./source/include/smb.h Fri Jun 4 05:34:14 2004
-@@ -1652,4 +1652,10 @@
-
- extern struct poptOption popt_common_debug[];
-
-+struct ads_struct {
-+ SMB_BIG_UINT size;
-+ SMB_BIG_UINT allocation_size;
-+ char *name;
-+};
-+
- #endif /* _SMB_H */
-diff -ur ../smb3.0a20.orig/source/include/smb_macros.h ./source/include/smb_macros.h
---- ../smb3.0a20.orig/source/include/smb_macros.h Mon Jan 6 18:04:22 2003
-+++ ./source/include/smb_macros.h Fri Jun 4 18:24:20 2004
-@@ -291,4 +291,8 @@
-
- #define vfs_chdir(conn,fname) ((conn)->vfs_ops.chdir((conn),fname))
-
-+/*******************************************************************
-+ A wrapper for vfs_listads().
-+********************************************************************/
-+#define vfs_listads(conn,path,list,size) ((conn)->vfs_ops.listads((conn),(path),(list),(size)))
- #endif /* _SMB_MACROS_H */
-diff -ur ../smb3.0a20.orig/source/include/vfs.h ./source/include/vfs.h
---- ../smb3.0a20.orig/source/include/vfs.h Mon Jan 6 18:04:23 2003
-+++ ./source/include/vfs.h Fri Jun 4 16:30:14 2004
-@@ -45,7 +45,8 @@
- /* Changed to version 3 for POSIX acl extensions. JRA. */
- /* Changed to version 4 for cascaded VFS interface. Alexander Bokovoy. */
- /* Changed to version 5 for sendfile addition. JRA. */
--#define SMB_VFS_INTERFACE_VERSION 5
-+/* Changed to version 11 to include alternate data streams. */
-+#define SMB_VFS_INTERFACE_VERSION 11
-
-
- /* Version of supported cascaded interface backward copmatibility.
-@@ -173,6 +174,9 @@
- int (*sys_acl_free_text)(struct connection_struct *conn, char *text);
- int (*sys_acl_free_acl)(struct connection_struct *conn, SMB_ACL_T posix_acl);
- int (*sys_acl_free_qualifier)(struct connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype);
-+
-+ /* alternate stream operations. */
-+ ssize_t (*listads)(/* struct vfs_handle_struct *handle, */ struct connection_struct *conn,const char *path, char *list, size_t size);
- };
-
- struct vfs_options {
-@@ -269,6 +273,9 @@
- SMB_VFS_OP_SYS_ACL_FREE_ACL,
- SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER,
-
-+ /* alternate stream */
-+ SMB_VFS_OP_LISTADS,
-+
- /* This should always be last enum value */
-
- SMB_VFS_OP_LAST
-diff -ur ../smb3.0a20.orig/source/smbd/nttrans.c ./source/smbd/nttrans.c
---- ../smb3.0a20.orig/source/smbd/nttrans.c Mon Jan 6 18:05:44 2003
-+++ ./source/smbd/nttrans.c Fri Jul 2 07:38:38 2004
-@@ -52,6 +52,8 @@
- FILE_GENERIC_ALL
- };
-
-+#define SMB_VFS_LISTADS vfs_listads
-+
- /****************************************************************************
- Send the required number of replies back.
- We assume all fields other than the data fields are
-@@ -625,30 +627,40 @@
- * Check to see if this is a mac fork of some kind.
- */
-
-- if( strchr_m(fname, ':')) {
-+ if( !strchr_m(fname, ':')) {
-+ /* it's not an alternate stream */
-+ END_PROFILE(SMBntcreateX);
-+ return(ERROR_DOS(ERRDOS,ERRbadfid));
-+ }
-+ else if (-1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
-+ /* fs have no support for alternate streams */
- END_PROFILE(SMBntcreateX);
- return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
- }
-- END_PROFILE(SMBntcreateX);
-- return(ERROR_DOS(ERRDOS,ERRbadfid));
-- }
-
-- /*
-- * Copy in the base directory name.
-- */
-+ /*
-+ * Copy in the base name.
-+ */
-+ pstrcpy( fname, dir_fsp->fsp_name );
-+ dir_name_len = strlen(fname);
-+ }
-+ else { /* it's a dir */
-+ /*
-+ * Copy in the base directory name.
-+ */
-
-- pstrcpy( fname, dir_fsp->fsp_name );
-- dir_name_len = strlen(fname);
-+ pstrcpy( fname, dir_fsp->fsp_name );
-+ dir_name_len = strlen(fname);
-
-- /*
-- * Ensure it ends in a '\'.
-- */
--
-- if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
-- pstrcat(fname, "\\");
-- dir_name_len++;
-- }
-+ /*
-+ * Ensure it ends in a '\'.
-+ */
-
-+ if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
-+ pstrcat(fname, "\\");
-+ dir_name_len++;
-+ }
-+ }
- srvstr_pull_buf(inbuf, &fname[dir_name_len], smb_buf(inbuf), sizeof(fname)-dir_name_len, STR_TERMINATE);
- } else {
- srvstr_pull_buf(inbuf, fname, smb_buf(inbuf), sizeof(fname), STR_TERMINATE);
-@@ -656,8 +668,7 @@
- /*
- * Check to see if this is a mac fork of some kind.
- */
--
-- if( strchr_m(fname, ':')) {
-+ if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
- END_PROFILE(SMBntcreateX);
- return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
- }
-@@ -1138,12 +1149,10 @@
- srvstr_pull(inbuf, fname, params+53, sizeof(fname), total_parameter_count-53, STR_TERMINATE);
-
- /*
-- * Check to see if this is a mac fork of some kind.
-+ * Check to see if this is a mac fork of some kind. FIXME ADS
- */
--
-- if( strchr_m(fname, ':'))
-+ if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
- return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
--
- return ERROR_DOS(ERRDOS,ERRbadfid);
- }
-
-@@ -1172,7 +1181,7 @@
- * Check to see if this is a mac fork of some kind.
- */
-
-- if( strchr_m(fname, ':'))
-+ if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
- return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
- }
-
-diff -ur ../smb3.0a20.orig/source/smbd/trans2.c ./source/smbd/trans2.c
---- ../smb3.0a20.orig/source/smbd/trans2.c Mon Jan 6 18:05:48 2003
-+++ ./source/smbd/trans2.c Thu Jul 1 03:06:42 2004
-@@ -50,6 +50,162 @@
- return ret;
- }
-
-+#define SMB_VFS_STAT vfs_stat
-+#define SMB_VFS_LISTADS vfs_listads
-+
-+/****************************************************************************
-+ ****************************************************************************
-+ Return a linked list of the alternate streams Plus the total size
-+****************************************************************************/
-+struct ads_list {
-+ struct ads_list *next, *prev;
-+ struct ads_struct ads;
-+};
-+
-+static struct ads_list *get_ads_list(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *fname, size_t *pads_total_len)
-+{
-+ /* Get a list of all ads with size, lax namesize is 64k. */
-+ size_t ads_namelist_size = 4096;
-+ char *ads_namelist;
-+ char *p;
-+ ssize_t sizeret;
-+ int i;
-+ struct ads_list *ads_list_head = NULL;
-+
-+ *pads_total_len = 0;
-+
-+ DEBUG(10,("get_ads_list\n" ));
-+
-+ for (i = 0, ads_namelist = talloc(mem_ctx, ads_namelist_size); i < 6;
-+ ads_namelist = talloc_realloc(mem_ctx, ads_namelist, ads_namelist_size), i++) {
-+
-+ sizeret = SMB_VFS_LISTADS(conn, fname, ads_namelist, ads_namelist_size);
-+ if (sizeret == -1 && errno == ERANGE) {
-+ ads_namelist_size *= 2;
-+ } else {
-+ break;
-+ }
-+ }
-+
-+ if (sizeret == -1)
-+ return NULL;
-+
-+ DEBUG(10,("get_ads_list: ads_namelist size = %d\n", sizeret ));
-+
-+ if (sizeret) {
-+ for (p = ads_namelist; p - ads_namelist < sizeret; p += strlen(p) +1) {
-+ struct ads_list *listp, *tmp;
-+ SMB_STRUCT_STAT sbuf;
-+ char *t;
-+
-+ listp = talloc(mem_ctx, sizeof(struct ads_list));
-+ if (!listp)
-+ return NULL;
-+
-+ listp->ads.name = talloc_strdup(mem_ctx, p);
-+ if (!listp->ads.name)
-+ return NULL;
-+
-+ listp->ads.size = 0;
-+ listp->ads.allocation_size = 0;
-+
-+ t = talloc_asprintf(mem_ctx, "%s%s", fname, p);
-+ if (!t)
-+ return NULL;
-+ if (!SMB_VFS_STAT(conn, t ,&sbuf)) {
-+ listp->ads.size = get_file_size(sbuf);
-+ listp->ads.allocation_size = get_allocation_size(NULL,&sbuf);
-+ }
-+ /* FIXME get ride of this */
-+ {
-+ fstring dos_ads_name;
-+
-+ push_ascii_fstring(dos_ads_name, listp->ads.name);
-+ *pads_total_len += strlen(dos_ads_name) + 1 + 24;
-+ DEBUG(10,("get_ads_list: total_len = %u, %s, size = %llu\n",
-+ *pads_total_len, dos_ads_name, listp->ads.size ));
-+ }
-+ DLIST_ADD_END(ads_list_head, listp, tmp);
-+ }
-+ }
-+
-+ DEBUG(10,("get_ads_list: total_len = %u\n", *pads_total_len));
-+ return ads_list_head;
-+}
-+
-+/****************************************************************************
-+ Fill a qfilepathinfo buffer with alternate streams.
-+ Returns the length of the buffer that was filled.
-+****************************************************************************/
-+
-+static unsigned int fill_ads_buffer(char *pdata, unsigned int total_data_size,
-+ connection_struct *conn, files_struct *fsp, const char *fname)
-+{
-+ unsigned int ret_data_size = 0;
-+ char *p = pdata;
-+ size_t total_ads_len;
-+ TALLOC_CTX *mem_ctx;
-+ struct ads_list *ads_list;
-+
-+ SMB_ASSERT(total_data_size >= 24);
-+
-+ mem_ctx = talloc_init(/*"fill_ads_buffer"*/);
-+ if (!mem_ctx) {
-+ return 0;
-+ }
-+
-+ ads_list = get_ads_list(mem_ctx, conn, fsp, fname, &total_ads_len);
-+ if (!ads_list) {
-+ talloc_destroy(mem_ctx);
-+ return 0;
-+ }
-+
-+ if (total_ads_len > total_data_size) {
-+ talloc_destroy(mem_ctx);
-+ return 0;
-+ }
-+
-+ for (p = pdata; ads_list; ads_list = ads_list->next) {
-+#if 0
-+ size_t dos_namelen;
-+ fstring dos_ads_name;
-+
-+ push_ascii_fstring(dos_ads_name, ads_list->ads.name);
-+ dos_namelen = strlen(dos_ads_name);
-+ if (dos_namelen > 255 || dos_namelen == 0) {
-+ break;
-+ }
-+ if (dos_namelen + 24 > total_data_size) {
-+ break;
-+ }
-+#endif
-+ /* We know we have room. */
-+ size_t byte_len = dos_PutUniCode(p +24, ads_list->ads.name, -1, False);
-+ size_t off = SMB_ROUNDUP(24 +byte_len, 8);
-+
-+ SIVAL(p,0,0); /* from ethereal next entry offset */
-+ SIVAL(p,4, byte_len); /* Byte length of unicode string :filename:$DATA */
-+ SOFF_T(p,8, ads_list->ads.size);
-+ SOFF_T(p,16, ads_list->ads.allocation_size);
-+ if (ads_list->next) {
-+ SIVAL(p,0, off);
-+ }
-+ else {
-+ /* don't pad the last one */
-+ off = 24 +byte_len;
-+ }
-+
-+ total_data_size -= off;
-+ p += off;
-+ }
-+
-+ ret_data_size = PTR_DIFF(p, pdata);
-+ DEBUG(10,("fill_ads_buffer: data_size = %u, total_ads_len = %u\n",
-+ ret_data_size, total_ads_len ));
-+ talloc_destroy(mem_ctx);
-+ return ret_data_size;
-+}
-+
- /****************************************************************************
- Send the required number of replies back.
- We assume all fields other than the data fields are
-@@ -1934,7 +2090,7 @@
- break;
- }
-
--#if 0
-+#if 1
- /*
- * NT4 server just returns "invalid query" to this - if we try to answer
- * it then NTws gets a BSOD! (tridge).
-@@ -1943,16 +2099,24 @@
- case SMB_QUERY_FILE_STREAM_INFO:
- #endif
- case SMB_FILE_STREAM_INFORMATION:
-- if (mode & aDIR) {
-- data_size = 0;
-- } else {
-- size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
-- SIVAL(pdata,0,0); /* ??? */
-- SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
-- SOFF_T(pdata,8,file_size);
-- SIVAL(pdata,16,allocation_size);
-- SIVAL(pdata,20,0); /* ??? */
-- data_size = 24 + byte_len;
-+ {
-+ size_t off;
-+
-+ if (mode & aDIR) {
-+ off = 0;
-+ } else {
-+ size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
-+
-+ off = SMB_ROUNDUP(24 +byte_len, 8); /* FIXME or 8 ? */
-+ SIVAL(pdata,0,0); /* from ethereal next entry offset */
-+ SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
-+ SOFF_T(pdata,8,file_size);
-+ SOFF_T(pdata,16,allocation_size);
-+ }
-+ if ((data_size = fill_ads_buffer(pdata +off, data_size, conn, fsp, fname))) {
-+ SIVAL(pdata,0,off);
-+ }
-+ data_size += off;
- }
- break;
-
-diff -ur ../smb3.0a20.orig/source/smbd/vfs-wrap.c ./source/smbd/vfs-wrap.c
---- ../smb3.0a20.orig/source/smbd/vfs-wrap.c Mon Jan 6 18:05:49 2003
-+++ ./source/smbd/vfs-wrap.c Fri Jun 4 16:33:16 2004
-@@ -739,3 +739,14 @@
- {
- return sys_acl_free_qualifier(qualifier, tagtype);
- }
-+
-+/****************************************************************
-+ Alternate stream operations.
-+*****************************************************************/
-+
-+ssize_t vfswrap_listads(/* struct vfs_handle_struct *handle, */ struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+ errno = ENOSYS;
-+ return -1;
-+}
-+
-Only in ./source/smbd: vfs-wrap.c.orig
-diff -ur ../smb3.0a20.orig/source/smbd/vfs.c ./source/smbd/vfs.c
---- ../smb3.0a20.orig/source/smbd/vfs.c Mon Jan 6 18:05:48 2003
-+++ ./source/smbd/vfs.c Fri Jun 4 05:40:09 2004
-@@ -124,7 +124,10 @@
- vfswrap_sys_acl_get_perm,
- vfswrap_sys_acl_free_text,
- vfswrap_sys_acl_free_acl,
-- vfswrap_sys_acl_free_qualifier
-+ vfswrap_sys_acl_free_qualifier,
-+
-+ /* alternate streams operations. */
-+ vfswrap_listads
- };
-
- /****************************************************************************
+++ /dev/null
-diff -Nur vfs/Makefile vfs.new/Makefile
---- vfs/Makefile Thu Jan 1 00:00:00 1970
-+++ vfs.new/Makefile Mon Jul 12 10:48:56 2004
-@@ -0,0 +1,40 @@
-+##########################################################################
-+# Makefile for Samba VFS modules
-+###########################################################################
-+
-+CC=gcc -g
-+LIBTOOL=/usr/bin/libtool
-+# REPLACE with samba source
-+SMB=/u/redhat/paris/cvs/samba/smb3.0a20
-+
-+# REPLACE with samba build folder
-+BUILD=/mnt/hdd/build/smb.1.3
-+
-+CFLAGS=-Wall -I $(BUILD)/include \
-+-I$(SMB)/source -I$(SMB)/source/include -I$(SMB)/source/ubiqx -I$(SMB)/source/smbwrapper
-+
-+
-+LDFLAGS=-shared
-+
-+VFS_OBJS=vfs_ads.so
-+
-+SHELL=/bin/sh
-+
-+default: $(VFS_OBJS)
-+
-+# Pattern rules
-+
-+%.so: %.lo
-+ @echo Linking $<
-+ @$(LIBTOOL) --mode=link $(CC) -o $@ $< $(LDFLAGS)
-+
-+%.lo: %.c
-+ @echo Compiling $<
-+ @$(LIBTOOL) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
-+
-+# Misc targets
-+
-+clean:
-+ rm -rf .libs */.libs
-+ rm -f core *~ *% *.bak *.o */*.o *.lo $(VFS_OBJS)
-+
-diff -Nur vfs/README vfs.new/README
---- vfs/README Thu Jan 1 00:00:00 1970
-+++ vfs.new/README Tue Jul 13 02:28:21 2004
-@@ -0,0 +1,34 @@
-+This a vfs for NT ADS
-+you must set SMB and BUILD variables in Makefile.
-+
-+old smb.conf
-+[test_ads]
-+ comment = test ADS Mac/PC directory
-+ path=/home/test_ads/
-+# /.AppleD* is mandatory
-+ veto files = /.AppleD*/Network Trash Folder/Icon\r/
-+ delete veto files = True
-+# full path to vfs_ads.so
-+ vfs object = /usr/src/samba/vfs/vfs_ads.so
-+ browseable = yes
-+ writable = yes
-+
-+new one (current svn tree)
-+copy vfs_ads.so as ads.so in <prefix>/lib/vfs/
-+eg
-+cp vfs_ads.so /opt/lib/vfs/ads.so
-+
-+smb.conf
-+[test_ads]
-+ comment = test ADS Mac/PC directory
-+ path=/home/test_ads/
-+
-+# /.AppleD* is mandatory
-+ veto files = /.AppleD*/Network Trash Folder/Icon\r/
-+ delete veto files = True
-+ vfs objects = ads
-+ browseable = yes
-+ writable = yes
-+
-+
-+Didier
-diff -Nur vfs/vfs_ads.c vfs.new/vfs_ads.c
---- vfs/vfs_ads.c Thu Jan 1 00:00:00 1970
-+++ vfs.new/vfs_ads.c Wed Jul 14 16:37:15 2004
-@@ -0,0 +1,1029 @@
-+/*
-+ * CAP VFS module for Samba 3.x Version 0.3
-+ *
-+ * Copyright (C) Tim Potter, 1999-2000
-+ * Copyright (C) Alexander Bokovoy, 2002-2003
-+ * Copyright (C) Stefan (metze) Metzmacher, 2003
-+ * Copyright (C) TAKAHASHI Motonobu (monyo), 2003
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ *
-+ * modified for alternate data stream
-+ * Copyright (C) Didier Gautheron 2004
-+ *
-+ * this module should compile with old 3.0 API and 2004-07 svn API
-+ */
-+
-+
-+#include "includes.h"
-+
-+#undef DBGC_CLASS
-+#define DBGC_CLASS DBGC_VFS
-+
-+#define ADS_FOLDER ".AppleDouble"
-+#define ADOUBLEMODE 0777
-+
-+/* FIXME found a better test */
-+#ifdef SMB_VFS_OP
-+#define ADS_NEW_MODULE
-+
-+/* for current svn tree */
-+#define ADS_TALLOC_INIT(a) talloc_init(a)
-+
-+#define HANDLE_PARAMETER vfs_handle_struct *handle,
-+#define HANDLE handle,
-+
-+/* ------------------- */
-+#else
-+
-+#define ADS_TALLOC_INIT(a) talloc_init()
-+
-+#define HANDLE_PARAMETER
-+#define HANDLE
-+
-+/* VFS operations */
-+static struct vfs_ops default_vfs_ops; /* For passthrough operation */
-+static struct smb_vfs_handle_struct *ads_handle;
-+
-+#define SMB_VFS_NEXT_DISK_FREE(a,b,c,d,e,f,g) default_vfs_ops.disk_free(b,c,d,e,f,g)
-+#define SMB_VFS_NEXT_OPENDIR(a,b,c) default_vfs_ops.opendir(b,c)
-+#define SMB_VFS_NEXT_READDIR(a,b,c) default_vfs_ops.readdir(b,c)
-+#define SMB_VFS_NEXT_MKDIR(a,b,c,d) default_vfs_ops.mkdir(b,c,d)
-+#define SMB_VFS_NEXT_RMDIR(a,b,c) default_vfs_ops.rmdir(b,c)
-+#define SMB_VFS_NEXT_OPEN(a,b,c,d,e) default_vfs_ops.open(b,c,d,e)
-+#define SMB_VFS_NEXT_RENAME(a,b,c,d) default_vfs_ops.rename(b,c,d)
-+#define SMB_VFS_NEXT_STAT(a,b,c,d) default_vfs_ops.stat(b,c,d)
-+#define SMB_VFS_NEXT_LSTAT(a,b,c,d) default_vfs_ops.lstat(b,c,d)
-+#define SMB_VFS_NEXT_UNLINK(a,b,c) default_vfs_ops.unlink(b,c)
-+#define SMB_VFS_NEXT_CHMOD(a,b,c,d) default_vfs_ops.chmod(b,c,d)
-+#define SMB_VFS_NEXT_CHOWN(a,b,c,d,e) default_vfs_ops.chown(b,c,d,e)
-+#define SMB_VFS_NEXT_CHDIR(a,b,c) default_vfs_ops.chdir(b,c)
-+#define SMB_VFS_NEXT_UTIME(a,b,c,d) default_vfs_ops.utime(b,c,d)
-+#define SMB_VFS_NEXT_SYMLINK(a,b,c,d) default_vfs_ops.symlink(b,c,d)
-+#define SMB_VFS_NEXT_READLINK(a,b,c,d,e) default_vfs_ops.readlink(b,c,d,e)
-+#define SMB_VFS_NEXT_LINK(a,b,c,d) default_vfs_ops.link(b,c,d)
-+#define SMB_VFS_NEXT_MKNOD(a,b,c,d,e) default_vfs_ops.mknod(b,c,d,e)
-+#define SMB_VFS_NEXT_REALPATH(a,b,c,d) default_vfs_ops.realpath(b,c,d)
-+#define SMB_VFS_NEXT_SET_NT_ACL(a,b,c,d,e) default_vfs_ops.set_nt_acl(b,c,d,e)
-+#define SMB_VFS_NEXT_CHMOD_ACL(a,b,c,d) default_vfs_ops.chmod_acl(b,c,d)
-+#define SMB_VFS_NEXT_SYS_ACL_GET_FILE(a,b,c,d) default_vfs_ops.sys_acl_get_file(b,c,d)
-+#define SMB_VFS_NEXT_SYS_ACL_SET_FILE(a,b,c,d,e) default_vfs_ops.sys_acl_set_file(b,c,d,e)
-+#define SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(a,b,c) default_vfs_ops.sys_acl_delete_def_file(b,c)
-+/* ads functions */
-+
-+#endif
-+
-+/* -------------------------
-+ * format
-+ * .AppleDouble/filename/stream name
-+ *
-+ * return the *LAST* '/' in path
-+ */
-+static int ads_get_path_ptr(char *path)
-+{
-+ int i = 0;
-+ int ptr = 0;
-+
-+ for (i = 0; path[i]; i ++) {
-+ if (path[i] == '/')
-+ ptr = i;
-+ }
-+
-+ return ptr;
-+}
-+
-+/* ------------------------------
-+ * return the *FIRST* ':' in path
-+*/
-+static int ads_get_stream_ptr(const char *path)
-+{
-+ int i = 0;
-+ int ptr = 0;
-+
-+ for (i = 0; path[i]; i ++) {
-+ if (path[i] == ':') {
-+ ptr = i;
-+ break;
-+ }
-+ }
-+ return ptr;
-+}
-+
-+/* ----------------
-+ * fname is only a filename
-+*/
-+
-+static char *ads_canonical_dir(TALLOC_CTX *ctx, const char *path, const char *fname, int isdir)
-+{
-+ if (isdir) {
-+ return talloc_asprintf(ctx, "%s/%s/%s/.Parent", path, fname, ADS_FOLDER);
-+ }
-+ return talloc_asprintf(ctx, "%s/%s/%s", path, ADS_FOLDER, fname);
-+
-+}
-+
-+/* ----------------
-+ * return directory pathname for an alternate data stream
-+ * fname is *NOT* an altername name (ie foo:bar)
-+*/
-+static char *ads_dir(TALLOC_CTX *ctx, const char *path, const char *fname, int isdir)
-+{
-+ int ptr0 = 0;
-+ int ptr1 = 0;
-+ char *temp;
-+
-+#if 0
-+ if (fname[0] == '.') ptr0 ++;
-+ if (fname[1] == '/') ptr0 ++;
-+#endif
-+ temp = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
-+ ptr1 = ads_get_path_ptr(temp);
-+ temp[ptr1] = '\0';
-+ return ads_canonical_dir(ctx, temp, &temp[ptr1 + 1], isdir);
-+}
-+
-+/* ----------------------------------
-+ * build the pathname for stream, create folder if (mode & O_CREAT)
-+ * return -1 on error
-+ * 0 it's not a stream
-+ * 1 it's a stream
-+ *
-+ * main_path : file fullpathname with :$DATA removed
-+ * ads_path: unix pathname
-+ * if it's not an ADS then main_path == ads_path
-+ *
-+ */
-+static int ads_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
-+ char **ads_path, char **main_path, SMB_STRUCT_STAT **main_info, int flag)
-+{
-+ int ret = 0;
-+ int ptr0 = 0;
-+ int ptr1 = 0;
-+ int ptr2 = 0;
-+ int ptr3 = 0;
-+ char *dname = 0;
-+ char *name = 0;
-+ SMB_STRUCT_STAT ads_info;
-+
-+ if (!ctx || !path || !fname || !ads_path || !main_path || !main_info || !*main_info)
-+ return -1;
-+#if 1
-+ DEBUG(3, ("ADS: PATH: %s[%s]\n", path, fname));
-+#endif
-+ if (strstr(path, ADS_FOLDER) || strstr(fname, ADS_FOLDER)) {
-+ DEBUG(1, ("ADS: path %s[%s] already contains %s\n", path, fname, ADS_FOLDER));
-+ return -1;
-+ }
-+
-+#if 0
-+ if (fname[0] == '.') ptr0 ++;
-+ if (fname[1] == '/') ptr0 ++;
-+#endif
-+
-+ *main_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
-+ *ads_path = NULL;
-+
-+ /* get pointer to last '/' */
-+ ptr1 = ads_get_path_ptr(*main_path);
-+ ptr2 = ads_get_stream_ptr(*main_path +ptr1 +1);
-+ /* FIXME
-+ * what about ::$DATA or :name:$DATA
-+ */
-+
-+ if (ptr2) {
-+ /* it's an alternate stream */
-+ ptr2 += ptr1 +1;
-+ (*main_path)[ptr2] = 0;
-+ ptr3 = ads_get_stream_ptr(*main_path +ptr2 +1);
-+ if (ptr3) {
-+ ptr3 += ptr2 +1;
-+ /* check it's $DATA */
-+ if (!strcmp("$DATA", &(*main_path)[ptr3+1])) {
-+ (*main_path)[ptr3] = 0;
-+ }
-+ }
-+
-+ DEBUG(3, ("ADS: MAIN DATA %s\n", *main_path));
-+
-+ if (sys_lstat(*main_path, *main_info) < 0) {
-+ /* if we can't get the main file give up */
-+ return -1;
-+ }
-+ (*main_path)[ptr2] = ':';
-+ dname = talloc_strdup(ctx, *main_path);
-+ dname[ptr1] = '\0';
-+ name = *main_path;
-+ name[ptr2] = '\0';
-+ if (S_ISDIR((*main_info)->st_mode)) {
-+ *ads_path = talloc_asprintf(ctx, "%s/%s/%s/.Parent/%s", dname, &name[ptr1 + 1], ADS_FOLDER, &name[ptr2 + 1]);
-+ }
-+ else {
-+ *ads_path = talloc_asprintf(ctx, "%s/%s/%s/%s", dname, ADS_FOLDER, &name[ptr1 + 1], &name[ptr2 + 1]);
-+ }
-+ /* XXX are we always the right user ?*/
-+ if (sys_lstat(*ads_path, &ads_info) < 0) {
-+ int st_ret;
-+ /* */
-+ if (errno == ENOENT && (flag & O_CREAT)) {
-+ char *ads_base = ads_canonical_dir(ctx, dname, &name[ptr1 + 1], S_ISDIR((*main_info)->st_mode));
-+ mode_t mode;
-+
-+ st_ret = mkdir(ads_base, 0777);
-+ if (st_ret < 0) {
-+ if (errno == ENOENT) {
-+ char *ads_double;
-+ if (S_ISDIR((*main_info)->st_mode)) {
-+ ads_double = talloc_asprintf(ctx, "%s/%s/%s", dname, &name[ptr1 + 1], ADS_FOLDER);
-+ }
-+ else {
-+ ads_double = talloc_asprintf(ctx, "%s/%s", dname, ADS_FOLDER);
-+ }
-+ if (mkdir(ads_double, 0777) < 0)
-+ return -1;
-+ if ((st_ret = mkdir(ads_base, 0777)) < 0)
-+ return -1;
-+
-+ /* we just created .AppleDouble/file/ update mode with dir search
-+ * XXX what about acl?
-+ */
-+ mode = (*main_info)->st_mode;
-+ if ((mode & (S_IRUSR | S_IWUSR )))
-+ mode |= S_IXUSR;
-+ if ((mode & (S_IRGRP | S_IWGRP )))
-+ mode |= S_IXGRP;
-+ if ((mode & (S_IROTH | S_IWOTH )))
-+ mode |= S_IXOTH;
-+ chmod(ads_base, mode);
-+ }
-+ else
-+ errno = ENOENT;
-+ }
-+ }
-+ else
-+ return -1;
-+ }
-+ ret = 1;
-+ }
-+ else {
-+ *ads_path = *main_path;
-+ if (sys_lstat(*main_path, *main_info) < 0) {
-+ *main_info = NULL;
-+ }
-+ }
-+#if 1
-+ DEBUG(3, ("ADS: DEBUG:[%s] [%s]\n", *main_path, *ads_path));
-+#endif
-+ return ret;
-+}
-+
-+/* ------------------------ */
-+static SMB_BIG_UINT ads_disk_free(HANDLE_PARAMETER connection_struct *conn, const char *path,
-+ BOOL small_query, SMB_BIG_UINT *bsize,
-+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
-+{
-+ return SMB_VFS_NEXT_DISK_FREE(handle, conn, path, small_query, bsize, dfree, dsize);
-+}
-+
-+static DIR *ads_opendir(HANDLE_PARAMETER connection_struct *conn, const char *fname)
-+{
-+ return SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
-+}
-+
-+static struct dirent *ads_readdir(HANDLE_PARAMETER connection_struct *conn, DIR *dirp)
-+{
-+ struct dirent *result;
-+ DEBUG(3,("ads: ads_readdir\n"));
-+ result = SMB_VFS_NEXT_READDIR(handle, conn, dirp);
-+ if (result) {
-+ DEBUG(3,("ads: ads_readdir: %s\n", result->d_name));
-+ }
-+ return result;
-+}
-+
-+/* ------------------------- */
-+static int ads_mkdir(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode)
-+{
-+ return SMB_VFS_NEXT_MKDIR(handle, conn, path, mode);
-+}
-+
-+/* ------------------------- */
-+static int unlink_file(const char *path)
-+{
-+int ret = 0;
-+
-+ become_root();
-+ ret = unlink(path);
-+ unbecome_root();
-+ return ret;
-+}
-+
-+/* ------------------------- */
-+static int unlink_folder(const char *path)
-+{
-+int ret = 0;
-+
-+ become_root();
-+ ret = rmdir(path);
-+ unbecome_root();
-+ return ret;
-+}
-+
-+/* -------------------------
-+ remove all files in an AppleDouble folder
-+*/
-+static void rrmdir(TALLOC_CTX *ctx, char *path)
-+{
-+ int n;
-+ char *dpath;
-+ struct dirent **namelist;
-+
-+ if (!path) return;
-+
-+ n = scandir(path, &namelist, 0, alphasort);
-+ if (n < 0) {
-+ return;
-+ }
-+ while (n --) {
-+ if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) {
-+ free(namelist[n]);
-+ continue;
-+ }
-+ if ((dpath = talloc_asprintf(ctx, "%s/%s",path, namelist[n]->d_name))) {
-+ unlink_file(dpath);
-+ }
-+ free(namelist[n]);
-+ }
-+ free(namelist);
-+ unlink_folder(path);
-+}
-+
-+/* --------------------------- */
-+static void rrm_adsdir(TALLOC_CTX *ctx, char *path)
-+{
-+ int n;
-+ char *dpath;
-+ struct dirent **namelist;
-+
-+ if (!path) return;
-+
-+ n = scandir(path, &namelist, 0, alphasort);
-+ if (n < 0) {
-+ return;
-+ }
-+ while (n --) {
-+ if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) {
-+ free(namelist[n]);
-+ continue;
-+ }
-+ if ((dpath = talloc_asprintf(ctx, "%s/%s",path, namelist[n]->d_name))) {
-+ rrmdir(ctx, dpath);
-+ }
-+ free(namelist[n]);
-+ }
-+ free(namelist);
-+ unlink_folder(path);
-+}
-+
-+/* -------------------------
-+ * XXX
-+ * if in smb.conf there's :
-+ * delete veto files = True
-+ * veto files = /.AppleD* /
-+*/
-+static int ads_rmdir( HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+ BOOL add = False;
-+ TALLOC_CTX *ctx = 0;
-+ char *dpath;
-+ int ret = 0;
-+
-+ if (!conn || !conn->origpath || !path) goto exit_rmdir;
-+
-+ /* .AppleD* */
-+ strstr(path, ADS_FOLDER) ? (add = False) : (add = True);
-+
-+ if (!(ctx = ADS_TALLOC_INIT("ads_rmdir")))
-+ goto exit_rmdir;
-+
-+ if (!(dpath = talloc_asprintf(ctx, "%s/%s%s",conn->origpath, path, add ? "/"ADS_FOLDER : "")))
-+ goto exit_rmdir;
-+
-+ /* remove folder .AppleDouble */
-+ rrm_adsdir(ctx, dpath);
-+
-+exit_rmdir:
-+ ret = SMB_VFS_NEXT_RMDIR(handle, conn, path);
-+ talloc_destroy(ctx);
-+
-+ return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_open(HANDLE_PARAMETER connection_struct *conn, const char *fname, int flags, mode_t mode)
-+{
-+ int ret = 0;
-+ char *ads_path = 0;
-+ char *main_path = 0;
-+ TALLOC_CTX *ctx;
-+ SMB_STRUCT_STAT st;
-+ SMB_STRUCT_STAT *main_info = &st;
-+
-+ DEBUG(3,("ads: ads_open for %s %x\n", fname, flags));
-+ if (!(ctx = ADS_TALLOC_INIT("ads_open")))
-+ return -1;
-+ /* convert to */
-+ if (ads_build_paths(ctx, conn->origpath, fname, &ads_path, &main_path, &main_info, flags) < 0) {
-+ talloc_destroy(ctx);
-+ return -1;
-+ }
-+
-+ ret = SMB_VFS_NEXT_OPEN(handle, conn, ads_path, flags, mode);
-+ talloc_destroy(ctx);
-+ return ret;
-+
-+}
-+
-+static int isDir(SMB_STRUCT_STAT *st)
-+{
-+ if (st == NULL) {
-+ return 0;
-+ }
-+ return S_ISDIR(st->st_mode);
-+}
-+
-+/* ------------------------- */
-+static int ads_rename(HANDLE_PARAMETER connection_struct *conn, const char *old, const char *new)
-+{
-+ int ret = 0;
-+ TALLOC_CTX *ctx;
-+ char *ads_path = 0;
-+ char *main_path = 0;
-+ SMB_STRUCT_STAT st;
-+ SMB_STRUCT_STAT *main_info = &st;
-+
-+ DEBUG(3,("ads: ads_rename %s --> %sx\n", old, new));
-+
-+ if (!(ctx = ADS_TALLOC_INIT("ads_rename")))
-+ return -1;
-+
-+ if (ads_build_paths(ctx, conn->origpath, old, &ads_path, &main_path, &main_info, 0) < 0) {
-+ talloc_destroy(ctx);
-+ return -1;
-+ }
-+
-+ if (ads_path != main_path) {
-+ /* you can't rename an ads ! */
-+ talloc_destroy(ctx);
-+ errno = EINVAL;
-+ return -1;
-+ }
-+
-+ ret = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
-+ if (!ret && !isDir(main_info)) {
-+ int ptr1;
-+ int ptr2;
-+
-+ char *ads_old = ads_dir(ctx, conn->origpath, old, 0);
-+ char *ads_new = ads_dir(ctx, conn->origpath, new, 0);
-+
-+ /* is dest folder .Adouble there ? */
-+ ptr1 = ads_get_path_ptr(ads_new);
-+ ptr2 = ads_get_path_ptr(ads_old);
-+
-+ ads_new[ptr1] = '\0';
-+ ads_old[ptr2] = '\0';
-+ if (strcmp(ads_new, ads_old)) {
-+ mkdir(ads_new, 0777);
-+ }
-+
-+ ads_new[ptr1] = '/';
-+ ads_old[ptr2] = '/';
-+
-+ SMB_VFS_NEXT_RENAME(handle, conn, ads_old, ads_new);
-+ }
-+
-+ talloc_destroy(ctx);
-+ return ret;
-+}
-+
-+/* -------------------------
-+ * For an ADS what do we need to return , ADS ? main DATA?
-+*/
-+static int ads_stat(HANDLE_PARAMETER connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
-+{
-+ int ret = 0;
-+ char *ads_path = 0;
-+ char *main_path = 0;
-+ TALLOC_CTX *ctx;
-+ SMB_STRUCT_STAT st;
-+ SMB_STRUCT_STAT *main_info = &st;
-+
-+ DEBUG(3,("ads: ads_stat for %s\n", fname));
-+
-+ if (!(ctx = ADS_TALLOC_INIT("ads_stat")))
-+ return -1;
-+ /* which inode ?
-+ */
-+ if (ads_build_paths(ctx, conn->origpath, fname, &ads_path, &main_path, &main_info, 0) < 0) {
-+ talloc_destroy(ctx);
-+ return -1;
-+ }
-+
-+ ret = SMB_VFS_NEXT_STAT(handle, conn, ads_path, sbuf);
-+ talloc_destroy(ctx);
-+ return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_lstat(HANDLE_PARAMETER connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
-+{
-+ int ret = 0;
-+ char *ads_path = 0;
-+ char *main_path = 0;
-+ TALLOC_CTX *ctx;
-+ SMB_STRUCT_STAT st;
-+ SMB_STRUCT_STAT *main_info = &st;
-+
-+ if (!(ctx = ADS_TALLOC_INIT("ads_lstat")))
-+ return -1;
-+ /* which inode ?
-+ */
-+ if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+ talloc_destroy(ctx);
-+ return -1;
-+ }
-+
-+ return SMB_VFS_NEXT_LSTAT(handle, conn, ads_path, sbuf);
-+ talloc_destroy(ctx);
-+ return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_unlink(HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+ int ret = 0;
-+
-+ char *ads_path = 0;
-+ char *main_path = 0;
-+ TALLOC_CTX *ctx;
-+ SMB_STRUCT_STAT st;
-+ SMB_STRUCT_STAT *main_info = &st;
-+
-+ DEBUG(3,("ads: ads_unlink %s\n", path));
-+ if (!(ctx = ADS_TALLOC_INIT("ads_unlink")))
-+ return -1;
-+
-+ if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+ talloc_destroy(ctx);
-+ return -1;
-+ }
-+
-+ ret = SMB_VFS_NEXT_UNLINK(handle, conn, ads_path);
-+ /*
-+ if data stream
-+ for each stream
-+ unlink
-+ */
-+ if (!ret && ads_path == main_path) {
-+ char *ads_base = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+ struct dirent *dent = 0;
-+ DIR *dir = opendir(ads_base);
-+
-+ if (dir) {
-+ char *dpath;
-+
-+ while (NULL != (dent = readdir(dir))) {
-+ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+ continue;
-+ if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
-+ continue;
-+ /* XXX need to be root ? */
-+ SMB_VFS_NEXT_UNLINK(handle, conn, dpath);
-+ }
-+ closedir(dir);
-+ rmdir(ads_base);
-+ }
-+ }
-+
-+ talloc_destroy(ctx);
-+ return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_chmod(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode)
-+{
-+ int ret = 0;
-+ char *ads_path = 0;
-+ char *main_path = 0;
-+ TALLOC_CTX *ctx;
-+ SMB_STRUCT_STAT st;
-+ SMB_STRUCT_STAT *main_info = &st;
-+
-+ DEBUG(3,("ads: ads_chmod %s\n", path));
-+ /* if stream
-+ error ?, change only the stream
-+ */
-+ if (!(ctx = ADS_TALLOC_INIT("ads_chmod")))
-+ return -1;
-+
-+ if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+ talloc_destroy(ctx);
-+ return -1;
-+ }
-+
-+ ret = SMB_VFS_NEXT_CHMOD(handle, conn, ads_path, mode);
-+ /*
-+ if data stream
-+ for each stream
-+ chmod
-+ */
-+ if (!ret && ads_path == main_path) {
-+ char *ads_base = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+ struct dirent *dent = 0;
-+ DIR *dir = opendir(ads_base);
-+
-+ if (dir) {
-+ char *dpath;
-+
-+ while (NULL != (dent = readdir(dir))) {
-+ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+ continue;
-+ if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
-+ continue;
-+ /* XXX need to be root ? */
-+ SMB_VFS_NEXT_CHMOD(handle, conn, dpath, mode);
-+ }
-+ closedir(dir);
-+ /* XXX need to change ads_base too*/
-+ }
-+ }
-+
-+ talloc_destroy(ctx);
-+ return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_chown(HANDLE_PARAMETER connection_struct *conn, const char *path, uid_t uid, gid_t gid)
-+{
-+ int ret = 0;
-+ char *ads_path = 0;
-+ char *main_path = 0;
-+ TALLOC_CTX *ctx;
-+ SMB_STRUCT_STAT st;
-+ SMB_STRUCT_STAT *main_info = &st;
-+
-+ DEBUG(3,("ads: ads_chown %s\n", path));
-+ /* if stream
-+ error ?, change only the stream
-+ */
-+ if (!(ctx = ADS_TALLOC_INIT("ads_chown")))
-+ return -1;
-+
-+ if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+ talloc_destroy(ctx);
-+ return -1;
-+ }
-+
-+ ret = SMB_VFS_NEXT_CHOWN(handle, conn, ads_path, uid, gid);
-+ /* if data stream
-+ for each stream
-+ chmod
-+ */
-+ if (!ret && ads_path == main_path) {
-+ char *ads_base = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+ struct dirent *dent = 0;
-+ DIR *dir = opendir(ads_base);
-+
-+ if (dir) {
-+ char *dpath;
-+
-+ while (NULL != (dent = readdir(dir))) {
-+ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+ continue;
-+ if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
-+ continue;
-+ /* XXX need to be root ?, what do we do in case of error? */
-+ SMB_VFS_NEXT_CHOWN(handle, conn, dpath, uid, gid);
-+ }
-+ closedir(dir);
-+ SMB_VFS_NEXT_CHOWN(handle, conn, ads_path, uid, gid);
-+ }
-+ }
-+
-+ talloc_destroy(ctx);
-+ return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_chdir(HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+ DEBUG(3,("ads: ads_chdir for %s\n", path));
-+ return SMB_VFS_NEXT_CHDIR(handle, conn, path);
-+}
-+
-+static int ads_utime(HANDLE_PARAMETER connection_struct *conn, const char *path, struct utimbuf *times)
-+{
-+ return SMB_VFS_NEXT_UTIME(handle, conn, path, times);
-+}
-+
-+
-+static BOOL ads_symlink(HANDLE_PARAMETER connection_struct *conn, const char *oldpath, const char *newpath)
-+{
-+ return SMB_VFS_NEXT_SYMLINK(handle, conn, oldpath, newpath);
-+}
-+
-+static BOOL ads_readlink(HANDLE_PARAMETER connection_struct *conn, const char *path, char *buf, size_t bufsiz)
-+{
-+ return SMB_VFS_NEXT_READLINK(handle, conn, path, buf, bufsiz);
-+}
-+
-+static int ads_link( HANDLE_PARAMETER connection_struct *conn, const char *oldpath, const char *newpath)
-+{
-+ return SMB_VFS_NEXT_LINK(handle, conn, oldpath, newpath);
-+}
-+
-+static int ads_mknod(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev)
-+{
-+ return SMB_VFS_NEXT_MKNOD(handle, conn, path, mode, dev);
-+}
-+
-+static char *ads_realpath(HANDLE_PARAMETER connection_struct *conn, const char *path, char *resolved_path)
-+{
-+ return SMB_VFS_NEXT_REALPATH(handle, conn, path, resolved_path);
-+}
-+
-+static BOOL ads_set_nt_acl(HANDLE_PARAMETER files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd)
-+{
-+ return SMB_VFS_NEXT_SET_NT_ACL(handle, fsp, name, security_info_sent, psd);
-+}
-+
-+static int ads_chmod_acl(HANDLE_PARAMETER connection_struct *conn, const char *name, mode_t mode)
-+{
-+ /* If the underlying VFS doesn't have ACL support... */
-+#ifdef ADS_NEW_MODULE
-+ if (!handle->vfs_next.ops.chmod_acl) {
-+#else
-+ if (!default_vfs_ops.chmod_acl) {
-+#endif
-+ errno = ENOSYS;
-+ return -1;
-+ }
-+ return SMB_VFS_NEXT_CHMOD_ACL(handle, conn, name, mode);
-+}
-+
-+static SMB_ACL_T ads_sys_acl_get_file(HANDLE_PARAMETER connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
-+{
-+ return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, conn, path_p, type);
-+}
-+
-+static int ads_sys_acl_set_file(HANDLE_PARAMETER connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
-+{
-+ return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, conn, name, acltype, theacl);
-+}
-+
-+static int ads_sys_acl_delete_def_file(HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+ return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, conn, path);
-+}
-+
-+#ifdef ADS_NEW_MODULE
-+static ssize_t ads_getxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
-+{
-+ return SMB_VFS_NEXT_GETXATTR(handle, conn, path, name, value, size);
-+}
-+
-+static ssize_t ads_lgetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t
-+size)
-+{
-+ return SMB_VFS_NEXT_LGETXATTR(handle, conn, path, name, value, size);
-+}
-+
-+static ssize_t ads_fgetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
-+{
-+ return SMB_VFS_NEXT_FGETXATTR(handle, fsp, fd, name, value, size);
-+}
-+
-+static ssize_t ads_listxattr(vfs_handle_struct *handle, connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+ return SMB_VFS_NEXT_LISTXATTR(handle, conn, path, list, size);
-+}
-+
-+static ssize_t ads_llistxattr(vfs_handle_struct *handle,struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+ return SMB_VFS_NEXT_LLISTXATTR(handle, conn, path, list, size);
-+}
-+
-+static int ads_removexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
-+{
-+ return SMB_VFS_NEXT_REMOVEXATTR(handle, conn, path, name);
-+}
-+
-+static int ads_lremovexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
-+{
-+ return SMB_VFS_NEXT_LREMOVEXATTR(handle, conn, path, name);
-+}
-+
-+static int ads_fremovexattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
-+{
-+ return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, fd, name);
-+}
-+
-+static int ads_setxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
-+{
-+ return SMB_VFS_NEXT_SETXATTR(handle, conn, path, name, value, size, flags);
-+}
-+
-+static int ads_lsetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
-+{
-+ return SMB_VFS_NEXT_LSETXATTR(handle, conn, path, name, value, size, flags);
-+}
-+
-+static int ads_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
-+{
-+ return SMB_VFS_NEXT_FSETXATTR(handle, fsp, fd, name, value, size, flags);
-+}
-+
-+#endif
-+
-+/* ----------------------------------
-+ * enumerate
-+*/
-+static ssize_t ads_listads(HANDLE_PARAMETER struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+ char *ads_path = 0;
-+ char *main_path = 0;
-+ TALLOC_CTX *ctx;
-+ size_t len, total = 0;
-+ SMB_STRUCT_STAT st;
-+ SMB_STRUCT_STAT *main_info = &st;
-+
-+
-+ if (!list || !path) {
-+ /* aka we have ads functionnality */
-+ return 0;
-+ }
-+
-+ DEBUG(3,("ads: ads_listads %s\n", path));
-+
-+ if (!(ctx = ADS_TALLOC_INIT("ads_listads")))
-+ return -1;
-+
-+ if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+ talloc_destroy(ctx);
-+ return -1;
-+ }
-+
-+ /*
-+ if data stream
-+ for each stream
-+ */
-+ if (ads_path == main_path) {
-+ char *ads_base = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+ struct dirent *dent = 0;
-+ DIR *dir = opendir(ads_base);
-+
-+ /* XXX need to be root ? */
-+ if (dir) {
-+
-+ while (NULL != (dent = readdir(dir))) {
-+ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+ continue;
-+ len = strlen(dent->d_name) +8 ;
-+ total += len;
-+ if (total >= size) {
-+ talloc_destroy(ctx);
-+ errno = ERANGE;
-+ return -1;
-+ }
-+ snprintf (list, len, ":%s:$DATA", dent->d_name);
-+ list += len;
-+ }
-+ closedir(dir);
-+ }
-+ }
-+
-+ talloc_destroy(ctx);
-+ return total;
-+}
-+
-+/* ------------------------------------
-+ * VFS operations structure */
-+
-+#ifndef SMB_VFS_OP
-+#define SMB_VFS_OP(x) ((void *) x)
-+#endif
-+
-+static vfs_op_tuple ads_op_tuples[] = {
-+
-+ /* Disk operations */
-+
-+ {SMB_VFS_OP(ads_disk_free), SMB_VFS_OP_DISK_FREE, SMB_VFS_LAYER_TRANSPARENT},
-+
-+ /* Directory operations */
-+
-+ {SMB_VFS_OP(ads_opendir), SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_readdir), SMB_VFS_OP_READDIR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_mkdir), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_rmdir), SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT},
-+
-+ /* File operations */
-+
-+ {SMB_VFS_OP(ads_open), SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_rename), SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_stat), SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_lstat), SMB_VFS_OP_LSTAT, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_chmod), SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_chown), SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_chdir), SMB_VFS_OP_CHDIR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_utime), SMB_VFS_OP_UTIME, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_symlink), SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_readlink), SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_link), SMB_VFS_OP_LINK, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_mknod), SMB_VFS_OP_MKNOD, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_realpath), SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
-+
-+ /* NT File ACL operations */
-+
-+ {SMB_VFS_OP(ads_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
-+
-+ /* POSIX ACL operations */
-+
-+ {SMB_VFS_OP(ads_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
-+
-+ {SMB_VFS_OP(ads_sys_acl_get_file), SMB_VFS_OP_SYS_ACL_GET_FILE, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_sys_acl_set_file), SMB_VFS_OP_SYS_ACL_SET_FILE, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_sys_acl_delete_def_file), SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, SMB_VFS_LAYER_TRANSPARENT},
-+#ifdef ADS_NEW_MODULE
-+ /* EA operations. */
-+ {SMB_VFS_OP(ads_getxattr), SMB_VFS_OP_GETXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_lgetxattr), SMB_VFS_OP_LGETXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_fgetxattr), SMB_VFS_OP_FGETXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_listxattr), SMB_VFS_OP_LISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_llistxattr), SMB_VFS_OP_LLISTXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_lremovexattr), SMB_VFS_OP_LREMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_fremovexattr), SMB_VFS_OP_FREMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_setxattr), SMB_VFS_OP_SETXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_lsetxattr), SMB_VFS_OP_LSETXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+ {SMB_VFS_OP(ads_fsetxattr), SMB_VFS_OP_FSETXATTR, SMB_VFS_LAYER_TRANSPARENT},
-+#endif
-+ /* ADS operations */
-+ {SMB_VFS_OP(ads_listads), SMB_VFS_OP_LISTADS, SMB_VFS_LAYER_TRANSPARENT},
-+
-+ {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
-+};
-+
-+#ifdef ADS_NEW_MODULE
-+
-+#if 0
-+NTSTATUS vfs_ads_init(void)
-+{
-+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ads", ads_op_tuples);
-+}
-+#endif
-+
-+NTSTATUS vfs_ads_init(void)
-+{
-+ DEBUG(3, ("ADS: vfs_ads_init\n"));
-+ return NT_STATUS_OK;
-+}
-+
-+
-+NTSTATUS init_module(void)
-+{
-+ DEBUG(3, ("ADS: init_module\n" ));
-+ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ads", ads_op_tuples);
-+}
-+
-+#else
-+/* VFS initialisation function. Return vfs_op_tuple array back to SAMBA. */
-+vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,struct smb_vfs_handle_struct *vfs_handle)
-+{
-+ *vfs_version = SMB_VFS_INTERFACE_VERSION;
-+ memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
-+
-+ ads_handle = vfs_handle;
-+ DEBUG(3, ("ADS: vfs module loaded\n"));
-+ return ads_op_tuples;
-+}
-+
-+/* VFS finalization function. */
-+void vfs_done(connection_struct *conn)
-+{
-+ DEBUG(3, ("ADS: vfs module unloaded\n"));
-+}
-+
-+#endif
+++ /dev/null
-Index: configure.in
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/configure.in,v
-retrieving revision 1.205
-diff -u -w -b -r1.205 configure.in
---- configure.in 9 Sep 2006 04:30:01 -0000 1.205
-+++ configure.in 25 Jul 2008 13:48:05 -0000
-@@ -1053,6 +1053,7 @@
- contrib/shell_utils/apple_rm
- contrib/shell_utils/asip-status.pl
- contrib/shell_utils/cleanappledouble.pl
-+ contrib/shell_utils/permtest.pl
- contrib/timelord/Makefile
- contrib/a2boot/Makefile
- distrib/Makefile
-Index: contrib/shell_utils/Makefile.am
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/contrib/shell_utils/Makefile.am,v
-retrieving revision 1.16
-diff -u -w -b -r1.16 Makefile.am
---- contrib/shell_utils/Makefile.am 28 Apr 2005 20:49:36 -0000 1.16
-+++ contrib/shell_utils/Makefile.am 25 Jul 2008 13:48:05 -0000
-@@ -9,6 +9,9 @@
- apple_cp apple_mv apple_rm \
- cleanappledouble.pl \
- asip-status.pl
-+EXTRASCRIPTS = \
-+ permtest.pl \
-+ permtest.cfg
-
- SUFFIXES = .tmpl .
-
-@@ -22,4 +25,4 @@
-
- bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES)
-
--EXTRA_DIST = $(PERLSCRIPTS) $(TEMPLATE_FILES)
-+EXTRA_DIST = $(PERLSCRIPTS) $(TEMPLATE_FILES) $(EXTRASCRIPTS)
+++ /dev/null
-# Exactly follow this layout! Don't put extra white space in config lines !!
-# Order doesn't matter.
-
-# We use a ssh executed stat command to verify uid,gid and mode. Therefore ssh access with
-# PKI authentication must be setup and working!
-sshLogin = USER@HOST
-
-# self explaining
-mountAFPVolume = afp://USER:PASSWORD@HOST/VOLUME
-
-# These files will be created
-createFile = PATH_TO_FILE_ON_CLIENT
-
-# These files will be stat'ed. You can use different server-side paths here for files
-# created with "createFile" directive
-testFile = PATH_TO_FILE_ON_SERVER,user=USERNAME,group=GROUPNAME,mode=MODE
-
-# These dirs will be created
-createDir = PATH_TO_DIR_ON_CLIENT
-
-# These will be verified.
-testDir = PATH_TO_DIR_ON_CLIENT,user=USERNAME,group=GROUPNAME,mode=MODE
-
-# EOF. Leave this as a last line. We delibaretly chop in perl which might otherwise truncate
-# your last "testDir" definition.
\ No newline at end of file
+++ /dev/null
-#!@PERL@ -w
-
-###########################################################################
-#
-# Characterization testing netatalks permission model
-#
-# (c) 2008 by Frank Lahm <franklahm@googlemail.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-###########################################################################
-
-###########################################################################
-#
-# Usage:
-#
-# "permtest.cfg" must be in your CWD. Must be run on a OS X host. Tested
-# with 10.4.11. Uses Applescript through system("osascript ...") to mount
-# AFP Volumes. Uses `ssh LOGIN@HOST stat FILE|DIR`. Therefor PKI
-# authentication must be setup and working!
-# See "permtest.cfg" for more details, it's pretty much self-explaining.
-#
-###########################################################################
-
-use strict;
-
-my $DEBUG = 0;
-
-###########################################################################
-
-sub parseConfig;
-sub mountAPFVols;
-sub createTestFiles;
-sub createTestDirs;
-sub verifyTestFiles;
-sub verifyTestDirs;
-sub unmountAfp;
-
-my ($sshLogin, $sshResult, %sshStat, @AFPVols, @createFiles, @createDirs, @testFiles, @testDirs);
-my ($dir, $file, $user, $group, $perms, $mode, $cmd);
-
-parseConfig();
-mountAPFVols();
-createTestFiles();
-createTestDirs();
-print "\n";
-verifyTestFiles();
-verifyTestDirs();
-unmountAfp();
-
-exit 0;
-
-###########################################################################
-
-# parse config file
-sub parseConfig
-{
- open CFG, "permtest.cfg" or die "Config file not found!";
- while (<CFG>) {
- chop;
- if (/^#/) { next; };
- if (/^sshLogin/) {
- $sshLogin = $_;
- $sshLogin =~ s/^sshLogin ?= ?// ;
- next;
- }
- if (/^mountAFPVolume/) {
- s/^mountAFPVolume ?= ?// ;
- print "Found AFP Volume Definition \"$_\"\n" if $DEBUG;
- push @AFPVols, $_;
- next;
- }
- if (/^createFile/) {
- s/^createFile ?= ?// ;
- push @createFiles, $_;
- next;
- }
- if (/^createDir/) {
- s/^createDir ?= ?// ;
- push @createDirs, $_;
- next;
- }
- if (/^testFile/) {
- push @testFiles, $_;
- next;
- }
- if (/^testDir/) {
- push @testDirs, $_;
- next;
- }
- }
- close CFG;
-}
-
-# mount AFP Volumes
-sub mountAPFVols
-{
- foreach (@AFPVols) {
- print "Mounting AFP Volume \"$_\"\n";
- $cmd = "osascript -e 'tell application \"Finder\"' -e 'mount volume \"$_\"' -e 'end tell' &> /dev/null";
- print "Going to run the following Applescript:\n" . $cmd . "\n\n" if $DEBUG;
- system($cmd);
- if ($? != 0) { die "Error mounting \"$_\"\n"; }
- }
-}
-
-# Create test files
-sub createTestFiles
-{
- foreach (@createFiles) {
- s/^createFile ?= ?// ;
- system("rm \"$_\" &> /dev/null");
- print "Creating file: \"$_\"\n";
- system("touch \"$_\"");
- if ($? != 0) { die "Error creating file \"$_\"\n"; }
- }
-}
-
-# Create test dirs
-sub createTestDirs
-{
- foreach (@createDirs) {
- s/^createDir ?= ?// ;
- system("rmdir \"$_\" &> /dev/null");
- print "Creating dir: \"$_\"\n";
- system("mkdir \"$_\"");
- if ($? != 0) { die "Error creating dir \"$_\"\n"; }
- }
-}
-
-# Verify files and dirs
-sub verifyTestFiles
-{
- foreach (@testFiles) {
- my @line = split(",");
- foreach (@line) {
- if (/^testFile/) {
- $file = $_;
- $file =~ s/^testFile ?= ?//;
- }
- elsif (/^user/) {
- $user = $_;
- $user =~ s/^user ?= ?//;
- }
- elsif (/^group/) {
- $group = $_;
- $group =~ s/^group ?= ?//;
- }
- elsif (/^mode/) {
- $mode = $_;
- $mode =~ s/^mode ?= ?//;
- }
- } # foreach (@elems)
- print "File: $file, User: $user, Group: $group, Perms: $perms\n" if $DEBUG;
-
- $sshResult = `ssh $sshLogin stat -c \"user,%U,group,%G,mode,0%a\" \"$file\"`;
- if ($? != 0) { die "Error stat'ing file \"$file\"\n"; }
- chop $sshResult;
- print "ssh stat $file gave us: $sshResult\n" if $DEBUG;
-
- %sshStat = split(",", $sshResult);
- if ( ($sshStat{user} ne $user) or ($sshStat{group} ne $group) or ($sshStat{mode} ne $mode) ) {
- print "Creatin error for: \"$file\"!\nExpected:\t$user, $group, $mode.\nGot:\t\t$sshStat{user}, $sshStat{group}, $sshStat{mode}.\n\n";
- }
- system("rm \"$file\"");
- if ($? != 0) { die "Couldn't delete \"$file\"\n"; }
- }
-}
-
-sub verifyTestDirs
-{
- foreach (@testDirs) {
- my @line = split(",");
- foreach (@line) {
- if (/^testDir/) {
- $dir = $_;
- $dir =~ s/^testDir ?= ?//;
- }
- elsif (/^user/) {
- $user = $_;
- $user =~ s/^user ?= ?//;
- }
- elsif (/^group/) {
- $group = $_;
- $group =~ s/^group ?= ?//;
- }
- elsif (/^mode/) {
- $mode = $_;
- $mode =~ s/^mode ?= ?//;
- }
- } # foreach (@elems)
- print "Dir: $dir, User: $user, Group: $group, Perms: $perms\n" if $DEBUG;
-
- $sshResult = `ssh $sshLogin stat -c \"user,%U,group,%G,mode,0%a\" \"$dir\"`;
- if ($? != 0) { die "Error stat'ing file \"$dir\"\n"; }
- chop $sshResult;
- print "ssh stat $dir gave us: $sshResult\n" if $DEBUG;
-
- %sshStat = split(",", $sshResult);
- if ( ($sshStat{user} ne $user) or ($sshStat{group} ne $group) or ($sshStat{mode} ne $mode) ) {
- print "Creatin error for: \"$dir\"!\nExpected:\t$user, $group, $mode.\nGot:\t\t$sshStat{user}, $sshStat{group}, $sshStat{mode}.\n\n";
- }
- system("rmdir \"$dir\""); if ($? != 0) { die "Couldn't delete \"$dir\"\n"; }
- }
-}
-
-sub unmountAfp
-{
- foreach (@AFPVols) {
- print "Goint to eject Volume \"$_\"\n";
- s#^(.*/)## ;
- $cmd = "osascript -e 'tell application \"Finder\"' -e 'eject \"$_\"' -e 'end tell' &> /dev/null";
- print "Going to run the following Applescript:\n" . $cmd . "\n\n" if $DEBUG;
- system($cmd);
- }
-}
TEMPLATE_FILES = lp2pap.sh.tmpl
PERLSCRIPTS = \
afpd-mtab.pl \
- apple_cp apple_mv apple_rm \
asip-status.pl \
apple_dump
+++ /dev/null
-#!@PERL@
-#
-# $Id: apple_cp.in,v 1.1 2002-01-17 05:59:25 srittau Exp $
-
-$USAGE = <<USAGE;
-Usage: $0 filename1 filename2
- $0 filename ... directory
-Do an apple copy, copying the resource fork as well
-USAGE
-
-die $USAGE if @ARGV < 2;
-
-@from = @ARGV; pop(@from);
-$to = $ARGV[-1];
-
-if (-f $to && @from > 1) { die $USAGE; }
-
-foreach $from (@from) {
- if (!-f $from) {
- print STDERR "file $from does not exist\n";
- die $USAGE;
- }
-
- if (!-d $to && @from >1) {
- print STDERR "directory $to does not exist\nCan't copy multiple files into one file.\n";
- die $USAGE;
- }
-
- $cmd = "cp '$from' '$to'";
- system $cmd || die "error executing $cmd";
-
- ($from_dir, $from_file) = split_dir_file($from);
-
- if (-d $to) {
- if (!-d "$to/.AppleDouble") {
- mkdir("$to/.AppleDouble", 0777);
- }
- $cmd = "cp '$from_dir/.AppleDouble/$from_file' '$to/.AppleDouble/$from_file'";
- } else {
- ($to_dir, $to_file) = split_dir_file($to);
- if (!-d "$to_dir/.AppleDouble") {
- mkdir("$to_dir/.AppleDouble", 0777);
- }
- $cmd = "cp '$from_dir/.AppleDouble/$from_file' '$to_dir/.AppleDouble/$to_file'";
- }
-
- system $cmd || die "error executing $cmd";
-}
-
-# split a file path into a directory and file name.
-sub split_dir_file {
- my $path = shift;
-
- @path_elems = split(/\//, $path);
-
- my $file = pop(@path_elems);
- my $dir;
- if (!@path_elems) {
- $dir = '.';
- } else {
- $dir = join('/', @path_elems);
- }
-
- $dir, $file;
-}
-
-
+++ /dev/null
-#!@PERL@
-#
-# $Id: apple_mv.in,v 1.1 2002-01-17 05:59:25 srittau Exp $
-
-$USAGE = <<USAGE;
-Usage: $0 filename1 filename2
- $0 filename ... directory
-Do an apple move, moving the resource fork as well
-USAGE
-
-@from = @ARGV; pop(@from);
-$to = $ARGV[-1];
-
-if (-f $to && @from > 1) { die $USAGE; }
-
-foreach $from (@from) {
- if (!-f $from) {
- print STDERR "file $from does not exist\n";
- die $USAGE;
- }
-
- if (!-d $to && @from >1) {
- print STDERR "directory $to does not exist\nCan't move multiple files into one file.\n";
- die $USAGE;
- }
-
- $from = escape_bad_chars($from);
- $to = escape_bad_chars($to);
- $cmd = "mv $from $to";
- system $cmd || die "error executing $cmd";
-
- ($from_dir, $from_file) = split_dir_file($from);
-
- if (-d $to) {
- if (!-d "$to/.AppleDouble") {
- mkdir("$to/.AppleDouble", 0777);
- }
- $cmd = "mv $from_dir/.AppleDouble/$from_file $to/.AppleDouble/$from_file";
- } else {
- ($to_dir, $to_file) = split_dir_file($to);
-
- if (!-d $to_dir) {
- print STDERR "directory $to does not exist\n";
- die $USAGE;
- }
-
- if (!-d "$to_dir/.AppleDouble") {
- mkdir("$to_dir/.AppleDouble", 0777);
- }
- $cmd = "mv $from_dir/.AppleDouble/$from_file $to_dir/.AppleDouble/$to_file";
- }
-
- system $cmd || die "error executing $cmd";
-}
-
-sub escape_bad_chars {
- my($file) = @_;
- $file=~s/([^a-zA-Z0-9.-_])/\\$1/;
- return $file;
-}
-
-# split a file path into a directory and file name.
-sub split_dir_file {
- my $path = shift;
-
- @path_elems = split(/\//, $path);
-
- my $file = pop(@path_elems);
- my $dir;
- if (!@path_elems) {
- $dir = '.';
- } else {
- $dir = join('/', @path_elems);
- }
-
- $dir, $file;
-}
-
-
+++ /dev/null
-#!@PERL@
-#
-# $Id: apple_rm.in,v 1.1 2002-01-17 05:59:25 srittau Exp $
-
-$USAGE = <<USAGE;
-Usage: $0 filename ...
-Do an apple remove, remove the resource fork as well
-USAGE
-
-die $USAGE if @ARGV < 1;
-
-foreach $path (@ARGV) {
- if (!-f $path) {
- print STDERR "file $path does not exist\n";
- die $USAGE;
- }
-
- ($dir, $file) = &split_dir_file($path);
-
- $cmd = "rm '$path'";
- system $cmd || die "error executing $cmd";
-
- $cmd = "rm '$dir/.AppleDouble/$file'";
- system $cmd || die "error executing $cmd";
-}
-
-# split a file path into a directory and file name.
-sub split_dir_file {
- my $path = shift;
-
- @path_elems = split(/\//, $path);
-
- my $file = pop(@path_elems);
- my $dir;
- if (!@path_elems) {
- $dir = '.';
- } else {
- $dir = join('/', @path_elems);
- }
-
- $dir, $file;
-}
push (@flags, "SupportsReconnect") if ($flags & (1<<7));
push (@flags, "SupportsOpenDirectory") if ($flags & (1<<8));
push (@flags, "SupportsUTF8Servername") if ($flags & (1<<9));
+ push (@flags, "SupportsUUIDs") if ($flags & (1<<10));
push (@flags, "SupportsSuperClient") if ($flags & (1<<15));
return @flags;
+++ /dev/null
-This is the pre-packaged Debian version of the Netatalk protocol suite.
-To find out more about netatalk, visit http://netatalk.sourceforge.net/
-
-This package was originally put together by Klee Dienes <klee@debian.org>
-and was later maintained by late Joel Klecker <espy@debian.org> and
-David Huggins-Daines <dhd@debian.org>. It was repackaged by its current
-maintainer Sebastian Rittau <srittau@debian.org>.
-
- OpenSSL
- =======
-
-Netatalk supports OpenSSL to provide a secure means for authentication.
-Unfortunately it's currently not possible for Debian to distribute
-Netatalk with SSL support enabled, since OpenSSL's license is incompatible
-with the GPL used by Netatalk.
-
-But it's possible to build Netatalk with SSL support locally:
-
-1. Make sure that the package libssl-dev is installed:
-
- apt-get install libssl-dev
-
-2. Make sure all other build dependencies are fulfilled:
-
- apt-get build-dep netatalk
-
-3. Download the Netatalk sources:
-
- apt-get source netatalk
-
-4. Edit the rules file to enable SSL compilation:
-
- cd netatalk-1.5pre8 && \
- cp debian/rules debian/rules.bak && \
- sed -e 's/^#USE_SSL=yes/USE_SSL=yes/' debian/rules.bak >debian/rules
-
- (You may need to substitute the directory name netatalk-1.5pre8 with the
- proper one.)
-
-5. Build the package:
-
- dpkg-buildpackage -rfakeroot
+++ /dev/null
-netatalk (1.5pre8cvs-0) unstable; urgency=low
-
- * Unofficial CVS release.
- * Correct included docs, since all platform specific docs were merged.
- * Upstream does now have manpages for apple_cp(1), apple_mv(1), and
- apple_rm(1), written by Lance Levsen <l.levsen@printwest.com>.
- Removed the links to undocumented(7).
- * Don't supply --with-did=last anymore, since this is now the default.
- * Added cnid_didname_verify(1) to list of undocumented binaries.
-
- -- Sebastian Rittau <srittau@debian.org> Mon, 10 Dec 2001 13:46:14 +0100
-
-netatalk (1.5pre8-5) unstable; urgency=low
-
- * Appletalk -> AppleTalk in package short descriptions. Thanks to Matt
- Zimmerman and his spell checking effort for pointing this out.
- * Really install README.Debian this time.
- * Removed afppasswd and afppasswd(1) from Debian distribution, since
- they are of no use when SSL support is not compiled in.
-
- -- Sebastian Rittau <srittau@debian.org> Sun, 18 Nov 2001 15:13:53 +0100
-
-netatalk (1.5pre8-4) unstable; urgency=low
-
- * Fixed uams_pam.so. (Closes: #118889)
- * Explain why we don't link against OpenSSL in README.Debian.
- * Modified debian/rules so that setting a variable called USE_SSL to
- "yes" enables SSL support. This should ease the local compilation of
- SSL-enabled netatalk packages.
-
- -- Sebastian Rittau <srittau@debian.org> Sat, 10 Nov 2001 19:05:12 +0100
-
-netatalk (1.5pre8-3) unstable; urgency=low
-
- * Corrected upstream version number (pre8 instead of pre7). This corrects
- afpd -v and similar commands.
- * Raised default number of allowed afpd clients. Suggestion by Jonas
- Smedegaard.
- * Small logcheck fix by Jonas.
- * Removed ATALK_BACKGROUND description from netatalk.conf(5).
- * Removed obsolete --with-config-dir configure option.
-
- -- Sebastian Rittau <srittau@debian.org> Sat, 27 Oct 2001 15:36:30 +0200
-
-netatalk (1.5pre8-2) unstable; urgency=low
-
- * Work around the fact that upstream includes sym-links to mkinstalldirs and
- missing instead of verbatim copies. We do that by including our own copies
- in debian and copy them before running the build. (Closes: #114915)
-
- -- Sebastian Rittau <srittau@debian.org> Wed, 10 Oct 2001 14:03:34 +0200
-
-netatalk (1.5pre8-1) unstable; urgency=low
-
- * New upstream version, containing most Debian patches.
- * Added a patch to configure.in that fixes PAM detection and compilation.
-
- -- Sebastian Rittau <srittau@debian.org> Sun, 7 Oct 2001 12:46:15 +0200
-
-netatalk (1.5pre7-5) unstable; urgency=low
-
- * More patches by Jonas Smedegaard <dr@jones.dk>:
- + 001_logcheck_fix_typo_and_optimize...
- Logcheck fixes and improvements. (Closes: #114448)
- + 005_visible_home_dir_in_config_(again!)
- Name user home directories "Home Directory" by default to make them
- appear in the MacOS chooser. (Patch had already been applied in
- 1.5pre7-2, but had been lost since.)
- + Jonas made more patches, which I haven't applied yet, but either
- committed upstream or sent upstream for discussion.
-
- -- Sebastian Rittau <srittau@debian.org> Thu, 4 Oct 2001 22:31:50 +0200
-
-netatalk (1.5pre7-4) unstable; urgency=low
-
- * Fixed Build-Dependencies. (pam-cracklib -> cracklib2-dev) (Closes: #113356)
- * Restored symlinks in /usr/lib/atalk/filters and other directories.
- (Closes: #113746)
- * Patches by Jonas Smedegaard <dr@jones.dk>:
- + 002_correctly_calculate_perl_depends
- + 003_remove_cap_line_from_logcheck
- Small logcheck change.
- + 004_add_misc_logcheck_lines
- Another logcheck change.
- + 011_strip_pam_paths
- Not applied, but patched config/netatalk.pamd to strip /lib/security
- from its path.
-
- -- Sebastian Rittau <srittau@debian.org> Mon, 1 Oct 2001 08:30:17 +0200
-
-netatalk (1.5pre7-3) unstable; urgency=low
-
- * Fixed a stupid typo I made in the new init script.
- * Put add_netatalk_printer and netatalkshorternamelinks.pl in the
- examples directory instead of /usr/bin. Suggestion from Jonas
- Smedegaard.
-
- -- Sebastian Rittau <srittau@debian.org> Sun, 23 Sep 2001 19:08:43 +0200
-
-netatalk (1.5pre7-2) unstable; urgency=low
-
- * Integrated a lot of patches by Jonas Smedegaard <dr@jones.dk>:
- + 001_etc2ps paths
- Correct paths in etc2ps and suggest tetex-base.
- + 005_visible_home_dir_in_config
- Name user home directories "Home Directory" by default to make them
- appear in the MacOS chooser.
- + 007_logcheck
- Support for the logcheck log file checking package.
- + 011_avoid_symlinks_and_force_using_autoconf_2.50
- Partly applied: Patch configure.in so that the use of autoconf 2.50
- is forced. (Debian autoconf hack workaround.)
- + 012_netatalk.conf
- Improved init script. Also, make use of netatalk.conf again.
- I patched the patch so that netatalk.conf is placed in /etc/default.
- + 015_recommend_lsof_(for_macusers)_and_suggest_quota
- Recommend lsof and suggest quota.
- + 021_enable_ssl_and_cracklib_and_correct_pam_paths
- Partly applied: Enable cracklib support.
- * Fixed paths in add_netatalk_printer.
- * Removed lp2pap.sh since it's of no use on Debian systems.
- * Removed test_parse_mtab and afpd-mtab.pl because we are not using
- the mtab DID scheme.
- * Comparison operator is '=', not '==' in the 'test' command. Fixed
- my patch.
- * Removed netatalk.conf.5 as well, since we don't install netatalk.conf
- anymore.
- * Removed superfluous file /etc/netatalk/netatalk.pamd.
- * Moved all *.la and *.a files to netatalk-dev. Added appropriate
- conflicts and replaces.
- * debian/rules: Do not copy files to package build directories instead of
- removing them afterwards.
-
- -- Sebastian Rittau <srittau@debian.org> Sun, 23 Sep 2001 14:04:06 +0200
-
-netatalk (1.5pre7-1) unstable; urgency=medium
-
- * New upstream version. Most patches were applied upstream.
- * This release uses libtool for UAM stuff. Also, the correct flag
- for dynamic linking is supplied, so the problems with unresolved
- symbols should be gone now. (Closes: #95399)
- * Non-DSFG free code was removed. Copyright notice was changed accordingly.
- * Use ./configure --sysconfdir instead of --with-config-dir.
- * Upstream package does now install PAM file in the correct directory.
- Removed rule, correcting this from Debian rules file.
- * Added man pages for netatalk-config(1) and timelord(8). (Upstream
- does now also include a man page for timeout(1), but since we're not
- distributing it anymore, we don't care.)
- * Some doc files were removed, others were added.
- * Use debhelper compatibility level 3 and performed general packaging
- cleanups at the same time.
- * Standards-Version 3.5.6.0. No changes needed.
- * Netatalk is now GPL'ed. Added a note stating that to copyright.
- Also, we can't link against libssl anymore. Removed SSL stuff.
- I had to patch configure.in to do that.
- * Removed emacs stuff from changelog.
- * Applied a patch to getiface.c for a problem that could lead to
- segfaults. Thanks to Kai Henningsen <kaih@khms.westfalen.de>
- for actually being affected by this bug, and - more importantly -
- finding the problem. (Closes: #109310)
-
- -- Sebastian Rittau <srittau@debian.org> Thu, 30 Aug 2001 02:02:17 +0200
-
-netatalk (1.5pre6-7) unstable; urgency=low
-
- * Cleaned up CFLAGS handling in ./configure call.
- * Updated config.{sub,guess} again, just to make sure ...
- * Depend on the timeout package from tct. Also, don't distribute
- /usr/bin/timeout and remove the timeout(1) link to undocumented(7).
- Make preparations to remove the proper timeout(1) man page that will
- get distributed with netatalk 1.5pre7.
-
- -- Sebastian Rittau <srittau@debian.org> Sun, 19 Aug 2001 18:05:55 +0200
-
-netatalk (1.5pre6-6) unstable; urgency=medium
-
- * ./configure --with-did=last
- This should fix errors with MacOS X.
- * Fixed typo in add_netatalk_printer. (Closes: #104192)
- * Removed /etc/netatalk/netatalk.conf, since it's not used by Debian's
- init script. (Closes: #103539)
- * Disabled pam_guest module by default. (Closes: #106637)
-
- -- Sebastian Rittau <srittau@debian.org> Sat, 28 Jul 2001 14:49:15 +0200
-
-netatalk (1.5pre6-5) unstable; urgency=low
-
- * Removed --without-ssl option from ./configure invocation. Not
- that it had any effect before.
- * Updated config.{sub,guess} (manually for now). I will switch to
- dh_autotools if and when this is available. (Closes: #102861)
-
- -- Sebastian Rittau <srittau@debian.org> Fri, 6 Jul 2001 00:46:18 +0200
-
-netatalk (1.5pre6-4) unstable; urgency=low
-
- * Changed section of netatalk-dev to non-US, too.
- * Make netatalk-dev depend on netatalk.
-
- -- Sebastian Rittau <srittau@debian.org> Tue, 19 Jun 2001 01:40:07 +0200
-
-netatalk (1.5pre6-3) unstable; urgency=low
-
- * Thanks to my former sponsor Michael 'grisu' Bramer for his efforts.
- * Changed maintainer address to <srittau@debian.org>.
- * Moved to section non-US and link against libssl. Changed Build-Depends
- accordingly.
- * Link against libdb3 instead of libdb2. Changed Build-Depends
- accordingly.
- * Sources were not obtained from CVS, and are available by HTTP.
- * Removed patch to contrib/Makefile.* to enable compilation of timelord.
- Instead, use configure option --with-timelord.
- * Added symlinks to megatron. Use patch from upstream CVS. (Closes: #95944)
- * Clean up patch for etc/psf/Makefile.am.
- * Added DEB_BUILD_OPTIONS handling. (Closes: #99705)
- * Added links to undocumented(7) from binheader(1) and nadheader(1).
- * Standards-Version: 3.5.5.0.
-
- -- Sebastian Rittau <srittau@debian.org> Sun, 17 Jun 2001 15:50:13 +0200
-
-netatalk (1.5pre6-2) unstable; urgency=low
-
- * This version will hopefully clean up the version mess, I created.
- * Conforms to standards-version 3.5.3.0 (no changes needed).
- * Link cleanappledouble.pl(1) to undocumented(7).
- * Removed all hand-crafted {pre,post}{inst,rm} files.
- * Give files in /etc/netatalk/nls a mode of 0644, instead of 0755. Fixes
- lintian warnings.
- * Build-Depends on libdb2-dev do exist since -1. (Closes: #92774)
- * Distribute missing pagecount.ps. (Closes: #95117)
- * Compile timelord.
- * Use --enable-fhs instead of --with-fhs. Should fix some paths.
- * Compile with shadow support. (Closes: #95186)
- * Use the pam_unix.so module instead of pam_pwdb.so in /etc/pam.d/netatalk.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de> Tue, 1 May 2001 03:38:57 +0200
-
-netatalk (1.5pre6-1) unstable; urgency=low
-
- * New upstream release.
- * Re-added changes made in 1.4b2+asun2.1.3-8.
- * Added --prefix=/usr to ./configure options.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de> Fri, 13 Apr 2001 00:27:47 +0200
-
-netatalk (1.5pre5-3) unstable; urgency=low
-
- * Re-added changes made in 1.4b2+asun2.1.3-8.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de> Fri, 6 Apr 2001 23:44:47 +0200
-
-netatalk (1.5pre5-2) unstable; urgency=low
-
- * Added copyright of University of Newcastle upon Tyne to debian/copyright.
- * Removed patches/uams_dhx_pam.c.patch as it was applied upstream.
- * Some documentation files were moved into the doc subdirectory.
- * Added more documentation files.
- * Added some temporary build fixes.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de> Wed, 8 Mar 2001 00:03:30 +0100
-
-netatalk (1.5pre5-1) unstable; urgency=low
-
- * New upstream version.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de> Fri, 23 Feb 2001 21:07:18 +0100
-
-netatalk (1.5pre4-1) unstable; urgency=low
-
- * New upstream version.
- * Some reorganisations to allow building directly from CVS.
- * Debian packaging is now included in upstream CVS.
- * Modified debian/copyright to include CVS instructions.
- * Call ./configure with --with-fhs and removed --with-uams-path option.
- * Removed patches/paths.h.patch as this is now supported by --with-fhs.
- * Removed various build patches now included upstream.
- * Use dh_installman from debhelper v3. Updated build dependencies
- accordingly.
- * Removed comment about Debian specific changes from debian/copyright.
- * Build with libssl support. (Closes: #48871)
- * Added libssl096-dev to Build-Depends.
- * Ship FAQ in /usr/share/doc/netatalk
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de> Thu, 22 Feb 2001 20:44:41 +0100
-
-netatalk (1.5pre3-1) unstable; urgency=low
-
- * New upstream version from netatalk.sourceforge.net.
- (Closes: #69232, #78781)
- * Repackaged using debhelper.
- * Conforms to policy version 3.5.1.0.
- * Removed some Debian specific patches integrated upstream.
- * Updated debian/copyright.
- * Changed priority from optional to extra.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de> Thu, 22 Feb 2001 10:18:07 +0100
-
-netatalk (1.4b2+asun2.1.3-8) unstable; urgency=low
-
- * Added libdb2-dev to build-depends. (Closes: #92774)
- * Complies with Debian policy version 3.5.2.0.
- * Added netatalk homepage and current maintainer to debian/copyright.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de> Tue, 3 Apr 2001 23:59:38 +0200
-
-netatalk (1.4b2+asun2.1.3-7) unstable; urgency=low
-
- * New maintainer. (Closes: #82386)
- * Fixed a build problem.
- * Strip .note and .comment sections from /usr/lib/atalk/psa.
- * Added debhelper as build-dependency.
- * Complies with Debian policy version 3.2.1.0.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de> Sun, 21 Jan 2001 15:49:11 +0100
-
-netatalk (1.4b2+asun2.1.3-6) unstable; urgency=low
-
- * The "looks like I picked the wrong week to quit sniffing glue" release.
- * Update the maintainer name in the control file.
- * Move psa and etc2ps to /usr/lib/atalk, as they are not user binaries
- (this also shuts lintian up).
-
- -- David Huggins-Daines <dhd@debian.org> Fri, 14 Jan 2000 21:04:24 -0500
-
-netatalk (1.4b2+asun2.1.3-5) unstable; urgency=low
-
- * New maintainer.
- * Compensate for stupid new 'install -s' behaviour. (closes:Bug#51423)
- * Fix psf(8) manpage. (closes:Bug#30839)
- * Updated Standards-Version.
- * Fixed symlinks to be relative, as per lintian's warnings.
- * Added /usr/doc symlinks in the postinst/prerm.
-
- -- David Huggins-Daines <dhd@debian.org> Wed, 22 Dec 1999 20:24:26 -0500
-
-netatalk (1.4b2+asun2.1.3-4) unstable; urgency=low
-
- * Fix init script to always kill papd even if ENABLE_PAP=no (closes:Bug#48783).
-
- -- Joel Klecker <espy@debian.org> Sun, 31 Oct 1999 07:43:29 -0800
-
-netatalk (1.4b2+asun2.1.3-3) unstable; urgency=low
-
- * Remove libatalk1 and libatalk1-dev (I think it is a mistake to "fork" a
- shared version of a library in Debian, if the library is static upstream
- then upstream isn't gonna be careful with the ABI).
- * Create netatalk-dev.
- * netatalk.init: Use $() instead of ``.
- Use /bin/hostname explicitly.
- s/daemons/Daemons/g.
- Remove module fiddling (closes:Bug#44767,#43319).
- * Remove "glibc 2.1 fix" it's no longer needed.
- * Compile with sendfile support.
- * Use /usr/share/doc.
- * Cleanup bashisms in debian/rules.
-
- -- Joel Klecker <espy@debian.org> Sat, 23 Oct 1999 20:59:24 -0700
-
-netatalk (1.4b2+asun2.1.3-2) unstable; urgency=low
-
- * (netatalk): Make /etc/netatalk/afpd.conf a conffile (closes:Bug#37628).
-
- -- Joel Klecker <espy@debian.org> Thu, 13 May 1999 10:54:37 -0700
-
-netatalk (1.4b2+asun2.1.3-1) unstable; urgency=low
-
- * New upstream release (closes:Bug#33982).
- * Correct paths in psf.8 (closes:Bug#30839).
- * There is now a different way to control CRLF translation on a
- per-volume basis upstream so I have removed the patch that
- provides the -e option to afpd.
- * (netatalk): Depend on libpam-modules.
- * Put man pages in /usr/share/man.
-
- -- Joel Klecker <espy@debian.org> Tue, 30 Mar 1999 12:17:36 -0800
-
-netatalk (1.4b2+asun2.1.1-2) frozen unstable; urgency=low
-
- * Incorporated glibc 2.1 fixes from Christian Meder.
- * Remove explicit add-log-mailing-address from debian/changelog.
-
- -- Joel Klecker <espy@debian.org> Fri, 15 Jan 1999 07:28:11 -0800
-
-netatalk (1.4b2+asun2.1.1-1.1) frozen unstable; urgency=low
-
- * non maintainer, sparc only upload
- * fix #includes for glibc2.1
-
- -- Christian Meder <meder@isr.uni-stuttgart.de> Mon, 4 Jan 1999 12:37:13 +0100
-
-netatalk (1.4b2+asun2.1.1-1) frozen unstable; urgency=low
-
- * New upstream bugfix release.
- * Recompile against libc6 2.0.7u-7 to get rid of versioned
- libc6 dependency.
-
- -- Joel Klecker <espy@debian.org> Thu, 3 Dec 1998 07:45:42 -0800
-
-netatalk (1.4b2+asun2.1.0-5) frozen unstable; urgency=high
-
- * [libatalk/atp/atp_rsel.c] Minor change for libnatali compatibility
- (closes:Bug#30092).
- * Rebuild with libc6 2.0.7u-6 for i386.
-
- -- Joel Klecker <espy@debian.org> Fri, 27 Nov 1998 22:58:11 -0800
-
-netatalk (1.4b2+asun2.1.0-4) frozen unstable; urgency=low
-
- * binary-arch target now depends on pre-binary (closes:Bug#29508)
-
- -- Joel Klecker <espy@debian.org> Tue, 17 Nov 1998 04:46:50 -0800
-
-netatalk (1.4b2+asun2.1.0-3) frozen unstable; urgency=low
-
- * Now installs /usr/lib/atalk/pagecount.ps (closes:Bug#29323)
-
- -- Joel Klecker <espy@debian.org> Thu, 12 Nov 1998 00:30:53 -0800
-
-netatalk (1.4b2+asun2.1.0-2) frozen unstable; urgency=low
-
- * Should build from freshly unpacked source now (Bug#28810)
-
- -- Joel Klecker <espy@debian.org> Sun, 1 Nov 1998 19:34:52 -0800
-
-netatalk (1.4b2+asun2.1.0-1) unstable; urgency=low
-
- * New upstream release.
- * Incorporate megatron patch from Rob Browning (Bug#25598).
- * Don't install /usr/include/netatalk on glibc 2.1 architectures.
- * Fix paths in /etc/pam.d/netatalk file.
-
- -- Joel Klecker <espy@debian.org> Thu, 29 Oct 1998 23:54:13 -0800
-
-netatalk (1.4b2+asun2.0a18.2-1) frozen unstable; urgency=low
-
- * New "upstream" release.
- * This does add new features, however, it also fixes at
- least one nasty bug (Bug#13973).
- * Applied patch which adds a command-line option to disable
- CR/LF translation (thanks to Davide Welton and Jon Nelson).
- (Note to release manager: this patch is applied so this
- package has the exact functionality of netatalk-asun)
- * Renamed libatalk-dev to libatalk1-dev.
- * Symlinked /usr/man/man1/nbpunrgstr.1.gz to /usr/man/man1/nbprgstr.1.gz
- to keep lintian happy.
- * Changed the "lock directory" to /var/run and the names of the "lock files" to <foo>.pid,
- since what the source calls locks are really the same as the .pid files other daemons
- put in /var/run.
- * This package provides all the functionality of netatalk-asun, and
- it will replace netatalk-asun in the distribution.
-
- -- Joel Klecker <jk@espy.org> Tue, 12 May 1998 19:31:54 -0700
-
-netatalk (1.4b2-5) frozen unstable; urgency=low
-
- * New Maintainer (I can finally close bugs
- I fixed in previous releases ;).
- * Changed library package names again.
- * Upgraded to Debian Policy 2.4.0.0.
- * Moved conffiles to /etc/netatalk.
- * Fixes almost all lintian warnings/errors.
- * Cleaned up changelog.
-
- -- Joel Klecker <jk@espy.org> Sun, 22 Mar 1998 21:50:00 -0800
-
-netatalk (1.4b2-4.5) unstable; urgency=low
-
- * Non-maintainer release (again :>)
- * Made libatalk14g-dev conflict with libc5-dev to fix overlap
- (Bug:#17848)
-
- -- Joel Klecker <jk@espy.org> Thu, 5 Feb 1998 20:42:51 -0800
-
-netatalk (1.4b2-4.4) unstable; urgency=low
-
- * Yet Another non-maintainer release.
- * Added patch to fix "dancing icon" problems with Macs running Mac OS 8.
- * Changed comment in /etc/AppleVolumes.default (Bug:#15279)
- * Implemented variable for "server name" in init script
- (as suggested in Bug:#12024)
- * Added a kluge to /etc/init.d/netatalk to remove kernel appletalk
- module (if there is one) at stop and reinsert it at start, this
- is needed or else netatalk will not start once stopped (Bug:#12142,11349)
-
- -- Joel Klecker <jk@espy.org> Fri, 30 Jan 1998 07:50:00 -0800
-
-netatalk (1.4b2-4.3) unstable; urgency=low
-
- * Non-maintainer release.
- * Fixed dependencies.
-
- -- Joel Klecker <jk@espy.org> Thu, 8 Jan 1998 16:14:17 -0800
-
-netatalk (1.4b2-4.2) unstable; urgency=low
-
- * Non-maintainer release.
- * Changed library package names.
-
- -- Joel Klecker <jk@espy.org> Wed, 7 Jan 1998 00:00:00 -0800
-
-netatalk (1.4b2-4.1) unstable; urgency=low
-
- * Non-maintainer libc6 compile.
-
- -- Joel Klecker <jk@espy.org> Tue, 6 Jan 1998 00:00:00 -0800
-
-netatalk (1.4b2-4) unstable; urgency=low
-
- * Recompiled against newer PAM libraries.
- * Added /etc/pam.d/samba.
-
- -- Klee Dienes <klee@debian.org> Sat, 8 Mar 1997 01:17:09 -0500
-
-netatalk (1.4b2-3) unstable; urgency=low
-
- * Added PAM support.
- * Split into libatalk, libatalk-dev, and netatalk.
- * Added patch from Randy Gobbel <gobbel@cogsci.ucsd.edu> to allow case
- translation to be specified at config-time rather than compile time.
- Note that configuration files that make use of this feature may not
- work with other releases of netatalk, and that this feature may be
- removed in the future if UMich rejects the patch or implements it
- differently.
- * Startup messages now conform to 'Standard for Console Messages' (fixes
- #5399).
- * No longer creates new subdirectories (to appease dpkg-buildpackage).
-
- -- Klee Dienes <klee@debian.org> Wed, 26 Feb 1997 21:02:02 -0500
-
-netatalk (1.4b2-2) unstable; urgency=low
-
- * Resend_request made external for libnatali.
- * Added shared libraries.
- * Next revision will split into libatalk, libatalk-dev, and netatalk.
-
- -- Klee Dienes <klee@debian.org> Fri, 24 Jan 1997 22:37:22 -0500
-
-netatalk (1.4b2-1) unstable; urgency=low
-
- * Updated to upstream version 1.4b2.
- * Added preliminary PAM support (currently disabled).
- * Made /etc/init.d/netatalk a conffile.
- * Changed /etc/init.d/netatalk to complete only once appletalk services
- are running. Configurating an Appletalk interface can take many (> 15)
- seconds, so the previous version would fork a process to configure the
- interface and then start up the other Appletalk services. Although
- possibly controversial, this change is necessary so that packages like
- ppr can be ensured that netatalk will be started before they run
- without undue complication.
-
- -- Klee Dienes <klee@debian.org> Sat, 2 Nov 1996 19:42:04 -0700
-
-netatalk (1.4b1-1) unstable; urgency=low
-
- * Updated to new upstream version.
- * Updated to new packaging format.
-
- -- Klee Dienes <klee@debian.org> Wed, 2 Oct 1996 10:18:14 -0700
-
-netatalk (1.3.3-3);
-
- * Fixed location of include files.
-
- -- Klee Dienes <klee@mit.edu> Mon Jan 8 10:46:52 MST 1996
-
-netatalk (1.3.3-2);
-
- * Fixed bug in postrm script.
-
- -- Klee Dienes <klee@mit.edu> Thu Dec 21 08:22:24 MST 1995
-
-netatalk (1.3.3-1);
-
- * Initial Release.
-
- -- Klee Dienes <klee@mit.edu> Wed Dec 13 22:58:31 MST 1995
+++ /dev/null
-Source: netatalk
-Section: non-US
-Priority: extra
-Maintainer: Sebastian Rittau <srittau@debian.org>
-Standards-Version: 3.5.6.0
-Build-Depends: debhelper (>= 3.0.0), libdb3-dev, libwrap0-dev, libpam0g-dev, cracklib2-dev
-
-Package: netatalk
-Architecture: any
-Depends: netbase, timeout, libpam-modules, ${shlibs:Depends}, ${perl:Depends}
-Recommends: lsof, libpam-cracklib
-Suggests: tetex-base, quota
-Conflicts: netatalk-asun, libatalk14g, libatalk1
-Replaces: netatalk-asun, libatalk14, libatalk1
-Description: AppleTalk user binaries
- Netatalk is an implementation of the AppleTalk Protocol Suite for
- BSD-derived systems. The current release contains support for
- EtherTalk Phase I and II, DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and
- AFP.
-
-Package: netatalk-dev
-Architecture: any
-Depends: netatalk (>= 1.5)
-Conflicts: netatalk (<< 1.5pre7-2), libatalk14g-dev, libatalk14-dev, netatalk-asun, libatalk1-dev
-Replaces: netatalk (<< 1.5pre7-2), libatalk14g-dev, libatalk14-dev, netatalk-asun, libatalk1-dev
-Description: AppleTalk library and development files
- Netatalk is an implementation of the AppleTalk Protocol Suite for
- BSD-derived systems. The current release contains support for
- EtherTalk Phase I and II, DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and
- AFP.
-
+++ /dev/null
-This is the Debian GNU/Linux prepackaged version of netatalk.
-
-This package was originally put together by Joel 'epsy' Klecker.
-Current maintainer is Sebastian Rittau <srittau@debian.org>.
-
-The sources were obtained from CVS: cvs.netatalk.sourceforge.net.
-See the Netatalk homepage at <http://netatalk.sourceforge.net/> for
-anonymous CVS instructions.
-
-The following copyrights/licenses apply to this software:
-
-The GNU General Public License (GPL). See /usr/share/common-licenses/GPL
-for more information.
-
-This software includes software developed by the University of Michigan.
-
-Copyright (c) 1990,1996 Regents of The University of Michigan.
-All Rights Reserved.
-
- Permission to use, copy, modify, and distribute this software and
- its documentation for any purpose and without fee is hereby granted,
- provided that the above copyright notice appears in all copies and
- that both that copyright notice and this permission notice appear
- in supporting documentation, and that the name of The University
- of Michigan not be used in advertising or publicity pertaining to
- distribution of the software without specific, written prior
- permission. This software is supplied as is without expressed or
- implied warranties of any kind.
-
-This product includes software developed by the University of
-California, Berkeley and its contributors.
-
-Solaris code is encumbered by the following:
- Copyright (C) 1996 by Sun Microsystems Computer Co.
-
- Permission to use, copy, modify, and distribute this software and
- its documentation for any purpose and without fee is hereby
- granted, provided that the above copyright notice appear in all
- copies and that both that copyright notice and this permission
- notice appear in supporting documentation. This software is
- provided "as is" without express or implied warranty.
-
-Modifications for Appleshare IP and other files copyrighted by Adrian
-Sun are under the following copyright:
-
- Copyright (c) 1997,1998,1999,2000 Adrian Sun (asun@cobalt.com)
- All Rights Reserved.
-
- Permission to use, copy, modify, and distribute this software and
- its documentation for any purpose and without fee is hereby granted,
- provided that the above copyright notice appears in all copies and
- that both that copyright notice and this permission notice appear
- in supporting documentation. This software is supplied as is
- without expressed or implied warranties of any kind.
-
-Research Systems Unix Group
-The University of Michigan
-c/o Wesley Craig
-535 W. William Street
-Ann Arbor, Michigan
-+1-313-764-2278
-netatalk@umich.edu
+++ /dev/null
-#!/bin/sh
-
-# Execute this script from the main netatalk source directory.
-
-set -e
-
-debiandir=distrib/debian
-
-if test ! -d libatalk; then
- echo "Not in netatalk directory"
- exit 1
-fi
-
-VERSION=`cat VERSION`
-DEBVERSION="${VERSION}cvs"
-DISTDIR="netatalk-$VERSION"
-DISTTGZ="netatalk_$DEBVERSION.orig.tar.gz"
-
-if test ! -f INSTALL; then
- if test -e INSTALL; then
- echo "INSTALL is in the way, please move it away"
- exit 1
- fi
- touch INSTALL
-fi
-
-if test ! -x configure; then
- ./autogen.sh
-fi
-
-if test ! -e Makefile; then
- if test -x config.status; then
- ./config.status
- else
- ./configure
- fi
-fi
-
-make dist
-
-mv "netatalk-$VERSION.tar.gz" "$DISTTGZ"
-rm -rf "$DISTDIR" || true
-tar xzf "$DISTTGZ"
-
-for FILE in `find $debiandir/patches/*.patch`; do
- patch --dir="$DISTDIR" --strip=1 <$FILE
-done
-
-cp -a "$debiandir" "$DISTDIR"
-rm -r "$DISTDIR/debian/CVS"
-rm -r "$DISTDIR/debian/patches"
-rm -r "$DISTDIR/debian/split-init"
-rm "$DISTDIR/debian/cvs2deb.sh"
-
-cd $DISTDIR
-
-touch INSTALL
-
-automake
-
-dpkg-buildpackage -rfakeroot
-
-cd ..
-rm -r "$DISTDIR"
-rm INSTALL
+++ /dev/null
-afpd\[.*\]: ((dhx|dhx2) )?login: [[:alnum:]]+
-afpd\[.*\]: (server_child\[[[:digit:]]+\] [[:digit:]]+ )?(done|exited 1)
-afpd\[.*\]: [\.[:alnum:]]+ read, [\.[:alnum:]]+ written
-afpd\[.*\]: .*: Broken pipe
-afpd\[.*\]: .*: Connection reset by peer
-afpd\[.*\]: .*: Connection timed out
-afpd\[.*\]: .*: No route to host
-afpd\[.*\]: .*: No such file or directory
-afpd\[.*\]: .*: Permission denied
-afpd\[.*\]: .*: child timed out
-afpd\[.*\]: ASIP session:[[:digit:]]+\([[:digit:]]+\) from [\.:[:digit:]]+\([[:digit:]]+\)
-afpd\[.*\]: Connection terminated
-afpd\[.*\]: afp_openfork: ad_open: File Exists
-afpd\[.*\]: asp_alrm: [[:digit:]]+ timed out
-afpd\[.*\]: login [[:alnum:]]+ \(uid [[:digit:]]+, gid [[:digit:]]+\)
-afpd\[.*\]: login noauth
-afpd\[.*\]: logout [[:alnum:]]+
-afpd\[.*\]: registering [[:alnum:]]+ \(uid [[:digit:]]+\) on [\.[:digit:]]+ as /.+/net[\.[:digit:]]+node[[:digit:]]+
-afpd\[.*\]: session from [\.:[:digit:]]+ on [\.:[:digit:]]+
-afpd\[.*\]: uams_dhx_pam.c :PAM: PAM (Auth OK!|Success -- Success)
-afpd\[.*\]: uams_dhx2_pam.c :PAM: PAM (Auth OK!|Success -- Success)
-afpd\[.*\]: using codepage directory: /etc/netatalk/nls/maccode\.[\.[:alnum:]-]+
-atalkd\[.*\]: .*: Network is unreachable
-atalkd\[.*\]: zip gnireply from [\.[:digit:]]+ \(.* [[:digit:]]\)
-atalkd\[.*\]: zip ignoring gnireply
-papd\[.*\]: child [[:digit:]]+ for ".+" from [\.[:digit:]]+
-papd\[.*\]: child [[:digit:]]+ done
+++ /dev/null
-afpd\[.*\]: afp_die: asp_shutdown: Connection timed out
-afpd\[.*\]: afp_getsrvrparms: stat /.+/: Permission denied
-afpd\[.*\]: dsi_stream_read\([[:digit:]]+\): Permission denied
-afpd\[.*\]: getforkparms: (ad_refresh|of_find): Permission denied
+++ /dev/null
-doc/DEVELOPER
+++ /dev/null
-usr/bin/netatalk-config
-usr/include/atalk/adouble.h
-usr/include/atalk/aep.h
-usr/include/atalk/afp.h
-usr/include/atalk/asp.h
-usr/include/atalk/atp.h
-usr/include/atalk/cnid.h
-usr/include/atalk/compat.h
-usr/include/atalk/ddp.h
-usr/include/atalk/dsi.h
-usr/include/atalk/nbp.h
-usr/include/atalk/netddp.h
-usr/include/atalk/pap.h
-usr/include/atalk/paths.h
-usr/include/atalk/rtmp.h
-usr/include/atalk/server_child.h
-usr/include/atalk/uam.h
-usr/include/atalk/util.h
-usr/include/atalk/zip.h
-usr/include/netatalk/aarp.c
-usr/include/netatalk/aarp.h
-usr/include/netatalk/at_control.c
-usr/include/netatalk/at_proto.c
-usr/include/netatalk/at_var.h
-usr/include/netatalk/ddp.h
-usr/include/netatalk/ddp_input.c
-usr/include/netatalk/ddp_output.c
-usr/include/netatalk/ddp_usrreq.c
-usr/include/netatalk/ddp_var.h
-usr/include/netatalk/endian.h
-usr/include/netatalk/phase2.h
-usr/lib/libatalk.a
-usr/lib/libatalk.la
-usr/lib/netatalk/uams_*.la
-usr/lib/netatalk/uams_*.a
-usr/share/aclocal
-usr/share/man/man1/netatalk-config.1
-usr/share/man/man3
-usr/share/man/man4
+++ /dev/null
-etc/default
-etc/logcheck/ignore.d.server
-etc/logcheck/ignore.d.workstation
-etc/logcheck/violations.ignore.d
+++ /dev/null
-BUGS
-CHANGES
-CONTRIBUTORS
-TODO
-doc/CONFIGURE
-doc/FAQ
-doc/README.hidden-items
-doc/README.platforms
+++ /dev/null
-contrib/printing/add_netatalk_printer
+++ /dev/null
-etc/netatalk/AppleVolumes.default
-etc/netatalk/AppleVolumes.system
-etc/netatalk/afpd.conf
-etc/netatalk/atalkd.conf
-etc/netatalk/papd.conf
-etc/netatalk/nls
-etc/pam.d/netatalk
-usr/bin/achfile
-usr/bin/adv1tov2
-usr/bin/aecho
-usr/bin/afile
-usr/bin/apple_cp
-usr/bin/apple_mv
-usr/bin/apple_rm
-usr/bin/cleanappledouble.pl
-usr/bin/getzones
-usr/bin/macusers
-usr/bin/makecode
-usr/bin/megatron
-usr/bin/pap
-usr/bin/papstatus
-usr/bin/parsecode
-usr/bin/psorder
-usr/bin/nbplkup
-usr/bin/nbprgstr
-usr/bin/nbpunrgstr
-usr/bin/nu
-usr/bin/unbin
-usr/bin/unhex
-usr/bin/unsingle
-usr/bin/hqx2bin
-usr/bin/single2bin
-usr/bin/macbinary
-usr/bin/binheader
-usr/bin/nadheader
-usr/lib/atalk/filters/etc2ps.sh
-usr/lib/atalk/filters/ofpap
-usr/lib/atalk/filters/ifpap
-usr/lib/atalk/filters/tfpap
-usr/lib/atalk/filters/ifpaprev
-usr/lib/atalk/filters/tfpaprev
-usr/lib/atalk/filters/ofwpap
-usr/lib/atalk/filters/ifwpap
-usr/lib/atalk/filters/tfwpap
-usr/lib/atalk/filters/ifwpaprev
-usr/lib/atalk/filters/tfwpaprev
-usr/lib/atalk/filters/ofmpap
-usr/lib/atalk/filters/ifmpap
-usr/lib/atalk/filters/tfmpap
-usr/lib/atalk/filters/ifmpaprev
-usr/lib/atalk/filters/tfmpaprev
-usr/lib/atalk/filters/ofwmpap
-usr/lib/atalk/filters/ifwmpap
-usr/lib/atalk/filters/tfwmpap
-usr/lib/atalk/filters/ifwmpaprev
-usr/lib/atalk/filters/tfwmpaprev
-usr/lib/netatalk/uams_*.so
-usr/sbin/afpd
-usr/sbin/atalkd
-usr/sbin/papd
-usr/sbin/psf
-usr/sbin/psa
-usr/sbin/timelord
-usr/share/man/man1/aecho.1
-usr/share/man/man1/afile.1
-usr/share/man/man1/getzones.1
-usr/share/man/man1/megatron.1
-usr/share/man/man1/nbp.1
-usr/share/man/man1/pap.1
-usr/share/man/man1/achfile.1
-usr/share/man/man1/acleandir.1
-usr/share/man/man1/hqx2bin.1
-usr/share/man/man1/macbinary.1
-usr/share/man/man1/nbplkup.1
-usr/share/man/man1/nbprgstr.1
-usr/share/man/man1/nbpunrgstr.1
-usr/share/man/man1/papstatus.1
-usr/share/man/man1/psorder.1
-usr/share/man/man1/single2bin.1
-usr/share/man/man1/unbin.1
-usr/share/man/man1/unhex.1
-usr/share/man/man1/unsingle.1
-usr/share/man/man5/afpd.conf.5
-usr/share/man/man5/atalkd.conf.5
-usr/share/man/man5/papd.conf.5
-usr/share/man/man5/AppleVolumes.default.5
-usr/share/man/man8/pap.8
-usr/share/man/man8/papd.8
-usr/share/man/man8/papstatus.8
-usr/share/man/man8/psf.8
-usr/share/man/man8/timelord.8
-usr/share/man/man8/afpd.8
-usr/share/man/man8/atalkd.8
-usr/share/netatalk
+++ /dev/null
-#!/bin/sh
-
-test -x /usr/sbin/atalkd || exit 0
-
-# Set defaults. Please change these options in /etc/default/netatalk.
-AFPD_UAMLIST="-U uams_clrtxt.so,uams_randnum.so"
-AFPD_GUEST=nobody
-AFPD_MAX_CLIENTS=20
-ATALK_NAME=`/bin/hostname --short`
-
-# Read in netatalk configuration.
-if [ -f /etc/default/netatalk ]; then
- . /etc/default/netatalk
-fi
-
-OPTIONS_AFP="$AFPD_UAMLIST -g $AFPD_GUEST -c $AFPD_MAX_CLIENTS -n $ATALK_NAME"
-
-case "$1" in
- start)
- if [ "$ATALKD_RUN" = "yes" ]; then
- # Quickly probe for appletalk if it was supposed to be loaded
- if grep '^appletalk$' /etc/modules; then
- /sbin/modprobe appletalk || echo "[could not load appletalk module]"
- fi
-
- echo -n "Starting AppleTalk Daemons (this will take a while):"
- /usr/sbin/atalkd
- echo -n " atalkd"
-
- /usr/bin/nbprgstr -p 4 "$ATALK_NAME:Workstation"
- /usr/bin/nbprgstr -p 4 "$ATALK_NAME:netatalk"
- fi
-
- if [ "$AFPD_RUN" = "yes" ]; then
- /usr/sbin/afpd $OPTIONS_AFP
- echo -n " afpd"
- fi
-
- if [ "$ATALKD_RUN" = "yes" -a "$PAPD_RUN" = "yes" ]; then
- /usr/sbin/papd
- echo -n " papd"
- fi
-
- if [ "$TIMELORD_RUN" = "yes" ]; then
- /usr/sbin/timelord
- echo -n " timelord"
- fi
-
- echo "."
- ;;
-
- stop)
- echo -n "Stopping AppleTalk Daemons:"
- echo -n " afpd"; \
- start-stop-daemon --stop --quiet --oknodo --exec /usr/sbin/afpd
-
- echo -n " papd"; \
- start-stop-daemon --stop --quiet --oknodo --exec /usr/sbin/papd
-
- echo -n " timelord"; \
- start-stop-daemon --stop --quiet --oknodo --exec /usr/sbin/timelord
-
- echo -n " atalkd"; \
- start-stop-daemon --stop --quiet --oknodo --exec /usr/sbin/atalkd
-
- echo "."
- ;;
-
- restart)
- $0 force-reload
- ;;
-
- force-reload)
- echo -n "Restarting AppleTalk Daemons (this will take a while)"
- /etc/init.d/netatalk stop > /dev/null 2>&1
- echo -n "."
- sleep 2
- echo -n "."
- if /etc/init.d/netatalk start > /dev/null 2>&1; then
- echo "done."
- fi
- ;;
-
- *)
- echo "Usage: /etc/init.d/netatalk {start|stop|restart|force-reload}" >&2
- exit 1
- ;;
-esac
-
+++ /dev/null
-etc/logcheck/ignore.d.server/netatalk etc/logcheck/ignore.d.workstation/netatalk
+++ /dev/null
-binheader.1
-cleanappledouble.pl.1
-cnid_didname_verify.1
-macusers.1
-makecode.1
-nadheader.1
-nu.1
-parsecode.1
-psa.8
+++ /dev/null
---- netatalk.cvs/contrib/printing/add_netatalk_printer
-+++ netatalk.debian/contrib/printing/add_netatalk_printer
-@@ -43,10 +43,10 @@
-
- # allow for the env NETATALKHOME to override the guessed one from above
-
--NETATALKHOME=${NETATALKHOME:-$RUNHOME}
-+NETATALKHOME=${NETATALKHOME:-/usr}
- export NETATALKHOME
-
--PATH=/bin:${PATH}:${NETATALKHOME}/bin:${NETATALKHOME}/etc:${NETATALKHOME}/etc/filters:/usr/lib:/usr/sbin
-+PATH=/bin:${PATH}:${NETATALKHOME}/bin:${NETATALKHOME}/etc:${NETATALKHOME}/lib/atalk/filters:/usr/lib:/usr/sbin
-
- if [ ! -x ${NETATALKHOME}/bin/pap ]; then
- echo "OOPS: I don't see ${NETATALKHOME}/bin/pap ,"
-@@ -66,7 +66,7 @@
-
- echo ""
- echo "Looking for LaserWriters in Zone ${ZONE} ..."
--$NETATALKHOME/bin/nbplkup ":LaserWriter@${ZONE}"
-+${NETATALKHOME}/bin/nbplkup ":LaserWriter@${ZONE}"
-
- echo ""
- echo "Enter AppleTalk printer name: \c"
-@@ -80,7 +80,7 @@
-
- echo "checking nbplkup ${DEST}:LaserWriter@${ZONE}"
- echo ""
--TestDEST=`$NETATALKHOME/bin/nbplkup "${DEST}:LaserWriter@${ZONE}"`
-+TestDEST=`${NETATALKHOME}/bin/nbplkup "${DEST}:LaserWriter@${ZONE}"`
- echo "${TestDEST}"
- echo ""
-
-@@ -237,7 +237,7 @@
- Printer types: Netatalk
- Printers: any
- Filter type: fast
--Command: ${NETATALKHOME}/etc/filters/ifpap 2>&1 > /dev/null
-+Command: ${NETATALKHOME}/lib/atalk/filters/ifpap 2>&1 > /dev/null
- EOF
- chown lp:lp /etc/lp/fd/netatalk.fd
- chmod 664 /etc/lp/fd/netatalk.fd
-@@ -257,7 +257,7 @@
- Printer types: Netatalk-R
- Printers: any
- Filter type: fast
--Command: "/usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/etc/filters/ifpap 2>&1 >/dev/null"
-+Command: "/usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/lib/atalk/filters/ifpap 2>&1 >/dev/null"
- EOF
- chown lp:lp /etc/lp/fd/netatalk-r.fd
- chmod 664 /etc/lp/fd/netatalk-r.fd
+++ /dev/null
---- netatalk.cvs/etc/psf/etc2ps.sh
-+++ netatalk.debian/etc/psf/etc2ps.sh
-@@ -9,11 +9,11 @@
- # tag in the case.
- #
-
--DVIPSPATH=/usr/local/tex/bin
--DVIPS=/usr/local/tex/bin/dvips
-+DVIPSPATH=/usr/bin
-+DVIPS=/usr/bin/dvips
- DVIPSARGS="-f -q"
-
--TROFF2PS=/usr/local/psroff/troff2/troff2ps
-+TROFF2PS=/usr/bin/troff2ps
- TROFF2PSARGS="-Z -O-.10"
-
- PATH=/usr/bin:$DVIPSPATH; export PATH
+++ /dev/null
---- netatalk.cvs/etc/psf/Makefile.am
-+++ netatalk.debian/etc/psf/Makefile.am
-@@ -1,8 +1,11 @@
- # Makefile.am for etc/psf/
-
-+filterdir = $(libdir)/atalk/filters
-+
- sbin_PROGRAMS = psf psa
-
- pkgdata_DATA = pagecount.ps
-+filter_SCRIPTS = etc2ps.sh
-
- psf_SOURCES = psf.c
- psa_SOURCES = psa.c
-@@ -26,14 +29,16 @@
- # install sections for links
- #
-
-+# srittau: We do some dirty hard-coding for Debian to maintain compability.
- install-exec-local:
-+ $(mkinstalldirs) $(DESTDIR)$(filterdir)
- @list='$(psf_LINKS)'; for l in $$list; do \
-- $(LN_S) -f psf $(DESTDIR)$(sbindir)/$$l; \
-+ $(LN_S) -f ../../../sbin/psf $(DESTDIR)$(filterdir)/$$l; \
- done
-
- #
+++ /dev/null
---- netatalk.cvs/config/netatalk.conf
-+++ netatalk.debian/config/netatalk.conf
-@@ -6,7 +6,7 @@
- # NOTE: if you're zone has spaces in it, you're better off specifying
- # it in afpd.conf
- #ATALK_ZONE=@zone
--ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
-+ATALK_NAME=`/bin/hostname --short`
-
- # specify this if you don't want guest, clrtxt, and dhx
- # available options: uams_guest.so, uams_clrtxt.so, uams_dhx.so,
-@@ -21,6 +21,3 @@
- PAPD_RUN=yes
- AFPD_RUN=yes
- TIMELORD_RUN=no
--
--# Control whether the daemons are started in the background
--ATALK_BGROUND=no
+++ /dev/null
---- netatalk.cvs/config/netatalk.pamd
-+++ netatalk.debian/config/netatalk.pamd
-@@ -1,6 +1,6 @@
- #%PAM-1.0
--auth required /lib/security/pam_unix.so
--account required /lib/security/pam_unix.so
--#password required /lib/security/pam_cracklib.so
--#password required /lib/security/pam_unix.so use_authtok
--session required /lib/security/pam_unix.so
-+auth required pam_unix.so
-+account required pam_unix.so
-+password required pam_cracklib.so
-+password required pam_unix.so use_authtok
-+session required pam_unix.so
+++ /dev/null
---- netatalk.cvs/man/man8/psf.8.tmpl
-+++ netatalk.debian/man/man8/psf.8.tmpl
-@@ -90,13 +90,13 @@
- .RS
- .nf
- laser|lp|LaserWriter Plus on AppleTalk:\\
-- :sd=/usr/spool/lpd/laser:\\
-- :lp=/usr/spool/lpd/laser/null:\\
-- :lf=/var/adm/lpd-errs:pw#80:hl:\\
-- :of=:LIBDIR:/filters/ofpap:\\
-- :if=:LIBDIR:/filters/ifpaprev:\\
-- :tf=:LIBDIR:/filters/tfpaprev:\\
-- :df=:LIBDIR:/filters/dfpaprev:
-+ :sd=/var/spool/lpd/laser:\\
-+ :lp=/var/spool/lpd/laser/null:\\
-+ :lf=/var/log/lpd-errs:pw#80:hl:\\
-+ :of=:LIBDIR:/atalk/filters/ofpap:\\
-+ :if=:LIBDIR:/atalk/filters/ifpaprev:\\
-+ :tf=:LIBDIR:/atalk/filters/tfpaprev:\\
-+ :df=:LIBDIR:/atalk/filters/dfpaprev:
- .fi
- .RE
- .sp
+++ /dev/null
-#! /usr/bin/make -f
-
-# Uncomment the following line to enable OpenSSL support. (If it's installed.)
-#USE_SSL=yes
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-# This is the debhelper compatability version to use.
-export DH_COMPAT=3
-
-# support the DEB_BUILD_OPTIONS variable (partly stolen from gnome-utils)
-CFLAGS := -O2
-LDFLAGS :=
-ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
- CFLAGS += -g
- LDFLAGS += -g
-endif
-export CFLAGS
-export LDFLAGS
-
-CONFIGURE_FLAGS = \
- --with-shadow --enable-fhs --sysconfdir=/etc/netatalk \
- --with-tcp-wrappers --mandir=/usr/share/man --prefix=/usr \
- --enable-timelord --enable-overwrite \
- --with-cracklib=/var/cache/cracklib/cracklib_dict
-ifneq "x$(USE_SSL)" "xyes"
-CONFIGURE_FLAGS += --without-ssl-dir
-endif
-
-configure: configure-stamp
-configure-stamp:
- dh_testdir
-
- ./configure $(CONFIGURE_FLAGS)
-
- touch configure-stamp
-
-build: configure-stamp build-stamp
-build-stamp:
- dh_testdir
-
- $(MAKE)
-
- touch build-stamp
-
-clean:
- dh_testdir
- dh_testroot
- rm -f build-stamp configure-stamp
-
- -$(MAKE) distclean
-
- dh_clean
-
-install: build
- dh_testdir
- dh_testroot
- dh_clean -k
- dh_installdirs
-
- $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp
-
- # Manually move a file that would get installed in the wrong place.
- mv debian/tmp/etc/netatalk/netatalk.conf debian/netatalk/etc/default/netatalk
-
- # Install logcheck files
- install -m 644 debian/logcheck/ignore.d.server debian/netatalk/etc/logcheck/ignore.d.server/netatalk
- install -m 644 debian/logcheck/violations.ignore.d debian/netatalk/etc/logcheck/violations.ignore.d/netatalk
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-binary-arch: build install
- dh_testdir
- dh_testroot
- dh_movefiles
-
- dh_installdocs
- dh_installexamples
- dh_installinit --update-rcd-params="defaults 50 50"
- dh_installman
- dh_undocumented
- dh_installchangelogs ChangeLog
- dh_link
- dh_strip
- dh_compress
- dh_fixperms
- dh_makeshlibs
- dh_installdeb
- dh_shlibdeps
- dh_perl
- dh_gencontrol
- dh_md5sums
- dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install configure
if USE_SUSE
sysvdir = /etc/init.d
-sysv_SCRIPTS = atalk
+sysv_SCRIPTS = netatalk
$(sysv_SCRIPTS): rc.atalk.suse
cp -f rc.atalk.suse $(sysv_SCRIPTS)
if USE_COBALT
sysvdir = /etc/rc.d/init.d
-sysv_SCRIPTS = atalk
+sysv_SCRIPTS = netatalk
$(sysv_SCRIPTS): rc.atalk.cobalt
cp -f rc.atalk.cobalt $(sysv_SCRIPTS)
if USE_TRU64
sysvdir = /etc/init.d
-sysv_SCRIPTS = atalk
+sysv_SCRIPTS = netatalk
$(sysv_SCRIPTS): rc.atalk.tru64
cp -f rc.atalk.tru64 $(sysv_SCRIPTS)
if USE_GENTOO
sysvdir = /etc/init.d
-sysv_SCRIPTS = atalk
+sysv_SCRIPTS = netatalk
$(sysv_SCRIPTS): rc.atalk.gentoo
cp -f rc.atalk.gentoo $(sysv_SCRIPTS)
chmod a+x $(sysv_SCRIPTS)
install-data-hook:
- -rc-update add $(sysv_SCRIPTS) default
+# -rc-update add $(sysv_SCRIPTS) default
uninstall-startup:
- -rc-update del $(sysv_SCRIPTS) default
- rm -f $(DESTDIR)$(sysvdir)/$(sysv_SCRIPTS)
+# -rc-update del $(sysv_SCRIPTS) default
+# rm -f $(DESTDIR)$(sysvdir)/$(sysv_SCRIPTS)
endif
## /usr/etc/modload -sym :ETCDIR:/netatalk.o;
##fi
+ATALK_NAME=`hostname|sed 's/\..*$//'`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
+ATALK_ZONE=
+#ATALK_BGROUND=no
+
netatalk_conf=":ETCDIR:/netatalk.conf"
[ -f ${netatalk_conf} ] && . ${netatalk_conf}
fi
if [ -x :BINDIR:/nbprgstr ]; then
- :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:Workstation
- :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:netatalk
+ :BINDIR:/nbprgstr -p 4 ${ATALK_NAME}:Workstation
+ :BINDIR:/nbprgstr -p 4 ${ATALK_NAME}:netatalk
echo -n ' nbprgstr'
fi
fi
fi
if [ -x :SBINDIR:/cnid_metad -a X"${CNID_METAD_RUN}" != X"no" ]; then
- :SBINDIR:/cnid_metad $CNID_CONFIG
- echo -n ' cnid_metad'
+ :SBINDIR:/cnid_metad $CNID_CONFIG
+ echo -n ' cnid_metad'
fi
if [ -x :SBINDIR:/afpd -a X"${AFPD_RUN}" != X"no" ]; then
- :SBINDIR:/afpd; echo -n ' afpd'
+ :SBINDIR:/afpd ${AFPD_UAMLIST} -g ${AFPD_GUEST} \
+ -c ${AFPD_MAX_CLIENTS} -n ${ATALK_NAME}${ATALK_ZONE}
+ echo -n ' afpd'
fi
if [ -x :SBINDIR:/timelord -a X"${TIMELORD_RUN}" != X"no" ]; then
SCRIPTNAME=/etc/init.d/$NAME
# Guard to prevent execution if netatalk was removed.
-test -x :SBINDIR:/atalkd || exit 0
+test -x :SBINDIR:/afpd || exit 0
# Set defaults. Please change these options in /etc/default/netatalk
+ATALK_NAME=`/bin/hostname --short`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=50
AFPD_UAMLIST="-U uams_dhx2.so"
AFPD_GUEST=nobody
-AFPD_MAX_CLIENTS=50
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
ATALK_ZONE=
-ATALK_NAME=`/bin/hostname --short`
ATALK_BGROUND=no
-CNID_METAD_RUN=yes
-ATALK_MAC_CHARSET='MAC_ROMAN'
-ATALK_UNIX_CHARSET='LOCALE'
-# /etc/default/netatalk expects hostname in $HOSTNAME by default
+# old /etc/default/netatalk expected hostname in $HOSTNAME by default
HOSTNAME=`/bin/hostname`
-# next netatalk 2.2 will not expect $HOSTNAME.
# Read in netatalk configuration.
if [ -f /etc/default/netatalk ]; then
# Try to load the AppleTalk kernel module if it was intended.
if grep -q '^appletalk$' /etc/modules; then
- /sbin/modprobe appletalk || echo "[could not load appletalk module]"
+ /sbin/modprobe appletalk || echo "[could not load appletalk module]"
fi
# Start atalkd server.
case "$1" in
start)
- if test "x$ATALK_BGROUND" = "xyes"; then
+ if [ "x$ATALK_BGROUND" = "xyes" -a "x$ATALKD_RUN" = "xyes" ]; then
echo "Starting Netatalk services in the background."
atalk_startup >/dev/null &
else
echo -n " cnid_metad"
start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/cnid_metad
- echo -n " papd"
- start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/papd
+ if test -x :SBINDIR:/papd; then
+ echo -n " papd"
+ start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/papd
+ fi
if test -x :SBINDIR:/timelord; then
echo -n " timelord"
start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/timelord
fi
- echo -n " atalkd"
- start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/atalkd
+ if test -x :SBINDIR:/atalkd; then
+ echo -n " atalkd"
+ start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/atalkd
+ fi
echo "."
;;
# its data structures must have time to stablize before running the
# other processes.
+ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
+ATALK_ZONE=
+ATALK_BGROUND=no
+
depend() {
need net
use logger dns
start () {
. :ETCDIR:/netatalk.conf
- if [ x"${ATALK_BGROUND}" = x"yes" ]; then
+ if [ x"${ATALK_BGROUND}" = x"yes" -a "${ATALKD_RUN}" != "no" ]; then
echo "Starting netatalk in the background ... "
atalk_startup >& /dev/null &
else
# Source networking configuration.
. /etc/sysconfig/network
+# default
+ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+A2BOOT_RUN=no
+ATALK_ZONE=
+ATALK_BGROUND=no
+
# read in netatalk configuration
if [ -f ${ATALK_CONF_DIR}/netatalk.conf ]; then
. ${ATALK_CONF_DIR}/netatalk.conf
exit 1;
fi
- if [ ! -x ${ATALK_SBIN}/atalkd ]; then
- # Quickly probe for appletalk and warn if we can't find it
- #/sbin/modprobe appletalk || echo "[could not load appletalk module]"
- # Check for IP Encapsulation support
- #/sbin/modprobe ipddp || echo "[could not load IP encapsulation]"
- echo "[${ATALK_SBIN}/atalkd not found. Check for permissions]";
- exit 4;
- fi
-
if [ ! -f ${ATALK_CONF_DIR}/netatalk.conf ]; then
echo "[${ATALK_CONF_DIR}/netatalk.conf not found]";
exit 6;
fi
- if [ x"${ATALKD_RUN}" != x"no" ]; then
+ if [ x"${ATALKD_RUN}" != x"no" -a -x ${ATALK_SBIN}/atalkd ]; then
+ # Quickly probe for appletalk and warn if we can't find it
+ #/sbin/modprobe appletalk || echo "[could not load appletalk module]"
+ # Check for IP Encapsulation support
+ #/sbin/modprobe ipddp || echo "[could not load IP encapsulation]"
echo -n " Starting atalkd:"
daemon ${ATALK_SBIN}/atalkd
RETVAL_ATALKD=$?
case "$1" in
'start')
echo -n 'Starting Netatalk services: '
- if [ x"${ATALK_BGROUND}" = x"yes" ]; then
+ if [ x"${ATALK_BGROUND}" = x"yes" -a x"${ATALKD_RUN}" != x"no" ]; then
echo -n "(backgrounded)"
atalk_startup >& /dev/null &
else
return=$rc_done
}
+ATALK_NAME=`hostname|sed 's/\..*$//'`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
+ATALK_ZONE=
+ATALK_BGROUND=no
+
+
. :ETCDIR:/netatalk.conf
# startup code for everything
:SBINDIR:/atalkd
if [ -x :BINDIR:/nbprgstr ]; then
- :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:Workstation
- :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:netatalk
+ :BINDIR:/nbprgstr -p 4 ${ATALK_NAME}:Workstation
+ :BINDIR:/nbprgstr -p 4 ${ATALK_NAME}:netatalk
fi
case "$1" in
start)
- if [ x"${ATALK_BGROUND}" = x"yes" ]; then
+ if [ x"${ATALK_BGROUND}" = x"yes" -a x"${ATALKD_RUN}" != x"no" ]; then
echo "Starting netatalk in the background ... "
atalk_startup >& /dev/null &
else
[ "$pid" != "" ] && kill $pid
}
-# netatalk.conf expects hostname in $HOSTNAME by default
+# default
+ATALK_NAME=`hostname|cut -d. -f1`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
+ATALK_ZONE=
+ATALK_BGROUND=no
+
+# old netatalk.conf expected hostname in $HOSTNAME by default
HOSTNAME=`hostname`
. :ETCDIR:/netatalk.conf
case "$1" in
'start')
- if [ x"${ATALK_BGROUND}" = x"yes" ]; then
+ if [ x"${ATALK_BGROUND}" = x"yes" -a x"${ATALKD_RUN}" != x"no" ]; then
echo "Starting netatalk in the background ... "
atalk_startup > /dev/null &
else
+++ /dev/null
-#!/bin/sh
-#
-# buildrpm
-# $Id: buildrpm,v 1.3 2003-01-11 17:26:06 jmarcus Exp $
-#
-# automates the process of building the netatalk rpm
-#
-# To properly bootstrap the RPM from a raw CVS pull,
-# place the CVS sandbox under, e.g. /usr/src/redhat/BUILD
-# and name this new directory 'netatalk-$version' (where
-# $version is the contents of the 'VERSION' file under the source
-# root). Then, cd into the source root and run 'autogen.sh'
-# (with no arguments). Finally, copy this file to the BUILD
-# directory and run it from there, passing the full name of the
-# source directory as the sole argument.
-#
-if [ "x$1" = "x" ]; then
- echo "To avoid problems with builds on remote filesystems"
- echo "please copy this file to your redhat/BUILD directory"
- echo "and execute as 'buildrpm netatalk-xxy', using the actual"
- echo "full name (i.e. with version) of the source tree."
- exit 1
-fi
-
-CVSNAME=$1
-
-REDHAT_DIR=../
-
-VERSION=`cat $CVSNAME/VERSION`
-
-sed -e "s/__VERSION__/$VERSION/" \
- $CVSNAME/distrib/rpm/netatalk-redhat.spec \
- > ${REDHAT_DIR}/SPECS/netatalk.spec
-
-cp -f $CVSNAME/distrib/rpm/netatalk-rpmbuild.patch \
- ${REDHAT_DIR}/SOURCES
-
-# Newer distros use rpmbuild
-if `rpmbuild --version > /dev/null`; then
- RPM="rpmbuild"
-else
- RPM="rpm"
-fi
-
-# clean out objects and Makefiles
-(cd $CVSNAME && make distclean)
-
-# tar up the archive
-tar -c -v -z -f ${REDHAT_DIR}/SOURCES/$CVSNAME.tar.gz \
- --exclude="*/CVS" --exclude="*~" $CVSNAME
-
-# build the SRPM and binary and devel RPMs.
-${RPM} -ba ${REDHAT_DIR}/SPECS/netatalk.spec
+++ /dev/null
-Summary: AppleTalk networking programs
-Name: netatalk
-Version: 1.4b2+asun2.1.4
-Release: pre39
-Packager: iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-Copyright: BSD
-Group: Networking
-Source0: ftp://ftp.cobaltnet.com/pub/users/asun/testing/pre-asun2.1.4-36.tar.gz
-Patch0: netatalk-asun.makefile.patch
-Requires: pam >= 0.56
-BuildRoot: /var/tmp/atalk
-
-%description
-This package enables Linux to talk to Macintosh computers via the
-AppleTalk networking protocol. It includes a daemon to allow Linux
-to act as a file server over AppleTalk or IP for Mac's.
-
-%package devel
-Summary: Headers and static libraries for Appletalk development
-Group: Development/Libraries
-
-%description devel
-This packge contains the header files, and static libraries for building
-Appletalk networking programs.
-
-%prep
-%setup
-%patch0 -p1
-
-%build
-make OPTOPTS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" OSVERSION=2.0
-
-%install
-rm -rf $RPM_BUILD_ROOT
-mkdir -p $RPM_BUILD_ROOT/etc/atalk
-mkdir -p $RPM_BUILD_ROOT/etc/pam.d
-mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
-for i in 0 1 2 3 4 5 6; do
- mkdir -p $RPM_BUILD_ROOT/etc/rc.d/rc$i.d
-done
-mkdir -p $RPM_BUILD_ROOT/usr/lib/atalk
-
-make install INSTALL_PREFIX=$RPM_BUILD_ROOT
-
-for i in aecho getzones megatron nbplkup nbprgstr nbpunrgstr pap \
- papstatus psorder; do
- strip $RPM_BUILD_ROOT/usr/bin/$i
-done
-for i in afpd atalkd psf psa papd; do
- strip $RPM_BUILD_ROOT/usr/sbin/$i
-done
-
-install -m644 config/AppleVolumes.system $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.system
-install -m644 config/AppleVolumes.default $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.default
-install -m644 config/atalkd.conf $RPM_BUILD_ROOT/etc/atalk/atalkd.conf
-install -m644 config/papd.conf $RPM_BUILD_ROOT/etc/atalk/papd.conf
-
-# This is not necessary because chkconfig will make the links.
-for i in 0 1 2 6; do
- ln -sf ../init.d/atalk $RPM_BUILD_ROOT/etc/rc.d/rc$i.d/K35atalk
-done
-for i in 3 4 5; do
- ln -sf ../init.d/atalk $RPM_BUILD_ROOT/etc/rc.d/rc$i.d/S91atalk
-done
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%post
-/sbin/chkconfig --add atalk
-ldconfig
-# Do only for the first install
-if [ "$1" = 1 ] ; then
- # Add the ddp lines to /etc/services
- if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
- cat <<'_EOD1_' >&2
-warning: The DDP services appear to be present in /etc/services.
-warning: Please check them against services.atalk in the documentation.
-_EOD1_
- true
- else
- cat <<'_EOD2_' >>/etc/services
-# start of DDP services
-#
-# Everything between the 'start of DDP services' and 'end of DDP services'
-# lines will be automatically deleted when the netatalk package is removed.
-#
-rtmp 1/ddp # Routing Table Maintenance Protocol
-nbp 2/ddp # Name Binding Protocol
-echo 4/ddp # AppleTalk Echo Protocol
-zip 6/ddp # Zone Information Protocol
-
-afpovertcp 548/tcp # AFP over TCP
-afpovertcp 548/udp
-# end of DDP services
-_EOD2_
- fi
-fi
-
-%postun
-# Do only for the last un-install
-if [ "$1" = 0 ] ; then
- /sbin/chkconfig --del atalk
- # remove the ddp lines from /etc/services
- if (grep '^# start of DDP services$' /etc/services >/dev/null && \
- grep '^# end of DDP services$' /etc/services >/dev/null ); then
- sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
- </etc/services >/tmp/services.tmp$$
- cat /tmp/services.tmp$$ >/etc/services
- rm /tmp/services.tmp$$
- else
- cat <<'_EOD3_' >&2
-warning: Unable to find the lines `# start of DDP services' and
-warning: `# end of DDP services' in the file /etc/services.
-warning: You should remove the DDP services from /etc/service manually.
-_EOD3_
- fi
-fi
-
-%files
-%doc BUGS CHANGES CONTRIBUTORS COPYRIGHT ChangeLog INSTALL/ README* TODO VERSION contrib/ services.atalk
-%dir /etc/atalk
-%config /etc/atalk/AppleVolumes.default
-%config /etc/atalk/AppleVolumes.system
-%config /etc/atalk/netatalk.conf
-%config /etc/atalk/afpd.conf
-%config /etc/atalk/atalkd.conf
-%config /etc/atalk/papd.conf
-%config /etc/rc.d/init.d/atalk
-%config /etc/pam.d/netatalk
-/etc/rc.d/rc0.d/K35atalk
-/etc/rc.d/rc1.d/K35atalk
-/etc/rc.d/rc2.d/K35atalk
-/etc/rc.d/rc3.d/S91atalk
-/etc/rc.d/rc4.d/S91atalk
-/etc/rc.d/rc5.d/S91atalk
-/etc/rc.d/rc6.d/K35atalk
-/usr/sbin/afpd
-/usr/sbin/atalkd
-/usr/sbin/papd
-/usr/sbin/psa
-/usr/sbin/etc2ps
-/usr/sbin/psf
-/usr/bin/adv1tov2
-/usr/bin/aecho
-/usr/bin/afppasswd
-/usr/bin/binheader
-/usr/bin/getzones
-/usr/bin/hqx2bin
-/usr/bin/macbinary
-/usr/bin/megatron
-/usr/bin/nadheader
-/usr/bin/nbplkup
-/usr/bin/nbprgstr
-/usr/bin/nbpunrgstr
-/usr/bin/pap
-/usr/bin/papstatus
-/usr/bin/psorder
-/usr/bin/single2bin
-/usr/bin/unbin
-/usr/bin/unhex
-/usr/bin/unsingle
-%dir /usr/lib/atalk
-/usr/lib/atalk/filters/
-/usr/lib/atalk/nls/
-/usr/lib/atalk/pagecount.ps
-/usr/lib/atalk/uams/
-/usr/man/man1/aecho.1
-/usr/man/man1/getzones.1
-/usr/man/man1/hqx2bin.1
-/usr/man/man1/macbinary.1
-/usr/man/man1/megatron.1
-/usr/man/man1/nbp.1
-/usr/man/man1/nbplkup.1
-/usr/man/man1/nbprgstr.1
-/usr/man/man1/pap.1
-/usr/man/man1/papstatus.1
-/usr/man/man1/psorder.1
-/usr/man/man1/single2bin.1
-/usr/man/man1/unbin.1
-/usr/man/man1/unhex.1
-/usr/man/man1/unsingle.1
-/usr/man/man3/atalk_aton.3
-/usr/man/man3/nbp_name.3
-/usr/man/man4/atalk.4
-/usr/man/man8/afpd.8
-/usr/man/man8/atalkd.8
-/usr/man/man8/papd.8
-/usr/man/man8/psf.8
-
-%files devel
-/usr/lib/libatalk.a
-/usr/lib/libatalk_p.a
-/usr/include/atalk/
-/usr/include/netatalk/
-
-%changelog
-* Thu Jul 22 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- /etc/atalk/netatalk.config -> /etc/atalk/netatalk.conf
- Many parts of patch are merged into the original source code.
-* Tue Jul 13 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- AppleVolumes.system is merged into the original source code.
- /etc/atalk/config -> /etc/atalk/netatalk.config.
- Merge original rc.atalk.redhat and /etc/rc.d/init.d/atalk.
- Remove last sample line of patched afpd.conf.
-* Fri Jul 9 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-30]
-* Sun Jun 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-28]
-* Thu Jun 3 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-22]
-* Wed May 19 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-15]
- Make BerkleyDB=/usr.
-* Sun May 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-11]
- Integrate three patches into netatalk-asun.makefile.patch.
- Change /etc/uams dir to /usr/lib/atalk/uams.
- Add configuration line to /etc/atalk/afpd.conf and remove needless
- variables from /etc/atalk/config and /etc/rc.d/init.d/atalk.
-* Wed Apr 21 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-9]
- Move %chengelog section last.
-* Wed Mar 31 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- Comment out -DNEED_QUOTA_WRAPPER in sys/linux/Makefile.
-* Sat Mar 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- Correct symbolic links to psf.
- Remove asciize function from nbplkup so as to display Japanese hostname.
-* Thu Mar 11 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- Included MacPerl 5 script ICDumpSuffixMap which dumps suffix mapping
- containd in Internet Config Preference.
-* Tue Mar 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [asun2.1.3]
-* Mon Feb 15 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.2-8]
-* Sun Feb 7 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.2-6]
-* Mon Jan 25 1999 iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.2-3]
-* Thu Dec 17 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.2]
- Remove crlf patch. It is now a server's option.
-* Thu Dec 3 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use stable version source netatalk-1.4b2+asun2.1.1.tar.gz
- Add uams directory
-* Sat Nov 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.1-3 source.
-* Mon Nov 23 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.1-2 source.
-* Mon Nov 16 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Fix rcX.d's symbolic links.
-* Wed Oct 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0a-2 source. Remove '%exclusiveos linux' line.
-* Sat Oct 24 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use stable version source netatalk-1.4b2+asun2.1.0.tar.gz.
-* Mon Oct 5 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-10a source.
-* Thu Sep 19 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-8 source. Add chkconfig support.
-* Sat Sep 12 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Comment out -DCRLF. Use RPM_OPT_FLAGS.
-* Mon Sep 8 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-7 source. Rename atalk.init to atalk.
-* Mon Aug 22 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-6 source.
-* Mon Jul 27 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-5 source.
-* Tue Jul 21 1998 INOUE Koichi <inoue@ma.ns.musashi-techa.c.jp>
-- Use pre-asun2.1.0-3 source.
-* Tue Jul 7 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Add afpovertcp entries to /etc/services
-- Remove BuildRoot in man8 pages
-* Mon Jun 29 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use modified sources 1.4b2+asun2.1.0 produced by Adrian Sun
- <asun@saul9.u.washington.edu> to provide an AppleShareIP file server
-- Included AppleVolumes.system file maintained by Johnson
- <johnson@stpt.usf.edu>
-* Mon Aug 25 1997 David Gibson <D.Gibson@student.anu.edu.au>
-- Used a buildroot
-- Use RPM_OPT_FLAGS
-- Moved configuration parameters/files from atalk.init to /etc/atalk
-- Separated devel package
-- Built with shared libraries
-* Sun Jul 13 1997 Paul H. Hargrove <hargrove@sccm.Stanford.EDU>
-- Updated sources from 1.3.3 to 1.4b2
-- Included endian patch for Linux/SPARC
-- Use all the configuration files supplied in the source. This has the
- following advantages over the ones in the previous rpm release:
- + The printer 'lp' isn't automatically placed in papd.conf
- + The default file conversion is binary rather than text.
-- Automatically add and remove DDP services from /etc/services
-- Placed the recommended /etc/services in the documentation
-- Changed atalk.init to give daemons a soft kill
-- Changed atalk.init to make configuration easier
-
-* Wed May 28 1997 Mark Cornick <mcornick@zorak.gsfc.nasa.gov>
-Updated for /etc/pam.d
+++ /dev/null
-#################################################### VERSIONING INFORMATION
-%define name netatalk
-%define version 2.0.2
-%define release 2
-
-################################################# BASIC PACKAGE INFORMATION
-Summary: Appletalk and Appleshare/IP services for Linux
-Name: %{name}
-Version: %{version}
-Release: %{release}
-Copyright: BSD
-Group: Networking/Daemons
-Source0: %{name}-%{version}.tar.gz
-URL: http://netatalk.sourceforge.net/
-Packager: dan dickey <dan.dickey@savvis.net>
-
-############################################################## REQUIREMENTS
-Requires: cracklib, openssl, tcp_wrappers, pam
-BuildRequires: openssl-devel
-
-Prefix: %{_prefix}
-BuildRoot: /var/tmp/%{name}-buildroot
-
-%description
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 3, 2.2 and 2.1 (Appleshare IP).
-
-%package devel
-Group: Development/Networking
-Summary: Appletalk and Appleshare/IP services for Linux development files
-%description devel
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 3, 2.2 and 2.1 (Appleshare IP).
-
-This package is required for developing appletalk-based applications.
-
-%prep
-%setup -q -n %{name}-%{version}/
-
-%build
-CFLAGS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" ./configure \
- --prefix=%{prefix} \
- --libexec=%{prefix}/libexec/netatalk \
- --with-config-dir=/etc/atalk \
- --with-pkgconfdir=/etc/atalk \
- --with-uams-path=/etc/atalk/uams \
- --with-message-dir=/etc/atalk/msg \
- --enable-lastdid \
- --enable-redhat \
- --with-cracklib \
- --with-pam \
- --with-shadow \
- --with-tcp-wrappers \
- --with-ssl \
- --enable-pgp-uam \
- --enable-a2boot
-make all
-
-%install
-### INSTALL (USING "make install") ###
-mkdir -p $RPM_BUILD_ROOT{%{prefix},/etc/atalk/{uams,msg}}
-make DESTDIR=$RPM_BUILD_ROOT install-strip
-
-%post
-### RUN CHKCONFIG ###
-/sbin/chkconfig --add atalk
-/sbin/ldconfig
-# after the first install only
-if [ "$1" = 1 ]; then
- # add the ddp lines to /etc/services
- if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
- cat <<'_EOD1_' >&2
-warning: The DDP services appear to be present in /etc/services.
-warning: Please check them against services.atalk in the documentation.
-_EOD1_
- true
- else
- cat <<'_EOD2_' >>/etc/services
-# start of DDP services
-#
-# Everything between the 'start of DDP services' and 'end of DDP services'
-# lines will be automatically deleted when the netatalk package is removed.
-#
-rtmp 1/ddp # Routing Table Maintenance Protocol
-nbp 2/ddp # Name Binding Protocol
-echo 4/ddp # AppleTalk Echo Protocol
-zip 6/ddp # Zone Information Protocol
-
-afpovertcp 548/tcp # AFP over TCP
-afpovertcp 548/udp
-# end of DDP services
-_EOD2_
- fi
-fi
-
-%preun
-### RUN CHKCONFIG ###
-/sbin/chkconfig --del atalk
-
-%postun
-# do only for the last un-install
-if [ "$1" = 0 ]; then
- # remove the ddp lines from /etc/services
- if (grep '^# start of DDP services$' /etc/services >/dev/null && \
- grep '^# end of DDP services$' /etc/services >/dev/null ); then
- sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
- </etc/services >/tmp/services.tmp$$
- cat /tmp/services.tmp$$ >/etc/services
- rm /tmp/services.tmp$$
- else
- cat <<'_EOD3_' >&2
-warning: Unable to find the lines `# start of DDP services` and
-warning: `# end of DDP services` in the file /etc/services.
-warning: You should remove the DDP services from /etc/services manually.
-_EOD3_
- fi
-fi
-
-%clean
-[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
-
-%files
-%defattr(-,root,root)
-%doc doc/[A-L,N-Z]*
-%config /etc/atalk/Apple*
-%config /etc/atalk/*.conf
-%config /etc/pam.d/netatalk
-%dir /etc/atalk
-%dir /etc/atalk/msg
-%dir /etc/atalk/uams
-/etc/atalk/uams/*.so
-/etc/rc.d/init.d/atalk
-%{prefix}/bin/*
-%{prefix}/sbin/*
-%{prefix}/libexec/*
-%{prefix}/man/man*/*.gz
-%{prefix}/share/netatalk/pagecount.ps
-
-%files devel
-%defattr(-,root,root)
-%{prefix}/lib/*.a
-%{prefix}/lib/*.la
-/etc/atalk/uams/*.a
-/etc/atalk/uams/*.la
-%dir %{prefix}/include/atalk
-%{prefix}/include/atalk/*.h
-%dir %{prefix}/include/netatalk
-%{prefix}/include/netatalk/*.h
-%{prefix}/share/aclocal/netatalk.m4
-
-%changelog
-
-* Thu Apr 28 2005 Dan A. Dickey <dan.dickey@savvis.net>
- - Modify redhat spec file for Fedora Core.
-
-* Sat Jan 04 2002 Steven N. Hirsch <shirsch@adelphia.net>
- - Fix RedHat RPM build.
- - Build Apple2 boot support.
-
-* Thu Apr 12 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre6-1
- - pre-release 6 for sourceforge
-
-* Wed Mar 07 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre5-1
- - pre-release 5 for sourceforge
-
-* Fri Feb 23 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre5-0
- - pre-release 5 for sourceforge (prebuild)
-
-* Tue Feb 20 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre4-1
- - pre-release 4 for sourceforge
- - modified/split mandrake spec for redhat 7 build
-
-* Mon Dec 18 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre3-1mdk
- - pre-release 3 for sourceforge
- - moved away from 1.4.99 ...
-
-* Wed Nov 08 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.4.99-0.20001108mdk
- - pre-release 2 for sourceforge
-
-* Wed Sep 27 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.4.99-0.20000927mdk
- - pre-release 1 for sourceforge
+++ /dev/null
-#################################################### VERSIONING INFORMATION
-%define name netatalk
-%define version 1.5pre6
-%define release 1mdk
-%define tardir %{name}-%{version}
-
-################################################# BASIC PACKAGE INFORMATION
-Summary: Appletalk and Appleshare/IP services for Linux
-Name: %{name}
-Version: %{version}
-Release: %{release}
-Copyright: BSD
-Group: Networking/Daemons
-Source0: %{name}-%{version}.tar.bz2
-URL: http://netatalk.sourceforge.net/
-Packager: rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-Obsoletes: netatalk-1.4b2+asun netatalk-1.4.99
-
-############################################################## REQUIREMENTS
-Requires: cracklib, openssl, tcp_wrappers, pam
-BuildRequires: cracklib-devel, openssl-devel, pam-devel
-
-Prefix: %{_prefix}
-BuildRoot: %{_tmppath}/%{name}-buildroot
-
-%description
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 2.1 and 2.2 (Appleshare IP).
-
-%package devel
-Group: Development/Networking
-Summary: Appletalk and Appleshare/IP services for Linux development files
-%description devel
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 2.1 and 2.2 (Appleshare IP).
-
-This package is required for developing appletalk-based applications.
-
-%changelog
-
-* Thu Apr 12 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre6-1mdk
- - pre-release 6 for sourceforge
-
-* Wed Mar 07 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre5-1mdk
- - pre-release 5 for sourceforge
- - sync with redhat package
-
-* Mon Dec 18 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre3-1mdk
- - pre-release 3 for sourceforge
- - moved away from 1.4.99 ...
-
-* Wed Nov 08 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.4.99-0.20001108mdk
- - pre-release 2 for sourceforge
-
-* Wed Sep 27 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.4.99-0.20000927mdk
- - pre-release 1 for sourceforge
-
-%prep
-%setup -q -n %{tardir}/
-
-%build
-export LD_PRELOAD=
-CFLAGS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" ./configure \
- --prefix=%{prefix} \
- --with-config-dir=/etc/atalk \
- --with-uams-path=/etc/atalk/uams \
- --with-message-dir=/etc/atalk/msg \
- --enable-lastdid \
- --enable-redhat \
- --with-cracklib \
- --with-pam \
- --with-shadow \
- --with-tcp-wrappers \
- --with-ssl \
- --enable-pgp-uam
-make all
-
-%install
-### INSTALL (USING "make install") ###
-mkdir -p $RPM_BUILD_ROOT{%{prefix},/etc/atalk/{uams,msg}}
-make DESTDIR=$RPM_BUILD_ROOT install-strip
-
-# bzip2 man pages
-for i in 1 3 4 5 8; do
- bzip2 -v $RPM_BUILD_ROOT/usr/man/man$i/*.$i
-done
-
-%post
-### RUN CHKCONFIG ###
-/sbin/chkconfig --add atalk
-/sbin/ldconfig
-# after the first install only
-if [ "$1" = 1 ]; then
- # add the ddp lines to /etc/services
- if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
- cat <<'_EOD1_' >&2
-warning: The DDP services appear to be present in /etc/services.
-warning: Please check them against services.atalk in the documentation.
-_EOD1_
- true
- else
- cat <<'_EOD2_' >>/etc/services
-# start of DDP services
-#
-# Everything between the 'start of DDP services' and 'end of DDP services'
-# lines will be automatically deleted when the netatalk package is removed.
-#
-rtmp 1/ddp # Routing Table Maintenance Protocol
-nbp 2/ddp # Name Binding Protocol
-echo 4/ddp # AppleTalk Echo Protocol
-zip 6/ddp # Zone Information Protocol
-
-afpovertcp 548/tcp # AFP over TCP
-afpovertcp 548/udp
-# end of DDP services
-_EOD2_
- fi
-fi
-
-%preun
-### RUN CHKCONFIG ###
-/sbin/chkconfig --del atalk
-
-%postun
-# do only for the last un-install
-if [ "$1" = 0 ]; then
- # remove the ddp lines from /etc/services
- if (grep '^# start of DDP services$' /etc/services >/dev/null && \
- grep '^# end of DDP services$' /etc/services >/dev/null ); then
- sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
- </etc/services >/tmp/services.tmp$$
- cat /tmp/services.tmp$$ >/etc/services
- rm /tmp/services.tmp$$
- else
- cat <<'_EOD3_' >&2
-warning: Unable to find the lines `# start of DDP services` and
-warning: `# end of DDP services` in the file /etc/services.
-warning: You should remove the DDP services from /etc/services manually.
-_EOD3_
- fi
-fi
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-rm -rf $RPM_BUILD_DIR/%{tardir}/
-
-%files
-%defattr(-,root,root)
-%doc [A-Z][A-Z]* ChangeLog doc/[A-Z][A-Z]*
-%dir /etc/atalk
-%dir /etc/atalk/msg
-%config /etc/atalk/Apple*
-%config /etc/atalk/*.conf
-%config /etc/pam.d/netatalk
-%dir /etc/atalk/nls
-/etc/atalk/nls/*
-%dir /etc/atalk/uams
-/etc/atalk/uams/*.so
-/etc/rc.d/init.d/atalk
-%{prefix}/bin/*
-%{prefix}/sbin/*
-%{prefix}/man/man*/*
-
-%files devel
-%defattr(-,root,root)
-%{prefix}/lib/*.a
-%dir %{prefix}/include/atalk
-%{prefix}/include/atalk/*.h
-%dir %{prefix}/include/netatalk
-%{prefix}/include/netatalk/*.h
-%{prefix}/share/aclocal/netatalk.m4
+++ /dev/null
-#################################################### VERSIONING INFORMATION
-%define name netatalk
-%define version __VERSION__
-%define release 1
-
-################################################# BASIC PACKAGE INFORMATION
-Summary: Appletalk and Appleshare/IP services for Linux
-Name: %{name}
-Version: %{version}
-Release: %{release}
-Copyright: BSD
-Group: Networking/Daemons
-Source0: %{name}-%{version}.tar.gz
-Patch0: netatalk-rpmbuild.patch
-URL: http://netatalk.sourceforge.net/
-Packager: rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-Obsoletes: netatalk-1.4b2+asun netatalk-1.4.99
-
-############################################################## REQUIREMENTS
-Requires: cracklib, openssl, tcp_wrappers, pam
-BuildRequires: openssl-devel
-
-# Note: RedHat 7.3 build requires autoconf >= 2.53, automake >= 1.5, ac-archive >= 0.5
-
-Prefix: %{_prefix}
-BuildRoot: /var/tmp/%{name}-buildroot
-
-%description
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 2.1 and 2.2 (Appleshare IP).
-
-%package devel
-Group: Development/Networking
-Summary: Appletalk and Appleshare/IP services for Linux development files
-%description devel
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 2.1 and 2.2 (Appleshare IP).
-
-This package is required for developing appletalk-based applications.
-
-%changelog
-
-* Sat Jan 04 2002 Steven N. Hirsch <shirsch@adelphia.net>
- - Fix RedHat RPM build.
- - Build Apple2 boot support.
-
-* Thu Apr 12 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre6-1
- - pre-release 6 for sourceforge
-
-* Wed Mar 07 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre5-1
- - pre-release 5 for sourceforge
-
-* Fri Feb 23 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre5-0
- - pre-release 5 for sourceforge (prebuild)
-
-* Tue Feb 20 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre4-1
- - pre-release 4 for sourceforge
- - modified/split mandrake spec for redhat 7 build
-
-* Mon Dec 18 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.5pre3-1mdk
- - pre-release 3 for sourceforge
- - moved away from 1.4.99 ...
-
-* Wed Nov 08 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.4.99-0.20001108mdk
- - pre-release 2 for sourceforge
-
-* Wed Sep 27 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
- - v1.4.99-0.20000927mdk
- - pre-release 1 for sourceforge
-
-%prep
-%setup -q -n %{name}-%{version}/
-%patch0 -p1 -b .rpmbuild
-
-%build
-CFLAGS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" ./configure \
- --prefix=%{prefix} \
- --libexec=%{prefix}/libexec/netatalk \
- --with-config-dir=/etc/atalk \
- --with-pkgconfdir=/etc/atalk \
- --with-uams-path=/etc/atalk/uams \
- --with-message-dir=/etc/atalk/msg \
- --enable-lastdid \
- --enable-redhat \
- --with-cracklib \
- --with-pam \
- --with-shadow \
- --with-tcp-wrappers \
- --with-ssl \
- --enable-pgp-uam \
- --enable-a2boot
-make all
-
-%install
-### INSTALL (USING "make install") ###
-mkdir -p $RPM_BUILD_ROOT{%{prefix},/etc/atalk/{uams,msg}}
-make DESTDIR=$RPM_BUILD_ROOT install-strip
-
-%post
-### RUN CHKCONFIG ###
-/sbin/chkconfig --add atalk
-/sbin/ldconfig
-# after the first install only
-if [ "$1" = 1 ]; then
- # add the ddp lines to /etc/services
- if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
- cat <<'_EOD1_' >&2
-warning: The DDP services appear to be present in /etc/services.
-warning: Please check them against services.atalk in the documentation.
-_EOD1_
- true
- else
- cat <<'_EOD2_' >>/etc/services
-# start of DDP services
-#
-# Everything between the 'start of DDP services' and 'end of DDP services'
-# lines will be automatically deleted when the netatalk package is removed.
-#
-rtmp 1/ddp # Routing Table Maintenance Protocol
-nbp 2/ddp # Name Binding Protocol
-echo 4/ddp # AppleTalk Echo Protocol
-zip 6/ddp # Zone Information Protocol
-
-afpovertcp 548/tcp # AFP over TCP
-afpovertcp 548/udp
-# end of DDP services
-_EOD2_
- fi
-fi
-
-%preun
-### RUN CHKCONFIG ###
-/sbin/chkconfig --del atalk
-
-%postun
-# do only for the last un-install
-if [ "$1" = 0 ]; then
- # remove the ddp lines from /etc/services
- if (grep '^# start of DDP services$' /etc/services >/dev/null && \
- grep '^# end of DDP services$' /etc/services >/dev/null ); then
- sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
- </etc/services >/tmp/services.tmp$$
- cat /tmp/services.tmp$$ >/etc/services
- rm /tmp/services.tmp$$
- else
- cat <<'_EOD3_' >&2
-warning: Unable to find the lines `# start of DDP services` and
-warning: `# end of DDP services` in the file /etc/services.
-warning: You should remove the DDP services from /etc/services manually.
-_EOD3_
- fi
-fi
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-rm -rf $RPM_BUILD_DIR/%{name}/
-
-%files
-%defattr(-,root,root)
-%doc doc/[A-L,N-Z]*
-%config /etc/atalk/Apple*
-%config /etc/atalk/*.conf
-%config /etc/pam.d/netatalk
-/etc/atalk/nls/*
-/etc/atalk/uams/*.so
-/etc/rc.d/init.d/atalk
-%dir /etc/atalk
-%dir /etc/atalk/nls
-%dir /etc/atalk/uams
-%{prefix}/bin/*
-%{prefix}/sbin/*
-%{prefix}/libexec/*
-%{prefix}/man/man*/*.gz
-
-%files devel
-%defattr(-,root,root)
-%{prefix}/lib/*.a
-%dir %{prefix}/include/atalk
-%{prefix}/include/atalk/*.h
-%dir %{prefix}/include/netatalk
-%{prefix}/include/netatalk/*.h
-%{prefix}/share/aclocal/netatalk.m4
+++ /dev/null
---- netatalk-1.7cvs/bin/afile/Makefile.in.orig 2003-01-07 20:44:23.000000000 -0500
-+++ netatalk-1.7cvs/bin/afile/Makefile.in 2003-01-08 07:09:05.000000000 -0500
-@@ -117,12 +117,12 @@
- install_sh = @install_sh@
-
- bin_PROGRAMS = afile achfile
--bin_SCRIPTS = acleandir.rc
-+bin_SCRIPTS = # acleandir.rc
-
- afile_SOURCES = afile.c common.c common.h
- achfile_SOURCES = achfile.c common.c common.h
-
--EXTRA_DIST = acleandir.rc
-+EXTRA_DIST = # acleandir.rc
- subdir = bin/afile
- mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
- CONFIG_HEADER = $(top_builddir)/config.h
Linux-PAM is a suite of shared libraries that enable the local system
administrator to choose how applications authenticate users.
-
You can get the Linux PAM documentation and sources from
http://www.kernel.org/pub/linux/libs/pam/
-
Netatalk also supports other standard PAM implementations such as OpenPAM.
-8 Berkeley DB
+8. Berkeley DB
Berkeley DB is a programmatic toolkit that provides fast, reliable,
scalable, and mission-critical database support to software
developers. BDB can downloaded from
http://www.oracle.com/database/berkeley-db/index.html
Netatalk's CNID database uses the library and header files from BDB.
Currently, Netatalk supports BDB 4.6 and later.
+
+Error checking and logging
+==========================
+We wan't rigid error checking and concise log messages. This often leads
+to signifant code bloat where the relevant function call is buried in error
+checking and logging statements.
+In order to alleviate error checking and code readability, we provide a set
+of error checking macros in <atalk/errchk.h>. These macros compare the return
+value of statements againt 0, NULL, -1 (and maybe more, check it out).
+Every macro comes in four flavours: EC_CHECK, EC_CHECK_LOG, EC_CHECK_LOG_ERR
+and EC_CHECK_CUSTOM:
+- EC_CHECK just checks the CHECK
+- EC_CHECK_LOG additionally logs the stringified function call.
+- EC_CHECK_LOG_ERR allows specifying the return value
+- EC_CHECK_CUSTOM allows custom actions
+The macros EC_CHECK* unconditionally jump to a cleanup label where the
+neccessary cleanup can be done alongside controlling the return value.
+EC_CHECK_CUSTOM doesn't do that, so an extra "goto EC_CLEANUP" may be
+performed as appropiate.
+
+Example:
+- stat() without EC macro:
+ static int func(const char *name) {
+ int ret = 0;
+ ...
+ if ((ret = stat(name, &some_struct_stat)) != 0) {
+ LOG(...);
+ ret = -1; /* often needed to explicitly set the error indicating return value */
+ goto cleanup;
+ }
+
+ return ret;
+
+ cleanup:
+ ...
+ return ret;
+ }
+
+- stat() with EC macro:
+ static int func(const char *name) {
+ EC_INIT; /* expands to int ret = 0; */
+
+ char *uppername = NULL
+ EC_NULL(uppername = strdup(name));
+ EC_ZERO(strtoupper(uppername));
+
+ EC_ZERO(stat(uppername, &some_struct_stat)); /* expands to complete if block from above */
+
+ EC_STATUS(0);
+
+EC_CLEANUP:
+ if (uppername) free(uppername);
+ EC_EXIT;
+ }
+
+A boileplate function template is:
+
+int func(void)
+{
+ EC_INIT;
+
+ ...your code here...
+
+ EC_STATUS(0);
+
+EC_CLEANUP:
+ EC_EXIT;
+}
+++ /dev/null
-Netatalk Frequently Asked Questions
-($Id: FAQ,v 1.14 2010-04-25 13:59:53 hat001 Exp $)
-
------------------------------------------------------------------------------
-
-Q1: Where can I get more information on Netatalk?
-Q2: What is this I keep seeing about asun?
-Q3: How do I get the most recent version of Netatalk?
-Q4: Can I get an almost current version of Netatalk without having to learn Git?
-Q4a: Is there an RPM, package, or tarball for my platform?
-Q5: I'm having massive file deletion problems!
-Q6: I am having lots of file locking problems!
-Q7: I'm getting this message in my logs:
- WARNING: DID conflict for ... Are these the same file?
-Q8: I can't seem to use passwords longer than 8 characters for my netatalk
- accounts. How can I fix that?
-Q9: I would like to use encrypted passwords to authenticate to the Netatalk
- server. How do I do that?
-Q10: How can I set who has access to certain directories?
-Q11: What are the .AppleDouble and .Parent directories which are created in
- the netatalk locations?
-Q12: Hidden files - what's up with that?
-Q13: I get a "socket: Invalid argument" error when trying to start netatalk
- under Linux. What is causing this?
-Q14: Netatalk works over Appletalk, but my IP connections are refused, even
- though I have enabled them in the configuration files.
-Q15: I'm having Quark Express file locking problems, is there information on that?
-Q16: I'm getting this error in Quark Express when trying to save a file to
- the server: 'Error Type -50'
-Q17: Does netatalk work with Mac OSX?
-Q18: I'm getting an 'Application for this document not found' error on OS X.
-Q19: I'm getting an 'Error Type -43' error on OS X.
-Q20: How do I get the directories that are created by Netatalk to have the
- correct permissions by default?
-Q21: What does this error mean:
- 'afpd[#####]: setdirmode: chmod .AppleDouble Operation not permitted'
-Q22: I'm having problems with the Trash folder: either when someone drags
- files into it, the system want's them todelete them immeidately, or files
- get stuck in there and won't delete.
-Q23: The daemons aren't starting, things aren't showing up in the Chooser,
- and I get a message like this in the logs: afpd[####]: Can't register
- Tests:AFPServer@*
-Q24: I want to be able to allow users to change their passwords? How do
- I enable this feature. Every time I try I get an error that it was
- unable to save the password.
-Q25: Can a mount a Mac volume on my unix machine?
-Q26: Can I run Samba and Netatalk together to access the same files?
-Q27: Files I create on my Samba shares are invisible on the mac side.
-Q27a: How can I set netatalk to hide some files from the Samba (or
- unix) sides?
-Q28: Files I create on my netatalk shares are invisible on the PC side.
-Q28a: How can I set Samba to hide the netatalk specific files (e.g.
- .AppleDouble).
-Q29: I compiled Samba with the --with-netatalk flag. What did that do?
-Q30: What about the differences in naming schemes, and legal/illegal
- characters between Windows, Macs (and unix?)
-Q31: Where can I get the cnid-db (Berkely DB) software? (needed for
- --with-did=cnid)
-Q32: What about security in Netatalk?
-
-
-
------------------------------------------------------------------------------
-
-
-Q1: Where can I get more information on Netatalk?
-
-A: Netatalk's home page can be found at:
-
- http://netatalk.sourceforge.net/
-
- Netatalk is maintained at SourceForge. The Netatalk project page on
- SourceForge is located at:
-
- http://sourceforge.net/projects/netatalk/
-
- There are (at least) three very active e-mail lists to which you can
- subscribe. The first, netatalk-admins, is for usage and setup/compile
- questions. Subscription information as well as an archive are available at:
-
- http://lists.sourceforge.net/lists/listinfo/netatalk-admins
-
- This can be very high volume, but usually a few messages a day.
-
- Netatalk-devel list is more specific to coding and testing. The archive
- and more information can found at:
-
- http://lists.sourceforge.net/lists/listinfo/netatalk-devel
-
- This list varies in volume, but is usually moderately active.
-
- Netatalk-docs is specific to documentation. For more information see:
-
- http://lists.sourceforge.net/mailman/listinfo/netatalk-docs
-
- There are other netatalk information sites. Some of these are no
- longer actively updated, some are site-specific, but still have
- good information:
-
- http://www.anders.com/projects/netatalk/
- http://www.faredge.com.au/netatalk/index.html
-
-
-Q2: What is this I keep seeing about asun?
-
-A: Before Netatalk moved to SourceForge, Adrian Sun (asun) had written
- some patches to Netatalk which helped significantly with its usability,
- especially using AppleShare IP. These patches are still provided by many
- Unix vendors. All of these patches are included in the current SourceForge
- versions.
-
-
-Q3: How do I get the most recent version of Netatalk?
-
-A: Via Git from SourceForge.net. This is the actively maintained version
- of Netatalk, changes are being made constantly, and therefore it is not
- suitable for production environments. The netatalk at SourceForge is in
- Beta, so keep that in mind.
-
- Downloading the Git repository can be done quickly and easily.
-
- Make sure you have Git installed. which git should produce a path to git.
-
- $> which git
- /usr/bin/git
-
- If you don't have one make a source directory. cd to this directory.
-
- $> mkdir /path/to/new/source/dir
- $> cd /path/to/new/source/dir
-
- Now get the source:
-
- $> git clone git://netatalk.git.sourceforge.net/gitroot/netatalk/netatalk
- Initialized empty Git repository in /path/to/new/source/dir/netatalk/.git/
- remote: Counting objects: 2503, done.
- ...
-
- This will create a local directory called "netatalk" containing a complete
- and fresh copy of the whole Netatalk source from the Git repository.
-
- In order to keep your repository copy updated, occasionally run:
-
- $> git pull
-
- Now cd to the netatalk directory and run ./bootstrap. This will create the
- configure script required in the next step.
-
- $> ./bootstrap
-
-
-Q4: Can I get an almost current version of Netatalk without having to learn Git?
-
-A: Yes. Snapshots of the Git tree should be posted for the benefit of
- those that don't want to / can't use Git. They are available at:
-
- http://netatalk.git.sourceforge.net/git/gitweb-index.cgi
-
- You should be able to treat these images as you would a release. Just
- configure as you normally work, then run make (or gmake as the case may
- be). There is no need to run ./bootstrap on these images.
-
-
-Q4a: Is there an RPM, package, or tarball for my platform?
-
-A: Perhaps. These vary in how often they're updated:
-
- FreeBSD
- port: /usr/ports/net/netatalk - maintained by Joe Clark
- SuSE Linux
- included in the distribution
- OpenBSD
- port: /usr/ports/net/netatalk/ - not actively maintained
- Debian GNU/Linux
- included in all current distributions
- RedHat Linux
- included in the distribution
-
-
-Q5: I'm having massive file deletion problems!
-
-Q6: I am having lots of file locking problems!
-
-Q7: I'm getting this message in my logs:
- WARNING: DID conflict for ... Are these the same file?
-
-A: Compile with the --with-did=last flag set. This activates a different
- method of calculating inodes in the software, and will hopefully fix some
- of these problems. This code, along with the CNID code, was still being
- worked out in Pre7. The cnid/bdb flags also go along with this:
-
- --with-bdb=PATH specify path to Berkeley DB installation
- --with-did=[scheme] set DID scheme (cnid,last)
-
- (For more information on CNID, see the README.cnid file.)
-
- --with-did=last reverted things back to the old 1.4b2 directory ID
- calculation algorithm. This also solved the problem of the syslog
- messages and the users complaining of file deletions. It's also been
- found that by disabling *BSD's SOFTUPDATES feature on Netatalk volumes (on
- FreeBSD), multi-user interaction seemed to work better. This was back in
- a late 4.2-BETA, so it's not clear if this still holds true in 4.4-RELEASE
- or not.
-
-
-Q8: I can't seem to use passwords longer than 8 characters for my Netatalk
- accounts. How can I fix that?
-
-Q9: I would like to use encrypted passwords to authenticate to the Netatalk
- server. How do I do that?
-
-A: Update to a newer version of AppleShare Client (I think the most
- recent is 3.8.8). This allows longer passwords, and will allow you to
- use encrypted passwords. Set which way you would like to authenticate
- in either afpd.conf or netatalk.conf, depending on your setup.
-
- For more information on the AppleShare Client from Apple, and which clients
- are needed for which MacOS, see
-
- http://til.info.apple.com/techinfo.nsf/artnum/n60792?OpenDocument&software
-
- (this site requires cookies, and a registration and sign-in).
-
-
-Q10: How can I set who has access to certain directories?
-
-A: You can certainly do this with your Unix permissions, but also explore the
- allow/deny/rwlist/rolist options in the AppleVolumes.default file:
-
- # allow/deny/rwlist/rolist format [syntax: allow:user1,@group]:
- # user1,@group,user2 -> allows/denies access from listed users/groups
- # rwlist/rolist control whether or not the
- # volume is ro for those users.
-
- Also, some unices, specially FreeBSD, have other options:
- (By Joe Clark)
-
- "What about file and directory permissions? Since I didn't use the FORCE
- UID/GID code, I decided to use a feature of FreeBSD called SUIDDIR. From
- the LINT kernel config file:
-
- # If you are running a machine just as a fileserver for PC and MAC
- # users, using SAMBA or Netatalk, you may consider setting this option
- # and keeping all those users' directories on a filesystem that is
- # mounted with the suiddir option. This gives new files the same
- # ownership as the directory (similar to group). It's a security hole
- # if you let these users run programs, so confine it to file-servers
- # (but it'll save you lots of headaches in those cases). Root owned
- # directories are exempt and X bits are cleared. The suid bit must be
- # set on the directory as well; see chmod(1) PC owners can't see/set
- # ownerships so they keep getting their toes trodden on. This saves
- # you all the support calls as the filesystem it's used on will act as
- # they expect: "It's my dir so it must be my file".
-
- FORCE UID/GID code, I decided to use a feature of FreeBSD called
- SUIDDIR. From the LINT kernel config file:
-
- # If you are running a machine just as a fileserver for PC and MAC
- # users, using SAMBA or Netatalk, you may consider setting this option
- # and keeping all those users' directories on a filesystem that is
- # mounted with the suiddir option. This gives new files the same
- # ownership as the directory (similar to group). It's a security hole
- # if you let these users run programs, so confine it to file-servers
- # (but it'll save you lots of headaches in those cases). Root owned
- # directories are exempt and X bits are cleared. The suid bit must be
- # set on the directory as well; see chmod(1) PC owners can't see/set
- # ownerships so they keep getting their toes trodden on. This saves
- # you all the support calls as the filesystem it's used on will act as
- # they expect: "It's my dir so it must be my file".
-
- And the associated mount command:
-
- mount -o suiddir /dev/da2s1e /macvol/artfiles
-
- This was used on my dedicated Netatalk/Samba filesystems. On
- filesystems that were also used for interactive shell access, I chmod'd
- my Netatalk shares 2770. The reason for this is that I set up a UNIX
- group for each department in the ad agency. I had an art group, a media
- group, an accounting group, and then, or course, a general staff group.
- Each share was only allowed access by the group that needed to access
- the share. So, the Artfiles share allowed access only to the art group:
-
- /macvol/artfiles "Art Files" allow:@art
-
- And the others followed in kind. Therefore, the 2770 mask allowed only
- owners and people in the associated group access to read and write
- files. The leading 2 set the setgid bit so that all child files and
- directories would retain the same group permissions. I found this to
- work well.
-
- This was used on my dedicated Netatalk/Samba filesystems. On
- filesystems that were also used for interactive shell access, I chmod'd
- my Netatalk shares 2770. The reason for this is that I set up a UNIX
- group for each department in the ad agency. I had an art group, a media
- group, an accounting group, and then, or course, a general staff group.
- Each share was only allowed access by the group that needed to access
- the share. So, the Artfiles share allowed access only to the art group:
-
- /macvol/artfiles "Art Files" allow:@art
-
- And the others followed in kind. Therefore, the 2770 mask allowed only
- owners and people in the associated group access to read and write
- files. The leading 2 set the setgid bit so that all child files and
- directories would retain the same group permissions. I found this to
- work well."
-
-
-Q11: What are the .AppleDouble and .Parent directories which are created in
- the Netatalk locations?
-
-A: See the README.veto file in this directory.
-
- The .AppleDouble folders hold the resource fork information for the Mac
- files, plus other attributes which are not normally stored by Unix. For
- this reason, when you want to move files around in your Mac volumes, it's
- a good idea to do it from the Mac side (as opposed to from the Unix side,
- or Samba), unless you make absolutely sure you get the .AppleDouble
- directories. These directories are often hidden from the Samba side, via
- the veto files configuration.
-
- You can also set Netatalk to not create an .AppleDouble directory unless
- it absolutely needs it, by setting the noadouble setting in
- AppleVolumes.default.
-
-
-Q12: Hidden files - what's up with that?
-
-A: If you set the noadouble flag in AppleVolumes.default, you won't see
- the .Apple* or .Parent directories on the Mac side. If you use the veto
- files option in Samba, they may be hidden from the Windows side as well.
- (More information in the Samba section, and in the README.veto file in
- this directory.)
-
-
-Q13: I get a "socket: Invalid argument" error when trying to start Netatalk
- under Linux. What is causing this?
-
-A: The "appletalk" and "ipddp" kernel modules have to be installed under
- linux for Netatalk to function. The appletalk module can be automatically
- loaded by adding the line "alias net-pf-5 appletalk" to the
- /etc/modules.conf file. Issuing the command "modprobe (module)" will
- load the module for the current session.
-
-
-Q14: Netatalk works over AppleTalk, but my IP connections are refused, even
- though I have enabled them in the configuration files.
-
-A: If tcp_wrappers support is compiled into Netatalk, access has to be
- granted in /etc/hosts.allow for Netatalk to successfully accept IP
- connections. This can be done by the addition of the line:
-
- afpd: 127. xxx.xxx.xxx. (whatever other subnets)
-
-
-Q15: I'm having Quark Express file locking problems, is there information on
- that?
-
-A: Yes, see the question regarding DID conflicts and the --enable-did= flag.
- Also, try using the --flock-locks flag. Enabling this code disabled the
- new byte locking feature. With FLOCK locks, the whole file would be locked.
- With byte locks, a byte range could be locked without locking the whole
- file.
-
-
-Q16: I'm getting this error in Quark Express when trying to save a file to
- the server: 'Error Type -50'
-
-A: Turn off the document preview feature off in Quark.
-
-
-Q17: Does netatalk work with MacOS X?
-
-A: Yes, but only the most recent versions, and it's still being finalized.
- Versions prior to 1.5Pre7 did NOT work with OS X, although some really
- early versions did (netatalk 1.4+asun?).
-
-
-Q18: I'm getting an 'Application for this document not found' error on MacOS X.
-
-Q19: I'm getting an 'Error Type -43' error on MacOS X.
-
-A: Configure with --with-did=last. More info on this flag is given in the
- DID conflicts question.
-
-
-Q20: How do I get the directories that are created by Netatalk to have the
- correct permissions by default?
-
-A: Investigate the setgid bit on your Unix platform. It's a good idea to
- set this on your shared directories, and your .AppleDouble directories.
- From the mail archives: "Usually directories designated for use with
- AppleShare have the setgid (g+s) bit set. It forces inheritance of
- permissions. Without it, the .AppleDouble subdirectory can't be created
- since the new folder doesn't necessarily have the same write privileges."
-
- Information about the setgid bit can be found in Evi Nemeth's
- "Unix System Administration Handbook" (3rd. ed, chap 5.5, pg. 69):
-
- "The bits with octal values 4000 and 2000 are the setuid and setgid bits.
- These bits allow programs to access files and processes that would
- otherwise be off-limits to the users that run them. [...] When set on a
- directory, the setgid bit causes newly created files within the directory
- to take on the group membership of the directory rather than the defualt
- group of the user that created the file. This convention makes it easier
- to share a directory of files among several users, as long as they all
- belong to a common group. Check your system before relying on this
- feature, since not all version of UNIX provide it. [...] This interpretation
- of the setgid bit is unrelated to it's meaning when set on an executable
- file, but there is never any ambiguity as to which meaning is
- appropriate."
-
- NOTE: The setuid is usually discussed along with the setgid bit. The
- setuid bit is VERY dangerous. If you set it on an executable, and the
- executable is owned by root, anyone who runs that executable is root for
- the duration of that executable's run, so a clever person can leverage
- that into a full-scale compromise. The setgid bit also has other security
- implications, so be careful where you set it.
-
- You set it by doing a chmod 2xxx, where xxx are the normal file permissions
- (i.e. owner/group/other permissions).
-
-
-Q21: What does this error mean:
- 'afpd[#####]: setdirmode: chmod .AppleDouble Operation not permitted'
-
-A: This can be due to a few things.
-
- 1) The setgid bit might not be set on either your directory, or on the
- .AppleDouble directory. It has to be set recursively on the .AppleDouble
- folder.
-
- 2) You may not be member of the group set on the directory you're trying
- to write to.
-
- 3) This was a persistant bug in 1.5pre6 for awhile, upgrading might help.
-
-
-Q22: I'm having problems with the Trash folder: either when someone drags
- files into it, the system wants them to delete them immediately, or files
- get stuck in there and won't delete.
-
-A: chmod the Network Trash folder to 2775 (/home/public/Network Trash
- Folder for instance).
-
- As of 10/16/01, MacOS X trash didn't work properly with afps volumes.
- Apple is working on it.
-
-Q23: The daemons aren't starting, things aren't showing up in the Chooser,
- and I get a message like this in the logs: afpd[####]: Can't register
- Tests:AFPServer@*
-
- This is sometimes a result of missing NIC information in the atalkd.conf
- file. Put your network interface (something like le0, eth0, fxp0, lo0)
- alone on a line in atalkd.conf, and reboot. When atalkd starts, it will
- populate the file with a line such as:
-
- le1 -seed -phase 2 -addr 66.6 -net 66-67 -zone "No Parking"
-
- To find your network interface, run
-
- % ifconfig -a | more
-
- and see which interface has your IP address. Use that one.
-
-
-Q24: I want to be able to allow users to change their passwords. How do
- I enable this feature? Every time I try I get an error that it was
- unable to save the password.
-
-A: Use -[no]setpassword in afpd.conf. This enables or disables the ability of
- clients to change their passwords.
-
-
-Q25: Can a mount a Mac volume on my Unix machine?
-
-A: Well, maybe. MacOS X obviously might be able to do this with NFS.
- Also, there is a program called afpfs which was designed to do this,
- but is not actively maintained and has been reportedly highly unstable.
- It should be available from:
-
- http://www.panix.com/~dfoster/afpfs/
-
-Q26: Can I run Samba and Netatalk together to access the same files?
-
-A: Sure. Lots of us do. But there are some concerns. Quite often it's
- useful, for instance, to hide files of one OS from the other. See
- the AppleVolumes.default file in Netatalk, and investigate the veto
- files option in Samba. (See the README.veto file.)
-
- Also, when copying and moving files created on the Mac, it's better
- to do that from the Mac, rather than from the Unix server or from
- Samba. This is because the .AppleDouble folders hold the resource fork
- information for the Mac files, plus other attributes which are not
- normally stored by Unix.
-
- You can also set Netatalk to not create an .AppleDouble directory unless
- it absolutely needs it, by setting the noadouble setting in
- AppleVolumes.default.
-
-
-Q27: Files I create on my Samba shares are invisible on the Mac side.
-
-A: Have you checked the AppleVolumes(.default? .sytem? I don't remember
- which one hides files!) file?
-
- How long are the file names? Names longer than 31 BYTES (not characters)
- are not visible on the Mac side. This is because some old MacOS's don't
- accept long names, and some Finders crash when they encounter them.
- Therefore Netatalk hides long filenames to prevent crashes. If you
- prefer Netatalk to truncate the names, use the --with-mangling ./configure
- option when compiling Netatalk.
-
- The BYTES distiction is made because there exist doublebyte fonts too,
- which limit names to 15 chars.
-
-
-Q27a: How can I set Netatalk to hide some files created on the Samba
- (or Unix) sides?
-
-A: AppleVolumes(.system or .default?) allows you to hide certain files.
- This might be a good thing to set on, say, .cshrc, ssh keys, and
- the like.
-
-
-Q28: Files I create on my Netatalk shares are invisible on the PC side.
-
-Q28a: How can I set Samba to hide the Netatalk specific files (e.g.
- .AppleDouble).
-
-A: Check your Samba veto files option in smb.conf. It's often useful
- to hide files like .AppleDouble or the network trash folder here.
-
- Does the mac file have a \ or / in it? Would this cause Samba to
- not see the file?
-
-
-Q29: I compiled Samba with the --with-netatalk flag. What did that do?
-
-A: Nothing. Some code was written (by a Samba developer?), but as of
- Fall 2001, Samba doesn't utilize it.
-
-
-Q30: What about the differences in naming schemes, and legal/illegal
- characters between Windows, Macs, and Unix?
-
-A: Check out the documentation about the 'mswindows' flag in
- AppleVolumes.default. For instance, having / or \ or : in a name is
- especially bad, as they are path seperators on Unix, Windows, and MacOS,
- respectively). Educating the end user is important for this problem.
-
-
-Q31: Where can I get the cnid-db (Berkely DB) software? (needed for
- --with-did=cnid)
-
-A: First check to see if your Unix has a port or package. If not,
- Berkeley DB is available at:
-
- http://www.sleepycat.com/download.html
-
-Q32: What about security in Netatalk?
-
-A: Most of the security for Netatalk must be derived from the
- security of the Unix server on which it runs. Directory permissions,
- valid users, firewalls, IP filters, file integrity checkers, etc.
- are all part of the equation. That said, it is possible to configure
- Netatalk to minimize access, and close potential security holes.
-
- These two flags are especially important:
-
- --with-tcp-wrappers: enable TCP wrappers support.
-
- Enables Wietse Venema's network logger, also known as tcpd or
- LOG_TCP. These programs log the client host name of incoming
- telnet, ftp, rsh, rlogin, finger etc. requests. Security
- options are: access control per host, domain and/or service;
- detection of host name spoofing or host address spoofing;
- booby traps to implement an early-warning system. TCP
- Wrappers can be gotten at:
-
- ftp://ftp.porcupine.org/pub/security/
-
- Note, if you use TCP Wrappers, it would be a good idea to set your
- afpd.conf file to disable DDP, or accept connections only on TCP.
- You can also configure afpd to only run on a certain port, which
- you can then let through your IPFilter.
-
- --with-ssl-dirs=[PATH]: specify path to OpenSSL installation.
-
- NOTE: This is dependent on the same directory layout as the
- source distribution of OpenSSL. That is: include/ and
- lib/ to be on the same level. Many .rpm formats do not
- have their files laid out in this format.
- The OpenSSL Project is a collaborative effort to develop a
- robust, commercial-grade, full-featured, and Open Source
- toolkit implementing the Secure Sockets Layer (SSL v2/v3)
- and Transport Layer Security (TLS v1) protocols as well as a
- full-strength general purpose cryptography library.
- This is required to enable DHX login support, which
- will encrypt all of the passwords being sent across the
- connection. (Some old Mac clients don't support this, check
- this FAQ for the section on AppleShare clients.)
- Check to see if your Unix has OpenSSL already, or
- get everything at:
-
- http://www.openssl.org/
-
- --with-libgcrypt-dir=[PATH]: specify path to Libgcrypt installation.
-
- NOTE: This is dependent on the same directory layout as the
- source distribution of Libgcrypt. That is: include/ and
- lib/ to be on the same level.
- This is required to enable DHX2 login support, which
- will encrypt all of the passwords being sent across the
- connection. (Some old Mac clients don't support this, check
- this FAQ for the section on AppleShare clients.)
- Check to see if your Unix has Libgcrypt already, or
- get everything at:
-
- http://directory.fsf.org/project/libgcrypt/
-
- Be aware that on the volumes that are shared, some of the
- special folders (.AppleDesktop, "Network Trash Folder") get
- assigned. A lot of these get created as world-writable (because that's
- what the Mac clients are expecting them to be) which is often quite
- undesirable from the Unix system administrator's point of view.
- Documenting this behavior could be a somewhat daunting task, but
- highly desirable.
-
- Shares can be set to be read/write only by certain people and groups.
-
- The Netatalk code has not been through a major code audit. However,
- it's Open Source, so if you want to do said audit, contact the
- Netatalk maintainers (which can be done through the SourceForge site).
-
- Has anyone tried to run Netatalk in a chroot jail? If so, please
- share your experiences with the mailing lists.
# Makefile.am for INSTALL/
-EXTRA_DIST = \
- DEVELOPER \
- FAQ \
- README.documentation \
- README.hidden-items \
- README.ids \
- README.AppleTalk \
- README.ACLs
+EXTRA_DIST = DEVELOPER README.AppleTalk
+++ /dev/null
-
- ACLs - Konfiguration and Infos vor Developpers
- ==============================================
-
-ACL support for AFP is implemented with NFSv4 ACLs. Few filesystems and fewer OSes support
-these. At the time of implementation its only provided with ZFS on Solaris, Opensolaris and
-derived distributions.
-
- Configuration
- -------------
-
-In order to be able to support ACLs, the following things have to be configured:
-
-1. ZFS Volumes
-2. Authentication Domain
-3. Netatalk Volumes
-
- 1. ZFS Volumes:
-
- You MUST configure one ACL parameter for any volume you want to use with Netatalk:
-
- aclinherit = passthrough
-
- For an explanation of what these parameters mean and how to apply them see, your hosts
- ZFS documentation (e.g. man zfs).
-
- 2. Authentication Domain
-
- Your server and the clients must be part of a security association where identity data
- is coming from a common source. ACLs in Darwin are based on UUIDs and so is the ACL
- specification in AFP 3.2. Therefor your source of identity data has to provide an
- attribute for every user and group where a UUID is stored as a ASCII string.
-
- In other words:
- - you need an Open Directory Server or an LDAP server where you store UUIDs in some
- attribute
- - your clients must be configured to use this server
- - your server should be configured to use this server via nsswitch and PAM
- - configure Netatalk via afp_ldap.conf so that Netatalk is able to retrieve the UUID
- for users and groups via LDAP search queries
-
- 3. Netatalk Volumes
-
- Finally you can add "options:acls" to your volume defintions to add ACL support.
- In case your volume basedir doesn't grant read permissions via mode (like: 0700 root:adm)
- but only via ACLs, you MUST add the "nostat" option to the volume defintion.
-
- Implemantation Notes
- --------------------
-
-Some implementation details that are buried in the code are worthwhile to be documented.
-
-1. Darwin ACEs vs NFSv4 ACEs
-2. .AppleDouble VFS integration
-
- 1. Darwin ACEs vs NFSv4 ACEs
-
- Basically as far as implementing AFP support is concerned they're equivalent.
- Subtleties arise at other places:
-
- FPAccess
-
- The AFP client frequently checks the (DARWIN_)ACE_DELETE_CHILD right. This is most
- often not explicitly granted via an ACE. Therefor the client would get an no access
- error. The client in turn then declares the object in question read only.
- Thus we have to the check the mode for every directory and add ACE_DELETE_CHILD if
- the requestor has write permissions.
-
- FPGetFileDirParms
-
- 10.5 does not only use unix mode and FPAccess for permission check, but also OS 9
- access bits from FPGetFileDirParms. Thus we have to adjust the Access Rights bitmap
- user bits by including any ACL rigths.
-
- 2. .AppleDouble VFS integration
-
- FPSetACL sets ACLs on files and dirs. Our implementation also sets the same ACL on the
- .AppleDouble file for files and on the .AppleDouble dir itself for dirs.
-
- Thereafter ACLs for created files is taken care of by ACLs own inheritance rules.
-
- For dirs on the other hand whe have to make sure that any ACL the dir inherits is
- copied verbatim to its .AppleDouble dir.
-
-
- January 2009, Frank Lahm
\ No newline at end of file
+++ /dev/null
- ATTENTION
-
-The Netatalk documentation is now maintained in Docbook XML format.
-It is kept in the separate Git module 'netatalk-docs'.
+++ /dev/null
- Special folders created inside Netatalk shares
-
-(Originally by ali@gwc.org.uk 2001-10-20)
-(Amended by Sebastian Rittau 2001-11-03)
-(Amended by HAT 2010-03-30)
-
-Inside netatalk share points you will find several files and directories
-which are created automatically by the afpd process either for its own
-internal use or for the internal use of the MacOS.
-
-None of them should be directly visible in the Finder on the Mac.
-
-Many of them have to be writeable in order for netatalk to function
-properly. This can present problems if users have shell access to the
-netatalk server. At the very least, users can "hide" files inside these
-writeable folders. At worst, a malicious user could confuse netatalk in a
-bad way. It is unlikely that a malicious user could cause loss of another
-user's data by exploiting permissions on these items.
-
-Below is what I hope to be a comprehensive list of these files and
-directories, their purpose, and a discussion of what Unix permissions
-should be set on them.
-
-Note that in general on Netatalk shares, all directories should have the
-setgid bit set. This forces any new files or folders created to have the
-same group as the folder they were created in. On some operating systems,
-notably FreeBSD, the group owner is always inherited from the parent
-directory, so the setgid bit is not necessary.
-
-
-.AppleDouble/
-
-This directory exists inside each folder on a Netatalk share. It contains
-meta information like the resource fork, or creator/type of each file in that
-folder. Its permissions should match those of its parent directory, i.e.
-anyone who has write access to the parent directory must have write access to
-the corresponding .AppleDouble directory.
-
-
-.AppleDouble/.Parent
-
-This file specifically contains meta information about the directory.
-
-
-.AppleDesktop/
-
-This directory exists under the top level of each share point. It contains
-the "desktop database" which is the method by which the MacOS associates a
-type/creator code with a particular application. Without it, documents
-will lose their application-specific icons and will have a generic icon
-instead. Double-clicking documents will also fail.
-
-To allow the desktop database to be maintained correctly, any user who is
-likely to copy an application on to the share must have write access to
-this directory and all directories below it.
-
-
-Icon\r and .AppleDouble\Icon\r
-
-These files will exist in any folder, including the top level of a share,
-if it has a custom icon. Make them writeable to any user who should be
-allowed to change that custom icon; make them read-only if you don't want
-the custom icon to be changeable.
-
-
-.AppleDB/
-.AppleDBcnid.lock
-
-These will exist at the top level of each sharepoint on servers that run
-netatalk compiled with the new CNID DB code. Any user who has write access
-to any part of the share must have full write access to this directory /
-file and all the files within it otherwise the CNID DB code will not work
-properly.
-
-
-Network\ Trash\ Folder/
-
-This exists at the top level of each sharepoint. This is where files that
-are put in the Trash on clients go, until the Trash is emptied.
-
-The permissions of items in this directory are a pretty complicated
-subject, but basically you should make this directory and everything in it
-world-writeable if you want the Trash can to work properly. If you don't
-make it writeable then users will get a message "That item cannot be put
-in the Trash. Do you want to delete it immediately?" if they try to put
-something in the Trash.
-
-Unfortunately networked trash handling is broken in current versions of Mac
-OS X even if this directory is writeable. Apple is aware of this problem
-and is working on a solution.
-
-
-Temporary\ Items/
-.TemporaryItems/ (:2eTemporaryItems/)
-
-These folder may exist at the top level of a sharepoint. These folder is
-used by certain applications (Adobe PhotoShop among others) to store,
-well, temporary items. These programs may not work correctly if this
-folder is missing or not writeable, when a user tries to work on a
-document stored in that Netatalk share.
-
-
-TheFindByContentFolder/
-
-This folder is used by Sherlock 2 to store information use by its Find by
-Content feature. Make it writeable by users if you want to allow them to
-update the Find by Content index on a netatalk share. Otherwise, make it
-read-only.
-
-
-TheVolumeSettingsFolder/
-
-This folder is created at the top level of each share point. It
-always appears to be empty. It would be wise to set its permissions
-the same as the top level of the sharepoint.
-
-
-.DS_Store (:2eDS_Store)
-
-This file may appear in share points which have been accessed by a
-machine running Mac OS X. Its permissions should be set to match
-those of the enclosing directory.
-
-
-.FBCIndex (.FBCIndex)
-.FBCLockFolder/.FBCSemaphoreFile (:2eFBCLockFolder/:2eFBCSemaphoreFile)
-
-These are created to preserve retrieval information by Sherlock
-on Mac OS X 10.1. If these are removed, the next retrieval will slow.
-Mac OS X 10.2 and later do not use these.
+++ /dev/null
-File and Directory IDs explained.
-
-What are File and Directory ID's?
-
-On a mac the file system stores all files and directory information on each
-volume in a big file called the catalogue file. Inside the catalogue, all
-files and directories are accessed using a key, central to which is a number,
-called the ID. In the case of a file its a file ID (FID) or for a directory,
-a directory ID (DID).
-
-How many IDs can there be?
-
-The ID in a catalogue key is stored using 4 bytes, which means it can only
-be between 0 and 4,294,967,295 (FF FF FF FF in hex). However the first 16
-IDs are reserved so you don't have quite that many. Each ID is unique for
-ever, on any given volume. Once all 4 billion have been used, you cannot
-create new files, so will need to reformat the volume to continue using it.
-
-Why are IDs so important?
-
-Most system calls relating to files inside a mac (either vi a network or a
-hard disk) can refer to files or directories by ID. This makes the ID a
-powerful piece of information.
-
-So whats the problem?
-
-The problem lies in file servers that don't use IDs. The protocol used by
-macs to share files over a network is called the Apple Filing Protocol (AFP)
-and it requires the use of IDs. So if you want to support AFP fully, any
-AFP server, must adopt its own system for storing and maintaining a link
-between each file or directory and its respective ID. This is most critical
-when acessing directories.
-
-So why does this matter on a non mac server like netatalk?
-
-The three big stumbling blocks that crop up with AFP servers that don't
-fully support IDs are 1) aliases, 2) the trash can and 3) linked documents.
-
-Alias problems.
-
-An alias on a mac is quite special. Rather than just storing the path to
-the original file (like a unix symlink), it stores the ID to that file and
-a special identifier for the volume (and the server it's on). Ideally this
-is great. If the file moves or is renamed, the alias still works. However
-if either the file (or directory) ID changes, or the volume identifier
-(or server identifer), then the alias will break. The file it claims to
-point to will claim to have been removed.
-
-Trash can (accidentally deleted file) problems.
-
-The trash can has similar problems. Files that have been moved to the trash
-are represented by their ID. When you empty the trash all ID's listed are
-deleted. However if the ID of a file that was in the trash, is reallocated
-to an ordinary file, then when the trash is emptied that file will be deleted.
-
-Linked document problems.
-
-Finally linked documents: Linked documents are documents that contain hidden
-links to other documents. Print setting and layout application (such as
-Quark) use this technique. Sometimes these documents contain IDs linking to
-their embeded documents. These can break in the same way as aliases.
-
-So how does netatalk approach the problem?
-
-!!! The following is outdated, please refer to the Manual instead !!!
-
-Netatalk has two different methods of allocating IDs: last and cnid.
-
-DID = last.
-
-This uses a running number to allocate IDs. When an ID is allocated the
-server remembers this by adding it to a table. If an ID is referenced, then
-the server looks up on the table. When the server is restarted, the table is
-lost. This is the most simple method, but it is unreliable. If you stick to
-the mac features which don't rely heavily on IDs it works fine. If you try
-to use IDs much, things break.
-
-DID = cnid.
-
-The CNID scheme in Netatalk attempts to assign unique IDs to each file and
-directory, then keep those IDs persistent across mounts of the volume. This
-way, cross-volume aliases will work, and users are less likely to encounter
-duplicate CNID errors. Prior to Netatalk 1.6.0, the CNID calculation
-scheme was not persistent, and IDs were assigned based on the UNIX device and
-inode number of a given file or directory (see DID = last above). This was
-fine for the most part, but due to limitations, not all available CNIDs could
-be used. As well, these IDs could change independently from Netatalk, and
-thus were not persistent. As of Netatalk 1.6.0, the CNID scheme is now the
-default. On top of that, Netatalk uses the Concurrent Datastore method to
-avoid the need for database locking and transactions.
-
-As stated above, CNID requires Berkeley DB. Currently, Netatalk supports
-BDB 4.1.25 and 4.2.52 The recommended version is 4.2.52 as that is the version
-on which most testing has been done.
-
-CNID has seen many contributors over the years. It was conceived by
-Adrian Sun <asun@zoology.washington.edu>. His developer notes can be found
-libatalk/cnid/README file. It was later picked up and modernized by Uwe Hees
-<uwe.hees@rz-online.de>. Then, Joe Marcus Clarke <marcus@marcuscom.com>
-started fixing bugs and adding additional features. The Concurrent
-Datastore support was subsequently added by Dan Wilga <dwilga@mtholyoke.edu>.
-The CNID code is currently maintained by Joe Marcus Clarke.
+++ /dev/null
-NEW
-===
-Protocol level:
-* AFP 3.2
-* IPv6
-* Extended Attributes support
-* ACL support with ZFS
-* AppleTalk support in afpd and AppleTalk daemons (atalkd and papd) are disabled by default
-
-afpd:
-* default CNID backend is "dbd"
-* enable live debugging with SIGINT
-* afpd uses an in memory temporary DB if can't open the volume's database, but currently this only
- works with the "cbd" or "tdb" backends not with the default "dbd".
-
-atalkd:
-* atalkd: workaround for broken Linux 2.6 AT kernel module:
- Linux 2.6 sends broadcast queries to the first available socket which is in our case
- the last configured one. atalkd now tries to find the right one.
- Note: now a misconfigured or plugged router can broadcast a wrong route !
-
-Tools:
-* dbd: "dbd" CNID database and volume maintanance and intergrity check utility
-* apple_dump: dump AppleDouble files
-
-
-Upgrading from a 2.0 version
-============================
-2.1 and 2.0 filenames encoding and adouble headers are compatible thus
-it's possible to upgrade and downgrade between 2.0 and 2.1, you may have
-to upgrade/downgrade (or delete) your .AppleDB folders in case the bdb versions differ.
-
-Requirements
-============
-BerkeleyDB 4.6
-
-configure
-=========
-new default:
-* sendfile is enable on linux
-
-new options
---disable-sendfile
---enable-nfsv4acls NFSv4 ACL Support (only Solaris?)
-
-Webmin:
---with-webmin path where webmin is installed [$PKGCONFDIR/webmin]
---with-webminuser name for the webmin admin user
---with-webminversion Webmin version to fetch from sf.net [1.490]
---with-webminpass password for the webmin admin user
---with-webminport TCP port for webmin
-
-removed options
---with-logfile
---with-cnid-dbd-txn dbd always use transaction
---with-cnid-db3-backend use dbd for transaction
---with-cnid-hash-backend never really work
-
-afpd.conf
-=========
-new defaults:
-* slp is disable by default
-* ddp is disable by default
-
-new options:
--slp advertise with SRVLOC
--hostname
--volnamelen
--setuplog
--closevol
--ntdomain
--ntseparator
-
-removed options
--noslp
-
-AppleVolume.default
-===================
-new options:
-acl
-caseinsensitive volume is case insensitive (JFS in OS2 mode)
-nocnidcache
-ea:sys|ad
-
-removed options:
-cachecnid
-
-Todo:
-=====
-- Clean up error messages, many messages should be move to the info/debug level
- rather than error.
-- Are all options documented in the man pages/configuration files?
-- update dbd logic to others db.
-
-Best Practices:
-===============
-
-- use a separate user group for cnid_dbd daemons
- cnid_metad -u afpd -g afpd
-
-- All CNID databases in the same directory
-
- AppleVolumes.default
-
- :DEFAUT: dbpath:/var/lib/afpd/$v
-
- with /var/lib/afpd
-
- drwxr-xr-x afpd afpd 4096 2009-11-24 15:12 /var/lib/afpd
-
- afpd or cnid_metad will create the right subdirectory ($v is replaced by the
- volume name)
-
-MISC
-====
-Bonjour:
-with avahi you can add an afpd.service file in "/etc/avahi/services/".
-Drawback: AFP is advertised even if the server is down.
# Makefile.am for etc/
-SUBDIRS = afpd cnid_dbd atalkd papd psf uams
+SUBDIRS = afpd cnid_dbd uams
+
+if USE_APPLETALK
+SUBDIRS += atalkd papd psf
+endif
sbin_PROGRAMS = afpd
noinst_PROGRAMS = hash
-afpd_SOURCES = unix.c ofork.c main.c switch.c auth.c volume.c directory.c \
- file.c enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
- mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c \
- afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
- catsearch.c afprun.c hash.c extattrs.c
+afpd_SOURCES = \
+ afp_asp.c \
+ afp_avahi.c \
+ afp_config.c \
+ afp_dsi.c \
+ afp_options.c \
+ afp_util.c \
+ afp_zeroconf.c \
+ afprun.c \
+ afs.c \
+ appl.c \
+ auth.c \
+ catsearch.c \
+ desktop.c \
+ dircache.c \
+ directory.c \
+ enumerate.c \
+ extattrs.c \
+ file.c \
+ filedir.c \
+ fork.c \
+ gettok.c \
+ hash.c \
+ main.c \
+ mangle.c \
+ messages.c \
+ nfsquota.c \
+ ofork.c \
+ quota.c \
+ status.c \
+ switch.c \
+ uam.c \
+ uid.c \
+ unix.c \
+ volume.c
-if USE_NFSv4_ACLS
+afpd_LDADD = \
+ $(top_builddir)/libatalk/cnid/libcnid.la \
+ $(top_builddir)/libatalk/libatalk.la \
+ @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@
+
+afpd_LDFLAGS = -export-dynamic
+
+afpd_CFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/sys \
+ @SLP_CFLAGS@ @ZEROCONF_CFLAGS@ \
+ -DAPPLCNAME \
+ -DSERVERTEXT=\"$(SERVERTEXT)/\" \
+ -D_PATH_AFPDDEFVOL=\"$(pkgconfdir)/AppleVolumes.default\" \
+ -D_PATH_AFPDSYSVOL=\"$(pkgconfdir)/AppleVolumes.system\" \
+ -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
+ -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
+ -D_PATH_AFPDSIGCONF=\"$(pkgconfdir)/afp_signature.conf\" \
+ -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
+ -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\" \
+ -D_PATH_AFPDUUIDCONF=\"$(pkgconfdir)/afp_voluuid.conf\"
+
+if HAVE_ACLS
afpd_SOURCES += acls.c
endif
-afpd_LDADD = $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@
-afpd_LDFLAGS = -export-dynamic
-afpd_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/sys \
- @SLP_CFLAGS@ \
- -D_PATH_AFPDDEFVOL=\"$(pkgconfdir)/AppleVolumes.default\" \
- -D_PATH_AFPDSYSVOL=\"$(pkgconfdir)/AppleVolumes.system\" \
- -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
- -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
- -D_PATH_AFPDSIGCONF=\"$(pkgconfdir)/afp_signature.conf\" \
- -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
- -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\" \
- -DAPPLCNAME \
- -DSERVERTEXT=\"$(SERVERTEXT)/\"
noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \
- uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.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
hash_SOURCES = hash.c
hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
#ifndef ACL_MAPPINGS
#define ACL_MAPPINGS
+#ifdef HAVE_SOLARIS_ACLS
#include <sys/acl.h>
+#endif
+
#include "acls.h"
/*
u_int32_t to;
};
+#ifdef HAVE_SOLARIS_ACLS
struct ace_rights_map nfsv4_to_darwin_rights[] = {
{ACE_READ_DATA, DARWIN_ACE_READ_DATA},
{ACE_WRITE_DATA, DARWIN_ACE_WRITE_DATA},
{DARWIN_ACE_FLAGS_INHERITED, ACE_INHERITED_ACE},
{0,0}
};
+#endif /* HAVE_SOLARIS_ACLS */
#endif /* ACL_MAPPINGS */
/*
- Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+ Copyright (c) 2008, 2009, 2010 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <grp.h>
#include <pwd.h>
#include <errno.h>
+#ifdef HAVE_SOLARIS_ACLS
#include <sys/acl.h>
+#endif
+#ifdef HAVE_POSIX_ACLS
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#endif
+#include <atalk/errchk.h>
#include <atalk/adouble.h>
#include <atalk/vfs.h>
#include <atalk/afp.h>
#include <atalk/logger.h>
#include <atalk/uuid.h>
#include <atalk/acl.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
#include "directory.h"
#include "desktop.h"
#include "volume.h"
#include "fork.h"
-
+#include "unix.h"
#include "acls.h"
#include "acl_mappings.h"
+#include "auth.h"
/* for map_acl() */
-#define SOLARIS_2_DARWIN 1
-#define DARWIN_2_SOLARIS 2
+#define SOLARIS_2_DARWIN 1
+#define DARWIN_2_SOLARIS 2
+#define POSIX_DEFAULT_2_DARWIN 3
+#define POSIX_ACCESS_2_DARWIN 4
+#define DARWIN_2_POSIX_DEFAULT 5
+#define DARWIN_2_POSIX_ACCESS 6
+
+#define MAP_MASK 31
+#define IS_DIR 32
/********************************************************
- * Basic and helper funcs
+ * Solaris funcs
********************************************************/
-/*
- Takes a users name, uid and primary gid and checks if user is member of any group
- Returns -1 if no or error, 0 if yes
-*/
-static int check_group(char *name, uid_t uid, gid_t pgid, gid_t path_gid)
+#ifdef HAVE_SOLARIS_ACLS
+
+/*!
+ * Compile access rights for a user to one file-system object
+ *
+ * This combines combines all access rights for a user to one fs-object and
+ * returns the result as a Darwin allowed rights ACE.
+ * This must honor trivial ACEs which are a mode_t mapping.
+ *
+ * @param path (r) path to filesystem object
+ * @param sb (r) struct stat of path
+ * @param result (w) resulting Darwin allow ACE
+ *
+ * @returns 0 or -1 on error
+ */
+static int solaris_acl_rights(const char *path,
+ const struct stat *sb,
+ uint32_t *result)
{
- int i;
- struct group *grp;
+ EC_INIT;
+ int i, ace_count, checkgroup;
+ ace_t *aces = NULL;
+ uid_t who;
+ uint16_t flags, type;
+ uint32_t rights, allowed_rights = 0, denied_rights = 0, darwin_rights;
- if (pgid == path_gid)
- return 0;
+ /* Get ACL from file/dir */
+ EC_NEG1_LOG(ace_count = get_nfsv4_acl(path, &aces));
- grp = getgrgid(path_gid);
- if (!grp)
- return -1;
+ if (ace_count == 0)
+ goto EC_CLEANUP;
+ /* Now check requested rights */
i = 0;
- while (grp->gr_mem[i] != NULL) {
- if ( (strcmp(grp->gr_mem[i], name)) == 0 ) {
- LOG(log_debug, logtype_afpd, "check_group: requested user:%s is member of: %s", name, grp->gr_name);
- return 0;
+ do { /* Loop through ACEs */
+ who = aces[i].a_who;
+ flags = aces[i].a_flags;
+ type = aces[i].a_type;
+ rights = aces[i].a_access_mask;
+
+ if (flags & ACE_INHERIT_ONLY_ACE)
+ continue;
+
+ /* Now the tricky part: decide if ACE effects our user. I'll explain:
+ if its a dedicated (non trivial) ACE for the user
+ OR
+ if its a ACE for a group we're member of
+ OR
+ if its a trivial ACE_OWNER ACE and requested UUID is the owner
+ OR
+ if its a trivial ACE_GROUP ACE and requested UUID is group
+ OR
+ if its a trivial ACE_EVERYONE ACE
+ THEN
+ process ACE */
+ if (((who == uuid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)))
+ ||
+ ((flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) && gmem(who))
+ ||
+ ((flags & ACE_OWNER) && (uuid == sb->st_uid))
+ ||
+ ((flags & ACE_GROUP) && gmem(sb->st_gid))
+ ||
+ (flags & ACE_EVERYONE)
+ ) {
+ /* Found an applicable ACE */
+ if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
+ allowed_rights |= rights;
+ else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
+ /* Only or to denied rights if not previously allowed !! */
+ denied_rights |= ((!allowed_rights) & rights);
}
- i++;
+ } while (++i < ace_count);
+
+
+ /* Darwin likes to ask for "delete_child" on dir,
+ "write_data" is actually the same, so we add that for dirs */
+ if (S_ISDIR(sb->st_mode) && (allowed_rights & ACE_WRITE_DATA))
+ allowed_rights |= ACE_DELETE_CHILD;
+
+ /* Remove denied from allowed rights */
+ allowed_rights &= ~denied_rights;
+
+ /* map rights */
+ darwin_rights = 0;
+ for (i=0; nfsv4_to_darwin_rights[i].from != 0; i++) {
+ if (allowed_rights & nfsv4_to_darwin_rights[i].from)
+ darwin_rights |= nfsv4_to_darwin_rights[i].to;
}
- return -1;
+ *result |= darwin_rights;
+
+EC_CLEANUP:
+ if (aces) free(aces);
+
+ EC_EXIT;
}
/*
Return numer of mapped ACEs or -1 on error.
All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
*/
-static int map_aces_solaris_to_darwin(ace_t *aces, darwin_ace_t *darwin_aces, int ace_count)
+static int map_aces_solaris_to_darwin(const ace_t *aces,
+ darwin_ace_t *darwin_aces,
+ int ace_count)
{
+ EC_INIT;
int i, count = 0;
uint32_t flags;
uint32_t rights;
struct passwd *pwd = NULL;
struct group *grp = NULL;
- LOG(log_debug7, logtype_afpd, "map_aces_solaris_to_darwin: parsing %d ACES", ace_count);
+ LOG(log_maxdebug, logtype_afpd, "map_aces_solaris_to_darwin: parsing %d ACES", ace_count);
while(ace_count--) {
- LOG(log_debug7, logtype_afpd, "map_aces_solaris_to_darwin: parsing ACE No. %d", ace_count + 1);
+ LOG(log_maxdebug, logtype_afpd, "ACE No. %d", ace_count + 1);
/* if its a ACE resulting from nfsv4 mode mapping, discard it */
if (aces->a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
- LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: trivial ACE");
+ LOG(log_debug, logtype_afpd, "trivial ACE");
aces++;
continue;
}
- if ( ! (aces->a_flags & ACE_IDENTIFIER_GROUP) ) {
- /* its a user ace */
- LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: found user ACE with uid: %d", aces->a_who);
- pwd = getpwuid(aces->a_who);
- if (!pwd) {
- LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getpwuid error: %s", strerror(errno));
- return -1;
- }
- LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: uid: %d -> name: %s", aces->a_who, pwd->pw_name);
- if ( (getuuidfromname(pwd->pw_name, UUID_USER, darwin_aces->darwin_ace_uuid)) != 0) {
- LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getuuidfromname error");
- return -1;
- }
- } else {
- /* its a group ace */
- LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: found group ACE with gid: %d", aces->a_who);
- grp = getgrgid(aces->a_who);
- if (!grp) {
- LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getgrgid error: %s", strerror(errno));
- return -1;
- }
- LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: gid: %d -> name: %s", aces->a_who, grp->gr_name);
- if ( (getuuidfromname(grp->gr_name, UUID_GROUP, darwin_aces->darwin_ace_uuid)) != 0) {
- LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getuuidfromname error");
- return -1;
- }
+ if ( ! (aces->a_flags & ACE_IDENTIFIER_GROUP) ) { /* user ace */
+ LOG(log_debug, logtype_afpd, "uid: %d", aces->a_who);
+ EC_NULL_LOG(pwd = getpwuid(aces->a_who));
+ LOG(log_debug, logtype_afpd, "uid: %d -> name: %s", aces->a_who, pwd->pw_name);
+ EC_ZERO_LOG(getuuidfromname(pwd->pw_name,
+ UUID_USER,
+ darwin_aces->darwin_ace_uuid));
+ } else { /* group ace */
+ LOG(log_debug, logtype_afpd, "gid: %d", aces->a_who);
+ EC_NULL_LOG(grp = getgrgid(aces->a_who));
+ LOG(log_debug, logtype_afpd, "gid: %d -> name: %s", aces->a_who, grp->gr_name);
+ EC_ZERO_LOG(getuuidfromname(grp->gr_name,
+ UUID_GROUP,
+ darwin_aces->darwin_ace_uuid));
}
/* map flags */
}
return count;
+EC_CLEANUP:
+ EC_EXIT;
}
/*
Return numer of mapped ACEs or -1 on error.
All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
*/
-int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces, ace_t *nfsv4_aces, int ace_count)
+static int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces,
+ ace_t *nfsv4_aces,
+ int ace_count)
{
+ EC_INIT;
int i, mapped_aces = 0;
uint32_t darwin_ace_flags;
uint32_t darwin_ace_rights;
uint16_t nfsv4_ace_flags;
uint32_t nfsv4_ace_rights;
- char *name;
+ char *name = NULL;
uuidtype_t uuidtype;
struct passwd *pwd;
struct group *grp;
nfsv4_ace_rights = 0;
/* uid/gid first */
- if ( (getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype)) != 0)
- return -1;
- if (uuidtype == UUID_USER) {
- pwd = getpwnam(name);
- if (!pwd) {
- LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: getpwnam: %s", strerror(errno));
- free(name);
- return -1;
- }
+ 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;
- } else { /* hopefully UUID_GROUP*/
- grp = getgrnam(name);
- if (!grp) {
- LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: getgrnam: %s", strerror(errno));
- free(name);
- return -1;
- }
+ break;
+ case UUID_GROUP:
+ EC_NULL_LOG(grp = getgrnam(name));
nfsv4_aces->a_who = (uid_t)(grp->gr_gid);
nfsv4_ace_flags |= ACE_IDENTIFIER_GROUP;
+ break;
}
free(name);
name = NULL;
}
return mapped_aces;
+EC_CLEANUP:
+ if (name)
+ free(name);
+ EC_EXIT;
}
+#endif /* HAVE_SOLARIS_ACLS */
/********************************************************
- * 2nd level funcs
+ * POSIX 1e funcs
********************************************************/
-/* Map between ACL styles (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS).
- Reads from 'aces' buffer, writes to 'rbuf' buffer.
- Caller must provide buffer.
- Darwin ACEs are read and written in network byte order.
- Needs to know how many ACEs are in the ACL (ace_count). Ignores trivial ACEs.
- Return no of mapped ACEs or -1 on error. */
-static int map_acl(int type, ace_t *nfsv4_aces, darwin_ace_t *buf, int ace_count)
+#ifdef HAVE_POSIX_ACLS
+
+static uint32_t posix_permset_to_darwin_rights(acl_entry_t e, int is_dir)
+{
+ EC_INIT;
+ uint32_t rights = 0;
+ acl_permset_t permset;
+
+ EC_ZERO_LOG(acl_get_permset(e, &permset));
+
+ if (acl_get_perm(permset, ACL_READ))
+ rights = DARWIN_ACE_READ_DATA
+ | DARWIN_ACE_READ_EXTATTRIBUTES
+ | DARWIN_ACE_READ_ATTRIBUTES
+ | DARWIN_ACE_READ_SECURITY;
+ if (acl_get_perm(permset, ACL_WRITE)) {
+ rights |= DARWIN_ACE_WRITE_DATA
+ | DARWIN_ACE_APPEND_DATA
+ | DARWIN_ACE_WRITE_EXTATTRIBUTES
+ | DARWIN_ACE_WRITE_ATTRIBUTES;
+ if (is_dir)
+ rights |= DARWIN_ACE_DELETE_CHILD;
+ }
+ if (acl_get_perm(permset, ACL_EXECUTE))
+ rights |= DARWIN_ACE_EXECUTE;
+
+EC_CLEANUP:
+ LOG(log_maxdebug, logtype_afpd, "mapped rights: 0x%08x", rights);
+ return rights;
+}
+
+/*!
+ * Compile access rights for a user to one file-system object
+ *
+ * This combines combines all access rights for a user to one fs-object and
+ * returns the result as a Darwin allowed rights ACE.
+ * This must honor trivial ACEs which are a mode_t mapping.
+ *
+ * @param path (r) path to filesystem object
+ * @param sb (r) struct stat of path
+ * @param result (rw) resulting Darwin allow ACE
+ *
+ * @returns 0 or -1 on error
+ */
+static int posix_acl_rights(const char *path,
+ const struct stat *sb,
+ uint32_t *result)
+{
+ EC_INIT;
+ int havemask = 0;
+ int entry_id = ACL_FIRST_ENTRY;
+ uint32_t rights = 0, maskrights = 0;
+ uid_t *uid = NULL;
+ gid_t *gid = NULL;
+ acl_t acl = NULL;
+ acl_entry_t e;
+ acl_tag_t tag;
+
+ EC_NULL_LOG(acl = acl_get_file(path, ACL_TYPE_ACCESS));
+
+ /* itereate through all ACEs to get the mask */
+ while (!havemask && acl_get_entry(acl, entry_id, &e) == 1) {
+ entry_id = ACL_NEXT_ENTRY;
+ EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+ switch (tag) {
+ case ACL_MASK:
+ maskrights = posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ LOG(log_maxdebug, logtype_afpd, "maskrights: 0x%08x", maskrights);
+ havemask = 1;
+ break;
+ default:
+ continue;
+ }
+ }
+
+ /* itereate through all ACEs */
+ entry_id = ACL_FIRST_ENTRY;
+ while (acl_get_entry(acl, entry_id, &e) == 1) {
+ entry_id = ACL_NEXT_ENTRY;
+ EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+ switch (tag) {
+ case ACL_USER:
+ EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
+ if (*uid == uuid) {
+ LOG(log_maxdebug, logtype_afpd, "ACL_USER: %u", *uid);
+ rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ }
+ acl_free(uid);
+ uid = NULL;
+ break;
+ case ACL_USER_OBJ:
+ if (sb->st_uid == uuid) {
+ LOG(log_maxdebug, logtype_afpd, "ACL_USER_OBJ: %u", sb->st_uid);
+ rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ }
+ break;
+ case ACL_GROUP:
+ EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
+ if (gmem(*gid)) {
+ LOG(log_maxdebug, logtype_afpd, "ACL_GROUP: %u", *gid);
+ rights |= (posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode)) & maskrights);
+ }
+ acl_free(gid);
+ gid = NULL;
+ break;
+ case ACL_GROUP_OBJ:
+ if (gmem(sb->st_gid)) {
+ LOG(log_maxdebug, logtype_afpd, "ACL_GROUP_OBJ: %u", sb->st_gid);
+ rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ }
+ break;
+ case ACL_OTHER:
+ LOG(log_maxdebug, logtype_afpd, "ACL_OTHER");
+ rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+ break;
+ default:
+ continue;
+ }
+ } /* while */
+
+ *result |= rights;
+
+EC_CLEANUP:
+ if (acl) acl_free(acl);
+ if (uid) acl_free(uid);
+ if (gid) acl_free(gid);
+ EC_EXIT;
+}
+
+/*!
+ * Add entries of one acl to another acl
+ *
+ * @param aclp (rw) destination acl where new aces will be added
+ * @param acl (r) source acl where aces will be copied from
+ *
+ * @returns 0 on success, -1 on error
+ */
+static int acl_add_acl(acl_t *aclp, const acl_t acl)
+{
+ EC_INIT;
+ int id;
+ acl_entry_t se, de;
+
+ for (id = ACL_FIRST_ENTRY; acl_get_entry(acl, id, &se) == 1; id = ACL_NEXT_ENTRY) {
+ EC_ZERO_LOG_ERR(acl_create_entry(aclp, &de), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(acl_copy_entry(de, se), AFPERR_MISC);
+ }
+
+EC_CLEANUP:
+ EC_EXIT;
+}
+
+/*!
+ * Map Darwin ACE rights to POSIX 1e perm
+ *
+ * We can only map few rights:
+ * DARWIN_ACE_READ_DATA -> ACL_READ
+ * DARWIN_ACE_WRITE_DATA -> ACL_WRITE
+ * DARWIN_ACE_DELETE_CHILD & (is_dir == 1) -> ACL_WRITE
+ * DARWIN_ACE_EXECUTE -> ACL_EXECUTE
+ *
+ * @param entry (rw) result of the mapping
+ * @param is_dir (r) 1 for dirs, 0 for files
+ *
+ * @returns mapping result as acl_perm_t, -1 on error
+ */
+static acl_perm_t map_darwin_right_to_posix_permset(uint32_t darwin_ace_rights, int is_dir)
+{
+ acl_perm_t perm = 0;
+
+ if (darwin_ace_rights & DARWIN_ACE_READ_DATA)
+ perm |= ACL_READ;
+
+ if (darwin_ace_rights & (DARWIN_ACE_WRITE_DATA | (DARWIN_ACE_DELETE_CHILD & is_dir)))
+ perm |= ACL_WRITE;
+
+ if (darwin_ace_rights & DARWIN_ACE_EXECUTE)
+ perm |= ACL_EXECUTE;
+
+ return perm;
+}
+
+/*!
+ * Add a ACL_USER or ACL_GROUP permission to an ACL, extending existing ACEs
+ *
+ * Add a permission of "type" for user or group "id" to an ACL. Scan the ACL
+ * for existing permissions for this type/id, if there is one add the perm,
+ * otherwise create a new ACL entry.
+ * perm can be or'ed ACL_READ, ACL_WRITE and ACL_EXECUTE.
+ *
+ * @param aclp (rw) pointer to ACL
+ * @param type (r) acl_tag_t of ACL_USER or ACL_GROUP
+ * @param id (r) uid_t uid for ACL_USER, or gid casted to uid_t for ACL_GROUP
+ * @param perm (r) acl_perm_t permissions to add
+ *
+ * @returns 0 on success, -1 on failure
+ */
+static int posix_acl_add_perm(acl_t *aclp, acl_tag_t type, uid_t id, acl_perm_t perm)
+{
+ EC_INIT;
+ uid_t *eid = NULL;
+ acl_entry_t e;
+ acl_tag_t tag;
+ int entry_id = ACL_FIRST_ENTRY;
+ acl_permset_t permset;
+
+ int found = 0;
+ for ( ; (! found) && acl_get_entry(*aclp, entry_id, &e) == 1; entry_id = ACL_NEXT_ENTRY) {
+ EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+ if (tag != ACL_USER && tag != ACL_GROUP)
+ continue;
+ EC_NULL_LOG(eid = (uid_t *)acl_get_qualifier(e));
+ if ((*eid == id) && (type == tag)) {
+ /* found an ACE for this type/id */
+ found = 1;
+ EC_ZERO_LOG(acl_get_permset(e, &permset));
+ EC_ZERO_LOG(acl_add_perm(permset, perm));
+ }
+
+ acl_free(eid);
+ eid = NULL;
+ }
+
+ if ( ! found) {
+ /* there was no existing ACE for this type/id */
+ EC_ZERO_LOG(acl_create_entry(aclp, &e));
+ EC_ZERO_LOG(acl_set_tag_type(e, type));
+ EC_ZERO_LOG(acl_set_qualifier(e, &id));
+ EC_ZERO_LOG(acl_get_permset(e, &permset));
+ EC_ZERO_LOG(acl_clear_perms(permset));
+ EC_ZERO_LOG(acl_add_perm(permset, perm));
+ EC_ZERO_LOG(acl_set_permset(e, permset));
+ }
+
+EC_CLEANUP:
+ if (eid) acl_free(eid);
+
+ EC_EXIT;
+}
+
+/*!
+ * Map Darwin ACL to POSIX ACL.
+ *
+ * aclp must point to a acl_init'ed acl_t or an acl_t that can eg contain default ACEs.
+ * Mapping pecularities:
+ * - we create a default ace (which inherits to files and dirs) if either
+ DARWIN_ACE_FLAGS_FILE_INHERIT or DARWIN_ACE_FLAGS_DIRECTORY_INHERIT is requested
+ * - we throw away DARWIN_ACE_FLAGS_LIMIT_INHERIT (can't be mapped), thus the ACL will
+ * not be limited
+ *
+ * @param darwin_aces (r) pointer to darwin_aces buffer
+ * @param def_aclp (rw) directories: pointer to an initialized acl_t with the default acl
+ * files: *def_aclp will be NULL
+ * @param acc_aclp (rw) pointer to an initialized acl_t with the access acl
+ * @param ace_count (r) number of ACEs in darwin_aces buffer
+ *
+ * @returns 0 on success storing the result in aclp, -1 on error.
+ */
+static int map_aces_darwin_to_posix(const darwin_ace_t *darwin_aces,
+ acl_t *def_aclp,
+ acl_t *acc_aclp,
+ int ace_count)
+{
+ EC_INIT;
+ char *name = NULL;
+ uuidtype_t uuidtype;
+ struct passwd *pwd;
+ struct group *grp;
+ uid_t id;
+ uint32_t darwin_ace_flags, darwin_ace_rights;
+ acl_tag_t tag;
+ acl_perm_t perm;
+
+ for ( ; ace_count != 0; ace_count--, darwin_aces++) {
+ /* type: allow/deny, posix only has allow */
+ darwin_ace_flags = ntohl(darwin_aces->darwin_ace_flags);
+ if ( ! (darwin_ace_flags & DARWIN_ACE_FLAGS_PERMIT))
+ continue;
+
+ darwin_ace_rights = ntohl(darwin_aces->darwin_ace_rights);
+ perm = map_darwin_right_to_posix_permset(darwin_ace_rights, (*def_aclp != NULL));
+ if (perm == 0)
+ continue; /* dont add empty perm */
+
+ LOG(log_debug, logtype_afpd, "map_ace: no: %u, flags: %08x, darwin: %08x, posix: %02x",
+ ace_count, darwin_ace_flags, darwin_ace_rights, perm);
+
+ /* 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 = pwd->pw_uid;
+ LOG(log_debug, logtype_afpd, "map_ace: name: %s, uid: %u", name, id);
+ break;
+ case UUID_GROUP:
+ EC_NULL_LOG(grp = getgrnam(name));
+ tag = ACL_GROUP;
+ id = (uid_t)(grp->gr_gid);
+ LOG(log_debug, logtype_afpd, "map_ace: name: %s, gid: %u", name, id);
+ break;
+ }
+ free(name);
+ name = NULL;
+
+ if (darwin_ace_flags & DARWIN_ACE_INHERIT_CONTROL_FLAGS) {
+ if (*def_aclp == NULL) {
+ /* ace request inheritane but we haven't got a default acl pointer */
+ LOG(log_warning, logtype_afpd, "map_acl: unexpected ACE, flags: 0x%04x",
+ darwin_ace_flags);
+ EC_FAIL;
+ }
+ /* add it as default ace */
+ EC_ZERO_LOG(posix_acl_add_perm(def_aclp, tag, id, perm));
+
+
+ if (! (darwin_ace_flags & DARWIN_ACE_FLAGS_ONLY_INHERIT))
+ /* if it not a "inherit only" ace, it must be added as access aces too */
+ EC_ZERO_LOG(posix_acl_add_perm(acc_aclp, tag, id, perm));
+ } else {
+ EC_ZERO_LOG(posix_acl_add_perm(acc_aclp, tag, id, perm));
+ }
+ }
+
+EC_CLEANUP:
+ if (name)
+ free(name);
+
+ EC_EXIT;
+}
+
+/*
+ * Map ACEs from POSIX to Darwin.
+ * type is either POSIX_DEFAULT_2_DARWIN or POSIX_ACCESS_2_DARWIN, cf. acl_get_file.
+ * Return number of mapped ACES, -1 on error.
+ */
+static int map_acl_posix_to_darwin(int type, const acl_t acl, darwin_ace_t *darwin_aces)
+{
+ EC_INIT;
+ int mapped_aces = 0;
+ int entry_id = ACL_FIRST_ENTRY;
+ acl_entry_t e;
+ acl_tag_t tag;
+ uid_t *uid = NULL;
+ gid_t *gid = NULL;
+ struct passwd *pwd = NULL;
+ struct group *grp = NULL;
+ uint32_t flags;
+ uint32_t rights, maskrights = 0;
+ darwin_ace_t *saved_darwin_aces = darwin_aces;
+
+ LOG(log_maxdebug, logtype_afpd, "map_aces_posix_to_darwin(%s)",
+ (type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN ?
+ "POSIX_DEFAULT_2_DARWIN" : "POSIX_ACCESS_2_DARWIN");
+
+ /* itereate through all ACEs */
+ while (acl_get_entry(acl, entry_id, &e) == 1) {
+ entry_id = ACL_NEXT_ENTRY;
+
+ /* get ACE type */
+ EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+
+ /* we return user and group ACE */
+ switch (tag) {
+ case ACL_USER:
+ EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
+ EC_NULL_LOG(pwd = getpwuid(*uid));
+ LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: uid: %d -> name: %s",
+ *uid, pwd->pw_name);
+ EC_ZERO_LOG(getuuidfromname(pwd->pw_name, UUID_USER, darwin_aces->darwin_ace_uuid));
+ acl_free(uid);
+ uid = NULL;
+ break;
+
+ case ACL_GROUP:
+ EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
+ EC_NULL_LOG(grp = getgrgid(*gid));
+ LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: gid: %d -> name: %s",
+ *gid, grp->gr_name);
+ EC_ZERO_LOG(getuuidfromname(grp->gr_name, UUID_GROUP, darwin_aces->darwin_ace_uuid));
+ acl_free(gid);
+ gid = NULL;
+ break;
+
+ case ACL_MASK:
+ maskrights = posix_permset_to_darwin_rights(e, type & IS_DIR);
+ continue;
+
+ default:
+ continue;
+ }
+
+ /* flags */
+ flags = DARWIN_ACE_FLAGS_PERMIT;
+ if ((type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN)
+ flags |= DARWIN_ACE_FLAGS_FILE_INHERIT
+ | DARWIN_ACE_FLAGS_DIRECTORY_INHERIT
+ | DARWIN_ACE_FLAGS_ONLY_INHERIT;
+ darwin_aces->darwin_ace_flags = htonl(flags);
+
+ /* rights */
+ rights = posix_permset_to_darwin_rights(e, type & IS_DIR);
+ darwin_aces->darwin_ace_rights = htonl(rights);
+
+ darwin_aces++;
+ mapped_aces++;
+ } /* while */
+
+ /* Loop through the mapped ACE buffer once again, applying the mask */
+ for (int i = mapped_aces; i > 0; i--) {
+ saved_darwin_aces->darwin_ace_rights &= htonl(maskrights);
+ saved_darwin_aces++;
+ }
+
+ EC_STATUS(mapped_aces);
+
+EC_CLEANUP:
+ if (uid) acl_free(uid);
+ if (gid) acl_free(gid);
+ EC_EXIT;
+}
+#endif
+
+/*
+ * Multiplex ACL mapping (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS, POSIX_2_DARWIN, DARWIN_2_POSIX).
+ * Reads from 'aces' buffer, writes to 'rbuf' buffer.
+ * Caller must provide buffer.
+ * Darwin ACEs are read and written in network byte order.
+ * Needs to know how many ACEs are in the ACL (ace_count) for Solaris ACLs.
+ * Ignores trivial ACEs.
+ * Return no of mapped ACEs or -1 on error.
+ */
+static int map_acl(int type, void *acl, darwin_ace_t *buf, int ace_count)
{
int mapped_aces;
LOG(log_debug9, logtype_afpd, "map_acl: BEGIN");
- switch (type) {
+ switch (type & MAP_MASK) {
+
+#ifdef HAVE_SOLARIS_ACLS
case SOLARIS_2_DARWIN:
- mapped_aces = map_aces_solaris_to_darwin( nfsv4_aces, buf, ace_count);
+ mapped_aces = map_aces_solaris_to_darwin( acl, buf, ace_count);
break;
case DARWIN_2_SOLARIS:
- mapped_aces = map_aces_darwin_to_solaris( buf, nfsv4_aces, ace_count);
+ mapped_aces = map_aces_darwin_to_solaris( buf, acl, ace_count);
break;
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+ case POSIX_DEFAULT_2_DARWIN:
+ mapped_aces = map_acl_posix_to_darwin(type, (const acl_t)acl, buf);
+ break;
+
+ case POSIX_ACCESS_2_DARWIN:
+ mapped_aces = map_acl_posix_to_darwin(type, (const acl_t)acl, buf);
+ break;
+
+ case DARWIN_2_POSIX_DEFAULT:
+ break;
+
+ case DARWIN_2_POSIX_ACCESS:
+ break;
+#endif /* HAVE_POSIX_ACLS */
default:
mapped_aces = -1;
return mapped_aces;
}
-/********************************************************
- * 1st level funcs
- ********************************************************/
-
-
/* Get ACL from object omitting trivial ACEs. Map to Darwin ACL style and
store Darwin ACL at rbuf. Add length of ACL written to rbuf to *rbuflen.
Returns 0 on success, -1 on error. */
static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
{
- int ace_count, mapped_aces, err;
- ace_t *aces;
+ EC_INIT;
+ int mapped_aces = 0;
+ int dirflag;
uint32_t *darwin_ace_count = (u_int32_t *)rbuf;
-
+#ifdef HAVE_SOLARIS_ACLS
+ int ace_count = 0;
+ ace_t *aces = NULL;
+#endif
+#ifdef HAVE_POSIX_ACLS
+ struct stat st;
+#endif
LOG(log_debug9, logtype_afpd, "get_and_map_acl: BEGIN");
/* Skip length and flags */
*rbuf = 0;
rbuf += 4;
- if ( (ace_count = get_nfsv4_acl(name, &aces)) == -1) {
- LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get ACL");
- return -1;
+#ifdef HAVE_SOLARIS_ACLS
+ EC_NEG1(ace_count = get_nfsv4_acl(name, &aces));
+ EC_NEG1(mapped_aces = map_acl(SOLARIS_2_DARWIN, aces, (darwin_ace_t *)rbuf, ace_count));
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+ acl_t defacl = NULL , accacl = NULL;
+
+ /* stat to check if its a dir */
+ EC_ZERO_LOG(lstat(name, &st));
+
+ /* if its a dir, check for default acl too */
+ dirflag = 0;
+ if (S_ISDIR(st.st_mode)) {
+ dirflag = IS_DIR;
+ EC_NULL_LOG(defacl = acl_get_file(name, ACL_TYPE_DEFAULT));
+ EC_NEG1(mapped_aces = map_acl(POSIX_DEFAULT_2_DARWIN | dirflag,
+ defacl,
+ (darwin_ace_t *)rbuf,
+ 0));
}
- if ( (mapped_aces = map_acl(SOLARIS_2_DARWIN, aces, (darwin_ace_t *)rbuf, ace_count)) == -1) {
- err = -1;
- goto cleanup;
- }
+ EC_NULL_LOG(accacl = acl_get_file(name, ACL_TYPE_ACCESS));
+
+ int tmp;
+ EC_NEG1(tmp = map_acl(POSIX_ACCESS_2_DARWIN | dirflag,
+ accacl,
+ (darwin_ace_t *)(rbuf + mapped_aces * sizeof(darwin_ace_t)),
+ 0));
+ mapped_aces += tmp;
+#endif /* HAVE_POSIX_ACLS */
+
LOG(log_debug, logtype_afpd, "get_and_map_acl: mapped %d ACEs", mapped_aces);
- err = 0;
*darwin_ace_count = htonl(mapped_aces);
*rbuflen += sizeof(darwin_acl_header_t) + (mapped_aces * sizeof(darwin_ace_t));
-cleanup:
- free(aces);
+ EC_STATUS(0);
+
+EC_CLEANUP:
+#ifdef HAVE_SOLARIS_ACLS
+ if (aces) free(aces);
+#endif
+#ifdef HAVE_POSIX_ACLS
+ if (defacl) acl_free(defacl);
+ if (accacl) acl_free(accacl);
+#endif /* HAVE_POSIX_ACLS */
LOG(log_debug9, logtype_afpd, "get_and_map_acl: END");
- return err;
+
+ EC_EXIT;
}
/* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
-static int remove_acl_vfs(const struct vol *vol,const char *path, int dir)
+static int remove_acl(const struct vol *vol,const char *path, int dir)
{
- int ret;
+ int ret = AFP_OK;
+#if (defined HAVE_SOLARIS_ACLS || defined HAVE_POSIX_ACLS)
/* Ressource etc. first */
if ((ret = vol->vfs->vfs_remove_acl(vol, path, dir)) != AFP_OK)
return ret;
/* now the data fork or dir */
- return (remove_acl(path));
+ ret = remove_acl_vfs(path);
+#endif
+ return ret;
}
/*
We will store inherited ACEs first, which is Darwins canonical order.
- returns AFPerror code
*/
-static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibuf)
+#ifdef HAVE_SOLARIS_ACLS
+static int set_acl(const struct vol *vol,
+ char *name,
+ int inherit,
+ darwin_ace_t *daces,
+ uint32_t ace_count)
{
- int ret, i, nfsv4_ace_count, tocopy_aces_count = 0, new_aces_count = 0, trivial_ace_count = 0;
+ EC_INIT;
+ int i, nfsv4_ace_count;
+ int tocopy_aces_count = 0, new_aces_count = 0, trivial_ace_count = 0;
ace_t *old_aces, *new_aces = NULL;
uint16_t flags;
- uint32_t ace_count;
LOG(log_debug9, logtype_afpd, "set_acl: BEGIN");
- /* Get no of ACEs the client put on the wire */
- ace_count = htonl(*((uint32_t *)ibuf));
- ibuf += 8; /* skip ACL flags (see acls.h) */
-
if (inherit)
/* inherited + trivial ACEs */
flags = ACE_INHERITED_ACE | ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
}
/* Now malloc buffer exactly sized to fit all new ACEs */
- new_aces = malloc( (ace_count + tocopy_aces_count) * sizeof(ace_t) );
- if (new_aces == NULL) {
+ if ((new_aces = malloc((ace_count + tocopy_aces_count) * sizeof(ace_t))) == NULL) {
LOG(log_error, logtype_afpd, "set_acl: malloc %s", strerror(errno));
- ret = AFPERR_MISC;
- goto cleanup;
+ EC_STATUS(AFPERR_MISC);
+ goto EC_CLEANUP;
}
/* Start building new ACL */
LOG(log_debug7, logtype_afpd, "set_acl: copied %d inherited ACEs", new_aces_count);
/* Now the ACEs from the client */
- ret = map_acl(DARWIN_2_SOLARIS, &new_aces[new_aces_count], (darwin_ace_t *)ibuf, ace_count);
- if (ret == -1) {
- ret = AFPERR_PARAM;
- goto cleanup;
+ if ((ret = (map_acl(DARWIN_2_SOLARIS,
+ &new_aces[new_aces_count],
+ daces,
+ ace_count))) == -1) {
+ EC_STATUS(AFPERR_PARAM);
+ goto EC_CLEANUP;
}
- new_aces_count += ace_count;
- LOG(log_debug7, logtype_afpd, "set_acl: mapped %d ACEs from client", ace_count);
+ new_aces_count += ret;
+ LOG(log_debug7, logtype_afpd, "set_acl: mapped %d ACEs from client", ret);
/* Now copy the trivial ACEs */
for (i=0; i < nfsv4_ace_count; i++) {
/* Ressourcefork first.
Note: for dirs we set the same ACL on the .AppleDouble/.Parent _file_. This
might be strange for ACE_DELETE_CHILD and for inheritance flags. */
- if ( (ret = vol->vfs->vfs_acl(vol, name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
+ if ((ret = (vol->vfs->vfs_acl(vol, name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
if (errno == (EACCES | EPERM))
- ret = AFPERR_ACCESS;
+ EC_STATUS(AFPERR_ACCESS);
else if (errno == ENOENT)
- ret = AFPERR_NOITEM;
+ EC_STATUS(AFPERR_NOITEM);
else
- ret = AFPERR_MISC;
- goto cleanup;
+ EC_STATUS(AFPERR_MISC);
+ goto EC_CLEANUP;
}
- if ( (ret = acl(name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
+ if ((ret = (acl(name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
if (errno == (EACCES | EPERM))
- ret = AFPERR_ACCESS;
+ EC_STATUS(AFPERR_ACCESS);
else if (errno == ENOENT)
- ret = AFPERR_NOITEM;
+ EC_STATUS(AFPERR_NOITEM);
else
- ret = AFPERR_MISC;
- goto cleanup;
+ EC_STATUS(AFPERR_MISC);
+ goto EC_CLEANUP;
}
- ret = AFP_OK;
+ EC_STATUS(AFP_OK);
-cleanup:
- free(old_aces);
- free(new_aces);
+EC_CLEANUP:
+ if (old_aces) free(old_aces);
+ if (new_aces) free(new_aces);
LOG(log_debug9, logtype_afpd, "set_acl: END");
- return ret;
+ EC_EXIT;
}
-
-/*
- Checks if a given UUID has requested_rights(type darwin_ace_rights) for path.
- Note: this gets called frequently and is a good place for optimizations !
-*/
-static int check_acl_access(const char *path, const uuidp_t uuid, uint32_t requested_darwin_rights)
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+static int set_acl(const struct vol *vol,
+ const char *name,
+ int inherit _U_,
+ darwin_ace_t *daces,
+ uint32_t ace_count)
{
- int ret, i, ace_count, dir, checkgroup;
- char *username = NULL; /* might be group too */
- uuidtype_t uuidtype;
- uid_t uid;
- gid_t pgid;
- uint32_t requested_rights = 0, allowed_rights = 0, denied_rights = 0;
- ace_t *aces;
- struct passwd *pwd;
- struct stat st;
- int check_user_trivace = 0, check_group_trivace = 0;
- uid_t who;
- uint16_t flags;
- uint16_t type;
- uint32_t rights;
-
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "check_access: BEGIN. Request: %08x", requested_darwin_rights);
-#endif
- /* Get uid or gid from UUID */
- if ( (getnamefromuuid(uuid, &username, &uuidtype)) != 0) {
- LOG(log_error, logtype_afpd, "check_access: error getting name from UUID");
- return AFPERR_PARAM;
- }
+ EC_INIT;
+ acl_t def_acl = NULL;
+ acl_t acc_acl = NULL;
- /* File or dir */
- if ((lstat(path, &st)) != 0) {
- LOG(log_error, logtype_afpd, "check_access: stat: %s", strerror(errno));
- ret = AFPERR_PARAM;
- goto exit;
- }
- dir = S_ISDIR(st.st_mode);
+ LOG(log_maxdebug, logtype_afpd, "set_acl: BEGIN");
- if (uuidtype == UUID_USER) {
- pwd = getpwnam(username);
- if (!pwd) {
- LOG(log_error, logtype_afpd, "check_access: getpwnam: %s", strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
- }
- uid = pwd->pw_uid;
- pgid = pwd->pw_gid;
+ struct stat st;
+ EC_ZERO_LOG_ERR(lstat(name, &st), AFPERR_NOOBJ);
- /* If user is file/dir owner we must check the user trivial ACE */
- if (uid == st.st_uid) {
- LOG(log_debug, logtype_afpd, "check_access: user: %s is files owner. Must check trivial user ACE", username);
- check_user_trivace = 1;
- }
+ /* seed default ACL with access ACL */
+ if (S_ISDIR(st.st_mode))
+ EC_NULL_LOG_ERR(def_acl = acl_get_file(name, ACL_TYPE_ACCESS), AFPERR_MISC);
- /* Now check if requested user is files owning group. If yes we must check the group trivial ACE */
- if ( (check_group(username, uid, pgid, st.st_gid)) == 0) {
- LOG(log_debug, logtype_afpd, "check_access: user: %s is in group: %d. Must check trivial group ACE", username, st.st_gid);
- check_group_trivace = 1;
- }
- } else { /* hopefully UUID_GROUP*/
- LOG(log_error, logtype_afpd, "check_access: afp_access for UUID of groups not supported!");
-#if 0
- grp = getgrnam(username);
- if (!grp) {
- LOG(log_error, logtype_afpd, "check_access: getgrnam: %s", strerror(errno));
- return -1;
- }
- if (st.st_gid == grp->gr_gid )
- check_group_trivace = 1;
-#endif
- }
+ /* for files def_acl will be NULL */
- /* Map requested rights to Solaris style. */
- for (i=0; darwin_to_nfsv4_rights[i].from != 0; i++) {
- if (requested_darwin_rights & darwin_to_nfsv4_rights[i].from)
- requested_rights |= darwin_to_nfsv4_rights[i].to;
- }
+ /* create access acl from mode */
+ EC_NULL_LOG_ERR(acc_acl = acl_from_mode(st.st_mode), AFPERR_MISC);
- /* Get ACL from file/dir */
- if ( (ace_count = get_nfsv4_acl(path, &aces)) == -1) {
- LOG(log_error, logtype_afpd, "check_access: error getting ACEs");
- ret = AFPERR_MISC;
- goto exit;
- }
- if (ace_count == 0) {
- LOG(log_debug, logtype_afpd, "check_access: 0 ACEs from get_nfsv4_acl");
- ret = AFPERR_MISC;
- goto exit;
+ /* adds the clients aces */
+ EC_ZERO_ERR(map_aces_darwin_to_posix(daces, &def_acl, &acc_acl, ace_count), AFPERR_MISC);
+
+ /* calcuate ACL mask */
+ EC_ZERO_LOG_ERR(acl_calc_mask(&acc_acl), AFPERR_MISC);
+
+ /* is it ok? */
+ EC_ZERO_LOG_ERR(acl_valid(acc_acl), AFPERR_MISC);
+
+ /* set it */
+ EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, acc_acl), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_ACCESS, 0, acc_acl), AFPERR_MISC);
+
+ if (def_acl) {
+ EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, def_acl), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_DEFAULT, 0, def_acl), AFPERR_MISC);
}
- /* Now check requested rights */
- ret = AFPERR_ACCESS;
- i = 0;
- do { /* Loop through ACEs */
- who = aces[i].a_who;
- flags = aces[i].a_flags;
- type = aces[i].a_type;
- rights = aces[i].a_access_mask;
+EC_CLEANUP:
+ acl_free(acc_acl);
+ acl_free(def_acl);
- if (flags & ACE_INHERIT_ONLY_ACE)
- continue;
+ LOG(log_maxdebug, logtype_afpd, "set_acl: END");
+ EC_EXIT;
+}
+#endif /* HAVE_POSIX_ACLS */
+
+/*!
+ * Checks if a given UUID has requested_rights(type darwin_ace_rights) for path.
+ *
+ * Note: this gets called frequently and is a good place for optimizations !
+ *
+ * @param vol (r) volume
+ * @param dir (r) 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,
+ const char *path,
+ const uuidp_t uuid,
+ uint32_t requested_rights)
+{
+ int ret;
+ uint32_t allowed_rights = 0;
+ char *username = NULL;
+ uuidtype_t uuidtype;
+ struct stat st;
+ bstring parent = NULL;
- /* Check if its a group ACE and set checkgroup to 1 if yes */
- checkgroup = 0;
- if ( (flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) ) {
- if ( (check_group(username, uid, pgid, who)) == 0)
- checkgroup = 1;
- else
- continue;
- }
+ LOG(log_maxdebug, logtype_afpd, "check_access: Request: 0x%08x", requested_rights);
- /* Now the tricky part: decide if ACE effects our user. I'll explain:
- if its a dedicated (non trivial) ACE for the user
- OR
- if its a ACE for a group we're member of
- OR
- if its a trivial ACE_OWNER ACE and requested UUID is the owner
- OR
- if its a trivial ACE_GROUP ACE and requested UUID is group
- OR
- if its a trivial ACE_EVERYONE ACE
- THEN
- process ACE */
- if (
- ( (who == uid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)) ) ||
- (checkgroup) ||
- ( (flags & ACE_OWNER) && check_user_trivace ) ||
- ( (flags & ACE_GROUP) && check_group_trivace ) ||
- ( flags & ACE_EVERYONE )
- ) {
- /* Found an applicable ACE */
- if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
- allowed_rights |= rights;
- else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
- /* Only or to denied rights if not previously allowed !! */
- denied_rights |= ((!allowed_rights) & rights);
+ /* Get uid or gid from UUID */
+ EC_ZERO_LOG_ERR(getnamefromuuid(uuid, &username, &uuidtype), AFPERR_PARAM);
+ EC_ZERO_LOG_ERR(lstat(path, &st), AFPERR_PARAM);
+
+ switch (uuidtype) {
+ case UUID_USER:
+ break;
+ case UUID_GROUP:
+ 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");
+ EC_STATUS(AFPERR_MISC);
+ goto EC_CLEANUP;
+ }
+
+#ifdef HAVE_SOLARIS_ACLS
+ 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));
+#endif
+
+ 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);
}
- } while (++i < ace_count);
+ LOG(log_debug, logtype_afpd,"parent: %s", cfrombstr(parent));
+ EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
- /* Darwin likes to ask for "delete_child" on dir,
- "write_data" is actually the same, so we add that for dirs */
- if (dir && (allowed_rights & ACE_WRITE_DATA))
- allowed_rights |= ACE_DELETE_CHILD;
+#ifdef HAVE_SOLARIS_ACLS
+ 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));
+#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 (requested_rights & denied_rights) {
- LOG(log_debug, logtype_afpd, "check_access: some requested right was denied:");
- ret = AFPERR_ACCESS;
- } else if ((requested_rights & allowed_rights) != requested_rights) {
- LOG(log_debug, logtype_afpd, "check_access: some requested right wasn't allowed:");
- ret = AFPERR_ACCESS;
+ if ((requested_rights & allowed_rights) != requested_rights) {
+ LOG(log_debug, logtype_afpd, "some requested right wasn't allowed: 0x%08x / 0x%08x",
+ requested_rights, allowed_rights);
+ EC_STATUS(AFPERR_ACCESS);
} else {
- LOG(log_debug, logtype_afpd, "check_access: all requested rights are allowed:");
- ret = AFP_OK;
+ LOG(log_debug, logtype_afpd, "all requested rights are allowed: 0x%08x",
+ requested_rights);
+ EC_STATUS(AFP_OK);
}
- LOG(log_debug, logtype_afpd, "check_access: Requested rights: %08x, allowed_rights: %08x, denied_rights: %08x, Result: %d",
- requested_rights, allowed_rights, denied_rights, ret);
+EC_CLEANUP:
+ if (username) free(username);
+ if (parent) bdestroy(parent);
-exit:
- free(aces);
- free(username);
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "check_access: END");
-#endif
- return ret;
+ EC_EXIT;
}
/********************************************************
struct path *s_path;
uuidp_t uuid;
- LOG(log_debug9, logtype_afpd, "afp_access: BEGIN");
-
*rbuflen = 0;
ibuf += 2;
return AFPERR_NOOBJ;
}
- ret = check_acl_access(s_path->u_name, uuid, darwin_ace_rights);
+ ret = check_acl_access(vol, dir, s_path->u_name, uuid, darwin_ace_rights);
- LOG(log_debug9, logtype_afpd, "afp_access: END");
return ret;
}
/* Shall we return ACL ? */
if (bitmap & kFileSec_ACL) {
LOG(log_debug, logtype_afpd, "afp_getacl: client requested files ACL");
- get_and_map_acl(s_path->u_name, rbuf, rbuflen);
+ if (get_and_map_acl(s_path->u_name, rbuf, rbuflen) != 0) {
+ LOG(log_error, logtype_afpd, "afp_getacl(\"%s/%s\"): mapping error",
+ getcwdpath(), s_path->u_name);
+ return AFPERR_MISC;
+ }
}
LOG(log_debug9, logtype_afpd, "afp_getacl: END");
/* Change owner: dont even try */
if (bitmap & kFileSec_UUID) {
- LOG(log_debug, logtype_afpd, "afp_setacl: change owner request. discarded.");
+ LOG(log_note, logtype_afpd, "afp_setacl: change owner request, discarded");
ret = AFPERR_ACCESS;
ibuf += UUID_BINSIZE;
}
/* Change group: certain changes might be allowed, so try it. FIXME: not implemented yet. */
if (bitmap & kFileSec_UUID) {
- LOG(log_debug, logtype_afpd, "afp_setacl: change group request. not supported this time.");
+ LOG(log_note, logtype_afpd, "afp_setacl: change group request, not supported");
ret = AFPERR_PARAM;
ibuf += UUID_BINSIZE;
}
/* Remove ACL ? */
if (bitmap & kFileSec_REMOVEACL) {
LOG(log_debug, logtype_afpd, "afp_setacl: Remove ACL request.");
- if ((ret = remove_acl_vfs(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
+ if ((ret = remove_acl(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
LOG(log_error, logtype_afpd, "afp_setacl: error from remove_acl");
}
/* Change ACL ? */
if (bitmap & kFileSec_ACL) {
LOG(log_debug, logtype_afpd, "afp_setacl: Change ACL request.");
-
- /* Check if its our job to preserve inherited ACEs */
- if (bitmap & kFileSec_Inherit)
- ret = set_acl_vfs(vol, s_path->u_name, 1, ibuf);
- else
- ret = set_acl_vfs(vol, s_path->u_name, 0, ibuf);
+ /* Get no of ACEs the client put on the wire */
+ uint32_t ace_count;
+ memcpy(&ace_count, ibuf, sizeof(uint32_t));
+ ace_count = htonl(ace_count);
+ ibuf += 8; /* skip ACL flags (see acls.h) */
+
+ ret = set_acl(vol,
+ s_path->u_name,
+ (bitmap & kFileSec_Inherit),
+ (darwin_ace_t *)ibuf,
+ ace_count);
if (ret == 0)
ret = AFP_OK;
else {
return ret;
}
-/*
- unix.c/accessmode calls this: map ACL to OS 9 mode
-*/
-void acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
+/********************************************************************
+ * ACL funcs interfacing with other parts
+ ********************************************************************/
+
+/*!
+ * map ACL to user maccess
+ *
+ * This is the magic function that makes ACLs usable by calculating
+ * the access granted by ACEs to the logged in user.
+ */
+int acltoownermode(char *path, struct stat *st, struct maccess *ma)
{
- struct passwd *pw;
- uuid_t uuid;
- int dir, r_ok, w_ok, x_ok;
-
- if ( ! (AFPobj->options.flags & OPTION_UUID))
- return;
+ EC_INIT;
+ uint32_t rights = 0;
- LOG(log_maxdebug, logtype_afpd, "acltoownermode('%s')", path);
+ if ( ! (AFPobj->options.flags & OPTION_ACL2MACCESS)
+ || (current_vol == NULL)
+ || ! (current_vol->v_flags & AFPVOL_ACLS))
+ return 0;
- if ((pw = getpwuid(uid)) == NULL) {
- LOG(log_error, logtype_afpd, "acltoownermode: %s", strerror(errno));
- return;
- }
+ LOG(log_maxdebug, logtype_afpd, "acltoownermode(\"%s/%s\", 0x%02x)",
+ getcwdpath(), path, ma->ma_user);
- /* We need the UUID for check_acl_access */
- if ((getuuidfromname(pw->pw_name, UUID_USER, uuid)) != 0)
- return;
+#ifdef HAVE_SOLARIS_ACLS
+ EC_ZERO_LOG(solaris_acl_rights(path, st, &rights));
+#endif
+#ifdef HAVE_POSIX_ACLS
+ EC_ZERO_LOG(posix_acl_rights(path, st, &rights));
+#endif
- /* These work for files and dirs */
- r_ok = check_acl_access(path, uuid, DARWIN_ACE_READ_DATA);
- w_ok = check_acl_access(path, uuid, (DARWIN_ACE_WRITE_DATA|DARWIN_ACE_APPEND_DATA));
- x_ok = check_acl_access(path, uuid, DARWIN_ACE_EXECUTE);
+ LOG(log_maxdebug, logtype_afpd, "rights: 0x%08x", rights);
- LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user before: %04o",ma->ma_user);
- if (r_ok == 0)
+ if (rights & DARWIN_ACE_READ_DATA)
ma->ma_user |= AR_UREAD;
- if (w_ok == 0)
+ if (rights & DARWIN_ACE_WRITE_DATA)
ma->ma_user |= AR_UWRITE;
- if (x_ok == 0)
+ if (rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
ma->ma_user |= AR_USEARCH;
- LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user after: %04o", ma->ma_user);
- return;
+ LOG(log_maxdebug, logtype_afpd, "resulting user maccess: 0x%02x", ma->ma_user);
+
+EC_CLEANUP:
+ EC_EXIT;
}
-/*
- We're being called at the end of afp_createdir. We're (hopefully) inside dir
- and ".AppleDouble" and ".AppleDouble/.Parent" should have already been created.
- We then inherit any explicit ACE from "." to ".AppleDouble" and ".AppleDouble/.Parent".
- FIXME: add to VFS layer ?
-*/
-void addir_inherit_acl(const struct vol *vol)
+/*!
+ * Check whether a volume supports ACLs
+ *
+ * @param vol (r) volume
+ *
+ * @returns 0 if not, 1 if yes
+ */
+int check_vol_acl_support(const struct vol *vol)
{
- ace_t *diraces = NULL, *adaces = NULL, *combinedaces = NULL;
- int diracecount, adacecount;
-
- LOG(log_debug9, logtype_afpd, "addir_inherit_acl: BEGIN");
+ int ret = 1;
- /* Check if ACLs are enabled for the volume */
- if (vol->v_flags & AFPVOL_ACLS) {
-
- if ((diracecount = get_nfsv4_acl(".", &diraces)) <= 0)
- goto cleanup;
- /* Remove any trivial ACE from "." */
- if ((diracecount = strip_trivial_aces(&diraces, diracecount)) <= 0)
- goto cleanup;
-
- /*
- Inherit to ".AppleDouble"
- */
-
- if ((adacecount = get_nfsv4_acl(".AppleDouble", &adaces)) <= 0)
- goto cleanup;
- /* Remove any non-trivial ACE from ".AppleDouble" */
- if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
- goto cleanup;
-
- /* Combine ACEs */
- if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
- goto cleanup;
-
- /* Now set new acl */
- if ((acl(".AppleDouble", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
- LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
-
- free(adaces);
- adaces = NULL;
- free(combinedaces);
- combinedaces = NULL;
-
- /*
- Inherit to ".AppleDouble/.Parent"
- */
-
- if ((adacecount = get_nfsv4_acl(".AppleDouble/.Parent", &adaces)) <= 0)
- goto cleanup;
- if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
- goto cleanup;
-
- /* Combine ACEs */
- if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
- goto cleanup;
-
- /* Now set new acl */
- if ((acl(".AppleDouble/.Parent", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
- LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
-
-
- }
+#ifdef HAVE_SOLARIS_ACLS
+ ace_t *aces = NULL;
+ if (get_nfsv4_acl(vol->v_path, &aces) == -1)
+ ret = 0;
+#endif
+#ifdef HAVE_POSIX_ACLS
+ acl_t acl = NULL;
+ if ((acl = acl_get_file(vol->v_path, ACL_TYPE_ACCESS)) == NULL)
+ ret = 0;
+#endif
-cleanup:
- LOG(log_debug9, logtype_afpd, "addir_inherit_acl: END");
+#ifdef HAVE_SOLARIS_ACLS
+ if (aces) free(aces);
+#endif
+#ifdef HAVE_POSIX_ACLS
+ if (acl) acl_free(acl);
+#endif /* HAVE_POSIX_ACLS */
- free(diraces);
- free(adaces);
- free(combinedaces);
+ LOG(log_debug, logtype_afpd, "Volume \"%s\" ACL support: %s",
+ vol->v_path, ret ? "yes" : "no");
+ return ret;
}
/*
- $Id: acls.h,v 1.3 2009-11-20 17:45:47 franklahm Exp $
Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#ifndef AFPD_ACLS_H
#define AFPD_ACLS_H
+#ifdef HAVE_SOLARIS_ACLS
#include <sys/acl.h>
-#include <atalk/uuid.h> /* for uuid_t */
+#endif
+
+#include <atalk/uuid.h> /* for atalk_uuid_t */
/*
* This is what Apple says about ACL flags in sys/kauth.h:
* the wire! We will ignore and spoil em.
*/
+#ifdef HAVE_SOLARIS_ACLS
/* Some stuff for the handling of NFSv4 ACLs */
#define ACE_TRIVIAL (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
+#endif /* HAVE_SOLARIS_ACLS */
/* FPGet|Set Bitmap */
enum {
/* ACE Flags */
#define DARWIN_ACE_FLAGS_KINDMASK 0xf
-#define DARWIN_ACE_FLAGS_PERMIT (1<<0)
-#define DARWIN_ACE_FLAGS_DENY (1<<1)
-#define DARWIN_ACE_FLAGS_INHERITED (1<<4)
-#define DARWIN_ACE_FLAGS_FILE_INHERIT (1<<5)
-#define DARWIN_ACE_FLAGS_DIRECTORY_INHERIT (1<<6)
-#define DARWIN_ACE_FLAGS_LIMIT_INHERIT (1<<7)
-#define DARWIN_ACE_FLAGS_ONLY_INHERIT (1<<8)
+#define DARWIN_ACE_FLAGS_PERMIT (1<<0) /* 0x00000001 */
+#define DARWIN_ACE_FLAGS_DENY (1<<1) /* 0x00000002 */
+#define DARWIN_ACE_FLAGS_INHERITED (1<<4) /* 0x00000010 */
+#define DARWIN_ACE_FLAGS_FILE_INHERIT (1<<5) /* 0x00000020 */
+#define DARWIN_ACE_FLAGS_DIRECTORY_INHERIT (1<<6) /* 0x00000040 */
+#define DARWIN_ACE_FLAGS_LIMIT_INHERIT (1<<7) /* 0x00000080 */
+#define DARWIN_ACE_FLAGS_ONLY_INHERIT (1<<8) /* 0x00000100 */
/* All flag bits controlling ACE inheritance */
#define DARWIN_ACE_INHERIT_CONTROL_FLAGS \
/* Access Control List Entry (ACE) */
typedef struct {
- uuid_t darwin_ace_uuid;
+ atalk_uuid_t darwin_ace_uuid;
uint32_t darwin_ace_flags;
uint32_t darwin_ace_rights;
} darwin_ace_t;
/* Parse afp_ldap.conf */
extern int acl_ldap_readconfig(char *name);
+/* Misc funcs */
+extern int acltoownermode(char *path, struct stat *st, struct maccess *ma);
+extern int check_vol_acl_support(const struct vol *vol);
#endif
--- /dev/null
+/*
+ * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Avahi based Zeroconf support
+ * Docs: http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_AVAHI
+
+#include <unistd.h>
+
+#include <avahi-common/strlst.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/dsi.h>
+#include <atalk/unicode.h>
+
+#include "afp_avahi.h"
+#include "afp_config.h"
+#include "volume.h"
+
+/*****************************************************************
+ * Global variables
+ *****************************************************************/
+struct context *ctx = NULL;
+
+/*****************************************************************
+ * Private functions
+ *****************************************************************/
+
+static void publish_reply(AvahiEntryGroup *g,
+ AvahiEntryGroupState state,
+ void *userdata);
+
+/*
+ * This function tries to register the AFP DNS
+ * SRV service type.
+ */
+static void register_stuff(void) {
+ uint port;
+ const AFPConfig *config;
+ const struct vol *volume;
+ DSI *dsi;
+ char name[MAXINSTANCENAMELEN+1];
+ AvahiStringList *strlist = NULL;
+ char tmpname[256];
+
+ assert(ctx->client);
+
+ if (!ctx->group) {
+ if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
+ LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+ }
+
+ if (avahi_entry_group_is_empty(ctx->group)) {
+ /* Register our service */
+
+ /* Build AFP volumes list */
+ int i = 0;
+ strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
+
+ for (volume = getvolumes(); volume; volume = volume->v_next) {
+
+ if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_name, -1, tmpname, 255) <= 0) {
+ LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
+ goto fail;
+ }
+
+ if (volume->v_flags & AFPVOL_TM) {
+ if (volume->v_uuid) {
+ LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
+ volume->v_localname, volume->v_uuid);
+ strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
+ i++, tmpname, volume->v_uuid);
+ } else {
+ LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
+ volume->v_localname);
+ strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1",
+ i++, tmpname);
+ }
+ }
+ }
+
+ /* AFP server */
+ for (config = ctx->configs; config; config = config->next) {
+
+ dsi = (DSI *)config->obj.handle;
+ port = getip_port((struct sockaddr *)&dsi->server);
+
+ if (convert_string(config->obj.options.unixcharset, CH_UTF8,
+ config->obj.options.server ? config->obj.options.server : config->obj.options.hostname, -1,
+ name, MAXINSTANCENAMELEN) <= 0) {
+ LOG ( log_error, logtype_afpd, "Could not set Zeroconf instance name");
+ goto fail;
+ }
+
+ if (avahi_entry_group_add_service(ctx->group,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ 0,
+ name,
+ AFP_DNS_SERVICE_TYPE,
+ NULL,
+ NULL,
+ port,
+ NULL) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+
+ if (i && avahi_entry_group_add_service_strlst(ctx->group,
+ AVAHI_IF_UNSPEC,
+ AVAHI_PROTO_UNSPEC,
+ 0,
+ name,
+ ADISK_SERVICE_TYPE,
+ NULL,
+ NULL,
+ 9, /* discard */
+ strlist) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to add service: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ } /* if */
+ } /* for config*/
+
+ if (avahi_entry_group_commit(ctx->group) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+
+ } /* if avahi_entry_group_is_empty*/
+
+ return;
+
+fail:
+ avahi_client_free (ctx->client);
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+}
+
+/* Called when publishing of service data completes */
+static void publish_reply(AvahiEntryGroup *g,
+ AvahiEntryGroupState state,
+ AVAHI_GCC_UNUSED void *userdata)
+{
+ assert(ctx->group == NULL || g == ctx->group);
+
+ switch (state) {
+
+ case AVAHI_ENTRY_GROUP_ESTABLISHED :
+ /* The entry group has been established successfully */
+ LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
+ break;
+
+ case AVAHI_ENTRY_GROUP_COLLISION:
+ /* With multiple names there's no way to know which one collided */
+ LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ avahi_client_free(avahi_entry_group_get_client(g));
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+ break;
+
+ case AVAHI_ENTRY_GROUP_FAILURE:
+ LOG(log_error, logtype_afpd, "Failed to register service: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ avahi_client_free(avahi_entry_group_get_client(g));
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+ break;
+
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ break;
+ case AVAHI_ENTRY_GROUP_REGISTERING:
+ break;
+ }
+}
+
+static void client_callback(AvahiClient *client,
+ AvahiClientState state,
+ void *userdata)
+{
+ ctx->client = client;
+
+ switch (state) {
+ case AVAHI_CLIENT_S_RUNNING:
+ /* The server has startup successfully and registered its host
+ * name on the network, so it's time to create our services */
+ if (!ctx->group)
+ register_stuff();
+ break;
+
+ case AVAHI_CLIENT_S_COLLISION:
+ if (ctx->group)
+ avahi_entry_group_reset(ctx->group);
+ break;
+
+ case AVAHI_CLIENT_FAILURE: {
+ if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
+ int error;
+
+ avahi_client_free(ctx->client);
+ ctx->client = NULL;
+ ctx->group = NULL;
+
+ /* Reconnect to the server */
+ if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+ AVAHI_CLIENT_NO_FAIL,
+ client_callback,
+ ctx,
+ &error))) {
+
+ LOG(log_error, logtype_afpd, "Failed to contact server: %s",
+ avahi_strerror(error));
+
+ avahi_client_free (ctx->client);
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+ }
+
+ } else {
+ LOG(log_error, logtype_afpd, "Client failure: %s",
+ avahi_strerror(avahi_client_errno(client)));
+ avahi_client_free (ctx->client);
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+ }
+ break;
+ }
+
+ case AVAHI_CLIENT_S_REGISTERING:
+ break;
+ case AVAHI_CLIENT_CONNECTING:
+ break;
+ }
+}
+
+/************************************************************************
+ * Public funcions
+ ************************************************************************/
+
+/*
+ * Tries to setup the Zeroconf thread and any
+ * neccessary config setting.
+ */
+void av_zeroconf_setup(const AFPConfig *configs) {
+ int error;
+
+ /* initialize the struct that holds our config settings. */
+ if (ctx) {
+ LOG(log_debug, logtype_afpd, "Resetting zeroconf records");
+ avahi_entry_group_reset(ctx->group);
+ } else {
+ ctx = calloc(1, sizeof(struct context));
+ ctx->configs = configs;
+ assert(ctx);
+ }
+
+/* first of all we need to initialize our threading env */
+ if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
+ goto fail;
+ }
+
+/* now we need to acquire a client */
+ if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+ AVAHI_CLIENT_NO_FAIL,
+ client_callback,
+ NULL,
+ &error))) {
+ LOG(log_error, logtype_afpd, "Failed to create client object: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ }
+
+ return;
+
+fail:
+ if (ctx)
+ av_zeroconf_unregister();
+
+ return;
+}
+
+/*
+ * This function finally runs the loop impl.
+ */
+int av_zeroconf_run(void) {
+ /* Finally, start the event loop thread */
+ if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
+ LOG(log_error, logtype_afpd, "Failed to create thread: %s",
+ avahi_strerror(avahi_client_errno(ctx->client)));
+ goto fail;
+ } else {
+ LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
+ }
+
+ ctx->thread_running = 1;
+ return 0;
+
+fail:
+ if (ctx)
+ av_zeroconf_unregister();
+
+ return -1;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from outside this thread.
+ */
+void av_zeroconf_shutdown() {
+ /* Call this when the app shuts down */
+ avahi_threaded_poll_stop(ctx->threaded_poll);
+ avahi_client_free(ctx->client);
+ avahi_threaded_poll_free(ctx->threaded_poll);
+ free(ctx);
+ ctx = NULL;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from inside this thread.
+ */
+int av_zeroconf_unregister() {
+ if (ctx->thread_running) {
+ /* First, block the event loop */
+ avahi_threaded_poll_lock(ctx->threaded_poll);
+
+ /* Than, do your stuff */
+ avahi_threaded_poll_quit(ctx->threaded_poll);
+
+ /* Finally, unblock the event loop */
+ avahi_threaded_poll_unlock(ctx->threaded_poll);
+ ctx->thread_running = 0;
+ }
+
+ if (ctx->client)
+ avahi_client_free(ctx->client);
+
+ if (ctx->threaded_poll)
+ avahi_threaded_poll_free(ctx->threaded_poll);
+
+ free(ctx);
+ ctx = NULL;
+
+ return 0;
+}
+
+#endif /* USE_AVAHI */
+
--- /dev/null
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Avahi based Zeroconf support
+ * Docs: http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifndef AFPD_AVAHI_H
+#define AFPD_AVAHI_H
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/thread-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include <atalk/logger.h>
+
+#include "afp_config.h"
+
+#define AFP_DNS_SERVICE_TYPE "_afpovertcp._tcp"
+#define ADISK_SERVICE_TYPE "_adisk._tcp"
+
+#define MAXINSTANCENAMELEN 63
+
+struct context {
+ /* Avahi stuff */
+ int thread_running;
+ AvahiThreadedPoll *threaded_poll;
+ AvahiClient *client;
+ AvahiEntryGroup *group;
+ /* Netatalk stuff */
+ const AFPConfig *configs;
+};
+
+/* prototype definitions */
+void av_zeroconf_setup(const AFPConfig *configs);
+int av_zeroconf_run(void);
+int av_zeroconf_unregister(void);
+void av_zeroconf_shutdown(void);
+
+#endif /* AFPD_AVAHI_H */
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-
-/* STDC check */
-#if STDC_HEADERS
#include <string.h>
-#else /* STDC_HEADERS */
-#ifndef HAVE_STRCHR
-#define strchr index
-#define strrchr index
-#endif /* HAVE_STRCHR */
-char *strchr (), *strrchr ();
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif /* STDC_HEADERS */
-
-#ifdef HAVE_UNISTD_H
#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
#include <ctype.h>
-#include <atalk/logger.h>
-#include <atalk/util.h>
-
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#ifdef USE_SRVLOC
+#include <slp.h>
+#endif /* USE_SRVLOC */
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
#include <atalk/dsi.h>
#include <atalk/atp.h>
#include <atalk/asp.h>
#include <atalk/afp.h>
#include <atalk/compat.h>
#include <atalk/server_child.h>
-#ifdef USE_SRVLOC
-#include <slp.h>
-#endif /* USE_SRVLOC */
-#ifdef HAVE_NFSv4_ACLS
+
+#ifdef HAVE_LDAP
#include <atalk/ldapconfig.h>
#endif
#include "afp_config.h"
#include "uam_auth.h"
#include "status.h"
+#include "volume.h"
+#include "afp_zeroconf.h"
#define LINESIZE 1024
}
free(p);
}
+
+ /* the master loaded the volumes for zeroconf, get rid of that */
+ unload_volumes_and_extmap();
}
#ifdef USE_SRVLOC
}
#endif /* USE_SRVLOC */
-#ifdef USE_SRVLOC
static void dsi_cleanup(const AFPConfig *config)
{
+#ifdef USE_SRVLOC
SLPError err;
SLPError callbackerr;
SLPHandle hslp;
srvloc_dereg_err:
dsi->srvloc_url[0] = '\0';
SLPClose(hslp);
-}
#endif /* USE_SRVLOC */
+}
#ifndef NO_DDP
static void asp_cleanup(const AFPConfig *config)
}
#endif /* no afp/asp */
-static int dsi_start(AFPConfig *config, AFPConfig *configs,
- server_child *server_children)
+static afp_child_t *dsi_start(AFPConfig *config, AFPConfig *configs,
+ server_child *server_children)
{
- DSI *dsi;
+ DSI *dsi = config->obj.handle;
+ afp_child_t *child = NULL;
- if (!(dsi = dsi_getsession(config->obj.handle, server_children,
- config->obj.options.tickleval))) {
- LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
- exit( EXITERR_CLNT );
+ if (!(child = dsi_getsession(dsi,
+ server_children,
+ config->obj.options.tickleval))) {
+ LOG(log_error, logtype_afpd, "dsi_start: session error: %s", strerror(errno));
+ return NULL;
}
/* we've forked. */
- if (dsi->child) {
+ if (parent_or_child == 1) {
configfree(configs, config);
+ config->obj.ipc_fd = child->ipc_fds[1];
+ close(child->ipc_fds[0]); /* Close parent IPC fd */
+ free(child);
afp_over_dsi(&config->obj); /* start a session */
exit (0);
}
- return 0;
+ return child;
}
#ifndef NO_DDP
}
#endif /* USE_SRVLOC */
-
config->fd = dsi->serversock;
config->obj.handle = dsi;
config->obj.config = config;
/* allocate server configurations. this should really store the last
* entry in config->last or something like that. that would make
* supporting multiple dsi transports easier. */
-static AFPConfig *AFPConfigInit(const struct afp_options *options,
+static AFPConfig *AFPConfigInit(struct afp_options *options,
const struct afp_options *defoptions)
{
AFPConfig *config = NULL, *next = NULL;
struct afp_options options;
AFPConfig *config=NULL, *first = NULL;
-#ifdef HAVE_NFSv4_ACLS
- /* Parse afp_ldap.conf first so we can set the uuid option */
- LOG(log_debug, logtype_afpd, "Start parsing afp_ldap.conf");
- acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
- LOG(log_debug, logtype_afpd, "Finished parsing afp_ldap.conf");
-#endif
-
/* if config file doesn't exist, load defaults */
if ((fp = fopen(cmdline->configfile, "r")) == NULL)
{
return AFPConfigInit(cmdline, cmdline);
}
- LOG(log_debug, logtype_afpd, "Loading ConfigFile");
-
/* scan in the configuration file */
len = 0;
while (!feof(fp)) {
if (!afp_options_parseline(p, &options))
continue;
-#ifdef HAVE_NFSv4_ACLS
- /* Enable UUID support if LDAP config is complete */
- if (ldap_config_valid)
- options.flags |= OPTION_UUID;
-#endif
-
- /* this should really get a head and a tail to simplify things. */
+ /* AFPConfigInit can return two linked configs due to DSI and ASP */
if (!first) {
if ((first = AFPConfigInit(&options, cmdline)))
config = first->next ? first->next : first;
}
}
+#ifdef HAVE_LDAP
+ /* Parse afp_ldap.conf */
+ acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
+#endif /* HAVE_LDAP */
+
LOG(log_debug, logtype_afpd, "Finished parsing Config File");
fclose(fp);
if (!have_option)
first = AFPConfigInit(cmdline, cmdline);
+ /* Now register with zeroconf, we also need the volumes for that */
+ load_volumes(&first->obj);
+ zeroconf_register(first);
+
return first;
}
unsigned char *optcount;
char status[1400];
const void *defoptions, *signature;
- int (*server_start) (struct AFPConfig *, struct AFPConfig *,
+ afp_child_t *(*server_start) (struct AFPConfig *, struct AFPConfig *,
server_child *);
void (*server_cleanup) (const struct AFPConfig *);
struct AFPConfig *next;
/*
- * $Id: afp_dsi.c,v 1.53 2010-03-30 12:55:26 franklahm Exp $
- *
* Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include <netinet/in.h>
#include <arpa/inet.h>
#include <atalk/logger.h>
+#include <setjmp.h>
#include <atalk/dsi.h>
#include <atalk/compat.h>
#include "switch.h"
#include "auth.h"
#include "fork.h"
+#include "dircache.h"
#ifdef FORCE_UIDGID
#warning UIDGID
#include "uid.h"
#endif /* FORCE_UIDGID */
-#define CHILD_DIE (1 << 0)
-#define CHILD_RUNNING (1 << 1)
-#define CHILD_SLEEPING (1 << 2)
-#define CHILD_DATA (1 << 3)
-
/*
* We generally pass this from afp_over_dsi to all afp_* funcs, so it should already be
* available everywhere. Unfortunately some funcs (eg acltoownermode) need acces to it
*/
AFPObj *AFPobj = NULL;
-static struct {
- AFPObj *obj;
- unsigned char flags;
- int tickle;
-} child;
+typedef struct {
+ uint16_t DSIreqID;
+ uint8_t AFPcommand;
+ uint32_t result;
+} rc_elem_t;
+/*
+ * AFP replay cache:
+ * - fix sized array
+ * - indexed just by taking DSIreqID mod REPLAYCACHE_SIZE
+ */
+static rc_elem_t replaycache[REPLAYCACHE_SIZE];
+static sigjmp_buf recon_jmp;
static void afp_dsi_close(AFPObj *obj)
{
DSI *dsi = obj->handle;
+ close(obj->ipc_fd);
+ obj->ipc_fd = -1;
+
/* we may have been called from a signal handler caught when afpd was running
* as uid 0, that's the wrong user for volume's prexec_close scripts if any,
* restore our login user
if (obj->logout)
(*obj->logout)();
- LOG(log_info, logtype_afpd, "%.2fKB read, %.2fKB written",
+ LOG(log_note, logtype_afpd, "AFP statistics: %.2f KB read, %.2f KB written",
dsi->read_count/1024.0, dsi->write_count/1024.0);
+ log_dircache_stat();
dsi_close(dsi);
}
*/
static void afp_dsi_die(int sig)
{
-static volatile int in_handler;
-
- if (in_handler) {
- return;
+ DSI *dsi = (DSI *)AFPobj->handle;
+
+ if (dsi->flags & DSI_RECONINPROG) {
+ /* Primary reconnect succeeded, got SIGTERM from afpd parent */
+ dsi->flags &= ~DSI_RECONINPROG;
+ return; /* this returns to afp_disconnect */
+ }
+
+ if (dsi->flags & DSI_DISCONNECTED) {
+ LOG(log_note, logtype_afpd, "Disconnected session terminating");
+ exit(0);
}
- /* it's not atomic but we don't care because it's an exit function
- * ie if a signal is received here, between the test and the affectation,
- * it will not return.
- */
- in_handler = 1;
-
- dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN);
- afp_dsi_close(child.obj);
+
+ dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN);
+ afp_dsi_close(AFPobj);
if (sig) /* if no signal, assume dieing because logins are disabled &
don't log it (maintenance mode)*/
LOG(log_info, logtype_afpd, "Connection terminated");
}
}
-/* */
-static void afp_dsi_sleep(void)
+/* SIGURG handler (primary reconnect) */
+static void afp_dsi_transfer_session(int sig _U_)
{
- child.flags |= CHILD_SLEEPING;
- dsi_sleep(child.obj->handle, 1);
+ uint16_t dsiID;
+ int socket;
+ DSI *dsi = (DSI *)AFPobj->handle;
+
+ LOG(log_debug, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session");
+
+ if (readt(AFPobj->ipc_fd, &dsiID, 2, 0, 2) != 2) {
+ LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive DSI id, goodbye");
+ afp_dsi_close(AFPobj);
+ exit(EXITERR_SYS);
+ }
+
+ if ((socket = recv_fd(AFPobj->ipc_fd, 1)) == -1) {
+ LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive session fd, goodbye");
+ afp_dsi_close(AFPobj);
+ exit(EXITERR_SYS);
+ }
+
+ LOG(log_debug, logtype_afpd, "afp_dsi_transfer_session: received socket fd: %i", socket);
+
+ dsi->proto_close(dsi);
+ dsi->socket = socket;
+ dsi->flags = DSI_RECONSOCKET;
+ dsi->datalen = 0;
+ dsi->eof = dsi->start = dsi->buffer;
+ dsi->in_write = 0;
+ dsi->header.dsi_requestID = dsiID;
+ dsi->header.dsi_command = DSIFUNC_CMD;
+
+ /*
+ * The session transfer happens in the middle of FPDisconnect old session, thus we
+ * have to send the reply now.
+ */
+ if (!dsi_cmdreply(dsi, AFP_OK)) {
+ LOG(log_error, logtype_afpd, "dsi_cmdreply: %s", strerror(errno) );
+ afp_dsi_close(AFPobj);
+ exit(EXITERR_CLNT);
+ }
+
+ LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: succesfull primary reconnect");
+ /*
+ * Now returning from this signal handler return to dsi_receive which should start
+ * reading/continuing from the connected socket that was passed via the parent from
+ * another session. The parent will terminate that session.
+ */
+ siglongjmp(recon_jmp, 1);
}
/* ------------------- */
{
struct sigaction sv;
struct itimerval it;
-
- child.flags |= CHILD_DIE;
+ DSI *dsi = (DSI *)AFPobj->handle;
+ dsi->flags |= DSI_DIE;
/* shutdown and don't reconnect. server going down in 5 minutes. */
setmessage("The server is going down for maintenance.");
- if (dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+ if (dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
AFPATTN_MESG | AFPATTN_TIME(5)) < 0) {
- DSI *dsi = (DSI *) child.obj->handle;
+ DSI *dsi = (DSI *)AFPobj->handle;
dsi->down_request = 1;
}
/* ---------------------------------
* SIGHUP reload configuration file
- * FIXME here or we wait ?
-*/
+ */
volatile int reload_request = 0;
static void afp_dsi_reload(int sig _U_)
}
/* ---------------------- */
-#ifdef SERVERTEXT
static void afp_dsi_getmesg (int sig _U_)
{
- DSI *dsi = (DSI *) child.obj->handle;
+ DSI *dsi = (DSI *)AFPobj->handle;
dsi->msg_request = 1;
- if (dsi_attention(child.obj->handle, AFPATTN_MESG | AFPATTN_TIME(5)) < 0)
+ if (dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5)) < 0)
dsi->msg_request = 2;
}
-#endif /* SERVERTEXT */
static void alarm_handler(int sig _U_)
{
int err;
- DSI *dsi = (DSI *) child.obj->handle;
+ DSI *dsi = (DSI *)AFPobj->handle;
- /* we have to restart the timer because some libraries
- * may use alarm() */
+ /* we have to restart the timer because some libraries may use alarm() */
setitimer(ITIMER_REAL, &dsi->timer, NULL);
- /* we got some traffic from the client since the previous timer
- * tick. */
- if ((child.flags & CHILD_DATA)) {
- child.flags &= ~CHILD_DATA;
+ /* we got some traffic from the client since the previous timer tick. */
+ if ((dsi->flags & DSI_DATA)) {
+ dsi->flags &= ~DSI_DATA;
return;
}
- /* if we're in the midst of processing something,
- don't die. */
- if ((child.flags & CHILD_SLEEPING) && child.tickle++ < child.obj->options.sleep) {
+ dsi->tickle++;
+ LOG(log_maxdebug, logtype_afpd, "alarm: tickles: %u, flags: %s|%s|%s|%s|%s|%s|%s|%s|%s",
+ dsi->tickle,
+ (dsi->flags & DSI_DATA) ? "DSI_DATA" : "-",
+ (dsi->flags & DSI_RUNNING) ? "DSI_RUNNING" : "-",
+ (dsi->flags & DSI_SLEEPING) ? "DSI_SLEEPING" : "-",
+ (dsi->flags & DSI_EXTSLEEP) ? "DSI_EXTSLEEP" : "-",
+ (dsi->flags & DSI_DISCONNECTED) ? "DSI_DISCONNECTED" : "-",
+ (dsi->flags & DSI_DIE) ? "DSI_DIE" : "-",
+ (dsi->flags & DSI_NOREPLY) ? "DSI_NOREPLY" : "-",
+ (dsi->flags & DSI_RECONSOCKET) ? "DSI_RECONSOCKET" : "-",
+ (dsi->flags & DSI_RECONINPROG) ? "DSI_RECONINPROG" : "-");
+
+ if (dsi->flags & DSI_SLEEPING) {
+ if (dsi->tickle > AFPobj->options.sleep) {
+ LOG(log_error, logtype_afpd, "afp_alarm: sleep time ended");
+ afp_dsi_die(EXITERR_CLNT);
+ }
return;
}
-
- if ((child.flags & CHILD_RUNNING) || (child.tickle++ < child.obj->options.timeout)) {
- if (!(err = pollvoltime(child.obj)))
- err = dsi_tickle(child.obj->handle);
- if (err <= 0)
+
+ if (dsi->flags & DSI_DISCONNECTED) {
+ if (dsi->tickle > AFPobj->options.disconnected) {
+ LOG(log_error, logtype_afpd, "afp_alarm: no reconnect within 10 minutes, goodbye");
afp_dsi_die(EXITERR_CLNT);
-
- } else { /* didn't receive a tickle. close connection */
- LOG(log_error, logtype_afpd, "afp_alarm: child timed out");
- afp_dsi_die(EXITERR_CLNT);
+ }
+ return;
+ }
+
+ /* 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;
+ return;
+ }
+
+ if ((err = pollvoltime(AFPobj)) == 0)
+ err = dsi_tickle(AFPobj->handle);
+ if (err <= 0) {
+ LOG(log_error, logtype_afpd, "afp_alarm: connection problem, entering disconnected state");
+ dsi->flags |= DSI_DISCONNECTED;
}
}
if (dsi->msg_request) {
if (dsi->msg_request == 2) {
/* didn't send it in signal handler */
- dsi_attention(child.obj->handle, AFPATTN_MESG | AFPATTN_TIME(5));
+ dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5));
}
dsi->msg_request = 0;
- readmessage(child.obj);
+ readmessage(AFPobj);
}
if (dsi->down_request) {
dsi->down_request = 0;
- dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+ dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
AFPATTN_MESG | AFPATTN_TIME(5));
}
}
void afp_over_dsi(AFPObj *obj)
{
DSI *dsi = (DSI *) obj->handle;
+ int rc_idx;
u_int32_t err, cmd;
u_int8_t function;
struct sigaction action;
obj->exit = afp_dsi_die;
obj->reply = (int (*)()) dsi_cmdreply;
obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
-
- obj->sleep = afp_dsi_sleep;
- child.obj = obj;
- child.tickle = child.flags = 0;
+ dsi->tickle = 0;
memset(&action, 0, sizeof(action));
sigaddset(&action.sa_mask, SIGTERM);
sigaddset(&action.sa_mask, SIGUSR1);
sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
sigaddset(&action.sa_mask, SIGUSR2);
-#endif
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, SIGHUP);
sigaddset(&action.sa_mask, SIGUSR1);
sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
sigaddset(&action.sa_mask, SIGUSR2);
-#endif
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);
}
-#ifdef SERVERTEXT
/* Added for server message support */
action.sa_handler = afp_dsi_getmesg;
sigemptyset( &action.sa_mask );
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
afp_dsi_die(EXITERR_SYS);
}
-#endif /* SERVERTEXT */
/* SIGUSR1 - set down in 5 minutes */
action.sa_handler = afp_dsi_timedown;
sigaddset(&action.sa_mask, SIGHUP);
sigaddset(&action.sa_mask, SIGTERM);
sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
sigaddset(&action.sa_mask, SIGUSR2);
-#endif
action.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &action, NULL) < 0 ) {
LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
sigaddset(&action.sa_mask, SIGTERM);
sigaddset(&action.sa_mask, SIGUSR1);
sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
sigaddset(&action.sa_mask, SIGUSR2);
-#endif
action.sa_flags = SA_RESTART;
if ((sigaction(SIGALRM, &action, NULL) < 0) ||
(setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) {
}
#endif /* DEBUGGING */
+ if (dircache_init(obj->options.dircachesize) != 0)
+ afp_dsi_die(EXITERR_SYS);
+
/* get stuck here until the end */
- while ((cmd = dsi_receive(dsi))) {
- child.tickle = 0;
- child.flags &= ~CHILD_SLEEPING;
- dsi_sleep(dsi, 0); /* wake up */
+ while (1) {
+ if (sigsetjmp(recon_jmp, 1) != 0)
+ /* returning from SIGALARM handler for a primary reconnect */
+ continue;
+
+ /* Blocking read on the network socket */
+ cmd = dsi_receive(dsi);
+
+ if (cmd == 0) {
+ /* cmd == 0 is the error condition */
+ if (dsi->flags & DSI_RECONSOCKET) {
+ /* we just got a reconnect so we immediately try again to receive on the new fd */
+ dsi->flags &= ~DSI_RECONSOCKET;
+ continue;
+ }
+ /* Some error on the client connection, enter disconnected state */
+ dsi->flags |= DSI_DISCONNECTED;
+ pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
+ continue; /* continue receiving until disconnect timer expires
+ * or a primary reconnect succeeds */
+ }
+
+ if (!(dsi->flags & DSI_EXTSLEEP) && (dsi->flags & DSI_SLEEPING)) {
+ LOG(log_debug, logtype_afpd, "afp_over_dsi: got data, ending normal sleep");
+ dsi->flags &= ~DSI_SLEEPING;
+ dsi->tickle = 0;
+ }
if (reload_request) {
reload_request = 0;
- load_volumes(child.obj);
+ load_volumes(AFPobj);
+ dircache_dump();
+ log_dircache_stat();
}
+ /* The first SIGINT enables debugging, the next restores the config */
if (debug_request) {
- char logstr[50];
+ static int debugging = 0;
debug_request = 0;
- /* The first SIGINT enables debugging, the second one kills us */
- action.sa_handler = afp_dsi_die;
- 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);
+ if (debugging) {
+ if (obj->options.logconfig)
+ setuplog(obj->options.logconfig);
+ else
+ setuplog("default log_note");
+ debugging = 0;
+ } else {
+ char logstr[50];
+ debugging = 1;
+ sprintf(logstr, "default log_maxdebug /tmp/afpd.%u.XXXXXX", getpid());
+ setuplog(logstr);
}
-
- sprintf(logstr, "default log_maxdebug /tmp/afpd.%u.XXXXXX", getpid());
- setuplog(logstr);
}
if (cmd == DSIFUNC_TICKLE) {
/* timer is not every 30 seconds anymore, so we don't get killed on the client side. */
- if ((child.flags & CHILD_DIE))
+ if ((dsi->flags & DSI_DIE))
dsi_tickle(dsi);
pending_request(dsi);
continue;
}
- child.flags |= CHILD_DATA;
+ dsi->flags |= DSI_DATA;
switch(cmd) {
case DSIFUNC_CLOSE:
afp_dsi_close(obj);
- LOG(log_info, logtype_afpd, "done");
+ LOG(log_note, logtype_afpd, "done");
return;
break;
function = (u_char) dsi->commands[0];
- /* send off an afp command. in a couple cases, we take advantage
- * of the fact that we're a stream-based protocol. */
- if (afp_switch[function]) {
- dsi->datalen = DSI_DATASIZ;
- child.flags |= CHILD_RUNNING;
+ /* AFP replay cache */
+ rc_idx = REPLAYCACHE_SIZE % dsi->clientID;
+ LOG(log_debug, logtype_afpd, "DSI request ID: %u", dsi->clientID);
- LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
+ if (replaycache[rc_idx].DSIreqID == dsi->clientID
+ && replaycache[rc_idx].AFPcommand == function) {
+ LOG(log_debug, logtype_afpd, "AFP Replay Cache match: id: %u / cmd: %s",
+ dsi->clientID, AfpNum2name(function));
+ err = replaycache[rc_idx].result;
+ /* AFP replay cache end */
+ } else {
+ /* send off an afp command. in a couple cases, we take advantage
+ * of the fact that we're a stream-based protocol. */
+ if (afp_switch[function]) {
+ dsi->datalen = DSI_DATASIZ;
+ dsi->flags |= DSI_RUNNING;
- err = (*afp_switch[function])(obj,
- (char *)&dsi->commands, dsi->cmdlen,
- (char *)&dsi->data, &dsi->datalen);
+ LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
+
+ err = (*afp_switch[function])(obj,
+ (char *)&dsi->commands, dsi->cmdlen,
+ (char *)&dsi->data, &dsi->datalen);
+
+ LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
+ AfpNum2name(function), AfpErr2name(err));
+
+ dir_free_invalid_q();
- LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
- AfpNum2name(function), AfpErr2name(err));
#ifdef FORCE_UIDGID
- /* bring everything back to old euid, egid */
- if (obj->force_uid)
- restore_uidgid ( &obj->uidgid );
+ /* bring everything back to old euid, egid */
+ if (obj->force_uid)
+ restore_uidgid ( &obj->uidgid );
#endif /* FORCE_UIDGID */
- child.flags &= ~CHILD_RUNNING;
- } else {
- LOG(log_error, logtype_afpd, "bad function %X", function);
- dsi->datalen = 0;
- err = AFPERR_NOOP;
+ dsi->flags &= ~DSI_RUNNING;
+
+ /* Add result to the AFP replay cache */
+ replaycache[rc_idx].DSIreqID = dsi->clientID;
+ replaycache[rc_idx].AFPcommand = function;
+ replaycache[rc_idx].result = err;
+ } else {
+ LOG(log_error, logtype_afpd, "bad function %X", function);
+ dsi->datalen = 0;
+ err = AFPERR_NOOP;
+ }
}
/* single shot toggle that gets set by dsi_readinit. */
- if (dsi->noreply) {
- dsi->noreply = 0;
+ if (dsi->flags & DSI_NOREPLY) {
+ dsi->flags &= ~DSI_NOREPLY;
break;
}
if (!dsi_cmdreply(dsi, err)) {
LOG(log_error, logtype_afpd, "dsi_cmdreply(%d): %s", dsi->socket, strerror(errno) );
- afp_dsi_die(EXITERR_CLNT);
+ dsi->flags |= DSI_DISCONNECTED;
}
break;
function = (u_char) dsi->commands[0];
if ( afp_switch[ function ] != NULL ) {
dsi->datalen = DSI_DATASIZ;
- child.flags |= CHILD_RUNNING;
+ dsi->flags |= DSI_RUNNING;
LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
AfpNum2name(function), AfpErr2name(err));
- child.flags &= ~CHILD_RUNNING;
+ dsi->flags &= ~DSI_RUNNING;
#ifdef FORCE_UIDGID
/* bring everything back to old euid, egid */
if (obj->force_uid)
if (!dsi_wrtreply(dsi, err)) {
LOG(log_error, logtype_afpd, "dsi_wrtreply: %s", strerror(errno) );
- afp_dsi_die(EXITERR_CLNT);
+ dsi->flags |= DSI_DISCONNECTED;
}
break;
#include <stdio.h>
#include <stdlib.h>
-
-/* STDC check */
-#if STDC_HEADERS
#include <string.h>
-#else /* STDC_HEADERS */
-#ifndef HAVE_STRCHR
-#define strchr index
-#define strrchr index
-#endif /* HAVE_STRCHR */
-char *strchr (), *strrchr ();
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif /* STDC_HEADERS */
-
#include <ctype.h>
-#ifdef HAVE_UNISTD_H
#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
#include <sys/param.h>
#include <sys/socket.h>
#include <atalk/logger.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif /* HAVE_NETDB_H */
+#ifdef ADMIN_GRP
+#include <grp.h>
+#include <sys/types.h>
+#endif /* ADMIN_GRP */
+
#include <atalk/paths.h>
#include <atalk/util.h>
+#include <atalk/compat.h>
+
#include "globals.h"
#include "status.h"
#include "auth.h"
-
-#include <atalk/compat.h>
-
-#ifdef ADMIN_GRP
-#include <grp.h>
-#include <sys/types.h>
-#endif /* ADMIN_GRP */
+#include "dircache.h"
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
free(opt->ntdomain);
if (opt->ntseparator && (opt->ntseparator != save->ntseparator))
free(opt->ntseparator);
+ if (opt->logconfig && (opt->logconfig != save->logconfig))
+ free(opt->logconfig);
}
/* initialize options */
options->systemvol.name = _PATH_AFPDSYSVOL;
options->configfile = _PATH_AFPDCONF;
options->sigconffile = _PATH_AFPDSIGCONF;
+ options->uuidconf = _PATH_AFPDUUIDCONF;
options->uampath = _PATH_AFPDUAMPATH;
options->uamlist = "uams_dhx.so,uams_dhx2.so";
options->guest = "nobody";
options->transports = AFPTRANS_TCP; /* TCP only */
options->passwdfile = _PATH_AFPDPWFILE;
options->tickleval = 30;
- options->timeout = 4;
- options->sleep = 10* 120; /* 10 h in 30 seconds tick */
+ options->timeout = 4; /* 4 tickles = 2 minutes */
+ options->sleep = 10 * 60 * 2; /* 10 h in 30 seconds tick */
+ options->disconnected = 10 * 60 * 2; /* 10 h in 30 seconds tick */
options->server_notif = 1;
options->authprintdir = NULL;
options->signatureopt = "auto";
/* don't advertize slp by default */
options->flags |= OPTION_NOSLP;
#endif
+ options->dircachesize = DEFAULT_MAX_DIRCACHE_SIZE;
+ options->flags |= OPTION_ACL2MACCESS;
+ options->flags |= OPTION_UUID;
}
/* parse an afpd.conf line. i'm doing it this way because it's
if (strstr(buf, " -slp"))
options->flags &= ~OPTION_NOSLP;
#endif
-
+#ifdef USE_ZEROCONF
+ if (strstr(buf, " -nozeroconf"))
+ options->flags |= OPTION_NOZEROCONF;
+#endif
if (strstr(buf, " -nouservolfirst"))
options->flags &= ~OPTION_USERVOLFIRST;
if (strstr(buf, " -uservolfirst"))
options->flags |= OPTION_CUSTOMICON;
if (strstr(buf, " -advertise_ssh"))
options->flags |= OPTION_ANNOUNCESSH;
+ if (strstr(buf, " -noacl2maccess"))
+ options->flags &= ~OPTION_ACL2MACCESS;
/* passwd bits */
if (strstr(buf, " -nosavepassword"))
char *optstr;
if ((optstr = getoption(c, "-setuplog"))) {
setuplog(optstr);
+ options->logconfig = optstr; /* at least store the last (possibly only) one */
c += sizeof("-setuplog");
}
}
if ((c = getoption(buf, "-ntseparator")) && (opt = strdup(c)))
options->ntseparator = opt;
+ if ((c = getoption(buf, "-dircachesize")))
+ options->dircachesize = atoi(c);
+
return 1;
}
puts( "afpd has been compiled with support for these features:\n" );
- printf( " AFP3.x support:\t" );
-#ifdef AFP3x
- puts( "Yes" );
-#else
- puts( "No" );
-#endif
+ printf( " AFP3.x support:\tYes\n" );
+ printf( " TCP/IP Support:\t" );
+ puts( "Yes" );
- printf( " Transport layers:\t" );
+ printf( "DDP(AppleTalk) Support:\t" );
#ifdef NO_DDP
- puts( "TCP/IP" );
+ puts( "No" );
#else
- puts( "TCP/IP DDP" );
+ puts( "Yes" );
#endif
printf( " CNID backends:\t" );
puts( "No" );
#endif
+ printf( " Zeroconf support:\t" );
+#ifdef USE_ZEROCONF
+ puts( "Yes" );
+#else
+ puts( "No" );
+#endif
+
printf( " TCP wrappers support:\t" );
#ifdef TCPWRAP
puts( "Yes" );
#else
puts( "No" );
#endif
+
+ printf( " ACL support:\t" );
+#ifdef HAVE_ACLS
+ puts( "Yes" );
+#else
+ puts( "No" );
+#endif
+
+ printf( " EA support:\t" );
+ puts( EA_MODULES );
+
+ printf( " LDAP support:\t" );
+#ifdef HAVE_LDAP
+ puts( "Yes" );
+#else
+ puts( "No" );
+#endif
}
/*
static void show_paths( void )
{
printf( " afpd.conf:\t%s\n", _PATH_AFPDCONF );
- printf( " afp_signature.conf:\t%s\n", _PATH_AFPDSIGCONF );
printf( " AppleVolumes.system:\t%s\n", _PATH_AFPDSYSVOL );
printf( " AppleVolumes.default:\t%s\n", _PATH_AFPDDEFVOL );
+ printf( " afp_signature.conf:\t%s\n", _PATH_AFPDSIGCONF );
+ printf( " afp_voluuid.conf:\t%s\n", _PATH_AFPDUUIDCONF );
+#ifdef HAVE_LDAP
+ printf( " afp_ldap.conf:\t%s\n", _PATH_ACL_LDAPCONF );
+#else
+ printf( " afp_ldap.conf:\tnot supported\n");
+#endif
printf( " UAM search path:\t%s\n", _PATH_AFPDUAMPATH );
- printf( " Server messages path:\t%s\n", SERVERTEXT);
+ printf( " Server messages path:\t%s\n", SERVERTEXT);
+ printf( " lockfile:\t%s\n", _PATH_AFPDLOCK);
}
/*
--- /dev/null
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ * particular Zeroconf implementation
+ * Doc: http://www.dns-sd.org/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "afp_zeroconf.h"
+#include "afp_config.h"
+
+#ifdef HAVE_AVAHI
+#include "afp_avahi.h"
+#endif
+
+
+/*
+ * Functions (actually they are just facades)
+ */
+void zeroconf_register(const AFPConfig *configs)
+{
+#if defined (HAVE_AVAHI)
+ LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using Avahi");
+
+ av_zeroconf_setup(configs);
+ av_zeroconf_run();
+#endif
+}
+
+void zeroconf_deregister(void)
+{
+#if defined (HAVE_AVAHI)
+ LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using Avahi");
+ av_zeroconf_shutdown();
+#endif
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author: Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ * particular Zeroconf implementation
+ * Doc: http://www.dns-sd.org/
+ *
+ */
+
+#ifndef AFPD_ZEROCONF_H
+#define AFPD_ZEROCONF_H
+
+#include "afp_config.h"
+
+/*
+ * Prototype Definitions
+ */
+
+/*
+ * registers service with a particular Zerconf implemenation.
+ */
+void zeroconf_register(const AFPConfig *configs);
+
+/*
+ * de-registers the ntpd service with a particular Zerconf implemenation.
+ */
+void zeroconf_deregister(void);
+
+#endif /* AFPD_ZEROCONF_H */
/*
- * $Id: appl.c,v 1.18 2009-10-15 10:43:13 didg Exp $
+ * $Id: appl.c,v 1.18.4.1 2010-02-01 10:56:08 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include <atalk/adouble.h>
#include <atalk/afp.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
#include "volume.h"
#include "globals.h"
* See afp_getappl() for the backward compatiblity code.
*/
static char *
-makemacpath(char *mpath, int mpathlen, struct dir *dir, char *path)
+makemacpath(const struct vol *vol, char *mpath, int mpathlen, struct dir *dir, char *path)
{
char *p;
p -= strlen( path );
memcpy( p, path, strlen( path ));
- while ( dir->d_parent != NULL ) {
- p -= strlen( dir->d_m_name ) + 1;
+ while ( dir->d_did != DIRDID_ROOT ) {
+ p -= blength(dir->d_m_name) + 1;
if (p < mpath) {
/* FIXME: pathname too long */
return NULL;
}
- strcpy( p, dir->d_m_name );
- dir = dir->d_parent;
+ memcpy(p, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1);
+ if ((dir = dirlookup(vol, dir->d_pdid)) == NULL)
+ return NULL;
}
return( p );
+
+
+#if 0
+ char buffer[12 + MAXPATHLEN + 1];
+ int buflen = 12 + MAXPATHLEN + 1;
+ char *ret = mpath;
+ char *path = name;
+ char *uname = NULL;
+ struct bstrList *pathlist = NULL;
+ cnid_t cnid = dir->d_pdid;
+
+ /* Create list for path elements, request 16 list elements for now*/
+ if ((pathlist = bstListCreateMin(16)) == NULL) {
+ LOG(log_error, logtype_afpd, "makemacpath: OOM: %s", strerror(errno));
+ return NULL;
+ }
+
+ while ( cnid != DIRDID_ROOT ) {
+
+ /* construct path, copy already found uname to path element list*/
+ if ((bstrListPush(pathlist, bfromcstr(path))) != BSTR_OK) {
+ afp_errno = AFPERR_MISC;
+ ret = NULL;
+ goto exit;
+ }
+
+ /* next part */
+ if ((uname = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) {
+ afp_errno = AFPERR_NOOBJ;
+ ret = NULL;
+ goto exit;
+ }
+
+ if ((path = utompath(vol, uname, cnid, utf8_encoding())) == NULL) {
+ afp_errno = AFPERR_MISC;
+ ret = NULL;
+ goto exit;
+ }
+ }
+
+
+
+exit:
+ if (pathlist)
+ bstrListDestroy(pathlist);
+
+ return(ret);
+#endif
}
return( AFPERR_PARAM );
}
mpath = obj->newtmp;
- mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
+ mp = makemacpath( vol, mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
if (!mp) {
return AFPERR_PARAM;
}
return( AFPERR_PARAM );
}
mpath = obj->newtmp;
- mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
+ mp = makemacpath( vol, mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
if (!mp) {
return AFPERR_PARAM ;
}
memcpy( q, p, len );
q = cbuf;
- if (( path = cname( vol, vol->v_dir, &q )) == NULL ) {
+ if (( path = cname( vol, vol->v_root, &q )) == NULL ) {
*rbuflen = 0;
return( AFPERR_NOITEM );
}
#include <time.h>
#include <pwd.h>
#include <grp.h>
-#include <atalk/logger.h>
-#include <atalk/server_ipc.h>
-#include <atalk/uuid.h>
#ifdef TRU64
#include <netdb.h>
extern void afp_get_cmdline( int *ac, char ***av );
#endif /* TRU64 */
+#include <atalk/logger.h>
+#include <atalk/server_ipc.h>
+#include <atalk/uuid.h>
+
#include "globals.h"
#include "auth.h"
#include "uam_auth.h"
#include "status.h"
#include "fork.h"
#include "extattrs.h"
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
#include "acls.h"
#endif
{ "AFPVersion 2.1", 21 },
#endif /* ! NO_DDP */
{ "AFP2.2", 22 },
-#ifdef AFP3x
{ "AFPX03", 30 },
{ "AFP3.1", 31 },
- { "AFP3.2", 32 }
-#endif /* AFP3x */
+ { "AFP3.2", 32 },
+ { "AFP3.3", 33 }
};
static struct uam_mod uam_modules = {NULL, NULL, &uam_modules, &uam_modules};
else {
afp_switch = postauth_switch;
switch (afp_version) {
+
+ case 33:
case 32:
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
uam_afpserver_action(AFP_GETACL, UAM_AFPSERVER_POSTAUTH, afp_getacl, NULL);
uam_afpserver_action(AFP_SETACL, UAM_AFPSERVER_POSTAUTH, afp_setacl, NULL);
uam_afpserver_action(AFP_ACCESS, UAM_AFPSERVER_POSTAUTH, afp_access, NULL);
-#endif
+#endif /* HAVE_ACLS */
uam_afpserver_action(AFP_GETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_getextattr, NULL);
uam_afpserver_action(AFP_SETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_setextattr, NULL);
uam_afpserver_action(AFP_REMOVEATTR, UAM_AFPSERVER_POSTAUTH, afp_remextattr, NULL);
uam_afpserver_action(AFP_LISTEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_listextattr, NULL);
+
case 31:
uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL);
uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL);
uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL);
uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL);
+
case 30:
uam_afpserver_action(AFP_ENUMERATE_EXT, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext, NULL);
uam_afpserver_action(AFP_BYTELOCK_EXT, UAM_AFPSERVER_POSTAUTH, afp_bytelock_ext, NULL);
}
/* ---------------------- */
-int afp_zzz ( /* Function 122 */
- AFPObj *obj,
- char *ibuf _U_, size_t ibuflen _U_,
- char *rbuf, size_t *rbuflen)
+int afp_zzz(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
{
- u_int32_t retdata;
+ uint32_t data;
+ DSI *dsi = (DSI *)AFPobj->handle;
*rbuflen = 0;
- retdata = obj->options.sleep /120;
- if (!retdata) {
- retdata = 1;
+ if (ibuflen < 4)
+ return AFPERR_MISC;
+ memcpy(&data, ibuf, 4); /* flag */
+ data = ntohl(data);
+
+ /*
+ * Possible sleeping states:
+ * 1) normal sleep: DSI_SLEEPING (up to 10.3)
+ * 2) extended sleep: DSI_SLEEPING | DSI_EXTSLEEP (starting with 10.4)
+ */
+
+ if (data & AFPZZZ_EXT_WAKEUP) {
+ /* wakeup request from exetended sleep */
+ if (dsi->flags & DSI_EXTSLEEP) {
+ LOG(log_debug, logtype_afpd, "afp_zzz: waking up from extended sleep");
+ dsi->flags &= ~(DSI_SLEEPING | DSI_EXTSLEEP);
+ }
+ } else {
+ /* sleep request */
+ dsi->flags |= DSI_SLEEPING;
+ if (data & AFPZZZ_EXT_SLEEP) {
+ LOG(log_debug, logtype_afpd, "afp_zzz: entering extended sleep");
+ dsi->flags |= DSI_EXTSLEEP;
+ } else {
+ LOG(log_debug, logtype_afpd, "afp_zzz: entering normal sleep");
+ }
+ }
+ /*
+ * According to AFP 3.3 spec we should not return anything,
+ * but eg 10.5.8 server still returns the numbers of hours
+ * the server is keeping the sessino (ie max sleeptime).
+ */
+ data = obj->options.sleep / 120; /* hours */
+ if (!data) {
+ data = 1;
}
- *rbuflen = sizeof(retdata);
- retdata = htonl(retdata);
- memcpy(rbuf, &retdata, sizeof(retdata));
- if (obj->sleep)
- obj->sleep();
- rbuf += sizeof(retdata);
+ *rbuflen = sizeof(data);
+ data = htonl(data);
+ memcpy(rbuf, &data, sizeof(data));
+ rbuf += sizeof(data);
+
return AFP_OK;
}
token = obj->sinfo.sessiontoken;
}
break;
- case 3: /* Jaguar */
+ case 3:
case 4:
if (ibuflen >= 8 ) {
p = ibuf;
if (ibuflen < idlen || idlen > (90-10)) {
return AFPERR_PARAM;
}
- server_ipc_write(IPC_GETSESSION, idlen+8, p );
+ ipc_child_write(obj->ipc_fd, IPC_GETSESSION, idlen+8, p);
tklen = obj->sinfo.sessiontoken_len;
token = obj->sinfo.sessiontoken;
}
}
/* ---------------------- */
-int afp_disconnect(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
+int afp_disconnect(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
{
+ DSI *dsi = (DSI *)obj->handle;
u_int16_t type;
-
u_int32_t tklen;
pid_t token;
int i;
}
}
- /* killed old session, not easy */
- server_ipc_write(IPC_KILLTOKEN, tklen, &token);
- sleep(1);
+ LOG(log_note, logtype_afpd, "afp_disconnect: trying primary reconnect");
+ dsi->flags |= DSI_RECONINPROG;
+
+ /* Deactivate tickle timer */
+ const struct itimerval none = {{0, 0}, {0, 0}};
+ 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)
+ goto exit;
+ /* write uint16_t DSI request ID */
+ if (writet(obj->ipc_fd, &dsi->header.dsi_requestID, 2, 0, 2) != 2) {
+ LOG(log_error, logtype_afpd, "afp_disconnect: couldn't send DSI request ID");
+ goto exit;
+ }
+ /* now send our connected AFP client socket */
+ if (send_fd(obj->ipc_fd, dsi->socket) != 0)
+ goto exit;
+ /* Now see what happens: either afpd master sends us SIGTERM because our session */
+ /* has been transfered to a old disconnected session, or we continue */
+ sleep(5);
+
+ if (!(dsi->flags & DSI_RECONINPROG)) { /* deleted in SIGTERM handler */
+ /* Reconnect succeeded, we exit now after sleeping some more */
+ sleep(2); /* sleep some more to give the recon. session time */
+ LOG(log_note, logtype_afpd, "afp_disconnect: primary reconnect succeeded");
+ exit(0);
+ }
+
+exit:
+ /* Reinstall tickle timer */
+ setitimer(ITIMER_REAL, &dsi->timer, NULL);
- return AFPERR_SESSCLOS; /* was AFP_OK */
+ LOG(log_error, logtype_afpd, "afp_disconnect: primary reconnect failed");
+ return AFPERR_MISC;
}
/* ---------------------- */
u_int8_t thisuser;
u_int32_t id;
u_int16_t bitmap;
+ char *bitmapp;
LOG(log_debug, logtype_afpd, "begin afp_getuserinfo:");
if ((bitmap & USERIBIT_ALL) != bitmap)
return AFPERR_BITMAP;
- /* copy the bitmap back to reply buffer */
+ /* remember place where we store the possibly modified bitmap later */
memcpy(rbuf, ibuf, sizeof(bitmap));
+ bitmapp = rbuf;
rbuf += sizeof(bitmap);
*rbuflen = sizeof(bitmap);
*rbuflen += sizeof(id);
}
-#ifdef HAVE_NFSv4_ACLS
if (bitmap & USERIBIT_UUID) {
- int ret;
- uuid_t uuid;
- char *uuidstring;
-
- if ( ! (obj->options.flags & OPTION_UUID))
- return AFPERR_BITMAP;
- LOG(log_debug, logtype_afpd, "afp_getuserinfo: get UUID for \'%s\'", obj->username);
- ret = getuuidfromname( obj->username, UUID_USER, uuid);
- if (ret != 0) {
- LOG(log_info, logtype_afpd, "afp_getuserinfo: error getting UUID !");
- return AFPERR_NOITEM;
- }
- if (0 == (uuid_bin2string( uuid, &uuidstring))) {
- LOG(log_debug, logtype_afpd, "afp_getuserinfo: got UUID: %s", uuidstring);
- free(uuidstring);
+ if ( ! (obj->options.flags & OPTION_UUID)) {
+ bitmap &= ~USERIBIT_UUID;
+ bitmap = htons(bitmap);
+ memcpy(bitmapp, &bitmap, sizeof(bitmap));
+ } else {
+ LOG(log_debug, logtype_afpd, "afp_getuserinfo: get UUID for \'%s\'", obj->username);
+ int ret;
+ atalk_uuid_t uuid;
+ ret = getuuidfromname( obj->username, UUID_USER, uuid);
+ if (ret != 0) {
+ LOG(log_info, logtype_afpd, "afp_getuserinfo: error getting UUID !");
+ return AFPERR_NOITEM;
+ }
+ LOG(log_debug, logtype_afpd, "afp_getuserinfo: got UUID: %s", uuid_bin2string(uuid));
+
+ memcpy(rbuf, uuid, UUID_BINSIZE);
+ rbuf += UUID_BINSIZE;
+ *rbuflen += UUID_BINSIZE;
}
- memcpy(rbuf, uuid, UUID_BINSIZE);
- rbuf += UUID_BINSIZE;
- *rbuflen += UUID_BINSIZE;
}
-#endif
LOG(log_debug, logtype_afpd, "END afp_getuserinfo:");
return AFP_OK;
/*
* Netatalk 2002 (c)
* Copyright (C) 1990, 1993 Regents of The University of Michigan
+ * Copyright (C) 2010 Frank Lahm
* All Rights Reserved. See COPYRIGHT
*/
*
* Initial version written by Rafal Lewczuk <rlewczuk@pronet.pl>
*
+ * Starting with Netatalk 2.2 searching by name criteria utilizes the
+ * CNID database in conjunction with an enhanced cnid_dbd. This requires
+ * the use of cnidscheme:dbd for the searched volume, the new functionality
+ * is not built into cnidscheme:cdb.
*/
#ifdef HAVE_CONFIG_H
#include <ctype.h>
#include <string.h>
#include <time.h>
-
-#if STDC_HEADERS
#include <string.h>
-#else
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif
-
#include <sys/file.h>
#include <netinet/in.h>
#include <atalk/afp.h>
#include <atalk/adouble.h>
#include <atalk/logger.h>
-#ifdef CNID_DB
#include <atalk/cnid.h>
-#endif /* CNID_DB */
+#include <atalk/cnid_dbd_private.h>
#include <atalk/util.h>
+#include <atalk/bstradd.h>
+#include <atalk/unicode.h>
#include "desktop.h"
#include "directory.h"
+#include "dircache.h"
#include "file.h"
#include "volume.h"
#include "globals.h"
/* Put new element. Allocate and copy lname and path. */
ds = dstack + dsidx++;
ds->dir = dir;
+ dir->d_flags |= DIRF_CACHELOCK;
ds->pidx = pidx;
ds->checked = 0;
if (pidx >= 0) {
while (dsidx > 0) {
if (dstack[dsidx-1].checked) {
dsidx--;
+ dstack[dsidx].dir->d_flags &= ~DIRF_CACHELOCK;
free(dstack[dsidx].path);
} else
return dsidx - 1;
save_cidx = -1;
while (dsidx > 0) {
dsidx--;
+ dstack[dsidx].dir->d_flags &= ~DIRF_CACHELOCK;
free(dstack[dsidx].path);
}
}
#define VETO_STR \
"./../.AppleDouble/.AppleDB/Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/.AppleDesktop/.Parent/"
-/* This function performs search. It is called directly from afp_catsearch
- * vol - volume we are searching on ...
- * dir - directory we are starting from ...
- * c1, c2 - search criteria
- * rmatches - maximum number of matches we can return
- * pos - position we've stopped recently
- * rbuf - output buffer
- * rbuflen - output buffer length
+/*!
+ * This function performs a filesystem search
+ *
+ * Uses globals c1, c2, the search criteria
+ *
+ * @param vol (r) volume we are searching on ...
+ * @param dir (rw) directory we are starting from ...
+ * @param rmatches (r) maximum number of matches we can return
+ * @param pos (r) position we've stopped recently
+ * @param rbuf (w) output buffer
+ * @param nrecs (w) number of matches
+ * @param rsize (w) length of data written to output buffer
+ * @param ext (r) extended search flag
*/
#define NUM_ROUNDS 200
-static int catsearch(struct vol *vol, struct dir *dir,
- int rmatches, u_int32_t *pos, char *rbuf, u_int32_t *nrecs, int *rsize, int ext)
+static int catsearch(struct vol *vol,
+ struct dir *dir,
+ int rmatches,
+ uint32_t *pos,
+ char *rbuf,
+ uint32_t *nrecs,
+ int *rsize,
+ int ext)
{
static u_int32_t cur_pos; /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */
static DIR *dirpos; /* UNIX structure describing currently opened directory. */
char *rrbuf = rbuf;
time_t start_time;
int num_rounds = NUM_ROUNDS;
- int cached;
int cwd = -1;
int error;
start_time = time(NULL);
while ((cidx = reducestack()) != -1) {
- cached = 1;
-
error = lchdir(dstack[cidx].path);
- if (!error && dirpos == NULL) {
+ if (!error && dirpos == NULL)
dirpos = opendir(".");
- cached = (dstack[cidx].dir->d_child != NULL);
- }
+
+ if (dirpos == NULL)
+ dirpos = opendir(dstack[cidx].path);
+
if (error || dirpos == NULL) {
switch (errno) {
case EACCES:
ie if in the same loop the parent dir wasn't in the cache
ALL dirsearch_byname will fail.
*/
- if (cached)
- path.d_dir = dirsearch_byname(vol, dstack[cidx].dir, path.u_name);
- else
- path.d_dir = NULL;
- if (!path.d_dir) {
+ int unlen = strlen(path.u_name);
+ path.d_dir = dircache_search_by_name(vol, dstack[cidx].dir, path.u_name, unlen, path.st.st_ctime);
+ if (path.d_dir == NULL) {
/* path.m_name is set by adddir */
- if (NULL == (path.d_dir = adddir( vol, dstack[cidx].dir, &path) ) ) {
+ if (NULL == (path.d_dir = dir_add( vol, dstack[cidx].dir, &path, unlen) ) ) {
result = AFPERR_MISC;
goto catsearch_end;
}
}
- path.m_name = path.d_dir->d_m_name;
+ path.m_name = cfrombstr(path.d_dir->d_m_name);
if (addstack(path.u_name, path.d_dir, cidx) == -1) {
result = AFPERR_MISC;
return result;
} /* catsearch() */
+/*!
+ * This function performs a CNID db search
+ *
+ * Uses globals c1, c2, the search criteria
+ *
+ * @param vol (r) volume we are searching on ...
+ * @param dir (rw) directory we are starting from ...
+ * @param uname (r) UNIX name of object to search
+ * @param rmatches (r) maximum number of matches we can return
+ * @param pos (r) position we've stopped recently
+ * @param rbuf (w) output buffer
+ * @param nrecs (w) number of matches
+ * @param rsize (w) length of data written to output buffer
+ * @param ext (r) extended search flag
+ */
+static int catsearch_db(struct vol *vol,
+ struct dir *dir,
+ const char *uname,
+ int rmatches,
+ uint32_t *pos,
+ char *rbuf,
+ uint32_t *nrecs,
+ int *rsize,
+ int ext)
+{
+ static char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)];
+ static uint32_t cur_pos;
+ static int num_matches;
+ int ccr ,r;
+ int result = AFP_OK;
+ struct path path;
+ char *rrbuf = rbuf;
+ char buffer[MAXPATHLEN +2];
+ uint16_t flags = CONV_TOLOWER;
+
+ LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u, name: %s}",
+ *pos, cur_pos, uname);
+
+ if (*pos != 0 && *pos != cur_pos) {
+ result = AFPERR_CATCHNG;
+ goto catsearch_end;
+ }
+
+ if (cur_pos == 0 || *pos == 0) {
+ if (convert_charset(vol->v_volcharset,
+ vol->v_volcharset,
+ vol->v_maccharset,
+ uname,
+ strlen(uname),
+ buffer,
+ MAXPATHLEN,
+ &flags) == (size_t)-1) {
+ LOG(log_error, logtype_cnid, "catsearch_db: conversion error");
+ result = AFPERR_MISC;
+ goto catsearch_end;
+ }
+
+ LOG(log_debug, logtype_afpd, "catsearch_db: %s", buffer);
+
+ if ((num_matches = cnid_find(vol->v_cdb,
+ buffer,
+ strlen(uname),
+ resbuf,
+ sizeof(resbuf))) == -1) {
+ result = AFPERR_MISC;
+ goto catsearch_end;
+ }
+ }
+
+ while (cur_pos < num_matches) {
+ char *name;
+ cnid_t cnid, did;
+ char resolvebuf[12 + MAXPATHLEN + 1];
+ struct dir *dir;
+
+ /* Next CNID to process from buffer */
+ memcpy(&cnid, resbuf + cur_pos * sizeof(cnid_t), sizeof(cnid_t));
+ did = cnid;
+
+ if ((name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1)) == NULL)
+ goto next;
+ LOG(log_debug, logtype_afpd, "catsearch_db: {pos: %u, name:%s, cnid: %u}",
+ cur_pos, name, ntohl(cnid));
+ if ((dir = dirlookup(vol, did)) == NULL)
+ goto next;
+ if (movecwd(vol, dir) < 0 )
+ goto next;
+
+ memset(&path, 0, sizeof(path));
+ path.u_name = name;
+ path.m_name = utompath(vol, name, cnid, utf8_encoding());
+
+ if (of_stat(&path) != 0) {
+ switch (errno) {
+ case EACCES:
+ case ELOOP:
+ goto next;
+ case ENOENT:
+
+ default:
+ result = AFPERR_MISC;
+ goto catsearch_end;
+ }
+ }
+ /* For files path.d_dir is the parent dir, for dirs its the dir itself */
+ if (S_ISDIR(path.st.st_mode))
+ if ((dir = dirlookup(vol, cnid)) == NULL)
+ goto next;
+ path.d_dir = dir;
+
+ LOG(log_maxdebug, logtype_afpd,"catsearch_db: dir: %s, cwd: %s, name: %s",
+ cfrombstr(dir->d_fullpath), getcwdpath(), path.u_name);
+
+ /* At last we can check the search criteria */
+ ccr = crit_check(vol, &path);
+ if ((ccr & 1)) {
+ LOG(log_debug, logtype_afpd,"catsearch_db: match: %s/%s",
+ getcwdpath(), path.u_name);
+ /* bit 1 means that criteria has been met */
+ r = rslt_add(vol, &path, &rrbuf, ext);
+ if (r == 0) {
+ result = AFPERR_MISC;
+ goto catsearch_end;
+ }
+ *nrecs += r;
+ /* Number of matches limit */
+ if (--rmatches == 0)
+ goto catsearch_pause;
+ /* Block size limit */
+ if (rrbuf - rbuf >= 448)
+ goto catsearch_pause;
+ }
+ next:
+ cur_pos++;
+ } /* while */
+
+ /* finished */
+ result = AFPERR_EOF;
+ cur_pos = 0;
+ goto catsearch_end;
+
+catsearch_pause:
+ *pos = cur_pos;
+
+catsearch_end: /* Exiting catsearch: error condition */
+ *rsize = rrbuf - rbuf;
+ LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u}", *pos, cur_pos);
+ return result;
+}
+
/* -------------------------- */
static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
char *rbuf, size_t *rbuflen, int ext)
size_t len;
u_int16_t namelen;
u_int16_t flags;
- char tmppath[256];
+ char tmppath[256];
+ char *uname;
*rbuflen = 0;
namelen = 255;
memcpy (c1.utf8name, spec1+2, namelen);
- c1.utf8name[(namelen+1)] =0;
+ c1.utf8name[namelen] = 0;
+ if ((uname = mtoupath(vol, c1.utf8name, 0, utf8_encoding())) == NULL)
+ return AFPERR_PARAM;
/* convert charset */
flags = CONV_PRECOMPOSE;
/* Call search */
*rbuflen = 24;
- ret = catsearch(vol, vol->v_dir, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+ if ((c1.rbitmap & (1 << FILPBIT_PDINFO))
+ && (strcmp(vol->v_cnidscheme, "dbd") == 0)
+ && (vol->v_flags & AFPVOL_SEARCHDB))
+ /* we've got a name and it's a dbd volume, so search CNID database */
+ ret = catsearch_db(vol, vol->v_root, uname, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+ else
+ /* perform a slow filesystem tree search */
+ ret = catsearch(vol, vol->v_root, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+
memcpy(rbuf, catpos, sizeof(catpos));
rbuf += sizeof(catpos);
/*
- * $Id: desktop.c,v 1.50 2010-01-22 04:40:38 didg Exp $
+ * $Id: desktop.c,v 1.50.2.1 2010-02-01 10:56:08 franklahm Exp $
*
* See COPYRIGHT.
*
u_int16_t flags;
if ( *mpath == '\0' ) {
- return( "." );
+ strcpy(upath, ".");
+ return upath;
}
/* set conversion flags */
if (ad_getentryoff(adp, ADEID_COMMENT)) {
if ( (ad_get_MD_flags( adp ) & O_CREAT) ) {
if ( *path->m_name == '\0' ) {
- name = curdir->d_m_name;
+ name = (char *)curdir->d_m_name->data;
} else {
name = path->m_name;
}
--- /dev/null
+/*
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+
+#include "dircache.h"
+#include "directory.h"
+#include "hash.h"
+#include "globals.h"
+
+/*
+ * Directory Cache
+ * ===============
+ *
+ * Cache files and directories in a LRU cache.
+ *
+ * The directory cache caches directories and files(!). The main reason for having the cache
+ * is avoiding recursive walks up the path, querying the CNID database each time, when
+ * we have to calculate the location of eg directory with CNID 30, which is located in a dir with
+ * CNID 25, next CNID 20 and then CNID 2 (the volume root as per AFP spec).
+ * If all these dirs where in the cache, each database look up can be avoided. Additionally there's
+ * the element "fullpath" in struct dir, which is used to avoid the recursion in any case. Wheneveer
+ * 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.
+ *
+ * The most frequent codepatch that leads to caching is directory enumeration (cf enumerate.c):
+ * - if a element is a directory:
+ * (1) the cache is searched by dircache_search_by_name()
+ * (2) if it wasn't found a new struct dir is created and cached both from within dir_add()
+ * - for files the caching happens a little bit down the call chain:
+ * (3) first getfilparams() is called, which calls
+ * (4) getmetadata() where the cache is searched with dircache_search_by_name()
+ * (5) if the element is not found
+ * (6) get_id() queries the CNID from the database
+ * (7) then a struct dir is initialized via dir_new() (note the fullpath arg is NULL)
+ * (8) finally added to the cache with dircache_add()
+ * (2) of course does contain the steps 6,7 and 8.
+ *
+ * The dircache is a LRU cache, whenever it fills up we call dircache_evict internally which removes
+ * DIRCACHE_FREE_QUANTUM elements from the cache.
+ *
+ * There is only one cache for all volumes, so of course we use the volume id in hashing calculations.
+ *
+ * In order to avoid cache poisoning, we store the cached entries st_ctime from stat in
+ * struct dir.ctime_dircache. Later when we search the cache we compare the stored
+ * value with the result of a fresh stat. If the times differ, we remove the cached
+ * entry and return "no entry found in cache".
+ * A elements ctime changes when
+ * 1) the element is renamed
+ * (we loose the cached entry here, but it will expire when the cache fills)
+ * 2) its a directory and an object has been created therein
+ * 3) the element is deleted and recreated under the same name
+ * Using ctime leads to cache eviction in case 2) where it wouldn't be necessary, because
+ * the dir itself (name, CNID, ...) hasn't changed, but there's no other way.
+ *
+ * Indexes
+ * =======
+ *
+ * The maximum dircache size is:
+ * max(DEFAULT_MAX_DIRCACHE_SIZE, min(size, MAX_POSSIBLE_DIRCACHE_SIZE)).
+ * It is a hashtable which we use to store "struct dir"s in. If the cache get full, oldest
+ * entries are evicted in chunks of DIRCACHE_FREE.
+ *
+ * We have/need two indexes:
+ * - a DID/name index on the main dircache, another hashtable
+ * - a queue index on the dircache, for evicting the oldest entries
+ * The cache supports locking of struct dir elements through the DIRF_CACHELOCK flag. A dir
+ * locked this way wont ever be removed from the cache, so be careful.
+ *
+ * Debugging
+ * =========
+ *
+ * Sending SIGHUP to a afpd child causes it to dump the dircache to a file "/tmp/dircache.PID".
+ */
+
+/********************************************************
+ * Local funcs and variables
+ ********************************************************/
+
+/*****************************
+ * the dircache */
+
+static hash_t *dircache; /* The actual cache */
+static unsigned int dircache_maxsize; /* cache maximum size */
+
+static struct dircache_stat {
+ unsigned long long lookups;
+ unsigned long long hits;
+ unsigned long long misses;
+ unsigned long long added;
+ unsigned long long removed;
+ unsigned long long expunged;
+ unsigned long long evicted;
+} dircache_stat;
+
+/* FNV 1a */
+static hash_val_t hash_vid_did(const void *key)
+{
+ const struct dir *k = (const struct dir *)key;
+ hash_val_t hash = 2166136261;
+
+ hash ^= k->d_vid >> 8;
+ hash *= 16777619;
+ hash ^= k->d_vid;
+ hash *= 16777619;
+
+ hash ^= k->d_did >> 24;
+ hash *= 16777619;
+ hash ^= (k->d_did >> 16) & 0xff;
+ hash *= 16777619;
+ hash ^= (k->d_did >> 8) & 0xff;
+ hash *= 16777619;
+ hash ^= (k->d_did >> 0) & 0xff;
+ hash *= 16777619;
+
+ return hash;
+}
+
+static int hash_comp_vid_did(const void *key1, const void *key2)
+{
+ const struct dir *k1 = key1;
+ const struct dir *k2 = key2;
+
+ return !(k1->d_did == k2->d_did && k1->d_vid == k2->d_vid);
+}
+
+/**************************************************
+ * DID/name index on dircache (another hashtable) */
+
+static hash_t *index_didname;
+
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+static hash_val_t hash_didname(const void *p)
+{
+ const struct dir *key = (const struct dir *)p;
+ const unsigned char *data = key->d_u_name->data;
+ int len = key->d_u_name->slen;
+ hash_val_t hash = key->d_pdid + key->d_vid;
+ hash_val_t tmp;
+
+ int rem = len & 3;
+ len >>= 2;
+
+ /* Main loop */
+ for (;len > 0; len--) {
+ hash += get16bits (data);
+ tmp = (get16bits (data+2) << 11) ^ hash;
+ hash = (hash << 16) ^ tmp;
+ data += 2*sizeof (uint16_t);
+ hash += hash >> 11;
+ }
+
+ /* Handle end cases */
+ switch (rem) {
+ case 3: hash += get16bits (data);
+ hash ^= hash << 16;
+ hash ^= data[sizeof (uint16_t)] << 18;
+ hash += hash >> 11;
+ break;
+ case 2: hash += get16bits (data);
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ break;
+ case 1: hash += *data;
+ hash ^= hash << 10;
+ hash += hash >> 1;
+ }
+
+ /* Force "avalanching" of final 127 bits */
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+
+ return hash;
+}
+
+static int hash_comp_didname(const void *k1, const void *k2)
+{
+ const struct dir *key1 = (const struct dir *)k1;
+ const struct dir *key2 = (const struct dir *)k2;
+
+ return ! (key1->d_vid == key2->d_vid
+ && key1->d_pdid == key2->d_pdid
+ && (bstrcmp(key1->d_u_name, key2->d_u_name) == 0) );
+}
+
+/***************************
+ * queue index on dircache */
+
+static q_t *index_queue; /* the index itself */
+static unsigned long queue_count;
+
+/*!
+ * @brief Remove a fixed number of (oldest) entries from the cache and indexes
+ *
+ * The default is to remove the 256 oldest entries from the cache.
+ * 1. Get the oldest entry
+ * 2. If it's in use ie open forks reference it or it's curdir requeue it,
+ * or it's locked (from catsearch) dont remove it
+ * 3. Remove the dir from the main cache and the didname index
+ * 4. Free the struct dir structure and all its members
+ */
+static void dircache_evict(void)
+{
+ int i = DIRCACHE_FREE_QUANTUM;
+ struct dir *dir;
+
+ LOG(log_debug, logtype_afpd, "dircache: {starting cache eviction}");
+
+ while (i--) {
+ if ((dir = (struct dir *)dequeue(index_queue)) == NULL) { /* 1 */
+ dircache_dump();
+ AFP_PANIC("dircache_evict");
+ }
+ queue_count--;
+
+ if (curdir == dir
+ || (dir->d_flags & DIRF_CACHELOCK)) { /* 2 */
+ if ((dir->qidx_node = enqueue(index_queue, dir)) == NULL) {
+ dircache_dump();
+ AFP_PANIC("dircache_evict");
+ }
+ queue_count++;
+ continue;
+ }
+
+ dircache_remove(NULL, dir, DIRCACHE | DIDNAME_INDEX); /* 3 */
+ dir_free(dir); /* 4 */
+ }
+
+ AFP_ASSERT(queue_count == dircache->hash_nodecount);
+ dircache_stat.evicted += DIRCACHE_FREE_QUANTUM;
+ LOG(log_debug, logtype_afpd, "dircache: {finished cache eviction}");
+}
+
+
+/********************************************************
+ * Interface
+ ********************************************************/
+
+/*!
+ * @brief Search the dircache via a CNID for a directory
+ *
+ * Found cache entries are expunged if both the parent directory st_ctime and the objects
+ * st_ctime are modified.
+ * 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
+ * (1a) remove it from the cache
+ * (1b) return NULL indicating nothing found
+ * (2) we can then use d_fullpath to stat the directory
+ *
+ * @param vol (r) pointer to struct vol
+ * @param cnid (r) CNID of the directory to search
+ *
+ * @returns Pointer to struct dir if found, else NULL
+ */
+struct dir *dircache_search_by_did(const struct vol *vol, cnid_t cnid)
+{
+ struct dir *cdir = NULL;
+ struct dir key;
+ struct stat st;
+ hnode_t *hn;
+
+ AFP_ASSERT(vol);
+ AFP_ASSERT(ntohl(cnid) >= CNID_START);
+
+ dircache_stat.lookups++;
+ key.d_vid = vol->v_vid;
+ key.d_did = cnid;
+ if ((hn = hash_lookup(dircache, &key)))
+ cdir = hnode_get(hn);
+
+ if (cdir) {
+ if (cdir->d_fullpath == NULL) { /* (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; /* (1b) */
+
+ }
+ if (lstat(cfrombstr(cdir->d_fullpath), &st) != 0) {
+ LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {missing:\"%s\"}",
+ ntohl(cnid), cfrombstr(cdir->d_fullpath));
+ (void)dir_remove(vol, cdir);
+ dircache_stat.expunged++;
+ return NULL;
+ }
+ if (cdir->ctime_dircache != st.st_ctime) {
+ LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {modified:\"%s\"}",
+ ntohl(cnid), cfrombstr(cdir->d_u_name));
+ (void)dir_remove(vol, cdir);
+ dircache_stat.expunged++;
+ return NULL;
+ }
+ LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {cached: path:\"%s\"}",
+ ntohl(cnid), cfrombstr(cdir->d_fullpath));
+ dircache_stat.hits++;
+ } else {
+ LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {not in cache}", ntohl(cnid));
+ dircache_stat.misses++;
+ }
+
+ return cdir;
+}
+
+/*!
+ * @brief Search the cache via did/name hashtable
+ *
+ * Found cache entries are expunged if both the parent directory st_ctime and the objects
+ * st_ctime are modified.
+ *
+ * @param vol (r) volume
+ * @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)
+{
+ struct dir *cdir = NULL;
+ struct dir key;
+
+ hnode_t *hn;
+ static_bstring uname = {-1, len, (unsigned char *)name};
+
+ AFP_ASSERT(vol);
+ AFP_ASSERT(dir);
+ AFP_ASSERT(name);
+ AFP_ASSERT(len == strlen(name));
+ AFP_ASSERT(len < 256);
+
+ dircache_stat.lookups++;
+ LOG(log_debug, logtype_afpd, "dircache_search_by_name(did:%u, \"%s\")",
+ ntohl(dir->d_did), name);
+
+ if (dir->d_did != DIRDID_ROOT_PARENT) {
+ key.d_vid = vol->v_vid;
+ key.d_pdid = dir->d_did;
+ key.d_u_name = &uname;
+
+ if ((hn = hash_lookup(index_didname, &key)))
+ cdir = hnode_get(hn);
+ }
+
+ if (cdir) {
+ if (cdir->ctime_dircache != ctime) {
+ LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {modified}",
+ ntohl(dir->d_did), name);
+ (void)dir_remove(vol, cdir);
+ dircache_stat.expunged++;
+ return NULL;
+ }
+ LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {found in cache}",
+ ntohl(dir->d_did), name);
+ dircache_stat.hits++;
+ } else {
+ LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {not in cache}",
+ ntohl(dir->d_did), name);
+ dircache_stat.misses++;
+ }
+
+ return cdir;
+}
+
+/*!
+ * @brief create struct dir from struct path
+ *
+ * Add a struct dir to the cache and its indexes.
+ *
+ * @param dir (r) pointer to parrent directory
+ *
+ * @returns 0 on success, -1 on error which should result in an abort
+ */
+int dircache_add(struct dir *dir)
+{
+ AFP_ASSERT(dir);
+ AFP_ASSERT(ntohl(dir->d_pdid) >= 2);
+ AFP_ASSERT(ntohl(dir->d_did) >= CNID_START);
+ AFP_ASSERT(dir->d_u_name);
+ AFP_ASSERT(dir->d_vid);
+ AFP_ASSERT(dircache->hash_nodecount <= dircache_maxsize);
+
+ /* Check if cache is full */
+ if (dircache->hash_nodecount == dircache_maxsize)
+ dircache_evict();
+
+ /* Add it to the main dircache */
+ if (hash_alloc_insert(dircache, dir, dir) == 0) {
+ dircache_dump();
+ exit(EXITERR_SYS);
+ }
+
+ /* Add it to the did/name index */
+ if (hash_alloc_insert(index_didname, dir, dir) == 0) {
+ dircache_dump();
+ exit(EXITERR_SYS);
+ }
+
+ /* Add it to the fifo queue index */
+ if ((dir->qidx_node = enqueue(index_queue, dir)) == NULL) {
+ dircache_dump();
+ exit(EXITERR_SYS);
+ } else {
+ queue_count++;
+ }
+
+ dircache_stat.added++;
+ LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {added}",
+ ntohl(dir->d_did), cfrombstr(dir->d_u_name));
+
+ AFP_ASSERT(queue_count == index_didname->hash_nodecount
+ && queue_count == dircache->hash_nodecount);
+
+ return 0;
+}
+
+/*!
+ * @brief Remove an entry from the dircache
+ *
+ * Callers outside of dircache.c should call this with
+ * flags = QUEUE_INDEX | DIDNAME_INDEX | DIRCACHE.
+ */
+void dircache_remove(const struct vol *vol _U_, struct dir *dir, int flags)
+{
+ hnode_t *hn;
+
+ AFP_ASSERT(dir);
+ AFP_ASSERT((flags & ~(QUEUE_INDEX | DIDNAME_INDEX | DIRCACHE)) == 0);
+
+ if (dir->d_flags & DIRF_CACHELOCK)
+ return;
+
+ if (flags & QUEUE_INDEX) {
+ /* remove it from the queue index */
+ dequeue(dir->qidx_node->prev); /* this effectively deletes the dequeued node */
+ queue_count--;
+ }
+
+ if (flags & DIDNAME_INDEX) {
+ if ((hn = hash_lookup(index_didname, dir)) == NULL) {
+ LOG(log_error, logtype_default, "dircache_remove(%u,\"%s\"): not in didname index",
+ ntohl(dir->d_did), cfrombstr(dir->d_u_name));
+ dircache_dump();
+ AFP_PANIC("dircache_remove");
+ }
+ hash_delete_free(index_didname, hn);
+ }
+
+ if (flags & DIRCACHE) {
+ if ((hn = hash_lookup(dircache, dir)) == NULL) {
+ LOG(log_error, logtype_default, "dircache_remove(%u,\"%s\"): not in dircache",
+ ntohl(dir->d_did), cfrombstr(dir->d_u_name));
+ dircache_dump();
+ AFP_PANIC("dircache_remove");
+ }
+ hash_delete_free(dircache, hn);
+ }
+
+ LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {removed}",
+ ntohl(dir->d_did), cfrombstr(dir->d_u_name));
+
+ dircache_stat.removed++;
+ AFP_ASSERT(queue_count == index_didname->hash_nodecount
+ && queue_count == dircache->hash_nodecount);
+}
+
+/*!
+ * @brief Initialize the dircache and indexes
+ *
+ * This is called in child afpd initialisation. The maximum cache size will be
+ * max(DEFAULT_MAX_DIRCACHE_SIZE, min(size, MAX_POSSIBLE_DIRCACHE_SIZE)).
+ * It initializes a hashtable which we use to store a directory cache in.
+ * It also initializes two indexes:
+ * - a DID/name index on the main dircache
+ * - a queue index on the dircache
+ *
+ * @param size (r) requested maximum size from afpd.conf
+ *
+ * @return 0 on success, -1 on error
+ */
+int dircache_init(int reqsize)
+{
+ dircache_maxsize = DEFAULT_MAX_DIRCACHE_SIZE;
+
+ /* Initialize the main dircache */
+ if (reqsize > DEFAULT_MAX_DIRCACHE_SIZE && reqsize < MAX_POSSIBLE_DIRCACHE_SIZE) {
+ while ((dircache_maxsize < MAX_POSSIBLE_DIRCACHE_SIZE) && (dircache_maxsize < reqsize))
+ dircache_maxsize *= 2;
+ }
+ if ((dircache = hash_create(dircache_maxsize, hash_comp_vid_did, hash_vid_did)) == NULL)
+ return -1;
+
+ LOG(log_debug, logtype_afpd, "dircache_init: done. max dircache size: %u", dircache_maxsize);
+
+ /* Initialize did/name index hashtable */
+ if ((index_didname = hash_create(dircache_maxsize, hash_comp_didname, hash_didname)) == NULL)
+ return -1;
+
+ /* Initialize index queue */
+ if ((index_queue = queue_init()) == NULL)
+ return -1;
+ else
+ queue_count = 0;
+
+ /* Initialize index queue */
+ if ((invalid_dircache_entries = queue_init()) == NULL)
+ return -1;
+
+ /* As long as directory.c hasn't got its own initializer call, we do it for it */
+ rootParent.d_did = DIRDID_ROOT_PARENT;
+ rootParent.d_fullpath = bfromcstr("ROOT_PARENT");
+ rootParent.d_m_name = bfromcstr("ROOT_PARENT");
+ rootParent.d_u_name = rootParent.d_m_name;
+
+ return 0;
+}
+
+/*!
+ * Log dircache statistics
+ */
+void log_dircache_stat(void)
+{
+ LOG(log_info, logtype_afpd, "dircache statistics: "
+ "entries: %lu, lookups: %llu, hits: %llu, misses: %llu, added: %llu, removed: %llu, expunged: %llu, evicted: %llu",
+ queue_count,
+ dircache_stat.lookups,
+ dircache_stat.hits,
+ dircache_stat.misses,
+ dircache_stat.added,
+ dircache_stat.removed,
+ dircache_stat.expunged,
+ dircache_stat.evicted);
+}
+
+/*!
+ * @brief Dump dircache to /tmp/dircache.PID
+ */
+void dircache_dump(void)
+{
+ char tmpnam[64];
+ FILE *dump;
+ qnode_t *n = index_queue->next;
+ hnode_t *hn;
+ hscan_t hs;
+ const struct dir *dir;
+ int i;
+
+ LOG(log_warning, logtype_afpd, "Dumping directory cache...");
+
+ sprintf(tmpnam, "/tmp/dircache.%u", getpid());
+ if ((dump = fopen(tmpnam, "w+")) == NULL) {
+ LOG(log_error, logtype_afpd, "dircache_dump: %s", strerror(errno));
+ return;
+ }
+ setbuf(dump, NULL);
+
+ fprintf(dump, "Number of cache entries in LRU queue: %lu\n", queue_count);
+ fprintf(dump, "Configured maximum cache size: %u\n\n", dircache_maxsize);
+
+ fprintf(dump, "Primary CNID index:\n");
+ fprintf(dump, " VID DID CNID STAT PATH\n");
+ fprintf(dump, "====================================================================\n");
+ hash_scan_begin(&hs, dircache);
+ i = 1;
+ while ((hn = hash_scan_next(&hs))) {
+ dir = hnode_get(hn);
+ fprintf(dump, "%05u: %3u %6u %6u %s%s %s\n",
+ i++,
+ ntohs(dir->d_vid),
+ ntohl(dir->d_pdid),
+ ntohl(dir->d_did),
+ dir->d_fullpath ? "d" : "f",
+ (dir->d_flags & DIRF_CACHELOCK) ? "l" : "-",
+ cfrombstr(dir->d_u_name));
+ }
+
+ fprintf(dump, "\nSecondary DID/name index:\n");
+ fprintf(dump, " VID DID CNID STAT PATH\n");
+ fprintf(dump, "====================================================================\n");
+ hash_scan_begin(&hs, index_didname);
+ i = 1;
+ while ((hn = hash_scan_next(&hs))) {
+ dir = hnode_get(hn);
+ fprintf(dump, "%05u: %3u %6u %6u %s%s %s\n",
+ i++,
+ ntohs(dir->d_vid),
+ ntohl(dir->d_pdid),
+ ntohl(dir->d_did),
+ dir->d_fullpath ? "d" : "f",
+ (dir->d_flags & DIRF_CACHELOCK) ? "l" : "-",
+ cfrombstr(dir->d_u_name));
+ }
+
+ fprintf(dump, "\nLRU Queue:\n");
+ fprintf(dump, " VID DID CNID STAT PATH\n");
+ fprintf(dump, "====================================================================\n");
+
+ for (i = 1; i <= queue_count; i++) {
+ if (n == index_queue)
+ break;
+ dir = (struct dir *)n->data;
+ fprintf(dump, "%05u: %3u %6u %6u %s%s %s\n",
+ i,
+ ntohs(dir->d_vid),
+ ntohl(dir->d_pdid),
+ ntohl(dir->d_did),
+ dir->d_fullpath ? "d" : "f",
+ (dir->d_flags & DIRF_CACHELOCK) ? "l" : "-",
+ cfrombstr(dir->d_u_name));
+ n = n->next;
+ }
+
+ fprintf(dump, "\n");
+ return;
+}
--- /dev/null
+/*
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ */
+
+#ifndef DIRCACHE_H
+#define DIRCACHE_H
+
+#include <sys/types.h>
+
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+
+/* Maximum size of the dircache hashtable */
+#define DEFAULT_MAX_DIRCACHE_SIZE 8192
+#define MAX_POSSIBLE_DIRCACHE_SIZE 131072
+#define DIRCACHE_FREE_QUANTUM 256
+
+/* flags for dircache_remove */
+#define DIRCACHE (1 << 0)
+#define DIDNAME_INDEX (1 << 1)
+#define QUEUE_INDEX (1 << 2)
+#define DIRCACHE_ALL (DIRCACHE|DIDNAME_INDEX|QUEUE_INDEX)
+
+extern int dircache_init(int reqsize);
+extern int dircache_add(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 void dircache_dump(void);
+extern void log_dircache_stat(void);
+#endif /* DIRCACHE_H */
/*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
- *
- * 19 jan 2000 implemented red-black trees for directory lookups
- * (asun@cobalt.com).
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
-/* STDC check */
-#if STDC_HEADERS
#include <string.h>
-#else /* STDC_HEADERS */
-#ifndef HAVE_STRCHR
-#define strchr index
-#define strrchr index
-#endif /* HAVE_STRCHR */
-char *strchr (), *strrchr ();
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif /* STDC_HEADERS */
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
#include <stdio.h>
#include <stdlib.h>
-
#include <grp.h>
#include <pwd.h>
#include <sys/param.h>
+#include <sys/stat.h>
#include <errno.h>
#include <utime.h>
+#include <assert.h>
#include <atalk/adouble.h>
#include <atalk/vfs.h>
#include <atalk/logger.h>
#include <atalk/uuid.h>
#include <atalk/unix.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
#include "directory.h"
+#include "dircache.h"
#include "desktop.h"
#include "volume.h"
#include "fork.h"
#include "mangle.h"
#include "hash.h"
-#ifdef HAVE_NFSv4_ACLS
-extern void addir_inherit_acl(const struct vol *vol);
-#endif
-
-/*
- * Directory caches
- * ================
- *
- * There are currently two cache structures where afpd caches directory information
- * a) a DID/dirname cache in a hashtable
- * b) a (red-black) tree with CNIDs as key
- *
- * a) is for searching by DID/dirname
- * b) is for searching by CNID
- *
- * Through additional parent, child, previous and next pointers, b) is also used to
- * represent the on-disk layout of the filesystem. parent and child point to parent
- * and child directory respectively, linking 2 or more subdirectories in one
- * directory with previous and next pointers.
- *
- * Usage examples, highlighting the main functions:
- *
- * a) is eg used in enumerate():
- * if IS_DIR
- * dir = dirsearch_byname() // search in cache
- * if (dir == NULL) // not found
- * dir = adddir() // add to cache
- * getdirparams()
- *
- * b) is eg used in afp_getfildirparams()
- * dirlookup() // wrapper for cache and db search
- * => dir = dirsearch() // search in cache
- * if (dir) // found
- * return
- * else // not found,
- * cnid_resolve() // resolve with CNID database
- * cname() // add to cache
+/*
+ * FIXMEs, loose ends after the dircache rewrite:
+ * o merge dircache_search_by_name and dir_add ??
+ * o case-insensitivity is gone from cname
*/
-struct dir *curdir;
+
+/*******************************************************************************************
+ * Globals
+ ******************************************************************************************/
+
int afp_errno;
+/* As long as directory.c hasn't got its own init call, this get initialized in dircache_init */
+struct dir rootParent = {
+ NULL, NULL, NULL, NULL, /* path, d_m_name, d_u_name, d_m_name_ucs2 */
+ NULL, 0, 0, /* qidx_node, ctime, d_flags */
+ 0, 0, 0, 0 /* pdid, did, offcnt, d_vid */
+};
+struct dir *curdir = &rootParent;
+struct path Cur_Path = {
+ 0,
+ "", /* mac name */
+ ".", /* unix name */
+ 0, /* id */
+ NULL,/* struct dir * */
+ 0, /* stat is not set */
+ 0, /* errno */
+ {0} /* struct stat */
+};
-#define SENTINEL (&sentinel)
-static struct dir sentinel = { SENTINEL, SENTINEL, NULL, /* left, right, back */
- DIRTREE_COLOR_BLACK, /* color */
- NULL, NULL, /* parent, child */
- NULL, NULL, /* previous, next */
- NULL, 0, 0, /* oforks, did, flags */
- 0, 0, /* ctime, offcnt */
- NULL, NULL, NULL}; /* mname, uname, ucs2-name */
-static struct dir rootpar = { SENTINEL, SENTINEL, NULL,
- 0,
- NULL, NULL,
- NULL, NULL,
- NULL, 0, 0,
- 0, 0,
- NULL, NULL, NULL};
-
-/* (from IM: Toolbox Essentials)
- * dirFinderInfo (DInfo) fields:
- * field bytes
- * frRect 8 folder's window rectangle
- * frFlags 2 flags
- * frLocation 4 folder's location in window
- * frView 2 folder's view (default == closedView (256))
- *
- * extended dirFinderInfo (DXInfo) fields:
- * frScroll 4 scroll position
- * frOpenChain: 4 directory ID chain of open folders
- * frScript: 1 script flag and code
- * frXFlags: 1 reserved
- * frComment: 2 comment ID
- * frPutAway: 4 home directory ID
+/*
+ * dir_remove queues struct dirs to be freed here. We can't just delete them immeidately
+ * eg in dircache_search_by_id, because a caller somewhere up the stack might be
+ * referencing it.
+ * So instead:
+ * - we mark it as invalid by setting d_did to CNID_INVALID (ie 0)
+ * - queue it in "invalid_dircache_entries" queue
+ * - which is finally freed at the end of every AFP func in afp_dsi.c.
*/
+q_t *invalid_dircache_entries;
-static struct dir *
-vol_tree_root(const struct vol *vol, u_int32_t did)
-{
- struct dir *dir;
- if (vol->v_curdir && vol->v_curdir->d_did == did) {
- dir = vol->v_curdir;
- }
- else {
- dir = vol->v_root;
- }
- return dir;
-}
+/*******************************************************************************************
+ * Locals
+ ******************************************************************************************/
-/*
- * redid did assignment for directories. now we use red-black trees.
- * how exciting.
- */
-struct dir *
-dirsearch(const struct vol *vol, u_int32_t did)
+
+/* -------------------------
+ appledouble mkdir afp error code.
+*/
+static int netatalk_mkdir(const struct vol *vol, const char *name)
{
- struct dir *dir;
+ int ret;
+ struct stat st;
+ if (vol->v_flags & AFPVOL_UNIX_PRIV) {
+ if (lstat(".", &st) < 0)
+ return AFPERR_MISC;
+ int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask);
+ LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}",
+ name, st.st_mode, vol->v_umask);
- /* check for 0 did */
- if (!did) {
- afp_errno = AFPERR_PARAM;
- return NULL;
- }
- if ( did == DIRDID_ROOT_PARENT ) {
- if (!rootpar.d_did)
- rootpar.d_did = DIRDID_ROOT_PARENT;
- rootpar.d_child = vol->v_dir;
- return( &rootpar );
+ ret = mkdir(name, mode);
+ } else {
+ ret = ad_mkdir(name, DIRBITS | 0777);
}
- dir = vol_tree_root(vol, did);
-
- afp_errno = AFPERR_NOOBJ;
- while ( dir != SENTINEL ) {
- if (dir->d_did == did)
- return dir->d_m_name ? dir : NULL;
- dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
+ if (ret < 0) {
+ switch ( errno ) {
+ case ENOENT :
+ return( AFPERR_NOOBJ );
+ case EROFS :
+ return( AFPERR_VLOCK );
+ case EPERM:
+ case EACCES :
+ return( AFPERR_ACCESS );
+ case EEXIST :
+ return( AFPERR_EXIST );
+ case ENOSPC :
+ case EDQUOT :
+ return( AFPERR_DFULL );
+ default :
+ return( AFPERR_PARAM );
+ }
}
- return NULL;
+ return AFP_OK;
}
/* ------------------- */
-int get_afp_errno(const int param)
+static int deletedir(int dirfd, char *dir)
{
- if (afp_errno != AFPERR_DID1)
- return afp_errno;
- return param;
-}
+ char path[MAXPATHLEN + 1];
+ DIR *dp;
+ struct dirent *de;
+ struct stat st;
+ size_t len;
+ int err = AFP_OK;
+ size_t remain;
-/* ------------------- */
-struct dir *
-dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
-{
- struct dir *dir = NULL;
+ if ((len = strlen(dir)) +2 > sizeof(path))
+ return AFPERR_PARAM;
- if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
- struct dir key;
- hnode_t *hn;
+ /* already gone */
+ if ((dp = opendirat(dirfd, dir)) == NULL)
+ return AFP_OK;
- key.d_parent = cdir;
- key.d_u_name = name;
- hn = hash_lookup(vol->v_hash, &key);
- if (hn) {
- dir = hnode_get(hn);
+ strcpy(path, dir);
+ strcat(path, "/");
+ len++;
+ remain = sizeof(path) -len -1;
+ while ((de = readdir(dp)) && err == AFP_OK) {
+ /* skip this and previous directory */
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ if (strlen(de->d_name) > remain) {
+ err = AFPERR_PARAM;
+ break;
+ }
+ strcpy(path + len, de->d_name);
+ if (lstatat(dirfd, path, &st)) {
+ continue;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ err = deletedir(dirfd, path);
+ } else {
+ err = netatalk_unlinkat(dirfd, path);
}
}
- return dir;
+ closedir(dp);
+
+ /* okay. the directory is empty. delete it. note: we already got rid
+ of .AppleDouble. */
+ if (err == AFP_OK) {
+ err = netatalk_rmdir(dirfd, dir);
+ }
+ return err;
}
-/* -----------------------------------------
- * if did is not in the cache resolve it with cnid
- *
- * FIXME
- * OSX call it with bogus id, ie file ID not folder ID,
- * and we are really bad in this case.
- */
-struct dir *
-dirlookup( struct vol *vol, u_int32_t did)
+/* do a recursive copy. */
+static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
{
- struct dir *ret;
- char *upath;
- cnid_t id, cnid;
- static char path[MAXPATHLEN + 1];
- size_t len, pathlen;
- char *ptr;
- static char buffer[12 + MAXPATHLEN + 1];
- int buflen = 12 + MAXPATHLEN + 1;
- char *mpath;
- int utf8;
- size_t maxpath;
+ char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
+ DIR *dp;
+ struct dirent *de;
+ struct stat st;
+ struct utimbuf ut;
+ size_t slen, dlen;
+ size_t srem, drem;
+ int err;
- ret = dirsearch(vol, did);
- if (ret != NULL || afp_errno == AFPERR_PARAM)
- return ret;
+ /* doesn't exist or the path is too long. */
+ if (((slen = strlen(src)) > sizeof(spath) - 2) ||
+ ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
+ ((dp = opendirat(dirfd, src)) == NULL))
+ return AFPERR_PARAM;
- utf8 = utf8_encoding();
- maxpath = (utf8)?MAXPATHLEN -7:255;
- id = did;
- if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
- afp_errno = AFPERR_NOOBJ;
- return NULL;
- }
- ptr = path + MAXPATHLEN;
- if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
- afp_errno = AFPERR_NOOBJ;
- return NULL;
+ /* try to create the destination directory */
+ if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
+ closedir(dp);
+ return err;
}
- len = strlen(mpath);
- pathlen = len; /* no 0 in the last part */
- len++;
- strcpy(ptr - len, mpath);
- ptr -= len;
- while (1) {
- ret = dirsearch(vol,id);
- if (ret != NULL) {
- break;
- }
- cnid = id;
- if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
- ||
- NULL == (mpath = utompath(vol, upath, cnid, utf8))
- ) {
- afp_errno = AFPERR_NOOBJ;
- return NULL;
- }
- len = strlen(mpath) + 1;
- pathlen += len;
- if (pathlen > maxpath) {
- afp_errno = AFPERR_PARAM;
- return NULL;
+ /* set things up to copy */
+ strcpy(spath, src);
+ strcat(spath, "/");
+ slen++;
+ srem = sizeof(spath) - slen -1;
+
+ strcpy(dpath, dst);
+ strcat(dpath, "/");
+ dlen++;
+ drem = sizeof(dpath) - dlen -1;
+
+ err = AFP_OK;
+ while ((de = readdir(dp))) {
+ /* skip this and previous directory */
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+ continue;
+
+ if (strlen(de->d_name) > srem) {
+ err = AFPERR_PARAM;
+ break;
}
- strcpy(ptr - len, mpath);
- ptr -= len;
- }
+ strcpy(spath + slen, de->d_name);
- /* fill the cache, another place where we know about the path type */
- if (utf8) {
- u_int16_t temp16;
- u_int32_t temp;
+ if (lstatat(dirfd, spath, &st) == 0) {
+ if (strlen(de->d_name) > drem) {
+ err = AFPERR_PARAM;
+ break;
+ }
+ strcpy(dpath + dlen, de->d_name);
- ptr -= 2;
- temp16 = htons(pathlen);
- memcpy(ptr, &temp16, sizeof(temp16));
+ if (S_ISDIR(st.st_mode)) {
+ if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
+ goto copydir_done;
+ } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
+ goto copydir_done;
- temp = htonl(kTextEncodingUTF8);
- ptr -= 4;
- memcpy(ptr, &temp, sizeof(temp));
- ptr--;
- *ptr = 3;
+ } else {
+ /* keep the same time stamp. */
+ ut.actime = ut.modtime = st.st_mtime;
+ utime(dpath, &ut);
+ }
+ }
}
- else {
- ptr--;
- *ptr = (unsigned char)pathlen;
- ptr--;
- *ptr = 2;
+
+ /* keep the same time stamp. */
+ if (lstatat(dirfd, src, &st) == 0) {
+ ut.actime = ut.modtime = st.st_mtime;
+ utime(dst, &ut);
}
- /* cname is not efficient */
- if (cname( vol, ret, &ptr ) == NULL )
- return NULL;
- return dirsearch(vol, did);
+copydir_done:
+ closedir(dp);
+ return err;
}
-/* child addition/removal */
-static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
+/* ---------------------
+ * is our cached offspring count valid?
+ */
+static int diroffcnt(struct dir *dir, struct stat *st)
{
- if (!a->d_child)
- a->d_child = b;
- else {
- b->d_next = a->d_child;
- b->d_prev = b->d_next->d_prev;
- b->d_next->d_prev = b;
- b->d_prev->d_next = b;
- }
- if (!hash_alloc_insert(vol->v_hash, b, b)) {
- LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
- }
+ return st->st_ctime == dir->ctime;
}
-static void dirchildremove(struct dir *a,struct dir *b)
+/* --------------------- */
+static int invisible_dots(const struct vol *vol, const char *name)
{
- if (a->d_child == b)
- a->d_child = (b == b->d_next) ? NULL : b->d_next;
- b->d_next->d_prev = b->d_prev;
- b->d_prev->d_next = b->d_next;
- b->d_next = b->d_prev = b;
+ return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
}
-/* --------------------------- */
-/* rotate the tree to the left */
-static void dir_leftrotate(struct vol *vol, struct dir *dir)
+/* ------------------ */
+static int set_dir_errors(struct path *path, const char *where, int err)
{
- struct dir *right = dir->d_right;
-
- /* whee. move the right's left tree into dir's right tree */
- dir->d_right = right->d_left;
- if (right->d_left != SENTINEL)
- right->d_left->d_back = dir;
-
- if (right != SENTINEL) {
- right->d_back = dir->d_back;
- right->d_left = dir;
+ switch ( err ) {
+ case EPERM :
+ case EACCES :
+ return AFPERR_ACCESS;
+ case EROFS :
+ return AFPERR_VLOCK;
}
-
- if (!dir->d_back) /* no parent. move the right tree to the top. */
- vol->v_root = right;
- else if (dir == dir->d_back->d_left) /* we were on the left */
- dir->d_back->d_left = right;
- else
- dir->d_back->d_right = right; /* we were on the right */
-
- /* re-insert dir on the left tree */
- if (dir != SENTINEL)
- dir->d_back = right;
+ LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
+ return AFPERR_PARAM;
}
-
-
-/* rotate the tree to the right */
-static void dir_rightrotate(struct vol *vol, struct dir *dir)
+/*!
+ * @brief Convert name in client encoding to server encoding
+ *
+ * Convert ret->m_name to ret->u_name from client encoding to server encoding.
+ * This only gets called from cname().
+ *
+ * @returns 0 on success, -1 on error
+ *
+ * @note If the passed ret->m_name is mangled, we'll demangle it
+ */
+static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct path *ret, int toUTF8)
{
- struct dir *left = dir->d_left;
+ static char temp[ MAXPATHLEN + 1];
+ char *t;
+ cnid_t fileid;
+
+ if (afp_version >= 30) {
+ if (toUTF8) {
+ if (dir->d_did == DIRDID_ROOT_PARENT) {
+ /*
+ * With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
+ * So we compare it with the longname from the current volume and if they match
+ * we overwrite the requested path with the utf8 volume name so that the following
+ * strcmp can match.
+ */
+ ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
+ if (strcasecmp(ret->m_name, temp) == 0)
+ ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, ret->m_name, AFPVOL_U8MNAMELEN);
+ } else {
+ /* toUTF8 */
+ if (mtoUTF8(vol, ret->m_name, strlen(ret->m_name), temp, MAXPATHLEN) == (size_t)-1) {
+ afp_errno = AFPERR_PARAM;
+ return -1;
+ }
+ strcpy(ret->m_name, temp);
+ }
+ }
- /* whee. move the left's right tree into dir's left tree */
- dir->d_left = left->d_right;
- if (left->d_right != SENTINEL)
- left->d_right->d_back = dir;
+ /* check for OS X mangled filename :( */
+ t = demangle_osx(vol, ret->m_name, dir->d_did, &fileid);
+ LOG(log_maxdebug, logtype_afpd, "cname_mtouname('%s',did:%u) {demangled:'%s', fileid:%u}",
+ ret->m_name, ntohl(dir->d_did), t, ntohl(fileid));
+
+ if (t != ret->m_name) {
+ ret->u_name = t;
+ /* duplicate work but we can't reuse all convert_char we did in demangle_osx
+ * flags weren't the same
+ */
+ if ( (t = utompath(vol, ret->u_name, fileid, utf8_encoding())) ) {
+ /* at last got our view of mac name */
+ strcpy(ret->m_name, t);
+ }
+ }
+ } /* afp_version >= 30 */
- if (left != SENTINEL) {
- left->d_back = dir->d_back;
- left->d_right = dir;
+ /* If we haven't got it by now, get it */
+ if (ret->u_name == NULL) {
+ if ((ret->u_name = mtoupath(vol, ret->m_name, dir->d_did, utf8_encoding())) == NULL) {
+ afp_errno = AFPERR_PARAM;
+ return -1;
+ }
}
- if (!dir->d_back) /* no parent. move the left tree to the top. */
- vol->v_root = left;
- else if (dir == dir->d_back->d_right) /* we were on the right */
- dir->d_back->d_right = left;
- else
- dir->d_back->d_left = left; /* we were on the left */
-
- /* re-insert dir on the right tree */
- if (dir != SENTINEL)
- dir->d_back = left;
+ return 0;
}
-#if 0
-/* recolor after a removal */
-static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
+/*!
+ * @brief Build struct path from struct dir
+ *
+ * The final movecwd in cname failed, possibly with EPERM or ENOENT. We:
+ * 1. move cwd into parent dir (we're often already there, but not always)
+ * 2. set struct path to the dirname
+ * 3. in case of
+ * AFPERR_ACCESS: the dir is there, we just cant chdir into it
+ * AFPERR_NOOBJ: the dir was there when we stated it in cname, so we have a race
+ * 4. indicate there's no dir for this path
+ * 5. remove the dir
+ */
+static struct path *path_from_dir(struct vol *vol, struct dir *dir, struct path *ret)
{
- struct dir *leaf;
-
- while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
- /* are we on the left tree? */
- if (dir == dir->d_back->d_left) {
- leaf = dir->d_back->d_right; /* get right side */
- if (leaf->d_color == DIRTREE_COLOR_RED) {
- /* we're red. we need to change to black. */
- leaf->d_color = DIRTREE_COLOR_BLACK;
- dir->d_back->d_color = DIRTREE_COLOR_RED;
- dir_leftrotate(vol, dir->d_back);
- leaf = dir->d_back->d_right;
- }
+ if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
+ return NULL;
- /* right leaf has black end nodes */
- if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
- (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
- leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
- dir = dir->d_back; /* ascend */
- } else {
- if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
- leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
- leaf->d_color = DIRTREE_COLOR_RED;
- dir_rightrotate(vol, leaf);
- leaf = dir->d_back->d_right;
- }
- leaf->d_color = dir->d_back->d_color;
- dir->d_back->d_color = DIRTREE_COLOR_BLACK;
- leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
- dir_leftrotate(vol, dir->d_back);
- dir = vol->v_root;
- }
- } else { /* right tree */
- leaf = dir->d_back->d_left; /* left tree */
- if (leaf->d_color == DIRTREE_COLOR_RED) {
- leaf->d_color = DIRTREE_COLOR_BLACK;
- dir->d_back->d_color = DIRTREE_COLOR_RED;
- dir_rightrotate(vol, dir->d_back);
- leaf = dir->d_back->d_left;
- }
+ switch (afp_errno) {
- /* left leaf has black end nodes */
- if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
- (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
- leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
- dir = dir->d_back; /* ascend */
- } else {
- if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
- leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
- leaf->d_color = DIRTREE_COLOR_RED;
- dir_leftrotate(vol, leaf);
- leaf = dir->d_back->d_left;
- }
- leaf->d_color = dir->d_back->d_color;
- dir->d_back->d_color = DIRTREE_COLOR_BLACK;
- leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
- dir_rightrotate(vol, dir->d_back);
- dir = vol->v_root;
- }
+ case AFPERR_ACCESS:
+ if (movecwd( vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
+ return NULL;
+
+ memcpy(ret->m_name, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1); /* 3 */
+ if (dir->d_m_name == dir->d_u_name) {
+ ret->u_name = ret->m_name;
+ } else {
+ ret->u_name = ret->m_name + blength(dir->d_m_name) + 1;
+ memcpy(ret->u_name, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1);
}
- }
- dir->d_color = DIRTREE_COLOR_BLACK;
- return dir;
-}
-#endif /* 0 */
+ ret->d_dir = dir;
-/* --------------------- */
-static void dir_hash_del(const struct vol *vol, struct dir *dir)
-{
- hnode_t *hn;
+ LOG(log_debug, logtype_afpd, "cname('%s') {path-from-dir: AFPERR_ACCESS. curdir:'%s', path:'%s'}",
+ cfrombstr(dir->d_fullpath),
+ cfrombstr(curdir->d_fullpath),
+ ret->u_name);
- hn = hash_lookup(vol->v_hash, dir);
- if (!hn) {
- LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
- }
- else {
- hash_delete(vol->v_hash, hn);
- }
-}
+ return ret;
-/* remove the node from the tree. this is just like insertion, but
- * different. actually, it has to worry about a bunch of things that
- * insertion doesn't care about. */
+ case AFPERR_NOOBJ:
+ if (movecwd(vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
+ return NULL;
-static void dir_remove( struct vol *vol, struct dir *dir)
-{
-#ifdef REMOVE_NODES
- struct ofork *of, *last;
- struct dir *node, *leaf;
-#endif /* REMOVE_NODES */
-
- if (!dir || (dir == SENTINEL))
- return;
-
- /* i'm not sure if it really helps to delete stuff. */
- dir_hash_del(vol, dir);
- vol->v_curdir = NULL;
-#ifndef REMOVE_NODES
- dirfreename(dir);
- dir->d_m_name = NULL;
- dir->d_u_name = NULL;
- dir->d_m_name_ucs2 = NULL;
-#else /* ! REMOVE_NODES */
-
- /* go searching for a node with at most one child */
- if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
- node = dir;
- } else {
- node = dir->d_right;
- while (node->d_left != SENTINEL)
- node = node->d_left;
- }
+ memcpy(ret->m_name, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1);
+ if (dir->d_m_name == dir->d_u_name) {
+ ret->u_name = ret->m_name;
+ } else {
+ ret->u_name = ret->m_name + blength(dir->d_m_name) + 1;
+ memcpy(ret->u_name, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1);
+ }
- /* get that child */
- leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
+ ret->d_dir = NULL; /* 4 */
+ dir_remove(vol, dir); /* 5 */
+ return ret;
- /* detach node */
- leaf->d_back = node->d_back;
- if (!node->d_back) {
- vol->v_root = leaf;
- } else if (node == node->d_back->d_left) { /* left tree */
- node->d_back->d_left = leaf;
- } else {
- node->d_back->d_right = leaf;
+ default:
+ return NULL;
}
- /* we want to free node, but we also want to free the data in dir.
- * currently, that's d_name and the directory traversal bits.
- * we just copy the necessary bits and then fix up all the
- * various pointers to the directory. needless to say, there are
- * a bunch of places that store the directory struct. */
- if (node != dir) {
- struct dir save, *tmp;
+ /* DEADC0DE: never get here */
+ return NULL;
+}
- memcpy(&save, dir, sizeof(save));
- memcpy(dir, node, sizeof(struct dir));
- /* restore the red-black bits */
- dir->d_left = save.d_left;
- dir->d_right = save.d_right;
- dir->d_back = save.d_back;
- dir->d_color = save.d_color;
+/*********************************************************************************************
+ * Interface
+ ********************************************************************************************/
- if (node == vol->v_dir) {/* we may need to fix up this pointer */
- vol->v_dir = dir;
- rootpar.d_child = vol->v_dir;
- } else {
- /* if we aren't the root directory, we have parents and
- * siblings to worry about */
- if (dir->d_parent->d_child == node)
- dir->d_parent->d_child = dir;
- dir->d_next->d_prev = dir;
- dir->d_prev->d_next = dir;
- }
+int get_afp_errno(const int param)
+{
+ if (afp_errno != AFPERR_DID1)
+ return afp_errno;
+ return param;
+}
- /* fix up children. */
- tmp = dir->d_child;
- while (tmp) {
- tmp->d_parent = dir;
- tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
- }
+/*!
+ * @brief Resolve a DID
+ *
+ * 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.
+ * 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.
+ * 6. Create the struct dir and populate it.
+ * 7. Add it to the cache.
+ *
+ * @param vol (r) pointer to struct vol
+ * @param did (r) DID to resolve
+ *
+ * @returns pointer to struct dir
+ *
+ * @note FIXME: OSX calls it with bogus id, ie file ID not folder ID,
+ * and we are really bad in this case.
+ */
+struct dir *dirlookup(const struct vol *vol, cnid_t did)
+{
+ static char buffer[12 + MAXPATHLEN + 1];
+ struct stat st;
+ struct dir *ret = NULL, *pdir;
+ bstring fullpath = NULL;
+ char *upath = NULL, *mpath;
+ cnid_t cnid, pdid;
+ size_t maxpath;
+ int buflen = 12 + MAXPATHLEN + 1;
+ int utf8;
+ int err = 0;
+
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {start}", ntohl(did));
- if (node == curdir) /* another pointer to fixup */
- curdir = dir;
+ /* check for did 0, 1 and 2 */
+ if (did == 0 || vol == NULL) { /* 1 */
+ afp_errno = AFPERR_PARAM;
+ return NULL;
+ } else if (did == DIRDID_ROOT_PARENT) {
+ rootParent.d_vid = vol->v_vid;
+ return (&rootParent);
+ } else if (did == DIRDID_ROOT) {
+ return vol->v_root;
+ }
- /* we also need to fix up oforks. bleah */
- if ((of = dir->d_ofork)) {
- last = of->of_d_prev;
- while (of) {
- of->of_dir = dir;
- of = (last == of) ? NULL : of->of_d_next;
+ /* Search the cache */
+ if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2a */
+ if (ret->d_fullpath == NULL) { /* 2b */
+ afp_errno = AFPERR_BADTYPE;
+ return NULL;
+ }
+ if (lstat(cfrombstr(ret->d_fullpath), &st) != 0) {
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {lstat: %s}", ntohl(did), 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));
+ dir_remove(vol, ret);
+ afp_errno = AFPERR_NOOBJ;
+ return NULL;
+ default:
+ return ret;
}
+ /* DEADC0DE */
+ return NULL;
}
+ return ret;
+ }
+
+ utf8 = utf8_encoding();
+ maxpath = (utf8) ? MAXPATHLEN - 7 : 255;
- /* set the node's d_name */
- node->d_m_name = save.d_m_name;
- node->d_u_name = save.d_u_name;
- node->d_m_name_ucs2 = save.d_m_name_ucs2;
+ /* Get it from the database */
+ cnid = did;
+ if ( (upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL
+ || (upath = strdup(upath)) == NULL) { /* 3 */
+ afp_errno = AFPERR_NOOBJ;
+ err = 1;
+ goto exit;
}
+ pdid = cnid;
- if (node->d_color == DIRTREE_COLOR_BLACK)
- dir_rmrecolor(vol, leaf);
+ /*
+ * Recurse up the tree, terminates in dirlookup when either
+ * - DIRDID_ROOT is hit
+ * - a cached entry is found
+ */
+ if ((pdir = dirlookup(vol, pdid)) == NULL) {
+ err = 1;
+ goto exit;
+ }
- if (node->d_m_name_ucs2)
- free(node->d_u_name_ucs2);
- if (node->d_u_name != node->d_m_name) {
- free(node->d_u_name);
+ /* build the fullpath */
+ if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL
+ || bconchar(fullpath, '/') != BSTR_OK
+ || bcatcstr(fullpath, upath) != BSTR_OK) {
+ err = 1;
+ goto exit;
}
- free(node->d_m_name);
- free(node);
-#endif /* ! REMOVE_NODES */
-}
-/* ---------------------------------------
- * remove the node and its childs from the tree
- *
- * FIXME what about opened forks with refs to it?
- * it's an afp specs violation because you can't delete
- * an opened forks. Now afpd doesn't care about forks opened by other
- * process. It's fixable within afpd if fnctl_lock, doable with smb and
- * next to impossible for nfs and local filesystem access.
- */
-static void dir_invalidate( struct vol *vol, struct dir *dir)
-{
- if (curdir == dir) {
- /* v_root can't be deleted */
- if (movecwd(vol, vol->v_root) < 0) {
- LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
+ /* stat it and check if it's a dir */
+ LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstr(fullpath));
+
+ if (stat(cfrombstr(fullpath), &st) != 0) { /* 5a */
+ switch (errno) {
+ case ENOENT:
+ afp_errno = AFPERR_NOOBJ;
+ err = 1;
+ goto exit;
+ case EPERM:
+ afp_errno = AFPERR_ACCESS;
+ err = 1;
+ goto exit;
+ default:
+ afp_errno = AFPERR_MISC;
+ err = 1;
+ goto exit;
}
+ } else {
+ if ( ! S_ISDIR(st.st_mode)) { /* 5b */
+ afp_errno = AFPERR_BADTYPE;
+ err = 1;
+ goto exit;
+ }
+ }
+
+ /* Get macname from unix name */
+ if ( (mpath = utompath(vol, upath, did, utf8)) == NULL ) {
+ afp_errno = AFPERR_NOOBJ;
+ err = 1;
+ goto exit;
+ }
+
+ /* Create struct dir */
+ if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, st.st_ctime)) == NULL) { /* 6 */
+ LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno));
+ err = 1;
+ goto exit;
+ }
+
+ /* Add it to the cache only if it's a dir */
+ if (dircache_add(ret) != 0) { /* 7 */
+ err = 1;
+ goto exit;
}
- /* FIXME */
- dirchildremove(dir->d_parent, dir);
- dir_remove( vol, dir );
-}
-/* ------------------------------------ */
-static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
-{
- struct dir *pdir;
-
- pdir = vol_tree_root(vol, dir->d_did);
- while (pdir->d_did != dir->d_did ) {
- if ( pdir->d_did > dir->d_did ) {
- if ( pdir->d_left == SENTINEL ) {
- pdir->d_left = dir;
- dir->d_back = pdir;
- return NULL;
- }
- pdir = pdir->d_left;
- } else {
- if ( pdir->d_right == SENTINEL ) {
- pdir->d_right = dir;
- dir->d_back = pdir;
- return NULL;
- }
- pdir = pdir->d_right;
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {end: did:%u, path:'%s'}",
+ ntohl(did), ntohl(pdid), cfrombstr(ret->d_fullpath));
+
+exit:
+ if (upath) free(upath);
+ if (err) {
+ LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {exit_error: %s}",
+ ntohl(did), AfpErr2name(afp_errno));
+ if (fullpath)
+ bdestroy(fullpath);
+ if (ret) {
+ dir_free(ret);
+ ret = NULL;
}
}
- return pdir;
+ return ret;
}
#define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
-int
-caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
+int caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
{
DIR *dp;
struct dirent *de;
}
-/*
- * attempt to extend the current dir. tree to include path
- * as a side-effect, movecwd to that point and return the new dir
+/*!
+ * @brief Construct struct dir
+ *
+ * Construct struct dir from parameters.
+ *
+ * @param m_name (r) directory name in UTF8-dec
+ * @param u_name (r) directory name in server side encoding
+ * @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
+ *
+ * @returns pointer to new struct dir or NULL on error
+ *
+ * @note Most of the time mac name and unix name are the same.
*/
-static struct dir *
-extenddir(struct vol *vol, struct dir *dir, struct path *path)
+struct dir *dir_new(const char *m_name,
+ const char *u_name,
+ const struct vol *vol,
+ cnid_t pdid,
+ cnid_t did,
+ bstring path,
+ time_t ctime)
{
- path->d_dir = NULL;
+ struct dir *dir;
- if ( path->u_name == NULL) {
- afp_errno = AFPERR_PARAM;
+ dir = (struct dir *) calloc(1, sizeof( struct dir ));
+ if (!dir)
return NULL;
- }
- if (check_name(vol, path->u_name)) {
- /* the name is illegal */
- LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
- path->u_name = NULL;
- afp_errno = AFPERR_PARAM;
+ if ((dir->d_m_name = bfromcstr(m_name)) == NULL) {
+ free(dir);
return NULL;
}
- if (of_stat( path ) != 0 ) {
- if (!(vol->v_flags & AFPVOL_CASEINSEN))
- return NULL;
- else if(caseenumerate(vol, path, dir) != 0)
- return(NULL);
- }
-
- if (!S_ISDIR(path->st.st_mode)) {
- return( NULL );
- }
-
- /* mac name is always with the right encoding (from cname()) */
- if (( dir = adddir( vol, dir, path)) == NULL ) {
- return( NULL );
- }
-
- path->d_dir = dir;
- if ( movecwd( vol, dir ) < 0 ) {
- return( NULL );
- }
-
- return( dir );
-}
-
-/* -------------------------
- appledouble mkdir afp error code.
-*/
-static int netatalk_mkdir(const struct vol *vol, const char *name)
-{
- int ret;
- struct stat st;
-
- if (vol->v_flags & AFPVOL_UNIX_PRIV) {
- if (lstat(".", &st) < 0)
- return AFPERR_MISC;
- int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask);
- LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}",
- name, st.st_mode, vol->v_umask);
-
- ret = mkdir(name, mode);
- } else {
- ret = ad_mkdir(name, DIRBITS | 0777);
- }
-
- if (ret < 0) {
- switch ( errno ) {
- case ENOENT :
- return( AFPERR_NOOBJ );
- case EROFS :
- return( AFPERR_VLOCK );
- case EPERM:
- case EACCES :
- return( AFPERR_ACCESS );
- case EEXIST :
- return( AFPERR_EXIST );
- case ENOSPC :
- case EDQUOT :
- return( AFPERR_DFULL );
- default :
- return( AFPERR_PARAM );
- }
- }
- return AFP_OK;
-}
-
-/* ------------------- */
-static int deletedir(int dirfd, char *dir)
-{
- char path[MAXPATHLEN + 1];
- DIR *dp;
- struct dirent *de;
- struct stat st;
- size_t len;
- int err = AFP_OK;
- size_t remain;
-
- if ((len = strlen(dir)) +2 > sizeof(path))
- return AFPERR_PARAM;
-
- /* already gone */
- if ((dp = opendirat(dirfd, dir)) == NULL)
- return AFP_OK;
-
- strcpy(path, dir);
- strcat(path, "/");
- len++;
- remain = sizeof(path) -len -1;
- while ((de = readdir(dp)) && err == AFP_OK) {
- /* skip this and previous directory */
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
- continue;
-
- if (strlen(de->d_name) > remain) {
- err = AFPERR_PARAM;
- break;
- }
- strcpy(path + len, de->d_name);
- if (lstatat(dirfd, path, &st)) {
- continue;
- }
- if (S_ISDIR(st.st_mode)) {
- err = deletedir(dirfd, path);
- } else {
- err = netatalk_unlinkat(dirfd, path);
- }
- }
- closedir(dp);
-
- /* okay. the directory is empty. delete it. note: we already got rid
- of .AppleDouble. */
- if (err == AFP_OK) {
- err = netatalk_rmdir(dirfd, dir);
- }
- return err;
-}
-
-/* do a recursive copy. */
-static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
-{
- char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
- DIR *dp;
- struct dirent *de;
- struct stat st;
- struct utimbuf ut;
- size_t slen, dlen;
- size_t srem, drem;
- int err;
-
- /* doesn't exist or the path is too long. */
- if (((slen = strlen(src)) > sizeof(spath) - 2) ||
- ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
- ((dp = opendirat(dirfd, src)) == NULL))
- return AFPERR_PARAM;
-
- /* try to create the destination directory */
- if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
- closedir(dp);
- return err;
+ if (convert_string_allocate( (utf8_encoding()) ? CH_UTF8_MAC : vol->v_maccharset,
+ CH_UCS2,
+ m_name,
+ -1, (char **)&dir->d_m_name_ucs2) == (size_t)-1 ) {
+ LOG(log_error, logtype_afpd, "dir_new(did: %u) {%s, %s}: couldn't set UCS2 name", ntohl(did), m_name, u_name);
+ dir->d_m_name_ucs2 = NULL;
}
- /* set things up to copy */
- strcpy(spath, src);
- strcat(spath, "/");
- slen++;
- srem = sizeof(spath) - slen -1;
-
- strcpy(dpath, dst);
- strcat(dpath, "/");
- dlen++;
- drem = sizeof(dpath) - dlen -1;
-
- err = AFP_OK;
- while ((de = readdir(dp))) {
- /* skip this and previous directory */
- if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
- continue;
-
- if (strlen(de->d_name) > srem) {
- err = AFPERR_PARAM;
- break;
- }
- strcpy(spath + slen, de->d_name);
-
- if (lstatat(dirfd, spath, &st) == 0) {
- if (strlen(de->d_name) > drem) {
- err = AFPERR_PARAM;
- break;
- }
- strcpy(dpath + dlen, de->d_name);
-
- if (S_ISDIR(st.st_mode)) {
- if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
- goto copydir_done;
- } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
- goto copydir_done;
-
- } else {
- /* keep the same time stamp. */
- ut.actime = ut.modtime = st.st_mtime;
- utime(dpath, &ut);
- }
- }
+ if (m_name == u_name || !strcmp(m_name, u_name)) {
+ dir->d_u_name = dir->d_m_name;
}
-
- /* keep the same time stamp. */
- if (lstatat(dirfd, src, &st) == 0) {
- ut.actime = ut.modtime = st.st_mtime;
- utime(dst, &ut);
+ else if ((dir->d_u_name = bfromcstr(u_name)) == NULL) {
+ bdestroy(dir->d_m_name);
+ free(dir);
+ return NULL;
}
-copydir_done:
- closedir(dp);
- return err;
+ dir->d_did = did;
+ dir->d_pdid = pdid;
+ dir->d_vid = vol->v_vid;
+ dir->d_fullpath = path;
+ dir->ctime_dircache = ctime;
+ return dir;
}
-
-/* --- public functions follow --- */
-
-/* NOTE: we start off with at least one node (the root directory). */
-static struct dir *dirinsert(struct vol *vol, struct dir *dir)
+/*!
+ * @brief Free a struct dir and all its members
+ *
+ * @param (rw) pointer to struct dir
+ */
+void dir_free(struct dir *dir)
{
- struct dir *node;
-
- if ((node = dir_insert(vol, dir)))
- return node;
-
- /* recolor the tree. the current node is red. */
- dir->d_color = DIRTREE_COLOR_RED;
-
- /* parent of this node has to be black. if the parent node
- * is red, then we have a grandparent. */
- while ((dir != vol->v_root) &&
- (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
- /* are we on the left tree? */
- if (dir->d_back == dir->d_back->d_back->d_left) {
- node = dir->d_back->d_back->d_right; /* get the right node */
- if (node->d_color == DIRTREE_COLOR_RED) {
- /* we're red. we need to change to black. */
- dir->d_back->d_color = DIRTREE_COLOR_BLACK;
- node->d_color = DIRTREE_COLOR_BLACK;
- dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
- dir = dir->d_back->d_back; /* finished. go up. */
- } else {
- if (dir == dir->d_back->d_right) {
- dir = dir->d_back;
- dir_leftrotate(vol, dir);
- }
- dir->d_back->d_color = DIRTREE_COLOR_BLACK;
- dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
- dir_rightrotate(vol, dir->d_back->d_back);
- }
- } else {
- node = dir->d_back->d_back->d_left;
- if (node->d_color == DIRTREE_COLOR_RED) {
- /* we're red. we need to change to black. */
- dir->d_back->d_color = DIRTREE_COLOR_BLACK;
- node->d_color = DIRTREE_COLOR_BLACK;
- dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
- dir = dir->d_back->d_back; /* finished. ascend */
- } else {
- if (dir == dir->d_back->d_left) {
- dir = dir->d_back;
- dir_rightrotate(vol, dir);
- }
- dir->d_back->d_color = DIRTREE_COLOR_BLACK;
- dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
- dir_leftrotate(vol, dir->d_back->d_back);
- }
- }
+ if (dir->d_u_name != dir->d_m_name) {
+ bdestroy(dir->d_u_name);
}
-
- vol->v_root->d_color = DIRTREE_COLOR_BLACK;
- return NULL;
+ if (dir->d_m_name_ucs2)
+ free(dir->d_m_name_ucs2);
+ bdestroy(dir->d_m_name);
+ bdestroy(dir->d_fullpath);
+ free(dir);
}
-/* ---------------------------- */
-struct dir *
-adddir(struct vol *vol, struct dir *dir, struct path *path)
+/*!
+ * @brief Create struct dir from struct path
+ *
+ * Create a new struct dir from struct path. Then add it to the cache.
+ *
+ * 1. Open adouble file, get CNID from it.
+ * 2. Search the database, hinting with the CNID from (1).
+ * 3. Build fullpath and create struct dir.
+ * 4. Add it to the cache.
+ *
+ * @param vol (r) pointer to struct vol, possibly modified in callee
+ * @param dir (r) pointer to parrent directory
+ * @param path (rw) pointer to struct path with valid path->u_name
+ * @param len (r) strlen of path->u_name
+ *
+ * @returns Pointer to new struct dir or NULL on error.
+ *
+ * @note Function also assigns path->m_name from path->u_name.
+ */
+struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, int len)
{
- struct dir *cdir, *edir;
- int upathlen;
- char *name;
- char *upath;
- struct stat *st;
- int deleted;
+ int err = 0;
+ struct dir *cdir = NULL;
+ cnid_t id;
struct adouble ad;
struct adouble *adp = NULL;
- cnid_t id;
-
- upath = path->u_name;
- st = &path->st;
- upathlen = strlen(upath);
+ bstring fullpath;
+
+ AFP_ASSERT(vol);
+ AFP_ASSERT(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) {
+ /* 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,
+ ntohl(cdir->d_did), cfrombstr(dir->d_fullpath));
+ if (dir_remove(vol, cdir) != 0) {
+ dircache_dump();
+ AFP_PANIC("dir_add");
+ }
+ }
/* get_id needs adp for reading CNID from adouble file */
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
- if ((ad_open_metadata(upath, ADFLAGS_DIR, 0, &ad)) == 0)
+ if ((ad_open_metadata(path->u_name, ADFLAGS_DIR, 0, &ad)) == 0) /* 1 */
adp = &ad;
- id = get_id(vol, adp, st, dir->d_did, upath, upathlen);
+ /* Get CNID */
+ if ((id = get_id(vol, adp, &path->st, dir->d_did, path->u_name, len)) == 0) { /* 2 */
+ err = 1;
+ goto exit;
+ }
if (adp)
ad_close_metadata(adp);
- if (id == 0) {
- return NULL;
- }
- if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
- return NULL;
- }
- name = path->m_name;
- if ((cdir = dirnew(name, upath)) == NULL) {
- LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
- return NULL;
- }
- if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, path->m_name, -1, (char **)&cdir->d_m_name_ucs2)) {
- LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
- cdir->d_m_name_ucs2 = NULL;
- }
-
- cdir->d_did = id;
-
- if ((edir = dirinsert( vol, cdir ))) {
- /* it's not possible with LASTDID
- for CNID:
- - someone else have moved the directory.
- - it's a symlink inside the share.
- - it's an ID reused, the old directory was deleted but not
- the cnid record and the server've reused the inode for
- the new dir.
- for HASH (we should get ride of HASH)
- - someone else have moved the directory.
- - it's an ID reused as above
- - it's a hash duplicate and we are in big trouble
- */
- deleted = (edir->d_m_name == NULL);
- if (!deleted)
- dir_hash_del(vol, edir);
- dirfreename(edir);
- edir->d_m_name = cdir->d_m_name;
- edir->d_u_name = cdir->d_u_name;
- edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
- free(cdir);
- cdir = edir;
- LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
- if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
- hash_alloc_insert(vol->v_hash, cdir, cdir);
- return cdir;
+ /* Get macname from unixname */
+ if (path->m_name == NULL) {
+ if ((path->m_name = utompath(vol, path->u_name, id, utf8_encoding())) == NULL) {
+ err = 2;
+ goto exit;
}
- /* the old was not in the same folder */
- if (!deleted)
- dirchildremove(cdir->d_parent, cdir);
}
- /* parent/child directories */
- cdir->d_parent = dir;
- dirchildadd(vol, dir, cdir);
- return( cdir );
-}
-
-/* --- public functions follow --- */
-/* free everything down. we don't bother to recolor as this is only
- * called to free the entire tree */
-void dirfreename(struct dir *dir)
-{
- if (dir->d_u_name != dir->d_m_name) {
- free(dir->d_u_name);
+ /* Build fullpath */
+ if ( ((fullpath = bstrcpy(dir->d_fullpath)) == NULL) /* 3 */
+ || (bconchar(fullpath, '/') != BSTR_OK)
+ || (bcatcstr(fullpath, path->u_name)) != BSTR_OK) {
+ LOG(log_error, logtype_afpd, "dir_add: fullpath: %s", strerror(errno) );
+ err = 3;
+ goto exit;
}
- if (dir->d_m_name_ucs2)
- free(dir->d_m_name_ucs2);
- free(dir->d_m_name);
-}
-
-void dirfree(struct dir *dir)
-{
- if (!dir || (dir == SENTINEL))
- return;
- if ( dir->d_left != SENTINEL ) {
- dirfree( dir->d_left );
+ /* 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 */
+ err = 4;
+ goto exit;
}
- if ( dir->d_right != SENTINEL ) {
- dirfree( dir->d_right );
+
+ if ((dircache_add(cdir)) != 0) { /* 4 */
+ LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstr(fullpath));
+ exit(EXITERR_SYS);
}
- if (dir != SENTINEL) {
- dirfreename(dir);
- free( dir );
+exit:
+ if (err != 0) {
+ LOG(log_debug, logtype_afpd, "dir_add('%s/%s'): error: %u",
+ cfrombstr(dir->d_u_name), path->u_name, err);
+
+ if (adp)
+ ad_close_metadata(adp);
+ if (!cdir && fullpath)
+ bdestroy(fullpath);
+ if (cdir)
+ dir_free(cdir);
+ cdir = NULL;
+ } else {
+ /* no error */
+ LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {cached: %u,'%s'}",
+ ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name,
+ ntohl(cdir->d_did), cfrombstr(cdir->d_fullpath));
}
+
+ return(cdir);
}
-/* --------------------------------------------
- * most of the time mac name and unix name are the same
+/*!
+ * Free the queue with invalid struct dirs
+ *
+ * This gets called at the end of every AFP func.
*/
-struct dir *dirnew(const char *m_name, const char *u_name)
+void dir_free_invalid_q(void)
{
struct dir *dir;
-
- dir = (struct dir *) calloc(1, sizeof( struct dir ));
- if (!dir)
- return NULL;
-
- if ((dir->d_m_name = strdup(m_name)) == NULL) {
- free(dir);
- return NULL;
- }
-
- if (m_name == u_name || !strcmp(m_name, u_name)) {
- dir->d_u_name = dir->d_m_name;
- }
- else if ((dir->d_u_name = strdup(u_name)) == NULL) {
- free(dir->d_m_name);
- free(dir);
- return NULL;
- }
-
- dir->d_m_name_ucs2 = NULL;
- dir->d_left = dir->d_right = SENTINEL;
- dir->d_next = dir->d_prev = dir;
- return dir;
+ while (dir = (struct dir *)dequeue(invalid_dircache_entries))
+ dir_free(dir);
}
-#if 0
-/* ------------------ */
-static hash_val_t hash_fun_dir(const void *key)
+/*!
+ * @brief Remove a dir from a cache and queue it for freeing
+ *
+ * 1. Check if the dir is locked or has opened forks
+ * 2. If it's a request to remove curdir, just chdir to volume root
+ * 3. Remove it from the cache
+ * 4. Queue it for removal
+ * 5. Mark it as invalid
+ *
+ * @param (r) pointer to struct vol
+ * @param (rw) pointer to struct dir
+ */
+int dir_remove(const struct vol *vol, struct dir *dir)
{
- const struct dir *k = key;
-
- static unsigned long randbox[] = {
- 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
- 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
- 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
- 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
- };
-
- const unsigned char *str = (unsigned char *)(k->d_u_name);
- hash_val_t acc = k->d_parent->d_did;
-
- while (*str) {
- acc ^= randbox[(*str + acc) & 0xf];
- acc = (acc << 1) | (acc >> 31);
- acc &= 0xffffffffU;
- acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
- acc = (acc << 2) | (acc >> 30);
- acc &= 0xffffffffU;
- }
- return acc;
-}
-#endif
+ AFP_ASSERT(vol);
+ AFP_ASSERT(dir);
-#undef get16bits
-#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
- || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
-#define get16bits(d) (*((const uint16_t *) (d)))
-#endif
+ if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
+ return 0;
-#if !defined (get16bits)
-#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
- +(uint32_t)(((const uint8_t *)(d))[0]) )
-#endif
+ if (dir->d_flags & DIRF_CACHELOCK) { /* 1 */
+ LOG(log_warning, logtype_afpd, "dir_remove(did:%u,'%s'): dir is locked",
+ ntohl(dir->d_did), cfrombstr(dir->d_u_name));
+ return 0;
+ }
-static hash_val_t hash_fun2_dir(const void *key)
-{
- const struct dir *k = key;
- const char *data = k->d_u_name;
- int len = strlen(k->d_u_name);
- hash_val_t hash = k->d_parent->d_did, tmp;
-
- int rem = len & 3;
- len >>= 2;
-
- /* Main loop */
- for (;len > 0; len--) {
- hash += get16bits (data);
- tmp = (get16bits (data+2) << 11) ^ hash;
- hash = (hash << 16) ^ tmp;
- data += 2*sizeof (uint16_t);
- hash += hash >> 11;
- }
-
- /* Handle end cases */
- switch (rem) {
- case 3: hash += get16bits (data);
- hash ^= hash << 16;
- hash ^= data[sizeof (uint16_t)] << 18;
- hash += hash >> 11;
- break;
- case 2: hash += get16bits (data);
- hash ^= hash << 11;
- hash += hash >> 17;
- break;
- case 1: hash += *data;
- hash ^= hash << 10;
- hash += hash >> 1;
+ if (curdir == dir) { /* 2 */
+ if (movecwd(vol, vol->v_root) < 0) {
+ LOG(log_error, logtype_afpd, "dir_remove: can't chdir to : %s", vol->v_root);
+ }
}
- /* Force "avalanching" of final 127 bits */
- hash ^= hash << 3;
- hash += hash >> 5;
- hash ^= hash << 4;
- hash += hash >> 17;
- hash ^= hash << 25;
- hash += hash >> 6;
+ LOG(log_debug, logtype_afpd, "dir_remove(did:%u,'%s'): {removing}",
+ ntohl(dir->d_did), cfrombstr(dir->d_u_name));
- return hash;
+ dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); /* 3 */
+ enqueue(invalid_dircache_entries, dir); /* 4 */
+ dir->d_did = CNID_INVALID; /* 5 */
+ return 0;
}
-/* ---------------- */
-static int hash_comp_dir(const void *key1, const void *key2)
+/*!
+ * @brief Modify a struct dir, adjust cache
+ *
+ * Any value that is 0 or NULL is not changed. If new_uname is NULL it is set to new_mname.
+ * If given new_uname == new_mname, new_uname will point to new_mname.
+ *
+ * @param vol (r) pointer to struct vol
+ * @param dir (rw) pointer to struct dir
+ * @param pdid (r) new parent DID
+ * @param did (r) new DID
+ * @param new_mname (r) new mac-name
+ * @param new_uname (r) new unix-name
+ * @param pdir_fullpath (r) new fullpath of parent dir
+ */
+int dir_modify(const struct vol *vol,
+ struct dir *dir,
+ cnid_t pdid,
+ cnid_t did,
+ const char *new_mname,
+ const char *new_uname,
+ bstring pdir_fullpath)
{
- const struct dir *k1 = key1;
- const struct dir *k2 = key2;
+ int ret = 0;
- return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
-}
+ /* Remove it from the cache */
+ dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
-/* ---------------- */
-hash_t *
-dirhash(void)
-{
- return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun2_dir);
-}
+ if (pdid)
+ dir->d_pdid = pdid;
+ if (did)
+ dir->d_did = did;
-/* ------------------ */
-static struct path *invalidate (struct vol *vol, struct dir *dir, struct path *ret)
-{
- /* it's tricky:
- movecwd failed some of dir path are not there anymore.
- FIXME Is it true with other errors?
- so we remove dir from the cache
- */
- if (dir->d_did == DIRDID_ROOT_PARENT)
- return NULL;
- if (afp_errno == AFPERR_ACCESS) {
- if ( movecwd( vol, dir->d_parent ) < 0 ) {
- return NULL;
- }
- /* FIXME should we set these?, don't need to call stat() after:
- ret->st_valid = 1;
- ret->st_errno = EACCES;
- */
- ret->m_name = dir->d_m_name;
- ret->u_name = dir->d_u_name;
- ret->d_dir = dir;
- return ret;
- } else if (afp_errno == AFPERR_NOOBJ) {
- if ( movecwd( vol, dir->d_parent ) < 0 ) {
- return NULL;
- }
- strcpy(ret->m_name, dir->d_m_name);
- if (dir->d_m_name == dir->d_u_name) {
- ret->u_name = ret->m_name;
+ if (new_mname) {
+ /* free uname if it's not the same as mname */
+ if (dir->d_m_name != dir->d_u_name)
+ bdestroy(dir->d_u_name);
+
+ if (new_uname == NULL)
+ new_uname = new_mname;
+
+ /* assign new name */
+ if ((bassigncstr(dir->d_m_name, new_mname)) != BSTR_OK) {
+ LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) );
+ return -1;
}
- else {
- size_t tp = strlen(ret->m_name)+1;
- ret->u_name = ret->m_name +tp;
- strcpy(ret->u_name, dir->d_u_name);
+ if (new_mname == new_uname || (strcmp(new_mname, new_uname) == 0)) {
+ dir->d_u_name = dir->d_m_name;
+ } else {
+ if ((dir->d_u_name = bfromcstr(new_uname)) == NULL) {
+ LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) );
+ return -1;
+ }
}
- /* FIXME should we set :
- ret->st_valid = 1;
- ret->st_errno = ENOENT;
- */
- dir_invalidate(vol, dir);
- return ret;
}
- dir_invalidate(vol, dir);
- return NULL;
-}
-/* -------------------------------------------------- */
-/* cname
- return
- if it's a filename:
- in extenddir:
- compute unix name
- stat the file or errno
- return
- filename
- curdir: filename parent directory
-
- if it's a dirname:
- not in the cache
- in extenddir
- compute unix name
- stat the dir or errno
- return
- if chdir error
- dirname
- curdir: dir parent directory
- sinon
- dirname: ""
- curdir: dir
- in the cache
- return
- if chdir error
- dirname
- curdir: dir parent directory
- else
- dirname: ""
- curdir: dir
+ if (pdir_fullpath) {
+ if (bassign(dir->d_fullpath, pdir_fullpath) != BSTR_OK)
+ return -1;
+ if (bcatcstr(dir->d_fullpath, "/") != BSTR_OK)
+ return -1;
+ if (bcatcstr(dir->d_fullpath, new_uname) != BSTR_OK)
+ return -1;
+ }
+
+ if (dir->d_m_name_ucs2)
+ free(dir->d_m_name_ucs2);
+ if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, -1, (char**)&dir->d_m_name_ucs2))
+ dir->d_m_name_ucs2 = NULL;
+
+ /* Re-add it to the cache */
+ if ((dircache_add(dir)) != 0) {
+ dircache_dump();
+ AFP_PANIC("dir_modify");
+ }
-*/
-struct path *
-cname(struct vol *vol, struct dir *dir, char **cpath)
+ return ret;
+}
+
+/*!
+ * @brief Resolve a catalog node name path
+ *
+ * 1. Evaluate path type
+ * 2. Move to start dir, if we cant, it might eg because of EACCES, build
+ * path from dirname, so eg getdirparams has sth it can chew on. curdir
+ * is dir parent then. All this is done in path_from_dir().
+ * 3. Parse next cnode name in path, cases:
+ * 4. single "\0" -> do nothing
+ * 5. two or more consecutive "\0" -> chdir("..") one or more times
+ * 6. cnode name -> copy it to path.m_name
+ * 7. Get unix name from mac name
+ * 8. Special handling of request with did 1
+ * 9. stat the cnode name
+ * 10. If it's not there, it's probably an afp_createfile|dir,
+ * return with curdir = dir parent, struct path = dirname
+ * 11. If it's there and it's a file, it must should be the last element of the requested
+ * path. Return with curdir = cnode name parent dir, struct path = filename
+ * 12. Treat symlinks like files, dont follow them
+ * 13. If it's a dir:
+ * 14. Search the dircache for it
+ * 15. If it's not in the cache, create a struct dir for it and add it to the cache
+ * 16. chdir into the dir and
+ * 17. set m_name to the mac equivalent of "."
+ * 18. goto 3
+ */
+struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
{
- struct dir *cdir, *scdir=NULL;
static char path[ MAXPATHLEN + 1];
static struct path ret;
+ struct dir *cdir;
char *data, *p;
- int extend = 0;
int len;
u_int32_t hint;
u_int16_t len16;
int size = 0;
- char sep;
int toUTF8 = 0;
+ LOG(log_maxdebug, logtype_afpd, "came('%s'): {start}", cfrombstr(dir->d_fullpath));
+
data = *cpath;
afp_errno = AFPERR_NOOBJ;
memset(&ret, 0, sizeof(ret));
- switch (ret.m_type = *data) { /* path type */
+
+ switch (ret.m_type = *data) { /* 1 */
case 2:
data++;
len = (unsigned char) *data++;
size = 2;
- sep = 0;
if (afp_version >= 30) {
ret.m_type = 3;
toUTF8 = 1;
len = ntohs(len16);
data += 2;
size = 7;
- sep = 0; /* '/';*/
break;
}
/* else it's an error */
return( NULL );
}
*cpath += len + size;
- *path = '\0';
+
+ path[0] = 0;
ret.m_name = path;
- for ( ;; ) {
- if ( len == 0 ) {
- if (movecwd( vol, dir ) < 0 ) {
- return invalidate(vol, dir, &ret );
- }
- if (*path == '\0') {
- ret.u_name = ".";
- ret.d_dir = dir;
- }
- return &ret;
- }
- if (*data == sep ) {
+ if (movecwd(vol, dir) < 0 ) {
+ LOG(log_debug, logtype_afpd, "cname(did:%u): failed to chdir to '%s'",
+ ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
+ if (len == 0)
+ return path_from_dir(vol, dir, &ret);
+ else
+ return NULL;
+ }
+
+ while (len) { /* 3 */
+ if (*data == 0) { /* 4 or 5 */
data++;
len--;
- }
- while (*data == sep && len > 0 ) {
- if ( dir->d_parent == NULL ) {
- return NULL;
+ while (len > 0 && *data == 0) { /* 5 */
+ /* chdir to parrent dir */
+ if ((dir = dirlookup(vol, dir->d_pdid)) == NULL)
+ return NULL;
+ if (movecwd( vol, dir ) < 0 ) {
+ dir_remove(vol, dir);
+ return NULL;
+ }
+ data++;
+ len--;
}
- dir = dir->d_parent;
- data++;
- len--;
+ continue;
}
- /* would this be faster with strlen + strncpy? */
- p = path;
- while ( *data != sep && len > 0 ) {
+ /* 6*/
+ for ( p = path; *data != 0 && len > 0; len-- ) {
*p++ = *data++;
if (p > &path[ MAXPATHLEN]) {
afp_errno = AFPERR_PARAM;
- return( NULL );
+ return NULL;
}
- len--;
- }
-
- /* short cut bits by chopping off a trailing \0. this also
- makes the traversal happy w/ filenames at the end of the
- cname. */
- if (len == 1)
- len--;
-
- *p = '\0';
-
- if ( p == path ) { /* end of the name parameter */
- continue;
}
+ *p = 0; /* Terminate string */
ret.u_name = NULL;
- if (afp_version >= 30) {
- char *t;
- cnid_t fileid;
- if (toUTF8) {
- static char temp[ MAXPATHLEN + 1];
-
- if (dir->d_did == DIRDID_ROOT_PARENT) {
- /*
- With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
- So we compare it with the longname from the current volume and if they match
- we overwrite the requested path with the utf8 volume name so that the following
- strcmp can match.
- */
- ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
- if (strcasecmp( path, temp) == 0)
- ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
- } else {
- /* toUTF8 */
- if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
- afp_errno = AFPERR_PARAM;
- return( NULL );
- }
- strcpy(path, temp);
- }
- }
+ if (cname_mtouname(vol, dir, &ret, toUTF8) != 0) { /* 7 */
+ LOG(log_error, logtype_afpd, "cname('%s'): error from cname_mtouname", path);
+ return NULL;
+ }
- if (strlen(ret.m_name) > 255) { /* Safeguard */
- afp_errno = AFPERR_PARAM;
- return( NULL );
- }
+ LOG(log_maxdebug, logtype_afpd, "came('%s'): {node: '%s}", cfrombstr(dir->d_fullpath), ret.u_name);
- /* check for OS X mangled filename :( */
+ /* Prevent access to our special folders like .AppleDouble */
+ if (check_name(vol, ret.u_name)) {
+ /* the name is illegal */
+ LOG(log_info, logtype_afpd, "cname: illegal path: '%s'", ret.u_name);
+ afp_errno = AFPERR_PARAM;
+ return NULL;
+ }
- t = demangle_osx(vol, path, dir->d_did, &fileid);
- if (t != path) {
- ret.u_name = t;
- /* duplicate work but we can't reuse all convert_char we did in demangle_osx
- * flags weren't the same
+ if (dir->d_did == DIRDID_ROOT_PARENT) { /* 8 */
+ /*
+ * Special case: CNID 1
+ * root parent (did 1) has one child: the volume. Requests for did=1 with
+ * some <name> must check against the volume name.
+ */
+ if ((strcmp(cfrombstr(vol->v_root->d_m_name), ret.m_name)) == 0)
+ cdir = vol->v_root;
+ else
+ return NULL;
+ } else {
+ /*
+ * CNID != 1, eg. most of the times we take this way.
+ * Now check if current path-part is a file or dir:
+ * o if it's dir we have to step into it
+ * o if it's a file we expect it to be the last part of the requested path
+ * and thus call continue which should terminate the while loop because
+ * len = 0. Ok?
+ */
+ if (of_stat(&ret) != 0) { /* 9 */
+ /*
+ * ret.u_name doesn't exist, might be afp_createfile|dir
+ * that means it should have been the last part
*/
- if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
- /* at last got our view of mac name */
- strcpy(path,t);
+ if (len > 0) {
+ /* it wasn't the last part, so we have a bogus path request */
+ afp_errno = AFPERR_NOOBJ;
+ return NULL;
}
+ /*
+ * this will terminate clean in while (1) because len == 0,
+ * probably afp_createfile|dir
+ */
+ LOG(log_maxdebug, logtype_afpd, "came('%s'): {leave-cnode ENOENT (possile create request): '%s'}",
+ cfrombstr(dir->d_fullpath), ret.u_name);
+ continue; /* 10 */
}
- }
- if (ret.u_name == NULL) {
- if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
- afp_errno = AFPERR_PARAM;
- return NULL;
- }
- }
- if ( !extend ) {
- ucs2_t *tmpname;
- cdir = dir->d_child;
- scdir = NULL;
- if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
- (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
- CH_UCS2, path, -1, (char **)&tmpname) )
- {
- while (cdir) {
- if (!cdir->d_m_name_ucs2) {
- LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
- /* this shouldn't happen !!!! */
- goto noucsfallback;
- }
- if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
- break;
- }
- if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
- scdir = cdir;
- }
- cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
- }
- free(tmpname);
- }
- else {
- noucsfallback:
- if (dir->d_did == DIRDID_ROOT_PARENT) {
- /*
- root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
- must check against the volume name.
- */
- if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
- cdir = vol->v_dir;
- else
- cdir = NULL;
+ switch (ret.st.st_mode & S_IFMT) {
+ case S_IFREG: /* 11 */
+ LOG(log_debug, logtype_afpd, "came('%s'): {file: '%s'}",
+ cfrombstr(dir->d_fullpath), ret.u_name);
+ if (len > 0) {
+ /* it wasn't the last part, so we have a bogus path request */
+ afp_errno = AFPERR_PARAM;
+ return NULL;
}
- else {
- cdir = dirsearch_byname(vol, dir, ret.u_name);
+ continue; /* continues while loop */
+ case S_IFLNK: /* 12 */
+ LOG(log_debug, logtype_afpd, "came('%s'): {link: '%s'}",
+ cfrombstr(dir->d_fullpath), ret.u_name);
+ if (len > 0) {
+ LOG(log_warning, logtype_afpd, "came('%s'): {symlinked dir: '%s'}",
+ cfrombstr(dir->d_fullpath), ret.u_name);
+ afp_errno = AFPERR_PARAM;
+ return NULL;
}
+ continue; /* continues while loop */
+ case S_IFDIR: /* 13 */
+ break;
+ default:
+ LOG(log_info, logtype_afpd, "cname: special file: '%s'", ret.u_name);
+ afp_errno = AFPERR_NODIR;
+ return NULL;
}
- if (cdir == NULL && scdir != NULL) {
- cdir = scdir;
- /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
- }
-
- if ( cdir == NULL ) {
- ++extend;
- /* if dir == curdir it always succeed,
- even if curdir is deleted.
- it's not a pb because it will fail in extenddir
- */
- if ( movecwd( vol, dir ) < 0 ) {
- /* dir is not valid anymore
- we delete dir from the cache and abort.
- */
- if ( dir->d_did == DIRDID_ROOT_PARENT) {
- afp_errno = AFPERR_NOOBJ;
- return NULL;
- }
- if (afp_errno == AFPERR_ACCESS)
- return NULL;
- dir_invalidate(vol, dir);
+ /* 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 */
+ if (cdir == NULL) {
+ /* Not in cache, create one */
+ if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */
+ LOG(log_error, logtype_afpd, "cname(did:%u, name:'%s', cwd:'%s'): failed to add dir",
+ ntohl(dir->d_did), ret.u_name, getcwdpath());
return NULL;
}
- cdir = extenddir( vol, dir, &ret );
}
+ } /* if/else cnid==1 */
+
+ /* Now chdir to the evaluated dir */
+ if (movecwd( vol, cdir ) < 0 ) { /* 16 */
+ LOG(log_debug, logtype_afpd, "cname(cwd:'%s'): failed to chdir to new subdir '%s': %s",
+ cfrombstr(curdir->d_fullpath), cfrombstr(cdir->d_fullpath), strerror(errno));
+ if (len == 0)
+ return path_from_dir(vol, cdir, &ret);
+ else
+ return NULL;
+ }
+ dir = cdir;
+ ret.m_name[0] = 0; /* 17, so we later know last token was a dir */
+ } /* while (len) */
- } else {
- cdir = extenddir( vol, dir, &ret );
- } /* if (!extend) */
+ if (curdir->d_did == DIRDID_ROOT_PARENT) {
+ afp_errno = AFPERR_DID1;
+ return NULL;
+ }
- if ( cdir == NULL ) {
+ if (ret.m_name[0] == 0) {
+ /* Last part was a dir */
+ ret.u_name = mtoupath(vol, ret.m_name, 0, 1); /* Force "." into a useable static buffer */
+ ret.d_dir = dir;
+ }
- if ( len > 0 || !ret.u_name ) {
- return NULL;
- }
+ LOG(log_debug, logtype_afpd, "came('%s') {end: curdir:'%s', path:'%s'}",
+ cfrombstr(dir->d_fullpath),
+ cfrombstr(curdir->d_fullpath),
+ ret.u_name);
- } else {
- dir = cdir;
- *path = '\0';
- }
- } /* for (;;) */
+ return &ret;
}
/*
- * Move curdir to dir, with a possible chdir()
+ * @brief chdir() to dir
+ *
+ * @param vol (r) pointer to struct vol
+ * @param dir (r) pointer to struct dir
+ *
+ * @returns 0 on success, -1 on error with afp_errno set appropiately
*/
-int movecwd(struct vol *vol, struct dir *dir)
+int movecwd(const struct vol *vol, struct dir *dir)
{
- char path[MAXPATHLEN + 1];
- struct dir *d;
- char *p, *u;
- int n;
- int ret;
+ int ret;
- if ( dir == curdir ) {
+ AFP_ASSERT(vol);
+ AFP_ASSERT(dir);
+
+ LOG(log_maxdebug, logtype_afpd, "movecwd(curdir:'%s', cwd:'%s')",
+ cfrombstr(curdir->d_fullpath), getcwdpath());
+
+ if ( dir == curdir)
return( 0 );
- }
- if ( dir->d_did == DIRDID_ROOT_PARENT) {
- afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
- return( -1 );
+ if (dir->d_did == DIRDID_ROOT_PARENT) {
+ curdir = &rootParent;
+ return 0;
}
- p = path + sizeof(path) - 1;
- *p = '\0';
- for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
- u = d->d_u_name;
- if (!u) {
- /* parent directory is deleted */
- afp_errno = AFPERR_NOOBJ;
- return -1;
- }
- n = strlen( u );
- if (p -n -1 < path) {
- afp_errno = AFPERR_PARAM;
- return -1;
- }
- *--p = '/';
- p -= n;
- memcpy( p, u, n );
- }
- if ( d != curdir ) {
- n = strlen( vol->v_path );
- if (p -n -1 < path) {
- afp_errno = AFPERR_PARAM;
- return -1;
- }
- *--p = '/';
- p -= n;
- memcpy( p, vol->v_path, n );
- }
- if ( (ret = lchdir(p )) != 0 ) {
- LOG(log_debug, logtype_afpd, "movecwd('%s'): ret:%d, %u/%s", p, ret, errno, strerror(errno));
+ LOG(log_debug, logtype_afpd, "movecwd(did:%u, '%s')",
+ ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
+ if ((ret = lchdir(cfrombstr(dir->d_fullpath))) != 0 ) {
+ LOG(log_debug, logtype_afpd, "movecwd('%s'): ret: %u, %s",
+ cfrombstr(dir->d_fullpath), ret, strerror(errno));
if (ret == 1) {
/* p is a symlink or getcwd failed */
afp_errno = AFPERR_BADTYPE;
- vol->v_curdir = curdir = vol->v_dir;
+
if (chdir(vol->v_path ) < 0) {
- LOG(log_debug, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
+ LOG(log_error, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
/* XXX what do we do here? */
}
+ curdir = vol->v_root;
return -1;
}
+
switch (errno) {
case EACCES:
case EPERM:
break;
default:
afp_errno = AFPERR_NOOBJ;
-
}
return( -1 );
}
- vol->v_curdir = curdir = dir;
+
+ curdir = dir;
return( 0 );
}
dir->d_flags &= ~DIRF_CNID;
}
-/* ---------------------
- * is our cached offspring count valid?
- */
-
-static int diroffcnt(struct dir *dir, struct stat *st)
-{
- return st->st_ctime == dir->ctime;
-}
/* ---------------------
* is our cached also for reenumerate id?
*/
-
int dirreenumerate(struct dir *dir, struct stat *st)
{
return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
}
-/* --------------------- */
-static int invisible_dots(const struct vol *vol, const char *name)
-{
- return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, "..");
-}
-
/* ------------------------------
(".", curdir)
(name, dir) with curdir:name == dir, from afp_enumerate
s_path->st.st_dev,
s_path->st.st_ino,
dir->d_did,
- dir->d_parent->d_did,
+ dir->d_pdid,
vol->v_stamp);
ad_flush( &ad);
}
}
}
- if ( dir->d_did == DIRDID_ROOT) {
- pdid = DIRDID_ROOT_PARENT;
- } else if (dir->d_did == DIRDID_ROOT_PARENT) {
- pdid = 0;
- } else {
- pdid = dir->d_parent->d_did;
- }
+ pdid = dir->d_pdid;
data = buf;
while ( bitmap != 0 ) {
case DIRPBIT_ATTR :
if ( isad ) {
ad_getattr(&ad, &ashort);
- } else if (invisible_dots(vol, dir->d_u_name)) {
+ } else if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
ashort = htons(ATTRBIT_INVISIBLE);
} else
ashort = 0;
case DIRPBIT_PDID :
memcpy( data, &pdid, sizeof( pdid ));
data += sizeof( pdid );
+ LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
+ s_path->u_name, ntohl(pdid));
break;
case DIRPBIT_CDATE :
memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
/* dot files are by default visible */
- if (invisible_dots(vol, dir->d_u_name)) {
+ if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
ashort = htons(FINDERINFO_INVISIBLE);
memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
}
case DIRPBIT_DID :
memcpy( data, &dir->d_did, sizeof( aint ));
data += sizeof( aint );
+ LOG(log_debug, logtype_afpd, "metadata('%s'): DID: %u",
+ s_path->u_name, ntohl(dir->d_did));
break;
case DIRPBIT_OFFCNT :
if ( l_nameoff ) {
ashort = htons( data - buf );
memcpy( l_nameoff, &ashort, sizeof( ashort ));
- data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
+ data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, 0);
}
if ( utf_nameoff ) {
ashort = htons( data - buf );
memcpy( utf_nameoff, &ashort, sizeof( ashort ));
- data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
+ data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, utf8);
}
if ( isad ) {
ad_close_metadata( &ad );
*
* assume path == '\0' eg. it's a directory in canonical form
*/
-
-struct path Cur_Path = {
- 0,
- "", /* mac name */
- ".", /* unix name */
- 0, /* id */
- NULL,/* struct dir */
- 0, /* stat is not set */
-};
-
-/* ------------------ */
-static int set_dir_errors(struct path *path, const char *where, int err)
-{
- switch ( err ) {
- case EPERM :
- case EACCES :
- return AFPERR_ACCESS;
- case EROFS :
- return AFPERR_VLOCK;
- }
- LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
- return AFPERR_PARAM;
-}
-
-/* ------------------ */
-int setdirparams(struct vol *vol,
- struct path *path, u_int16_t d_bitmap, char *buf )
+int setdirparams(struct vol *vol, struct path *path, u_int16_t d_bitmap, char *buf )
{
struct maccess ma;
struct adouble ad;
* to set our name, etc.
*/
if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
- ad_setname(&ad, curdir->d_m_name);
+ ad_setname(&ad, cfrombstr(curdir->d_m_name));
}
}
if (path->st_valid && !path->st_errno) {
struct stat *st = &path->st;
- if (dir && dir->d_parent) {
- ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_parent->d_did, vol->v_stamp);
+ if (dir && dir->d_pdid) {
+ ad_setid(&ad, st->st_dev, st->st_ino, dir->d_did, dir->d_pdid, vol->v_stamp);
}
}
ad_flush( &ad);
if (change_parent_mdate && dir->d_did != DIRDID_ROOT
&& gettimeofday(&tv, NULL) == 0) {
- if (!movecwd(vol, dir->d_parent)) {
+ if (movecwd(vol, dirlookup(vol, dir->d_pdid)) == 0) {
newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
/* be careful with bitmap because now dir is null */
bitmap = 1<<DIRPBIT_MDATE;
if ( fsync(dfd) < 0 )
LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
- vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
+ vol->ad_path(cfrombstr(dir->d_u_name), ADFLAGS_DIR), strerror(errno) );
close(dfd);
}
if (of_stat(s_path) < 0) {
return AFPERR_MISC;
}
+
curdir->offcnt++;
- if ((dir = adddir( vol, curdir, s_path)) == NULL) {
+
+ if ((dir = dir_add(vol, curdir, s_path, strlen(s_path->u_name))) == NULL) {
return AFPERR_MISC;
}
ad_close_metadata( &ad);
createdir_done:
-#ifdef HAVE_NFSv4_ACLS
- /* FIXME: are we really inside the created dir? */
- addir_inherit_acl(vol);
-#endif
-
memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
*rbuflen = sizeof( u_int32_t );
setvoltime(obj, vol );
char *newname)
{
struct adouble ad;
- struct dir *parent;
- char *buf;
- int len, err;
+ int err;
/* existence check moved to afp_moveandrename */
if ( unix_rename(dirfd, src, -1, dst ) < 0 ) {
vol->vfs->vfs_renamedir(vol, dirfd, src, dst);
- len = strlen( newname );
- /* rename() succeeded so we need to update our tree even if we can't open
- * metadata
- */
-
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
ad_close_metadata( &ad);
}
- dir_hash_del(vol, dir);
- if (dir->d_m_name == dir->d_u_name)
- dir->d_u_name = NULL;
-
- if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
- LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
- /* FIXME : fatal ? */
- return AFPERR_MISC;
- }
- dir->d_m_name = buf;
- strcpy( dir->d_m_name, newname );
-
- if (newname == dst) {
- free(dir->d_u_name);
- dir->d_u_name = dir->d_m_name;
- }
- else {
- if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
- LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
- return AFPERR_MISC;
- }
- dir->d_u_name = buf;
- strcpy( dir->d_u_name, dst );
- }
-
- if (dir->d_m_name_ucs2)
- free(dir->d_m_name_ucs2);
-
- dir->d_m_name_ucs2 = NULL;
- if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, -1, (char**)&dir->d_m_name_ucs2))
- dir->d_m_name_ucs2 = NULL;
-
- if (( parent = dir->d_parent ) == NULL ) {
- return( AFP_OK );
- }
- if ( parent == newparent ) {
- hash_alloc_insert(vol->v_hash, dir, dir);
- return( AFP_OK );
- }
-
- /* detach from old parent and add to new one. */
- dirchildremove(parent, dir);
- dir->d_parent = newparent;
- dirchildadd(vol, newparent, dir);
return( AFP_OK );
}
u_int16_t ashort;
int err;
- if ( curdir->d_parent == NULL ) {
+ if ( dirlookup(vol, curdir->d_pdid) == NULL ) {
return( AFPERR_ACCESS );
}
}
}
- if ( movecwd( vol, curdir->d_parent ) < 0 ) {
+ if ( movecwd(vol, dirlookup(vol, curdir->d_pdid)) < 0 ) {
err = afp_errno;
goto delete_done;
}
- err = netatalk_rmdir_all_errors(-1, fdir->d_u_name);
+ err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name));
if ( err == AFP_OK || err == AFPERR_NOOBJ) {
- dirchildremove(curdir, fdir);
cnid_delete(vol->v_cdb, fdir->d_did);
dir_remove( vol, fdir );
}
sfunc = (unsigned char) *ibuf++;
*rbuflen = 0;
-
if (sfunc >= 3 && sfunc <= 6) {
if (afp_version < 30) {
return( AFPERR_PARAM );
name = NULL;
}
break;
-#ifdef HAVE_NFSv4_ACLS
+
case 5 : /* UUID -> username */
case 6 : /* UUID -> groupname */
if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
return AFPERR_PARAM;
LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
uuidtype_t type;
- len = getnamefromuuid( ibuf, &name, &type);
+ len = getnamefromuuid((unsigned char*) ibuf, &name, &type);
if (len != 0) /* its a error code, not len */
return AFPERR_NOITEM;
- if (type == UUID_USER) {
+ switch (type) {
+ case UUID_USER:
if (( pw = getpwnam( name )) == NULL )
return( AFPERR_NOITEM );
LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
memcpy( rbuf, &id, sizeof( id ));
rbuf += sizeof( id );
*rbuflen = 2 * sizeof( id );
- } else { /* type == UUID_GROUP */
+ break;
+ case UUID_GROUP:
if (( gr = getgrnam( name )) == NULL )
return( AFPERR_NOITEM );
LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
memcpy( rbuf, &id, sizeof( id ));
rbuf += sizeof( id );
*rbuflen = 2 * sizeof( id );
+ break;
+ case UUID_LOCAL:
+ free(name);
+ return (AFPERR_NOITEM);
+ default:
+ return AFPERR_MISC;
}
break;
-#endif
+
default :
return( AFPERR_PARAM );
}
case 4 :
len = (unsigned char) *ibuf++;
break;
-#ifdef HAVE_NFSv4_ACLS
case 5 : /* username -> UUID */
case 6 : /* groupname -> UUID */
if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
len = ntohs(ulen);
ibuf += 2;
break;
-#endif
default :
return( AFPERR_PARAM );
}
memcpy( rbuf, &id, sizeof( id ));
*rbuflen = sizeof( id );
break;
-#ifdef HAVE_NFSv4_ACLS
case 5 : /* username -> UUID */
LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
- if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
+ if (0 != getuuidfromname(ibuf, UUID_USER, (unsigned char *)rbuf))
return AFPERR_NOITEM;
*rbuflen = UUID_BINSIZE;
break;
case 6 : /* groupname -> UUID */
LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
- if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
+ if (0 != getuuidfromname(ibuf, UUID_GROUP, (unsigned char *)rbuf))
return AFPERR_NOITEM;
*rbuflen = UUID_BINSIZE;
break;
-#endif
}
}
return( AFP_OK );
/*
- * $Id: directory.h,v 1.34 2010-03-12 15:16:49 franklahm Exp $
- *
* Copyright (c) 1990,1991 Regents of The University of Michigan.
* All Rights Reserved.
*
#include "globals.h"
#include "volume.h"
-#define DIRTREE_COLOR_RED 0
-#define DIRTREE_COLOR_BLACK 1
-
#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 DIRF_OFFCNT (1<<4) /* offsprings count is valid */
+#define DIRF_CNID (1<<5) /* renumerate id */
+#define DIRF_CACHELOCK (1<<6) /* lock in cache, don't remove in dircache_eviction, for catsearch */
#define AFPDIR_READ (1<<0)
#define FILDIRBIT_ISDIR (1 << 7) /* is a directory */
#define FILDIRBIT_ISFILE (0) /* is a file */
-/* reserved directory id's */
-#define DIRDID_ROOT_PARENT htonl(1) /* parent directory of root */
-#define DIRDID_ROOT htonl(2) /* root directory */
-
/* file/directory ids. what a mess. we scramble things in a vain attempt
* to get something meaningful */
#ifndef AFS
#define CNID(a,b) (((a)->st_ino & 0x7fffffff) | CNID_FILE(b))
#endif /* AFS */
-
struct maccess {
u_char ma_user;
u_char ma_world;
#define AR_UWRITE (1<<2)
#define AR_UOWN (1<<7)
-extern struct dir *dirnew (const char *, const char *);
-extern void dirfreename (struct dir *);
-extern void dirfree (struct dir *);
-extern struct dir *dirsearch (const struct vol *, u_int32_t);
-extern struct dir *dirlookup (struct vol *, u_int32_t);
-extern struct dir *dirsearch_byname (const struct vol *, struct dir *,char *);
-
-extern struct dir *adddir (struct vol *, struct dir *,
- struct path *);
-
-extern int movecwd (struct vol *, struct dir *);
-extern int deletecurdir (struct vol *);
-extern struct path *cname (struct vol *, struct dir *,
- char **);
-extern mode_t mtoumode (struct maccess *);
-extern void utommode (struct stat *, struct maccess *);
-extern int getdirparams (const struct vol *, u_int16_t, struct path *,
- struct dir *, char *, size_t *);
-extern int setdirparams (struct vol *, struct path *, u_int16_t, char *);
-extern int renamedir(const struct vol *, int, char *, char *, struct dir *,
- struct dir *, char *);
-extern int path_error (struct path *, int error);
-
-extern void setdiroffcnt (struct dir *dir, struct stat *st, u_int32_t count);
-extern int dirreenumerate (struct dir *dir, struct stat *st);
+q_t *invalid_dircache_entries;
typedef int (*dir_loop)(struct dirent *, char *, void *);
-extern int for_each_dirent (const struct vol *, char *, dir_loop , void *);
-
-extern int check_access (char *name , int mode);
-extern int file_access (struct path *path, int mode);
-
-extern int netatalk_unlink (const char *name);
+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);
+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,
+ const char *new_mname, const char *new_uname, bstring pdir_fullpath);
+extern int dir_remove(const struct vol *vol, struct dir *dir);
+extern struct dir *dirlookup (const struct vol *, cnid_t);
+extern int movecwd (const struct vol *, struct dir *);
+extern struct path *cname (struct vol *, struct dir *, char **);
+
+extern int deletecurdir (struct vol *);
+extern mode_t mtoumode (struct maccess *);
+extern void utommode (struct stat *, struct maccess *);
+extern int getdirparams (const struct vol *, u_int16_t, struct path *,
+ struct dir *, char *, size_t *);
-extern int caseenumerate (const struct vol *, struct path *, struct dir *);
+extern int setdirparams(struct vol *, struct path *, u_int16_t, char *);
+extern int renamedir(const struct vol *, int, char *, char *, struct dir *,
+ struct dir *, char *);
+extern int path_error(struct path *, int error);
+extern void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count);
+extern int dirreenumerate(struct dir *dir, struct stat *st);
+extern int for_each_dirent(const struct vol *, char *, dir_loop , void *);
+extern int check_access(char *name , int mode);
+extern int file_access(struct path *path, int mode);
+extern int netatalk_unlink (const char *name);
+extern int caseenumerate (const struct vol *, struct path *, struct dir *);
-extern hash_t *dirhash (void);
/* from enumerate.c */
-extern char *check_dirent (const struct vol *, char *);
+extern char *check_dirent (const struct vol *, char *);
/* FP functions */
int afp_createdir (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen);
/*
- * $Id: enumerate.c,v 1.49 2010-02-10 14:05:37 franklahm Exp $
- *
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
*/
#include <atalk/adouble.h>
#include <atalk/vfs.h>
#include <atalk/cnid.h>
+#include <atalk/util.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+
#include "desktop.h"
#include "directory.h"
+#include "dircache.h"
#include "volume.h"
#include "globals.h"
#include "file.h"
return path_error(o_path, AFPERR_NODIR );
}
- LOG(log_debug, logtype_afpd, "enumerate(vid:%u, did:%u, name:'%s', f/d:%04x/%04x, rc:%u, i:%u, max:%u)",
- ntohs(vid), ntohl(did), o_path->u_name, fbitmap, dbitmap, reqcnt, sindex, maxsz);
+ LOG(log_debug, logtype_afpd, "enumerate(\"%s/%s\", f/d:%04x/%04x, rc:%u, i:%u, max:%u)",
+ getcwdpath(), o_path->u_name, fbitmap, dbitmap, reqcnt, sindex, maxsz);
data = rbuf + 3 * sizeof( u_int16_t );
sz = 3 * sizeof( u_int16_t ); /* fbitmap, dbitmap, reqcount */
if ( dbitmap == 0 ) {
continue;
}
- dir = dirsearch_byname(vol, curdir, s_path.u_name);
- if (!dir && NULL == (dir = adddir( vol, curdir, &s_path) ) ) {
+ 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 = 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);
return AFPERR_MISC;
}
- if (AFP_OK != ( ret = getdirparams(vol, dbitmap, &s_path, dir,
- data + header , &esz ))) {
- return( ret );
}
+ if ((ret = getdirparams(vol, dbitmap, &s_path, dir, data + header , &esz)) != AFP_OK)
+ return( ret );
} else {
if ( fbitmap == 0 ) {
continue;
}
+ /* files are added to the dircache in getfilparams() -> getmetadata() */
if (AFP_OK != ( ret = getfilparams(vol, fbitmap, &s_path, curdir,
data + header , &esz )) ) {
return( ret );
/*
- * $Id: file.c,v 1.141 2010-03-12 15:16:49 franklahm Exp $
- *
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
*/
#include <atalk/unix.h>
#include "directory.h"
+#include "dircache.h"
#include "desktop.h"
#include "volume.h"
#include "fork.h"
(1 << FILPBIT_FNUM) |\
(1 << FILPBIT_UNIXPR)))
-/* -------------------------- */
-u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
- const cnid_t did, char *upath, const int len)
+/*!
+ * @brief Get CNID for did/upath args both from database and adouble file
+ *
+ * 1. Get the objects CNID as stored in its adouble file
+ * 2. Get the objects CNID from the database
+ * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
+ * 4. In case 2 and 3 differ, store 3 in the adouble file
+ *
+ * @param vol (rw) volume
+ * @param adp (rw) adouble struct of object upath, might be NULL
+ * @param st (r) stat of upath, must NOT be NULL
+ * @param did (r) parent CNID of upath
+ * @param upath (r) name of object
+ * @param len (r) strlen of upath
+ */
+uint32_t get_id(struct vol *vol,
+ struct adouble *adp,
+ const struct stat *st,
+ const cnid_t did,
+ const char *upath,
+ const int len)
{
static int first = 1; /* mark if this func is called the first time */
u_int32_t adcnid;
if (vol->v_cdb != NULL) {
/* prime aint with what we think is the cnid, set did to zero for
catching moved files */
- adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
+ adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
- dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
+ dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
/* Throw errors if cnid_add fails. */
if (dbcnid == CNID_INVALID) {
switch (errno) {
/* we have to do it here for "dbd" because it uses "lazy opening" */
/* In order to not end in a loop somehow with goto restart below */
/* */
- if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) {
+ if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
cnid_close(vol->v_cdb);
free(vol->v_cnidscheme);
vol->v_cnidscheme = strdup("tdb");
goto exit;
}
}
- else if (adp && (adcnid != dbcnid)) {
+ else if (adp && (adcnid != dbcnid)) { /* 4 */
/* Update the ressource fork. For a folder adp is always null */
- LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
+ LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
+ getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
ad_flush(adp);
}
struct stat *st;
struct maccess ma;
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "begin getmetadata:");
-#endif /* DEBUG */
-
upath = path->u_name;
st = &path->st;
-
data = buf;
if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
|| (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
|| (bitmap & (1 << FILPBIT_FNUM))) {
- if (!path->id)
- id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
- else
+ if (!path->id) {
+ struct dir *cachedfile;
+ int len = strlen(upath);
+ if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL)
+ id = cachedfile->d_did;
+ else {
+ id = get_id(vol, adp, st, dir->d_did, upath, len);
+
+ /* Add it to the cache */
+ LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
+ ntohl(dir->d_did), upath, ntohl(id));
+
+ /* Get macname from unixname first */
+ 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);
+ }
+ }
+
+ if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
+ LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
+ exit(EXITERR_SYS);
+ }
+
+ if ((dircache_add(cachedfile)) != 0) {
+ LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
+ exit(EXITERR_SYS);
+ }
+ }
+ } else {
id = path->id;
+ }
+
if (id == CNID_INVALID)
return afp_errno;
+
if (!path->m_name) {
path->m_name = utompath(vol, upath, id, utf8_encoding());
}
#endif
memcpy(data, &ashort, sizeof( ashort ));
data += sizeof( ashort );
+ LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
+ path->u_name, ntohs(ashort));
break;
case FILPBIT_PDID :
memcpy(data, &dir->d_did, sizeof( u_int32_t ));
data += sizeof( u_int32_t );
+ LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
+ path->u_name, ntohl(dir->d_did));
break;
case FILPBIT_CDATE :
case FILPBIT_FNUM :
memcpy(data, &id, sizeof( id ));
data += sizeof( id );
+ LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
+ path->u_name, ntohl(id));
break;
case FILPBIT_DFLEN :
int opened = 0;
int rc;
-#ifdef DEBUG
- LOG(log_debug9, logtype_default, "begin getfilparams:");
-#endif /* DEBUG */
-
opened = PARAM_NEED_ADP(bitmap);
adp = NULL;
if ( adp ) {
ad_close_metadata( adp);
}
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "end getfilparams:");
-#endif /* DEBUG */
return( rc );
}
}
if (NULL == ( dir = dirlookup( vol, id )) ) {
+ if (afp_errno == AFPERR_NOOBJ) {
+ err = AFPERR_NOOBJ;
+ goto delete;
+ }
return( AFPERR_PARAM );
}
else if (S_ISDIR(st.st_mode)) /* directories are bad */
return AFPERR_BADTYPE;
+delete:
if (cnid_delete(vol->v_cdb, fileid)) {
switch (errno) {
case EROFS:
/* id's need switching. src -> dest and dest -> src.
* we need to re-stat() if it was a cross device copy.
*/
- if (sid) {
- cnid_delete(vol->v_cdb, sid);
- }
- if (did) {
- cnid_delete(vol->v_cdb, did);
- }
+ if (sid)
+ cnid_delete(vol->v_cdb, sid);
+ if (did)
+ cnid_delete(vol->v_cdb, did);
+
if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
||
ad_close(addp, ADFLAGS_HF);
}
+ struct dir *cached;
+ if ((cached = dircache_search_by_did(vol, sid)) != NULL)
+ (void)dir_remove(vol, cached);
+ if ((cached = dircache_search_by_did(vol, did)) != NULL)
+ (void)dir_remove(vol, cached);
+
return err;
}
/*
- * $Id: file.h,v 1.26 2010-03-12 15:16:49 franklahm Exp $
+ * $Id: file.h,v 1.26 2010/03/12 15:16:49 franklahm Exp $
*
* Copyright (c) 1990,1991 Regents of The University of Michigan.
* All Rights Reserved.
extern size_t mtoUTF8 (const struct vol *, const char *, size_t , char *, size_t );
extern int copy_path_name (const struct vol *, char *, char *i);
-extern u_int32_t get_id (struct vol *, struct adouble *, const struct stat *,
- const cnid_t , char *, const int );
+extern uint32_t get_id (struct vol *,
+ struct adouble *,
+ const struct stat *,
+ cnid_t ,
+ const char *,
+ int );
/* FP functions */
int afp_exchangefiles (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen);
#include <atalk/cnid.h>
#include <atalk/logger.h>
#include <atalk/unix.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
#include <atalk/acl.h>
#include "directory.h"
+#include "dircache.h"
#include "desktop.h"
#include "volume.h"
#include "fork.h"
#ifdef DROPKLUDGE
int matchfile2dirperms(
/* Since it's kinda' big; I decided against an
-inline function */
- char *upath,
+ inline function */
+ char *upath,
struct vol *vol,
- int did)
+ int did)
/* The below code changes the way file ownership is determined in the name of
-fixing dropboxes. It has known security problem. See the netatalk FAQ for
-more information */
+ fixing dropboxes. It has known security problem. See the netatalk FAQ for
+ more information */
{
- struct stat st, sb;
- struct dir *dir;
- char *adpath;
+ struct stat st, sb;
+ struct dir *dir;
+ char *adpath;
uid_t uid;
int ret = AFP_OK;
#ifdef DEBUG
LOG(log_debug9, logtype_afpd, "begin matchfile2dirperms:");
-#endif
+#endif
if (stat(upath, &st ) < 0) {
LOG(log_error, logtype_afpd, "Could not stat %s: %s", upath, strerror(errno));
adpath, strerror(errno));
ret = AFPERR_ACCESS;
}
- seteuid(uid);
+ seteuid(uid);
}
} /* end else if stat success */
int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
{
- struct stat *st;
- struct vol *vol;
- struct dir *dir;
+ struct stat *st;
+ struct vol *vol;
+ struct dir *dir;
u_int32_t did;
- int ret;
- size_t buflen;
- u_int16_t fbitmap, dbitmap, vid;
+ int ret;
+ size_t buflen;
+ u_int16_t fbitmap, dbitmap, vid;
struct path *s_path;
*rbuflen = 0;
if (NULL == ( vol = getvolbyvid( vid )) ) {
/* was AFPERR_PARAM but it helps OS 10.3 when a volume has been removed
* from the list.
- */
+ */
return( AFPERR_ACCESS );
}
ibuf += sizeof( dbitmap );
if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
- return get_afp_errno(AFPERR_NOOBJ);
+ return get_afp_errno(AFPERR_NOOBJ);
}
- LOG(log_debug, logtype_afpd, "getfildirparams(vid:%u, did:%u, name:'%s', f/d:%04x/%04x) {cwd: %s}",
- ntohs(vid), ntohl(dir->d_did), s_path->u_name, fbitmap, dbitmap, getcwdpath());
+ LOG(log_debug, logtype_afpd, "getfildirparams(vid:%u, did:%u, f/d:%04x/%04x) {cwdid:%u, cwd: %s, name:'%s'}",
+ ntohs(vid), ntohl(dir->d_did), fbitmap, dbitmap,
+ ntohl(curdir->d_did), cfrombstr(curdir->d_fullpath), s_path->u_name);
st = &s_path->st;
if (!s_path->st_valid) {
of_statdir(vol, s_path);
}
if ( s_path->st_errno != 0 ) {
- return( AFPERR_NOOBJ );
+ if (afp_errno != AFPERR_ACCESS) {
+ return( AFPERR_NOOBJ );
+ }
}
if (S_ISDIR(st->st_mode)) {
if (dbitmap) {
dir = s_path->d_dir;
- if (!dir)
+ if (!dir)
return AFPERR_NOOBJ;
ret = getdirparams(vol, dbitmap, s_path, dir,
- rbuf + 3 * sizeof( u_int16_t ), &buflen );
+ rbuf + 3 * sizeof( u_int16_t ), &buflen );
if (ret != AFP_OK )
return( ret );
}
/* this is a directory */
*(rbuf + 2 * sizeof( u_int16_t )) = (char) FILDIRBIT_ISDIR;
} else {
- if (fbitmap && AFP_OK != (ret = getfilparams(vol, fbitmap, s_path, curdir,
- rbuf + 3 * sizeof( u_int16_t ), &buflen )) ) {
+ if (fbitmap && AFP_OK != (ret = getfilparams(vol, fbitmap, s_path, curdir,
+ rbuf + 3 * sizeof( u_int16_t ), &buflen )) ) {
return( ret );
}
/* this is a file */
int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
{
- struct stat *st;
- struct vol *vol;
- struct dir *dir;
+ struct stat *st;
+ struct vol *vol;
+ struct dir *dir;
struct path *path;
- u_int16_t vid, bitmap;
- int did, rc;
+ u_int16_t vid, bitmap;
+ int did, rc;
*rbuflen = 0;
ibuf += 2;
ibuf += sizeof( did);
if (NULL == ( dir = dirlookup( vol, did )) ) {
- return afp_errno;
+ return afp_errno;
}
memcpy( &bitmap, ibuf, sizeof( bitmap ));
ibuf += sizeof( bitmap );
if (NULL == ( path = cname( vol, dir, &ibuf ))) {
- return get_afp_errno(AFPERR_NOOBJ);
+ return get_afp_errno(AFPERR_NOOBJ);
}
st = &path->st;
}
if ( path->st_errno != 0 ) {
- return( AFPERR_NOOBJ );
+ if (afp_errno != AFPERR_ACCESS)
+ return( AFPERR_NOOBJ );
}
/*
* If ibuf is odd, make it even.
return( rc );
}
-/* --------------------------------------------
+/* --------------------------------------------
Factorise some checks on a pathname
*/
int check_name(const struct vol *vol, char *name)
}
} else {
id = sdir->d_did; /* we already have the CNID */
- p = ctoupath( vol, sdir->d_parent, oldname );
+ p = ctoupath( vol, dirlookup(vol, sdir->d_pdid), oldname );
if (!p) {
return AFPERR_PARAM;
}
adflags = ADFLAGS_DIR;
}
-
/*
* p now points to either
* a) full pathname of the source fs object (if renameat is not available)
goto exit;
}
path.u_name = upath;
- st = &path.st;
+ st = &path.st;
if (0 != (rc = check_name(vol, upath))) {
goto exit;
}
/* source == destination. we just silently accept this. */
- if ((!isdir && curdir == sdir) || (isdir && curdir == sdir->d_parent)) {
+ if ((!isdir && curdir == sdir) || (isdir && curdir->d_did == sdir->d_pdid)) {
if (strcmp(oldname, newname) == 0) {
rc = AFP_OK;
goto exit;
rc = AFPERR_MISC;
goto exit;
}
+
+ /* Remove it from the cache */
+ struct dir *cacheddir = dircache_search_by_did(vol, id);
+ if (cacheddir) {
+ LOG(log_warning, logtype_afpd,"Still cached: \"%s/%s\"", getcwdpath(), upath);
+ (void)dir_remove(vol, cacheddir);
+ }
+
/* fix up the catalog entry */
cnid_update(vol->v_cdb, id, st, curdir->d_did, upath, strlen(upath));
}
/* -------------------------------------------- */
int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
{
- struct vol *vol;
- struct dir *sdir;
+ struct vol *vol;
+ struct dir *sdir;
char *oldname, *newname;
struct path *path;
- u_int32_t did;
+ u_int32_t did;
int plen;
- u_int16_t vid;
+ u_int16_t vid;
int isdir = 0;
int rc;
memcpy( &did, ibuf, sizeof( did ));
ibuf += sizeof( did );
if (NULL == ( sdir = dirlookup( vol, did )) ) {
- return afp_errno;
+ return afp_errno;
}
/* source pathname */
if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
- return get_afp_errno(AFPERR_NOOBJ);
+ return get_afp_errno(AFPERR_NOOBJ);
}
sdir = curdir;
}
}
else {
- if ( sdir->d_parent == NULL ) { /* root directory */
+ if ( sdir->d_did == DIRDID_ROOT ) { /* root directory */
return( AFPERR_NORENAME );
}
/* move to destination dir */
- if ( movecwd( vol, sdir->d_parent ) < 0 ) {
+ if ( movecwd( vol, dirlookup(vol, sdir->d_pdid) ) < 0 ) {
return afp_errno;
}
- strcpy(oldname, sdir->d_m_name);
+ memcpy(oldname, cfrombstr(sdir->d_m_name), blength(sdir->d_m_name) +1);
}
/* another place where we know about the path type */
/* ------------------------------- */
int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
{
- struct vol *vol;
- struct dir *dir;
+ struct vol *vol;
+ struct dir *dir;
struct path *s_path;
- char *upath;
- int did, rc;
- u_int16_t vid;
+ char *upath;
+ int did, rc;
+ u_int16_t vid;
*rbuflen = 0;
ibuf += 2;
memcpy( &did, ibuf, sizeof( did ));
ibuf += sizeof( int );
+
if (NULL == ( dir = dirlookup( vol, did )) ) {
- return afp_errno;
+ return afp_errno;
}
if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
- return get_afp_errno(AFPERR_NOOBJ);
+ return get_afp_errno(AFPERR_NOOBJ);
}
upath = s_path->u_name;
if ( path_isadir( s_path) ) {
- if (*s_path->m_name != '\0') {
- rc = AFPERR_ACCESS;
- }
- else {
+ if (*s_path->m_name != '\0' || curdir->d_did == DIRDID_ROOT)
+ rc = AFPERR_ACCESS;
+ else
rc = deletecurdir( vol);
- }
} else if (of_findname(s_path)) {
rc = AFPERR_BUSY;
} else {
}
else {
rc = deletefile(vol, -1, upath, 1);
+
+ struct dir *cachedfile;
+ if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath), s_path->st.st_ctime))) {
+ dircache_remove(vol, cachedfile, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
+ dir_free(cachedfile);
+ }
}
}
if ( rc == AFP_OK ) {
- curdir->offcnt--;
+ curdir->offcnt--;
setvoltime(obj, vol );
}
/* ------------------------ */
char *absupath(const struct vol *vol, struct dir *dir, char *u)
{
- struct dir *d;
- static char path[ MAXPATHLEN + 1];
- char *p;
- int len;
+ static char pathbuf[MAXPATHLEN + 1];
+ bstring path;
- if (u == NULL)
+ if (u == NULL || dir == NULL || vol == NULL)
return NULL;
-
- p = path + sizeof( path ) - 1;
- *p = '\0';
- len = strlen( u );
- p -= len;
- memcpy( p, u, len );
- if (dir) for ( d = dir; d->d_parent; d = d->d_parent ) {
- u = d->d_u_name;
- len = strlen( u );
- if (p -len -1 < path) {
- /* FIXME
- rather rare so LOG error and/or client message ?
- */
- return NULL;
- }
- *--p = '/';
- p -= len;
- memcpy( p, u, len );
- }
- len = strlen( vol->v_path );
- if (p -len -1 < path) {
+
+ if ((path = bstrcpy(dir->d_fullpath)) == NULL)
+ return NULL;
+ if (bcatcstr(path, "/") != BSTR_OK)
+ return NULL;
+ if (bcatcstr(path, u) != BSTR_OK)
+ return NULL;
+ if (path->slen > MAXPATHLEN)
return NULL;
- }
- *--p = '/';
- p -= len;
- memcpy( p, vol->v_path, len );
- return( p );
+ LOG(log_debug, logtype_afpd, "absupath: %s", cfrombstr(path));
+
+ strncpy(pathbuf, cfrombstr(path), blength(path) + 1);
+ bdestroy(path);
+
+ return(pathbuf);
}
-/* ------------------------
- * FIXME dir could be NULL
-*/
char *ctoupath(const struct vol *vol, struct dir *dir, char *name)
{
+ if (vol == NULL || dir == NULL || name == NULL)
+ return NULL;
return absupath(vol, dir, mtoupath(vol, name, dir->d_did, utf8_encoding()));
}
/* ------------------------- */
int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
{
- struct vol *vol;
- struct dir *sdir, *ddir;
+ struct vol *vol;
+ struct dir *sdir, *ddir;
int isdir;
- char *oldname, *newname;
+ char *oldname, *newname;
struct path *path;
- int did;
- int pdid;
+ int did;
+ int pdid;
int plen;
- u_int16_t vid;
+ u_int16_t vid;
int rc;
#ifdef DROPKLUDGE
- int retvalue;
+ int retvalue;
#endif /* DROPKLUDGE */
int sdir_fd = -1;
/* source pathname */
if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
- return get_afp_errno(AFPERR_NOOBJ);
+ return get_afp_errno(AFPERR_NOOBJ);
}
sdir = curdir;
newname = obj->newtmp;
oldname = obj->oldtmp;
-
+
isdir = path_isadir(path);
if ( *path->m_name != '\0' ) {
if (isdir) {
}
strcpy(oldname, path->m_name); /* an extra copy for of_rename */
} else {
- strcpy(oldname, sdir->d_m_name);
+ memcpy(oldname, cfrombstr(sdir->d_m_name), blength(sdir->d_m_name) + 1);
}
#ifdef HAVE_RENAMEAT
}
/* This does the work */
+ LOG(log_debug, logtype_afpd, "afp_move(oldname:'%s', newname:'%s', isdir:%u)",
+ oldname, newname, isdir);
rc = moveandrename(vol, sdir, sdir_fd, oldname, newname, isdir);
if ( rc == AFP_OK ) {
char *upath = mtoupath(vol, newname, pdid, utf8_encoding());
-
+
if (NULL == upath) {
rc = AFPERR_PARAM;
goto exit;
* otherwise, 0 is returned.
*/
{
- int i; /* index to veto_str */
- int j; /* index to path */
+ int i; /* index to veto_str */
+ int j; /* index to path */
if ((veto_str == NULL) || (path == NULL))
return 0;
} else {
if (veto_str[i] != path[j]) {
while ((veto_str[i] != '/')
- && (veto_str[i] != '\0'))
+ && (veto_str[i] != '\0'))
i++;
j = 0;
continue;
}
vol = ofork->of_vol;
- dir = ofork->of_dir;
+ dir = dirlookup(vol, ofork->of_did);
if (NULL == (path.u_name = mtoupath(vol, of_name(ofork), dir->d_did, utf8_encoding()))) {
return( AFPERR_MISC );
/*
- * $Id: fork.h,v 1.18 2010-03-12 15:16:49 franklahm Exp $
- *
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
*/
#include "directory.h"
struct file_key {
- dev_t dev;
- ino_t inode;
+ dev_t dev;
+ ino_t inode;
};
struct ofork {
struct file_key key;
- struct adouble *of_ad;
+ struct adouble *of_ad;
struct vol *of_vol;
- struct dir *of_dir;
-
- u_int16_t of_refnum;
+ cnid_t of_did;
+ uint16_t of_refnum;
int of_flags;
-
struct ofork **prevp, *next;
- struct ofork *of_d_prev, *of_d_next;
+// struct ofork *of_d_prev, *of_d_next;
};
-#define OPENFORK_DATA (0)
-#define OPENFORK_RSCS (1<<7)
+#define OPENFORK_DATA (0)
+#define OPENFORK_RSCS (1<<7)
-#define OPENACC_RD (1<<0)
-#define OPENACC_WR (1<<1)
-#define OPENACC_DRD (1<<4)
-#define OPENACC_DWR (1<<5)
+#define OPENACC_RD (1<<0)
+#define OPENACC_WR (1<<1)
+#define OPENACC_DRD (1<<4)
+#define OPENACC_DWR (1<<5)
/* ofork.of_flags bits */
-#define AFPFORK_OPEN (1<<0)
-#define AFPFORK_RSRC (1<<1)
-#define AFPFORK_DATA (1<<2)
+#define AFPFORK_OPEN (1<<0)
+#define AFPFORK_RSRC (1<<1)
+#define AFPFORK_DATA (1<<2)
#define AFPFORK_DIRTY (1<<3)
#define AFPFORK_ACCRD (1<<4)
#define AFPFORK_ACCWR (1<<5)
#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 */
};
struct afp_options {
- int connections, transports, tickleval, timeout, server_notif, flags;
+ 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 char passwdbits, passwdminlen, loginmaxfail;
u_int32_t server_quantum;
char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile;
char *uampath, *fqdn;
char *pidfile;
char *sigconffile;
+ char *uuidconf;
struct afp_volume_name defaultvol, systemvol, uservol;
int closevol;
charset_t maccharset, unixcharset;
mode_t umask;
mode_t save_mask;
- int sleep;
#ifdef ADMIN_GRP
gid_t admingid;
#endif /* ADMIN_GRP */
/* default value for winbind authentication */
char *ntdomain, *ntseparator;
+ char *logconfig;
};
#define AFPOBJ_TMPSIZ (MAXPATHLEN)
typedef struct _AFPObj {
int proto;
unsigned long servernum;
- void *handle, *config;
+ 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);
- void (*sleep)(void);
/* 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;
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 */
#include <atalk/logger.h>
#include <sys/time.h>
#include <sys/socket.h>
-
+#include <sys/poll.h>
#include <errno.h>
+#include <sys/wait.h>
#include <atalk/adouble.h>
#include "status.h"
#include "fork.h"
#include "uam_auth.h"
+#include "afp_zeroconf.h"
#ifdef TRU64
#include <sys/security.h>
struct afp_options default_options;
static AFPConfig *configs;
static server_child *server_children;
-static fd_set save_rfds;
-static int Ipc_fd = -1;
+static sig_atomic_t reloadconfig = 0;
+
+/* Two pointers to dynamic allocated arrays which store pollfds and associated data */
+static struct pollfd *fdset;
+static struct polldata *polldata;
+static int fdset_size; /* current allocated size */
+static int fdset_used; /* number of used elements */
+
#ifdef TRU64
void afp_get_cmdline( int *ac, char ***av)
}
#endif /* TRU64 */
-static void afp_exit(const int i)
+/* This is registered with atexit() */
+static void afp_exit(void)
{
- server_unlock(default_options.pidfile);
- exit(i);
+ if (parent_or_child == 0)
+ /* Only do this in the parent */
+ server_unlock(default_options.pidfile);
}
+
/* ------------------
initialize fd set we are waiting for.
*/
-static void set_fd(int ipc_fd)
+static void fd_set_listening_sockets(void)
{
AFPConfig *config;
- FD_ZERO(&save_rfds);
for (config = configs; config; config = config->next) {
if (config->fd < 0) /* for proxies */
continue;
- FD_SET(config->fd, &save_rfds);
- }
- if (ipc_fd >= 0) {
- FD_SET(ipc_fd, &save_rfds);
+ fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd, LISTEN_FD, config);
}
}
+static void fd_reset_listening_sockets(void)
+{
+ AFPConfig *config;
+
+ for (config = configs; config; config = config->next) {
+ if (config->fd < 0) /* for proxies */
+ continue;
+ fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd);
+ }
+ fd_set_listening_sockets();
+}
+
/* ------------------ */
static void afp_goaway(int sig)
{
asp_kill(sig);
#endif /* ! NO_DDP */
- dsi_kill(sig);
+ 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;
+ for (config = configs; config; config = config->next)
+ if (config->server_cleanup)
+ config->server_cleanup(config);
+ server_unlock(default_options.pidfile);
+ exit(0);
break;
+
case SIGUSR1 :
- case SIGHUP :
- /* w/ a configuration file, we can force a re-read if we want */
nologin++;
auth_unload();
- if (sig == SIGHUP || ((nologin + 1) & 1)) {
- AFPConfig *config;
-
- LOG(log_info, logtype_afpd, "re-reading configuration file");
- for (config = configs; config; config = config->next)
- if (config->server_cleanup)
- config->server_cleanup(config);
+ LOG(log_info, logtype_afpd, "disallowing logins");
+ break;
- /* configfree close atp socket used for DDP tickle, there's an issue
- * with atp tid.
- */
- configfree(configs, NULL);
- if (!(configs = configinit(&default_options))) {
- LOG(log_error, logtype_afpd, "config re-read: no servers configured");
- afp_exit(EXITERR_CONF);
- }
- set_fd(Ipc_fd);
- } else {
- LOG(log_info, logtype_afpd, "disallowing logins");
- }
- if (sig == SIGHUP) {
- nologin = 0;
- }
+ case SIGHUP :
+ /* w/ a configuration file, we can force a re-read if we want */
+ reloadconfig = 1;
break;
+
default :
LOG(log_error, logtype_afpd, "afp_goaway: bad signal" );
}
- if ( sig == SIGTERM ) {
- AFPConfig *config;
-
- for (config = configs; config; config = config->next)
- if (config->server_cleanup)
- config->server_cleanup(config);
-
- afp_exit(0);
- }
return;
}
static void child_handler(int sig _U_)
{
- server_child_handler(server_children);
+ int fd;
+ int status, i;
+ pid_t pid;
+
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif /* ! WAIT_ANY */
+
+ while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
+ for (i = 0; i < server_children->nforks; i++) {
+ if ((fd = server_child_remove(server_children, i, pid)) != -1) {
+ fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd);
+ break;
+ }
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status))
+ LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
+ else
+ LOG(log_info, logtype_afpd, "child[%d]: done", pid);
+ } else {
+ if (WIFSIGNALED(status))
+ LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
+ else
+ LOG(log_info, logtype_afpd, "child[%d]: died", pid);
+ }
+ }
}
int main(int ac, char **av)
set_auth_parameters( ac, av );
#endif /* TRU64 */
-#ifdef DEBUG1
+ /* Log SIGBUS/SIGSEGV SBT */
fault_setup(NULL);
-#endif
+
+ /* Default log setup: log to syslog */
+ setuplog("default log_note");
+
afp_options_init(&default_options);
if (!afp_options_parse(ac, av, &default_options))
exit(EXITERR_CONF);
default: /* server */
exit(0);
}
+ atexit(afp_exit);
/* install child handler for asp and dsi. we do this before afp_goaway
* as afp_goaway references stuff from here.
if (!(server_children = server_child_alloc(default_options.connections,
CHILD_NFORKS))) {
LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
memset(&sv, 0, sizeof(sv));
-#ifdef AFP3x
/* linux at least up to 2.4.22 send a SIGXFZ for vfat fs,
even if the file is open with O_LARGEFILE ! */
#ifdef SIGXFSZ
sigemptyset( &sv.sa_mask );
if (sigaction(SIGXFSZ, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
-#endif
#endif
sv.sa_handler = child_handler;
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
sv.sa_handler = afp_goaway;
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
sigemptyset( &sv.sa_mask );
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGHUP, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
sv.sa_flags = SA_RESTART;
if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
- afp_exit(EXITERR_SYS);
+ exit(EXITERR_SYS);
}
/* afpd.conf: not in config file: lockfile, connections, configfile
#endif
sigaddset(&sigs, SIGCHLD);
- sigprocmask(SIG_BLOCK, &sigs, NULL);
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
if (!(configs = configinit(&default_options))) {
LOG(log_error, logtype_afpd, "main: no servers configured");
- afp_exit(EXITERR_CONF);
+ exit(EXITERR_CONF);
}
- sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+ pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
/* Register CNID */
cnid_init();
/* watch atp, dsi sockets and ipc parent/child file descriptor. */
- if ((ipc = server_ipc_create())) {
- Ipc_fd = server_ipc_parent(ipc);
- }
- set_fd(Ipc_fd);
+ fd_set_listening_sockets();
+
+ afp_child_t *child;
/* wait for an appleshare connection. parent remains in the loop
* while the children get handled by afp_over_{asp,dsi}. this is
* afterwards. establishing timeouts for logins is a possible
* solution. */
while (1) {
- rfds = save_rfds;
- sigprocmask(SIG_UNBLOCK, &sigs, NULL);
- ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
- sigprocmask(SIG_BLOCK, &sigs, NULL);
+ LOG(log_maxdebug, logtype_afpd, "main: polling %i fds", fdset_used);
+ pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
+ ret = poll(fdset, fdset_used, -1);
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+ int saveerrno = errno;
+
+ if (reloadconfig) {
+ nologin++;
+ auth_unload();
+
+ LOG(log_info, logtype_afpd, "re-reading configuration file");
+ for (config = configs; config; config = config->next)
+ if (config->server_cleanup)
+ config->server_cleanup(config);
+
+ /* configfree close atp socket used for DDP tickle, there's an issue
+ * with atp tid. */
+ configfree(configs, NULL);
+ if (!(configs = configinit(&default_options))) {
+ LOG(log_error, logtype_afpd, "config re-read: no servers configured");
+ exit(EXITERR_CONF);
+ }
+ fd_reset_listening_sockets();
+ nologin = 0;
+ reloadconfig = 0;
+ errno = saveerrno;
+ }
+
+ if (ret == 0)
+ continue;
+
if (ret < 0) {
if (errno == EINTR)
continue;
LOG(log_error, logtype_afpd, "main: can't wait for input: %s", strerror(errno));
break;
}
- if (Ipc_fd >=0 && FD_ISSET(Ipc_fd, &rfds)) {
- server_ipc_read(server_children);
- }
- for (config = configs; config; config = config->next) {
- if (config->fd < 0)
- continue;
- if (FD_ISSET(config->fd, &rfds)) {
- config->server_start(config, configs, server_children);
- }
- }
- }
+
+ for (int i = 0; i < fdset_used; i++) {
+ if (fdset[i].revents & POLLIN) {
+ switch (polldata[i].fdtype) {
+ case LISTEN_FD:
+ config = (AFPConfig *)polldata[i].data;
+ /* config->server_start is afp_config.c:dsi_start() for DSI */
+ if (child = config->server_start(config, configs, server_children)) {
+ /* Add IPC fd to select fd set */
+ 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;
+ }
+ break;
+ default:
+ LOG(log_debug, logtype_afpd, "main: IPC request for unknown type");
+ break;
+ } /* switch */
+ } /* if */
+ } /* for (i)*/
+ } /* while (1) */
return 0;
}
/*
- * $Id: mangle.c,v 1.19 2006-09-19 01:35:45 didg Exp $
+ * $Id: mangle.c,v 1.19.4.2 2010-02-01 14:25:45 franklahm Exp $
*
* Copyright (c) 2002. Joe Marcus Clarke (marcus@marcuscom.com)
* All Rights Reserved. See COPYRIGHT.
#include <stdio.h>
#include <ctype.h>
+
+#include <atalk/util.h>
+#include <atalk/bstradd.h>
+
#include "mangle.h"
#include "desktop.h"
-#include <atalk/util.h>
+
#define hextoint( c ) ( isdigit( c ) ? c - '0' : c + 10 - 'A' )
#define isuxdigit(x) (isdigit(x) || (isupper(x) && isxdigit(x)))
return 0;
}
-static char *demangle_checks ( const struct vol *vol, char* uname, char * mfilename, size_t prefix, char * ext)
+static char *demangle_checks(const struct vol *vol, char* uname, char * mfilename, size_t prefix, char * ext)
{
u_int16_t flags;
static char buffer[MAXPATHLEN +2]; /* for convert_charset dest_len parameter +2 */
/* We need to check, whether we really need to demangle the filename */
/* i.e. it's not just a file with a valid #HEX in the name ... */
/* but we don't want to miss valid demangle as well. */
-
/* check whether file extensions match */
- {
- char buf[MAX_EXT_LENGTH + 2]; /* for convert_charset dest_len parameter +2 */
- size_t ext_len = mangle_extension(vol, uname, buf, CH_UTF8_MAC);
- if (ext_len) {
- buf[ext_len] = '\0';
- if (strcmp(ext, buf)) return mfilename;
- } else {
- if (*ext) return mfilename;
- }
+ char buf[MAX_EXT_LENGTH + 2]; /* for convert_charset dest_len parameter +2 */
+ size_t ext_len = mangle_extension(vol, uname, buf, CH_UTF8_MAC);
+
+ if (ext_len) {
+ buf[ext_len] = '\0';
+ if (strcmp(ext, buf))
+ return mfilename;
+ } else {
+ if (*ext)
+ return mfilename;
}
/* First we convert the unix name to our volume maccharset */
}
/* is it a dir?, there's a conflict with pre OSX 'trash #2' */
- if ((dir = dirsearch(vol, id))) {
- if (dir->d_parent && dir->d_parent->d_did != did) {
+ if ((dir = dirlookup(vol, id))) {
+ if (dir->d_pdid != did) {
/* not in the same folder, there's a race with outdate cache
* but we have to live with it, hopefully client will recover
*/
}
if (!osx) {
/* it's not from cname so mfilename and dir must be the same */
- if (!strcmp(dir->d_m_name, mfilename)) {
- return dir->d_u_name;
+ if (strcmp(cfrombstr(dir->d_m_name), mfilename) == 0) {
+ return cfrombstr(dir->d_u_name);
}
- }
- else {
- return demangle_checks (vol, dir->d_u_name, mfilename, prefix, t);
- }
+ } else {
+ return demangle_checks(vol, cfrombstr(dir->d_u_name), mfilename, prefix, t);
+ }
}
else if (NULL != (u_name = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
if (id != did) {
/*
- * $Id: ofork.c,v 1.32 2010-03-12 15:16:49 franklahm Exp $
+ * $Id: ofork.c,v 1.32 2010/03/12 15:16:49 franklahm Exp $
*
* Copyright (c) 1996 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
#include <string.h>
#include <sys/stat.h> /* works around a bug */
#include <sys/param.h>
-#include <atalk/logger.h>
#include <errno.h>
+#include <atalk/logger.h>
#include <atalk/util.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
#include "globals.h"
#include "volume.h"
#define OFORK_HASHSIZE 64
static struct ofork *ofork_table[OFORK_HASHSIZE];
-static struct ofork **oforks = NULL;
-static int nforks = 0;
-static u_short lastrefnum = 0;
+static struct ofork **oforks = NULL;
+static int nforks = 0;
+static u_short lastrefnum = 0;
/* OR some of each character for the hash*/
while (*name) {
i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++;
}
-#endif
+#endif
return key->inode & (OFORK_HASHSIZE - 1);
}
#ifdef DEBUG1
void of_pforkdesc( FILE *f)
{
- int ofrefnum;
+ int ofrefnum;
if (!oforks)
return;
int of_flush(const struct vol *vol)
{
- int refnum;
+ int refnum;
if (!oforks)
return 0;
for ( refnum = 0; refnum < nforks; refnum++ ) {
if (oforks[ refnum ] != NULL && (oforks[refnum]->of_vol == vol) &&
- flushfork( oforks[ refnum ] ) < 0 ) {
+ flushfork( oforks[ refnum ] ) < 0 ) {
LOG(log_error, logtype_afpd, "of_flush: %s", strerror(errno) );
}
}
struct dir *olddir, const char *oldpath _U_,
struct dir *newdir, const char *newpath)
{
- struct ofork *of, *next, *d_ofork;
+ struct ofork *of, *next;
int done = 0;
if (!s_of)
return AFP_OK;
-
+
next = ofork_table[hashfn(&s_of->key)];
while ((of = next)) {
next = next->next; /* so we can unhash and still be all right. */
- if (vol == of->of_vol && olddir == of->of_dir &&
- s_of->key.dev == of->key.dev &&
- s_of->key.inode == of->key.inode ) {
- if (!done) {
- strlcpy( of_name(of), newpath, of->of_ad->ad_m_namelen);
- done = 1;
- }
- if (newdir != olddir) {
- of->of_d_prev->of_d_next = of->of_d_next;
- of->of_d_next->of_d_prev = of->of_d_prev;
- if (of->of_dir->d_ofork == of) {
- of->of_dir->d_ofork = (of == of->of_d_next) ? NULL : of->of_d_next;
- }
- of->of_dir = newdir;
- if (!(d_ofork = newdir->d_ofork)) {
- newdir->d_ofork = of;
- of->of_d_next = of->of_d_prev = of;
- } else {
- of->of_d_next = d_ofork;
- of->of_d_prev = d_ofork->of_d_prev;
- of->of_d_prev->of_d_next = of;
- d_ofork->of_d_prev = of;
- }
+ if (vol == of->of_vol
+ && olddir->d_did == of->of_did
+ && s_of->key.dev == of->key.dev
+ && s_of->key.inode == of->key.inode ) {
+ if (!done) {
+ strlcpy( of_name(of), newpath, of->of_ad->ad_m_namelen);
+ done = 1;
}
+ if (newdir != olddir)
+ of->of_did = newdir->d_did;
}
}
return AFP_OK;
}
-#define min(a,b) ((a)<(b)?(a):(b))
+#define min(a,b) ((a)<(b)?(a):(b))
struct ofork *
of_alloc(struct vol *vol,
- struct dir *dir,
- char *path,
- u_int16_t *ofrefnum,
- const int eid,
- struct adouble *ad,
- struct stat *st)
+ struct dir *dir,
+ char *path,
+ u_int16_t *ofrefnum,
+ const int eid,
+ struct adouble *ad,
+ struct stat *st)
{
- struct ofork *of, *d_ofork;
- u_int16_t refnum, of_refnum;
+ struct ofork *of;
+ u_int16_t refnum, of_refnum;
- int i;
+ int i;
if (!oforks) {
nforks = getdtablesize() - 10;
- /* protect against insane ulimit -n */
+ /* protect against insane ulimit -n */
nforks = min(nforks, 0xffff);
oforks = (struct ofork **) calloc(nforks, sizeof(struct ofork *));
if (!oforks)
}
}
/* grr, Apple and their 'uniquely identifies'
- the next line is a protection against
- of_alloc()
- refnum % nforks = 3
- lastrefnum = 3
- oforks[3] != NULL
- refnum = 4
- oforks[4] == NULL
- return 4
-
- close(oforks[4])
-
- of_alloc()
- refnum % nforks = 4
- ...
- return 4
- same if lastrefnum++ rather than ++lastrefnum.
+ the next line is a protection against
+ of_alloc()
+ refnum % nforks = 3
+ lastrefnum = 3
+ oforks[3] != NULL
+ refnum = 4
+ oforks[4] == NULL
+ return 4
+
+ close(oforks[4])
+
+ of_alloc()
+ refnum % nforks = 4
+ ...
+ return 4
+ same if lastrefnum++ rather than ++lastrefnum.
*/
lastrefnum = refnum;
if ( i == nforks ) {
of_refnum = refnum % nforks;
if (( oforks[ of_refnum ] =
- (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
+ (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) );
return NULL;
}
of->of_ad = ad;
of->of_vol = vol;
- of->of_dir = dir;
-
- if (!(d_ofork = dir->d_ofork)) {
- dir->d_ofork = of;
- of->of_d_next = of->of_d_prev = of;
- } else {
- of->of_d_next = d_ofork;
- of->of_d_prev = d_ofork->of_d_prev;
- d_ofork->of_d_prev->of_d_next = of;
- d_ofork->of_d_prev = of;
- }
+ of->of_did = dir->d_did;
*ofrefnum = refnum;
of->of_refnum = refnum;
}
/* -------------------------- */
-int of_stat (struct path *path)
+int of_stat(struct path *path)
{
-int ret;
+ int ret;
+
path->st_errno = 0;
path->st_valid = 1;
- if ((ret = lstat(path->u_name, &path->st)) < 0)
+
+ if ((ret = lstat(path->u_name, &path->st)) < 0) {
+ LOG(log_debug, logtype_afpd, "of_stat('%s/%s': %s)",
+ cfrombstr(curdir->d_fullpath), path->u_name, strerror(errno));
path->st_errno = errno;
- return ret;
+ }
+
+ return ret;
}
+
#ifdef HAVE_RENAMEAT
int of_fstatat(int dirfd, struct path *path)
{
stat(".") works even if "." is deleted thus
we have to stat ../name because we want to know if it's there
*/
-int of_statdir (struct vol *vol, struct path *path)
+int of_statdir(struct vol *vol, struct path *path)
{
-static char pathname[ MAXPATHLEN + 1] = "../";
-int ret;
+ static char pathname[ MAXPATHLEN + 1] = "../";
+ int ret;
+ size_t len;
+ struct dir *dir;
if (*path->m_name) {
/* not curdir */
path->st_errno = 0;
path->st_valid = 1;
/* FIXME, what about: we don't have r-x perm anymore ? */
- strlcpy(pathname +3, path->d_dir->d_u_name, sizeof (pathname) -3);
+ len = blength(path->d_dir->d_u_name);
+ if (len > (MAXPATHLEN - 3))
+ len = MAXPATHLEN - 3;
+ strncpy(pathname + 3, cfrombstr(path->d_dir->d_u_name), len + 1);
+
+ LOG(log_debug, logtype_afpd, "of_statdir: stating: '%s'", pathname);
if (!(ret = lstat(pathname, &path->st)))
return 0;
-
+
path->st_errno = errno;
+
/* hmm, can't stat curdir anymore */
- if (errno == EACCES && curdir->d_parent ) {
- if (movecwd(vol, curdir->d_parent))
+ if (errno == EACCES && (dir = dirlookup(vol, curdir->d_pdid))) {
+ if (movecwd(vol, dir))
return -1;
path->st_errno = 0;
- if ((ret = lstat(path->d_dir->d_u_name, &path->st)) < 0)
+
+ if ((ret = lstat(cfrombstr(path->d_dir->d_u_name), &path->st)) < 0)
path->st_errno = errno;
}
+
return ret;
}
{
struct ofork *of;
struct file_key key;
-
+
if (!path->st_valid) {
- of_stat(path);
+ of_stat(path);
}
-
+
if (path->st_errno)
return NULL;
return;
of_unhash(of);
-
- /* detach ofork */
- of->of_d_prev->of_d_next = of->of_d_next;
- of->of_d_next->of_d_prev = of->of_d_prev;
- if (of->of_dir->d_ofork == of) {
- of->of_dir->d_ofork = (of == of->of_d_next) ? NULL : of->of_d_next;
- }
-
oforks[ of->of_refnum % nforks ] = NULL;
/* decrease refcount */
int of_closefork(struct ofork *ofork)
{
struct timeval tv;
- int adflags, doflush = 0;
+ int adflags, doflush = 0;
int ret;
adflags = 0;
if ((ofork->of_flags & AFPFORK_DATA) && (ad_data_fileno( ofork->of_ad ) != -1)) {
- adflags |= ADFLAGS_DF;
+ adflags |= ADFLAGS_DF;
}
if ( (ofork->of_flags & AFPFORK_OPEN) && ad_reso_fileno( ofork->of_ad ) != -1 ) {
adflags |= ADFLAGS_HF;
ad_refresh( ofork->of_ad );
if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,tv.tv_sec);
- doflush++;
+ doflush++;
}
if ( doflush ) {
- ad_flush( ofork->of_ad );
+ ad_flush( ofork->of_ad );
}
}
}
if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
ret = -1;
}
-
+
of_dealloc( ofork );
return ret;
}
/* ----------------------
-*/
+ */
struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble *ad)
{
struct ofork *of;
return adp;
}
-/* ----------------------
+/* ----------------------
close all forks for a volume
*/
void of_closevol(const struct vol *vol)
{
- int refnum;
+ int refnum;
if (!oforks)
return;
static void status_flags(char *data, const int notif, const int ipok,
const unsigned char passwdbits, const int dirsrvcs _U_, int flags)
{
- u_int16_t status;
+ uint16_t status;
+
+ status = AFPSRVRINFO_COPY
+ | AFPSRVRINFO_SRVSIGNATURE
+ | AFPSRVRINFO_SRVMSGS
+ | AFPSRVRINFO_FASTBOZO
+ | AFPSRVRINFO_SRVRDIR
+ | AFPSRVRINFO_SRVUTF8
+ | AFPSRVRINFO_EXTSLEEP;
- status = AFPSRVRINFO_COPY;
if (passwdbits & PASSWD_SET) /* some uams may not allow this. */
status |= AFPSRVRINFO_PASSWD;
if (passwdbits & PASSWD_NOSAVE)
status |= AFPSRVRINFO_NOSAVEPASSWD;
- status |= AFPSRVRINFO_SRVSIGNATURE;
- /* only advertise tcp/ip if we have a valid address */
- if (ipok)
+ if (ipok) /* only advertise tcp/ip if we have a valid address */
status |= AFPSRVRINFO_TCPIP;
- status |= AFPSRVRINFO_SRVMSGS;
- /* Allow the user to decide if we should support server notifications.
- * With this turned off, the clients will poll for directory changes every
- * 10 seconds. This might be too costly to network resources, so make
- * this an optional thing. Default will be to _not_ support server
- * notifications. */
- if (notif) {
+ if (notif) /* Default is yes */
status |= AFPSRVRINFO_SRVNOTIFY;
- }
- status |= AFPSRVRINFO_FASTBOZO;
- status |= AFPSRVRINFO_SRVRDIR; /* AFP 3.1 specs says we need to specify this, but may set the count to 0 */
- /* We don't set the UTF8 name flag here, we don't know whether we have enough space ... */
-
- if (flags & OPTION_UUID) /* 05122008 FIXME: can we set AFPSRVRINFO_UUID here ? see AFPSRVRINFO_SRVRDIR*/
- status |= AFPSRVRINFO_UUID;
+ if (flags & OPTION_UUID)
+ status |= AFPSRVRINFO_UUID;
status = htons(status);
memcpy(data + AFPSTATUS_FLAGOFF, &status, sizeof(status));
data += len;
offset = htons(offset);
memcpy(begin + *nameoffset, &offset, sizeof(u_int16_t));
-
- /* Now set the flag ... */
- memcpy(&status, begin + AFPSTATUS_FLAGOFF, sizeof(status));
- status = ntohs(status);
- status |= AFPSRVRINFO_SRVUTF8;
- status = htons(status);
- memcpy(begin + AFPSTATUS_FLAGOFF, &status, sizeof(status));
}
/* return length of buffer */
char *servername_conf;
int header = 0;
char buf[1024], *p;
- FILE *fp, *randomp;
+ FILE *fp = NULL, *randomp;
size_t len;
char *server_tmp;
options->sigconffile, strerror(errno));
goto server_signature_random;
}
- } else { /* conf file don't exist */
+ } else { /* conf file don't exist */
if (( fd = creat(options->sigconffile, 0644 )) < 0 ) {
- LOG(log_error, logtype_atalkd, "ERROR: Cannot create %s (%s). Using one-time signature.",
+ LOG(log_error, logtype_atalkd, "Cannot create %s (%s). Using one-time signature.",
options->sigconffile, strerror(errno));
goto server_signature_random;
}
if (( fp = fdopen( fd, "w" )) == NULL ) {
- LOG(log_error, logtype_atalkd, "ERROR: Cannot fdopen %s (%s). Using one-time signature.",
+ LOG(log_error, logtype_atalkd, "Cannot fdopen %s (%s). Using one-time signature.",
options->sigconffile, strerror(errno));
close(fd);
goto server_signature_random;
server_signature_random:
/* generate signature from random number */
- if ((randomp = fopen("/dev/urandom", "r")) != NULL) { /* generate from /dev/urandom */
- for (i=0 ; i<16 ; i++) {
- (options->signature)[i] = fgetc(randomp);
- }
- LOG(log_note, logtype_afpd,
- "generate %s's signature %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X from /dev/urandom",
- server_tmp,
- (options->signature)[ 0], (options->signature)[ 1],
- (options->signature)[ 2], (options->signature)[ 3],
- (options->signature)[ 4], (options->signature)[ 5],
- (options->signature)[ 6], (options->signature)[ 7],
- (options->signature)[ 8], (options->signature)[ 9],
- (options->signature)[10], (options->signature)[11],
- (options->signature)[12], (options->signature)[13],
- (options->signature)[14], (options->signature)[15]);
-
- } else { /* genarate from random() because cannot open /dev/urandom */
- srandom((unsigned int)time(NULL) + (unsigned int)options + (unsigned int)server_tmp);
- for (i=0 ; i<16 ; i++) {
- (options->signature)[i] = random() & 0xFF;
- }
- LOG(log_note, logtype_afpd,
- "generate %s's signature %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X from random()",
- server_tmp,
- (options->signature)[ 0], (options->signature)[ 1],
- (options->signature)[ 2], (options->signature)[ 3],
- (options->signature)[ 4], (options->signature)[ 5],
- (options->signature)[ 6], (options->signature)[ 7],
- (options->signature)[ 8], (options->signature)[ 9],
- (options->signature)[10], (options->signature)[11],
- (options->signature)[12], (options->signature)[13],
- (options->signature)[14], (options->signature)[15]);
- }
+ randombytes(options->signature, 16);
- if (fp && header) { /* conf file is created or size=0 */
+ if (fp && header) { /* conf file is created or size=0 */
fprintf(fp, "# DON'T TOUCH NOR COPY THOUGHTLESSLY!\n");
fprintf(fp, "# This file is auto-generated by afpd.\n");
fprintf(fp, "# \n");
/*
- * $Id: switch.c,v 1.19 2009-10-15 10:43:13 didg Exp $
- *
* Copyright (c) 1990,1991 Regents of The University of Michigan.
* All Rights Reserved.
*
#include "filedir.h"
#include "status.h"
#include "misc.h"
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
#include "acls.h"
#endif
#include "auth.h"
#include "uam_auth.h"
-#ifdef AFP3x
#define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
-#endif
#ifdef TRU64
#include <netdb.h>
/*
- * $Id: unix.c,v 1.61 2010-02-10 14:05:37 franklahm Exp $
- *
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
*/
#include "volume.h"
#include "unix.h"
#include "fork.h"
-
-#ifdef HAVE_NFSv4_ACLS
-extern void acltoownermode(char *path, struct stat *st,uid_t uid, struct maccess *ma);
+#ifdef HAVE_ACLS
+#include "acls.h"
#endif
-
/*
* Get the free space on a partition.
*/
* dir parameter is used by AFS
*/
void accessmode(char *path, struct maccess *ma, struct dir *dir _U_, struct stat *st)
-
{
-struct stat sb;
+ struct stat sb;
ma->ma_user = ma->ma_owner = ma->ma_world = ma->ma_group = 0;
if (!st) {
st = &sb;
}
utommode( st, ma );
-#ifdef HAVE_NFSv4_ACLS
- /* 10.5 Finder looks at OS 9 mode, so we must do some mapping */
- acltoownermode( path, st, uuid, ma);
+#ifdef HAVE_ACLS
+ acltoownermode(path, st, ma);
#endif
}
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+
#include <atalk/asp.h>
#include <atalk/dsi.h>
#include <atalk/adouble.h>
#include <atalk/volinfo.h>
#include <atalk/logger.h>
#include <atalk/vfs.h>
-#include <atalk/ea.h>
+#include <atalk/uuid.h>
+
#ifdef CNID_DB
#include <atalk/cnid.h>
#endif /* CNID_DB*/
#include "mangle.h"
#include "fork.h"
#include "hash.h"
+#include "acls.h"
extern int afprun(int root, char *cmd, int *outfd);
#endif /* BYTE_ORDER == BIG_ENDIAN */
#endif /* ! NO_LARGE_VOL_SUPPORT */
+#ifndef UUID_PRINTABLE_STRING_LENGTH
+#define UUID_PRINTABLE_STRING_LENGTH 37
+#endif
+
+/* Globals */
+struct vol *current_vol; /* last volume from getvolbyvid() */
+
static struct vol *Volumes = NULL;
static u_int16_t lastvid = 0;
static char *Trash = "\02\024Network Trash Folder";
}
-/* handle variable substitutions. here's what we understand:
+#define is_var(a, b) (strncmp((a), (b), 2) == 0)
+
+/*
+ * Handle variable substitutions. here's what we understand:
* $b -> basename of path
* $c -> client ip/appletalk address
* $d -> volume pathname on server
* $z -> zone (may not exist)
* $$ -> $
*
+ * This get's called from readvolfile with
+ * path = NULL, volname = NULL for xlating the volumes path
+ * path = path, volname = NULL for xlating the volumes name
+ * ... and from volumes options parsing code when xlating eg dbpath with
+ * path = path, volname = volname
*
+ * Using this information we can reject xlation of any variable depeninding on a login
+ * context which is not given in the afp master, where we must evaluate this whole stuff
+ * too for the Zeroconf announcements.
*/
-#define is_var(a, b) (strncmp((a), (b), 2) == 0)
-
-static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
- char *src, struct passwd *pwd, char *path, char *volname)
+static char *volxlate(AFPObj *obj,
+ char *dest,
+ size_t destlen,
+ char *src,
+ struct passwd *pwd,
+ char *path,
+ char *volname)
{
char *p, *r;
const char *q;
int len;
char *ret;
+ int afpmaster = 0;
+ int xlatevolname = 0;
+
+ if (parent_or_child == 0)
+ afpmaster = 1;
+
+ if (path && !volname)
+ /* cf above */
+ xlatevolname = 1;
if (!src) {
return NULL;
/* now figure out what the variable is */
q = NULL;
if (is_var(p, "$b")) {
+ if (afpmaster && xlatevolname)
+ return NULL;
if (path) {
if ((q = strrchr(path, '/')) == NULL)
q = path;
q++;
}
} else if (is_var(p, "$c")) {
+ if (afpmaster && xlatevolname)
+ return NULL;
if (obj->proto == AFPPROTO_ASP) {
ASP asp = obj->handle;
destlen -= len;
}
} else if (is_var(p, "$d")) {
+ if (afpmaster && xlatevolname)
+ return NULL;
q = path;
- } else if (is_var(p, "$f")) {
+ } else if (pwd && is_var(p, "$f")) {
+ if (afpmaster && xlatevolname)
+ return NULL;
if ((r = strchr(pwd->pw_gecos, ',')))
*r = '\0';
q = pwd->pw_gecos;
- } else if (is_var(p, "$g")) {
+ } else if (pwd && is_var(p, "$g")) {
+ if (afpmaster && xlatevolname)
+ return NULL;
struct group *grp = getgrgid(pwd->pw_gid);
if (grp)
q = grp->gr_name;
} else if (is_var(p, "$h")) {
q = obj->options.hostname;
} else if (is_var(p, "$i")) {
+ if (afpmaster && xlatevolname)
+ return NULL;
if (obj->proto == AFPPROTO_ASP) {
ASP asp = obj->handle;
q = obj->options.server;
} else
q = obj->options.hostname;
- } else if (is_var(p, "$u")) {
+ } else if (obj->username && is_var(p, "$u")) {
+ if (afpmaster && xlatevolname)
+ return NULL;
char* sep = NULL;
if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
q = sep+1;
else
q = obj->username;
} else if (is_var(p, "$v")) {
+ if (afpmaster && xlatevolname)
+ return NULL;
if (volname) {
q = volname;
}
options[VOLOPT_ROOTPREEXEC].i_value = 1;
else if (strcasecmp(p, "upriv") == 0)
options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV;
- else if (strcasecmp(p, "acls") == 0)
- options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
else if (strcasecmp(p, "nodev") == 0)
options[VOLOPT_FLAGS].i_value |= AFPVOL_NODEV;
else if (strcasecmp(p, "caseinsensitive") == 0)
options[VOLOPT_FLAGS].i_value &= ~AFPVOL_CACHE;
else if (strcasecmp(p, "tm") == 0)
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
p = strtok(NULL, ",");
}
char suffix[6]; /* max is #FFFF */
u_int16_t flags;
+ LOG(log_debug, logtype_afpd, "createvol: Volume '%s'", name);
+
if ( name == NULL || *name == '\0' ) {
if ((name = strrchr( path, '/' )) == NULL) {
return -1; /* Obviously not a fully qualified path */
if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) )
return -1;
- LOG(log_debug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
+ LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
/* Maccharset Volume Name */
/* Firsty convert name from unixcharset to maccharset */
if ( 0 >= ( macvlen = convert_string(obj->options.maccharset, CH_UCS2, tmpname, tmpvlen, mactmpname, AFPVOL_U8MNAMELEN*2)) )
return -1;
- LOG(log_debug, logtype_afpd, "createvol: Volume '%s' -> Longname: '%s'", name, tmpname);
+ LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> Longname: '%s'", name, tmpname);
/* check duplicate */
for ( volume = Volumes; volume; volume = volume->v_next ) {
/* os X start at 1 and use network order ie. 1 2 3 */
volume->v_vid = ++lastvid;
volume->v_vid = htons(volume->v_vid);
+#ifdef HAVE_ACLS
+ if (check_vol_acl_support(volume))
+ volume->v_flags |= AFPVOL_ACLS
+;
+#endif
/* handle options */
if (options) {
- /* should we casefold? */
volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
-
- /* shift in some flags */
- volume->v_flags = options[VOLOPT_FLAGS].i_value;
+ volume->v_flags |= options[VOLOPT_FLAGS].i_value;
if (options[VOLOPT_EA_VFS].i_value)
volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
check_ea_sys_support(volume);
initvol_vfs(volume);
+ /* get/store uuid from file */
+ if (volume->v_flags & AFPVOL_TM) {
+ char *uuid = get_uuid(obj, volume->v_localname);
+ if (!uuid) {
+ LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
+ volume->v_localname);
+ } else {
+ volume->v_uuid = uuid;
+ LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
+ volume->v_localname, volume->v_uuid);
+ }
+ }
+
volume->v_next = Volumes;
Volumes = volume;
return 0;
/* ----------------------
* Read a volume configuration file and add the volumes contained within to
- * the global volume list. If p2 is non-NULL, the file that is opened is
+ * the global volume list. This gets called from the forked afpd childs.
+ * The master now reads this too for Zeroconf announcements.
+ *
+ * If p2 is non-NULL, the file that is opened is
* p1/p2
*
* Lines that begin with # and blank lines are ignored.
* <unix path> [<volume name>] [allow:<user>,<@group>,...] \
* [codepage:<file>] [casefold:<num>]
* <extension> TYPE [CREATOR]
+ *
*/
-static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent)
+int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent)
{
FILE *fp;
char path[MAXPATHLEN + 1];
char buf[BUFSIZ];
char type[5], creator[5];
char *u, *p;
+ int fd;
+ int i;
struct passwd *pw;
struct vol_option save_options[VOLOPT_NUM];
struct vol_option options[VOLOPT_NUM];
- int i;
struct stat st;
- int fd;
if (!p1->name)
return -1;
obj->options.umask);
save_options[VOLOPT_UMASK].i_value = obj->options.umask;
+ LOG(log_debug, logtype_afpd, "readvolfile: \"%s\"", path);
+
while ( myfgets( buf, sizeof( buf ), fp ) != NULL ) {
initline( strlen( buf ), buf );
parseline( sizeof( path ) - 1, path );
/* send path through variable substitution */
if (*path != '~') /* need to copy path to tmp */
strcpy(tmp, path);
- if (!pwent)
+ if (!pwent && obj->username)
pwent = getpwnam(obj->username);
- volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL);
+
+ if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
+ continue;
/* this is sort of braindead. basically, i want to be
* able to specify things in any order, but i don't want to
- * re-write everything.
- *
- * currently we have options:
- * volname
- * codepage:x
- * casefold:x
- * allow:x,y,@z
- * deny:x,y,@z
- * rwlist:x,y,@z
- * rolist:x,y,@z
- * options:prodos,crlf,noadouble,ro...
- * dbpath:x
- * password:x
- * preexec:x
- *
- * namemask:x,y,!z (not implemented yet)
- */
+ * re-write everything. */
+
memcpy(options, save_options, sizeof(options));
*volname = '\0';
volset(options, save_options, volname, sizeof(volname) - 1, tmp);
}
- /* check allow/deny lists:
+ /* check allow/deny lists (if not afpd master loading volumes for Zeroconf reg.):
allow -> either no list (-1), or in list (1)
deny -> either no list (-1), or not in list (0) */
- if (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
- (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1) &&
- hostaccessvol(VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value, obj) &&
- (hostaccessvol(VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value, obj) < 1)) {
+ if (parent_or_child == 0
+ ||
+ (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
+ (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1) &&
+ hostaccessvol(VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value, obj) &&
+ (hostaccessvol(VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value, obj) < 1))) {
/* handle read-only behaviour. semantics:
* 1) neither the rolist nor the rwlist exist -> rw
* 2) rolist exists -> ro if user is in it.
* 3) rwlist exists -> ro unless user is in it. */
- if (((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0) &&
- ((accessvol(options[VOLOPT_ROLIST].c_value,
- obj->username) == 1) ||
- !accessvol(options[VOLOPT_RWLIST].c_value,
- obj->username)))
+ if (parent_or_child == 1
+ &&
+ ((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0)
+ &&
+ ((accessvol(options[VOLOPT_ROLIST].c_value, obj->username) == 1) ||
+ !accessvol(options[VOLOPT_RWLIST].c_value, obj->username)))
options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
/* do variable substitution for volname */
- volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL);
+ if (volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL) == NULL)
+ continue;
+
creatvol(obj, pwent, path, tmp, options, p2 != NULL);
}
volfree(options, save_options);
free(vol->v_forceuid);
free(vol->v_forcegid);
#endif /* FORCE_UIDGID */
+ if (vol->v_uuid)
+ free(vol->v_uuid);
}
/* ------------------------------- */
free_volumes();
}
+ if (parent_or_child == 0) {
+ LOG(log_debug, logtype_afpd, "load_volumes: AFP MASTER");
+ } else {
+ LOG(log_debug, logtype_afpd, "load_volumes: user: %s", obj->username);
+ }
+
pwent = getpwnam(obj->username);
if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) {
readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent);
volume->v_path, volume->v_cnidscheme);
}
- LOG(log_info, logtype_afpd, "CNID server %s:%s",
+ LOG(log_info, logtype_afpd, "CNID server: %s:%s",
volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
volume->v_cnidport ? volume->v_cnidport : Cnid_port);
-
- volume->v_cdb = cnid_open(volume->v_dbpath ? volume->v_dbpath : volume->v_path,
+
+#if 0
+/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
+
+ /* Legacy pre 2.1 way of sharing eg CD-ROM */
+ if (strcmp(volume->v_cnidscheme, "last") == 0) {
+ /* "last" is gone. We support it by switching to in-memory "tdb" */
+ volume->v_cnidscheme = strdup("tdb");
+ flags |= CNID_FLAG_MEMORY;
+ }
+
+ /* New way of sharing CD-ROM */
+ if (volume->v_flags & AFPVOL_CDROM) {
+ flags |= CNID_FLAG_MEMORY;
+ if (strcmp(volume->v_cnidscheme, "tdb") != 0) {
+ free(volume->v_cnidscheme);
+ volume->v_cnidscheme = strdup("tdb");
+ LOG(log_info, logtype_afpd, "Volume %s is ejectable, switching to scheme %s.",
+ volume->v_path, volume->v_cnidscheme);
+ }
+ }
+#endif
+
+ volume->v_cdb = cnid_open(volume->v_path,
volume->v_umask,
volume->v_cnidscheme,
flags,
volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
volume->v_cnidport ? volume->v_cnidport : Cnid_port);
- if (!volume->v_cdb) {
+ if ( ! volume->v_cdb && ! (flags & CNID_FLAG_MEMORY)) {
+ /* The first attempt failed and it wasn't yet an attempt to open in-memory */
LOG(log_error, logtype_afpd, "Can't open volume \"%s\" CNID backend \"%s\" ",
volume->v_path, volume->v_cnidscheme);
- flags |= CNID_FLAG_MEMORY;
LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
volume->v_path);
+ flags |= CNID_FLAG_MEMORY;
volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL);
#ifdef SERVERTEXT
/* kill ourself with SIGUSR2 aka msg pending */
return (!volume->v_cdb)?-1:0;
}
-/*
- Check if the underlying filesystem supports EAs for ea:sys volumes.
- If not, switch to ea:ad.
- As we can't check (requires write access) on ro-volumes, we switch ea:auto
- volumes that are options:ro to ea:none.
+/*
+ Check if the underlying filesystem supports EAs for ea:sys volumes.
+ If not, switch to ea:ad.
+ As we can't check (requires write access) on ro-volumes, we switch ea:auto
+ volumes that are options:ro to ea:none.
*/
static void check_ea_sys_support(struct vol *vol)
{
if ((tmp = strdup(volume->v_path)) == NULL) {
free(volume->v_path);
return AFPERR_MISC;
- }
+ }
free(volume->v_path);
volume->v_path = tmp;
#endif
volume->max_filename = MACFILELEN;
}
- volume->v_dir = volume->v_root = NULL;
- volume->v_hash = NULL;
-
volume->v_flags |= AFPVOL_OPEN;
volume->v_cdb = NULL;
else if (*(vol_uname + 1) != '\0')
vol_uname++;
- if ((dir = dirnew(vol_mname, vol_uname) ) == NULL) {
+ if ((dir = dir_new(vol_mname,
+ vol_uname,
+ volume,
+ DIRDID_ROOT_PARENT,
+ DIRDID_ROOT,
+ bfromcstr(volume->v_path),
+ st.st_ctime)
+ ) == NULL) {
free(vol_mname);
LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
ret = AFPERR_MISC;
goto openvol_err;
}
free(vol_mname);
+ volume->v_root = dir;
+ curdir = dir;
- dir->d_did = DIRDID_ROOT;
- dir->d_color = DIRTREE_COLOR_BLACK; /* root node is black */
- dir->d_m_name_ucs2 = strdup_w(volume->v_name);
- volume->v_dir = volume->v_root = dir;
- volume->v_curdir = NULL;
- volume->v_hash = dirhash();
-
- curdir = volume->v_dir;
if (volume_openDB(volume) < 0) {
LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID or invalid CNID backend for %s: %s",
volume->v_path, volume->v_cnidscheme);
}
else {
p = Trash;
- cname( volume, volume->v_dir, &p );
+ cname( volume, volume->v_root, &p );
}
return( AFP_OK );
}
openvol_err:
- if (volume->v_dir) {
- hash_free( volume->v_hash);
- dirfree( volume->v_dir );
- volume->v_dir = volume->v_root = NULL;
+ if (volume->v_root) {
+ dir_free( volume->v_root );
+ volume->v_root = NULL;
}
volume->v_flags &= ~AFPVOL_OPEN;
if (!vol)
return;
- hash_free( vol->v_hash);
- dirfree( vol->v_root );
- vol->v_dir = NULL;
+ dir_free( vol->v_root );
+ vol->v_root = NULL;
if (vol->v_cdb != NULL) {
cnid_close(vol->v_cdb);
vol->v_cdb = NULL;
if ( ovol != NULL ) {
/* Even if chdir fails, we can't say afp_closevol fails. */
if ( chdir( ovol->v_path ) == 0 ) {
- curdir = ovol->v_dir;
+ curdir = ovol->v_root;
}
}
}
deletevol(vol);
+ current_vol = NULL;
return( AFP_OK );
}
set_uidgid ( vol );
#endif /* FORCE_UIDGID */
+ current_vol = vol;
+
return( vol );
}
free(q);
return (-1);
}
-
+
ad_setname(&ad, folder->name);
ad_getattr(&ad, &attr);
}
}
+const struct vol *getvolumes(void)
+{
+ return Volumes;
+}
+
+void unload_volumes_and_extmap(void)
+{
+ LOG(log_debug, logtype_afpd, "unload_volumes_and_extmap");
+ free_extmap();
+ free_volumes();
+}
+
+/*
+ * Get a volumes UUID from the config file.
+ * If there is none, it is generated and stored there.
+ *
+ * Returns pointer to allocated storage on success, NULL on error.
+ */
+char *get_uuid(const AFPObj *obj, const char *volname)
+{
+ char *volname_conf;
+ char buf[1024], uuid[UUID_PRINTABLE_STRING_LENGTH], *p;
+ FILE *fp;
+ struct stat tmpstat;
+ int fd;
+
+ if ((fp = fopen(obj->options.uuidconf, "r")) != NULL) { /* read open? */
+ /* scan in the conf file */
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ p = buf;
+ while (p && isblank(*p))
+ p++;
+ if (!p || (*p == '#') || (*p == '\n'))
+ continue; /* invalid line */
+ if (*p == '"') {
+ p++;
+ if ((volname_conf = strtok( p, "\"" )) == NULL)
+ continue; /* syntax error */
+ } else {
+ if ((volname_conf = strtok( p, " \t" )) == NULL)
+ continue; /* syntax error: invalid name */
+ }
+ p = strchr(p, '\0');
+ p++;
+ if (*p == '\0')
+ continue; /* syntax error */
+
+ if (strcmp(volname, volname_conf) != 0)
+ continue; /* another volume name */
+
+ while (p && isblank(*p))
+ p++;
+
+ if (sscanf(p, "%36s", uuid) == 1 ) {
+ for (int i=0; uuid[i]; i++)
+ uuid[i] = toupper(uuid[i]);
+ LOG(log_debug, logtype_afpd, "get_uuid('%s'): UUID: '%s'", volname, uuid);
+ fclose(fp);
+ return strdup(uuid);
+ }
+ }
+ }
+
+ if (fp)
+ fclose(fp);
+
+ /* not found or no file, reopen in append mode */
+
+ if (stat(obj->options.uuidconf, &tmpstat)) { /* no file */
+ if (( fd = creat(obj->options.uuidconf, 0644 )) < 0 ) {
+ LOG(log_error, logtype_atalkd, "ERROR: Cannot create %s (%s).",
+ obj->options.uuidconf, strerror(errno));
+ return NULL;
+ }
+ if (( fp = fdopen( fd, "w" )) == NULL ) {
+ LOG(log_error, logtype_atalkd, "ERROR: Cannot fdopen %s (%s).",
+ obj->options.uuidconf, strerror(errno));
+ close(fd);
+ return NULL;
+ }
+ } else if ((fp = fopen(obj->options.uuidconf, "a+")) == NULL) { /* not found */
+ LOG(log_error, logtype_afpd, "Cannot create or append to %s (%s).",
+ obj->options.uuidconf, strerror(errno));
+ return NULL;
+ }
+ fseek(fp, 0L, SEEK_END);
+ if(ftell(fp) == 0) { /* size = 0 */
+ fprintf(fp, "# DON'T TOUCH NOR COPY THOUGHTLESSLY!\n");
+ fprintf(fp, "# This file is auto-generated by afpd\n");
+ fprintf(fp, "# and stores UUIDs for TM volumes.\n\n");
+ } else {
+ fseek(fp, -1L, SEEK_END);
+ if(fgetc(fp) != '\n') fputc('\n', fp); /* last char is \n? */
+ }
+
+ /* generate uuid and write to file */
+ atalk_uuid_t id;
+ const char *cp;
+ randombytes((void *)id, 16);
+ cp = uuid_bin2string(id);
+
+ LOG(log_debug, logtype_afpd, "get_uuid('%s'): generated UUID '%s'", volname, cp);
+
+ fprintf(fp, "\"%s\"\t%36s\n", volname, cp);
+ fclose(fp);
+
+ return strdup(uuid);
+}
/*
- * $Id: volume.h,v 1.36 2009-10-15 10:43:13 didg Exp $
- *
* Copyright (c) 1990,1994 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
*/
#include "hash.h"
#endif
-extern struct vol *getvolbyvid (const u_int16_t);
+extern struct vol *getvolbyvid (const u_int16_t);
extern int ustatfs_getvolspace (const struct vol *,
VolSpace *, VolSpace *,
u_int32_t *);
extern void setvoltime (AFPObj *, struct vol *);
extern int pollvoltime (AFPObj *);
extern void load_volumes (AFPObj *obj);
+extern int readvolfile(AFPObj *obj,
+ struct afp_volume_name *p1,
+ char *p2,
+ int user,
+ struct passwd *pwent);
+extern const struct vol *getvolumes(void);
+extern void unload_volumes_and_extmap(void);
+extern char *get_uuid(const AFPObj *obj, const char *volname);
/* FP functions */
int afp_openvol (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen);
/* netatalk functions */
extern void close_all_vol (void);
+struct vol *current_vol; /* last volume from getvolbyvid() */
+
#endif
cnid_dbd_SOURCES = dbif.c pack.c comm.c db_param.c main.c \
dbd_add.c dbd_get.c dbd_resolve.c dbd_lookup.c \
dbd_update.c dbd_delete.c dbd_getstamp.c \
- dbd_rebuild_add.c dbd_dbcheck.c
-cnid_dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@
+ dbd_rebuild_add.c dbd_dbcheck.c dbd_search.c
+cnid_dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@ @ACL_LIBS@
cnid_metad_SOURCES = cnid_metad.c usockfd.c db_param.c
-cnid_metad_LDADD = $(top_builddir)/libatalk/libatalk.la
+cnid_metad_LDADD = $(top_builddir)/libatalk/libatalk.la @ACL_LIBS@
dbd_SOURCES = cmd_dbd.c \
cmd_dbd_scanvol.c \
dbd_rebuild_add.c \
dbd_resolve.c \
dbd_update.c
-dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@
+dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@ @ACL_LIBS@
noinst_HEADERS = dbif.h pack.h db_param.h dbd.h usockfd.h comm.h cmd_dbd.h
/*
- $Id: cmd_dbd.c,v 1.26 2010-04-20 16:46:20 hat001 Exp $
-
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
int nocniddb = 0; /* Dont open CNID database, only scan filesystem */
volatile sig_atomic_t alarmed;
+struct volinfo volinfo; /* needed by pack.c:idxname() */
static DBD *dbd;
static int verbose; /* Logging flag */
NULL, /* Volume dirpath */
1, /* bdb logfile autoremove */
64 * 1024, /* bdb cachesize (64 MB) */
+ 5000, /* maxlocks */
+ 5000, /* maxlockobjs */
-1, /* not used ... */
-1,
"",
-1,
-1
};
-static char dbpath[PATH_MAX]; /* Path to the dbd database */
+static char dbpath[MAXPATHLEN+1]; /* Path to the dbd database */
/*
Provide some logging
int dump=0, scan=0, rebuild=0, prep_upgrade=0, rebuildindexes=0, dumpindexes=0, force=0;
dbd_flags_t flags = 0;
char *volpath;
- struct volinfo volinfo;
int cdir;
if (geteuid() != 0) {
exit(EXIT_FAILURE);
}
+ /* Enuser dbpath is there, create if necessary */
+ struct stat st;
+ if (stat(volinfo.v_dbpath, &st) != 0) {
+ if (errno != ENOENT) {
+ dbd_log( LOGSTD, "Can't stat dbpath \"%s\": %s", volinfo.v_dbpath, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if ((mkdir(volinfo.v_dbpath, 0755)) != 0) {
+ dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
/* Put "/.AppleDB" at end of volpath, get path from volinfo file */
- if ( (strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > (PATH_MAX - 1) ) {
+ if ( (strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
dbd_log( LOGSTD, "Volume pathname too long");
exit(EXIT_FAILURE);
}
- strncpy(dbpath, volinfo.v_dbpath, PATH_MAX - 9 - 1);
+ strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
strcat(dbpath, "/.AppleDB");
/* Check or create dbpath */
/* Prepare upgrade ? */
if (prep_upgrade) {
- if (dbif_prep_upgrade(dbpath))
+ if (dbif_env_remove(dbpath))
goto exit_failure;
goto exit_success;
}
/* 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];
- snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", dbpath);
+ snprintf(cmd, 8 + MAXPATHLEN, "rm -rf \"%s\"", dbpath);
dbd_log( LOGDEBUG, "Removing old database of volume: '%s'", volpath);
system(cmd);
+ if ((mkdir(dbpath, 0755)) != 0) {
+ dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
dbd_log( LOGDEBUG, "Removed old database.");
}
dbif_close(dbd);
goto exit_failure;
}
-
- if (dbd_stamp(dbd) < 0) {
- dbif_close(dbd);
- goto exit_failure;
- }
}
/* Now execute given command scan|rebuild|dump */
extern int nocniddb; /* Dont open CNID database, only scan filesystem */
extern volatile sig_atomic_t alarmed;
-extern struct volinfo *volinfo;
-extern char cwdbuf[MAXPATHLEN+1];
extern void dbd_log(enum logtype lt, char *fmt, ...);
extern int cmd_dbd_scanvol(DBD *dbd, struct volinfo *volinfo, dbd_flags_t flags);
#define ADDIR_OK (addir_ok == 0)
#define ADFILE_OK (adfile_ok == 0)
-/* These must be accessible for cmd_dbd_* funcs */
-struct volinfo *volinfo;
-char cwdbuf[MAXPATHLEN+1];
-/* Some static vars */
+static struct volinfo *myvolinfo;
+static char cwdbuf[MAXPATHLEN+1];
static DBD *dbd;
static DBD *dbd_rebuild;
static dbd_flags_t dbd_flags;
u = upath;
outlen = strlen(upath);
- if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
+ if ((myvolinfo->v_casefold & AFPVOL_UTOMUPPER))
flags |= CONV_TOUPPER;
- else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
+ else if ((myvolinfo->v_casefold & AFPVOL_UTOMLOWER))
flags |= CONV_TOLOWER;
- if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
+ if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
flags |= CONV__EILSEQ;
}
/* convert charsets */
- if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
+ if ((size_t)-1 == ( outlen = convert_charset(myvolinfo->v_volcharset,
CH_UTF8_MAC,
- volinfo->v_maccharset,
+ myvolinfo->v_maccharset,
u, outlen, mpath, MAXPATHLEN, &flags)) ) {
dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
- volinfo->v_volcodepage, volinfo->v_maccodepage, u);
+ myvolinfo->v_volcodepage, myvolinfo->v_maccodepage, u);
return NULL;
}
}
/* set conversion flags */
- if (!(volinfo->v_flags & AFPVOL_NOHEX))
+ if (!(myvolinfo->v_flags & AFPVOL_NOHEX))
flags |= CONV_ESCAPEHEX;
- if (!(volinfo->v_flags & AFPVOL_USEDOTS))
+ if (!(myvolinfo->v_flags & AFPVOL_USEDOTS))
flags |= CONV_ESCAPEDOTS;
- if ((volinfo->v_casefold & AFPVOL_MTOUUPPER))
+ if ((myvolinfo->v_casefold & AFPVOL_MTOUUPPER))
flags |= CONV_TOUPPER;
- else if ((volinfo->v_casefold & AFPVOL_MTOULOWER))
+ else if ((myvolinfo->v_casefold & AFPVOL_MTOULOWER))
flags |= CONV_TOLOWER;
- if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
+ if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
flags |= CONV__EILSEQ;
}
outlen = MAXPATHLEN;
if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
- volinfo->v_volcharset,
- volinfo->v_maccharset,
+ myvolinfo->v_volcharset,
+ myvolinfo->v_maccharset,
m, inplen, u, outlen, &flags)) ) {
dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
- volinfo->v_volcodepage, mpath);
+ myvolinfo->v_volcodepage, mpath);
return NULL;
}
and can compare it with the currents volume path
*/
int i = 0;
- while (volinfo->v_path[i]) {
- if ((pathbuf[i] == 0) || (volinfo->v_path[i] != pathbuf[i])) {
+ while (myvolinfo->v_path[i]) {
+ if ((pathbuf[i] == 0) || (myvolinfo->v_path[i] != pathbuf[i])) {
dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name);
return 1;
}
else
adflags = ADFLAGS_DIR;
- adname = volinfo->ad_path(fname, adflags);
+ adname = myvolinfo->ad_path(fname, adflags);
if ((ret = access( adname, F_OK)) != 0) {
if (errno != ENOENT) {
return -1;
/* Create ad file */
- ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+ ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
chmod(adname, st->st_mode);
#endif
} else {
- ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+ ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
return -1;
}
/* Check for ".Parent" */
- if ( (adpar_ok = access(volinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
+ if ( (adpar_ok = access(myvolinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
if (errno != ENOENT) {
dbd_log(LOGSTD, "Access error on '%s/%s': %s",
- cwdbuf, volinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
+ cwdbuf, myvolinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
return -1;
}
dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
}
/* Create ad dir and set name */
- ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+ ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
return -1;
}
chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
- chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
+ chown(myvolinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
}
return 0;
char *namep, *namedup = NULL;
/* Check if this is an AFPVOL_EA_AD vol */
- if (volinfo->v_vfs_ea == AFPVOL_EA_AD) {
+ if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) {
/* Does the filename contain "::EA" ? */
namedup = strdup(name);
if ((namep = strstr(namedup, "::EA")) == NULL) {
/* Get CNID from ad-file if volume is using AFPVOL_CACHE */
ad_cnid = 0;
- if ( (volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
- ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+ if ( (myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
+ ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
if (dbd_flags & DBD_FLAGS_CLEANUP)
memset(&rply, 0, sizeof(struct cnid_dbd_rply));
rqst.did = did;
rqst.cnid = ad_cnid;
- if ( ! (volinfo->v_flags & AFPVOL_NODEV))
+ if ( ! (myvolinfo->v_flags & AFPVOL_NODEV))
rqst.dev = st->st_dev;
rqst.ino = st->st_ino;
rqst.type = S_ISDIR(st->st_mode)?1:0;
db_cnid = rply.cnid;
dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
- if ((volinfo->v_flags & AFPVOL_CACHE)
+ if ((myvolinfo->v_flags & AFPVOL_CACHE)
&& ADFILE_OK
&& ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
cwdbuf, name, ntohl(db_cnid));
- ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+ ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
cwdbuf, name, strerror(errno));
if ((ad_cnid == 0) && db_cnid) {
/* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
- if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
+ if ((myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
cwdbuf, name, ntohl(db_cnid));
- ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+ ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
cwdbuf, name, strerror(errno));
}
/* Check EA files */
- if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
+ if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD)
check_eafiles(ep->d_name);
/**************************************************************************
}
/* Make this stuff accessible from all funcs easily */
- volinfo = vi;
+ myvolinfo = vi;
dbd_flags = flags;
/* Init a fake struct vol with just enough so we can call ea_open and friends */
volume.v_adouble = AD_VERSION2;
- volume.v_vfs_ea = volinfo->v_vfs_ea;
+ volume.v_vfs_ea = myvolinfo->v_vfs_ea;
initvol_vfs(&volume);
/* Run with umask 0 */
umask(0);
/* Remove trailing slash from volume, chdir to vol */
- if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
- volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
- strcpy(cwdbuf, volinfo->v_path);
- chdir(volinfo->v_path);
+ if (myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] == '/')
+ myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] = 0;
+ strcpy(cwdbuf, myvolinfo->v_path);
+ chdir(myvolinfo->v_path);
/* Start recursion */
if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
rqst.cnid = htonl(dbd_cnid);
- ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
+ if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
+ dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
+ (void)dbif_txn_abort(dbd);
+ goto cleanup;
+ }
+
dbif_txn_close(dbd, ret);
deleted++;
}
if (dbd_cnid < rebuild_cnid) {
/* CNID is orphaned -> delete */
- dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
+ dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid);
if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
rqst.cnid = htonl(dbd_cnid);
- ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
+ if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
+ dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
+ (void)dbif_txn_abort(dbd);
+ goto cleanup;
+ }
dbif_txn_close(dbd, ret);
deleted++;
}
dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
goto cleanup;
}
- }
+ } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */
cleanup:
dbif_idwalk(dbd, NULL, 1); /* Close cursor */
/*
Main func called from cmd_dbd.c
*/
-int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
+int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *vi, dbd_flags_t flags)
{
int ret = 0;
struct db_param db_param = { 0 };
dbd = dbd_ref;
/* We only support unicode volumes ! */
- if ( volinfo->v_volcharset != CH_UTF8) {
- dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
+ if ( vi->v_volcharset != CH_UTF8) {
+ dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vi->v_volcodepage, vi->v_volcharset, CH_UTF8);
return -1;
}
goto exit_cleanup; /* Got signal, jump from dbd_readdir */
/* scanvol */
- if ( (scanvol(volinfo, flags)) != 0)
+ if ( (scanvol(vi, flags)) != 0)
return -1;
if (! nocniddb) {
/*
* Copyright (C) Joerg Lenneis 2003
- * Copyright (C) Frank Lahm 2010
+ * Copyright (C) Frank Lahm 2009, 2010
+ *
* All Rights Reserved. See COPYING.
*/
Result:
via TCP socket
4. afpd -------> cnid_dbd
+
+ cnid_metad and cnid_dbd have been converted to non-blocking IO in 2010.
*/
#include <errno.h>
#include <string.h>
#include <signal.h>
-#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
-#endif
-#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
-#endif
-#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
-#endif
#include <sys/un.h>
#define _XPG4_2 1
#include <sys/socket.h>
#include <stdio.h>
#include <time.h>
-#include <sys/ioctl.h>
#ifndef WEXITSTATUS
#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#include <atalk/logger.h>
#include <atalk/cnid_dbd_private.h>
#include <atalk/paths.h>
+#include <atalk/volinfo.h>
-#include "db_param.h"
#include "usockfd.h"
#define DBHOME ".AppleDB"
#define DEFAULTPORT "4700"
struct server {
- char *name;
+ struct volinfo *volinfo;
pid_t pid;
time_t tm; /* When respawned last */
int count; /* Times respawned in the last TESTTIME secondes */
daemon_exit(0);
}
-static struct server *test_usockfn(char *dir)
+static struct server *test_usockfn(struct volinfo *volinfo)
{
int i;
for (i = 0; i < MAXVOLS; i++) {
- if (srv[i].name && !strcmp(srv[i].name, dir)) {
+ if ((srv[i].volinfo) && (strcmp(srv[i].volinfo->v_path, volinfo->v_path) == 0)) {
return &srv[i];
}
}
}
/* -------------------- */
-static int send_cred(int socket, int fd)
-{
- int ret;
- struct msghdr msgh;
- struct iovec iov[1];
- struct cmsghdr *cmsgp = NULL;
- char *buf;
- size_t size;
- int er=0;
-
- size = CMSG_SPACE(sizeof fd);
- buf = malloc(size);
- if (!buf) {
- LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
- return -1;
- }
-
- memset(&msgh,0,sizeof (msgh));
- memset(buf,0, size);
-
- msgh.msg_name = NULL;
- msgh.msg_namelen = 0;
-
- msgh.msg_iov = iov;
- msgh.msg_iovlen = 1;
-
- iov[0].iov_base = &er;
- iov[0].iov_len = sizeof(er);
-
- msgh.msg_control = buf;
- msgh.msg_controllen = size;
-
- cmsgp = CMSG_FIRSTHDR(&msgh);
- cmsgp->cmsg_level = SOL_SOCKET;
- cmsgp->cmsg_type = SCM_RIGHTS;
- cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
-
- *((int *)CMSG_DATA(cmsgp)) = fd;
- msgh.msg_controllen = cmsgp->cmsg_len;
-
- do {
- ret = sendmsg(socket,&msgh, 0);
- } while ( ret == -1 && errno == EINTR );
- if (ret == -1) {
- LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
- free(buf);
- return -1;
- }
- free(buf);
- return 0;
-}
-
-/* -------------------- */
-static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn)
+static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo)
{
pid_t pid;
struct server *up;
time_t t;
char buf1[8];
char buf2[8];
+ char *volpath = volinfo->v_path;
- LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: dbdir: '%s', UNIX socket file: '%s'",
- dbdir, usockfn);
+ LOG(log_debug, logtype_cnid, "maybe_start_dbd: Volume: \"%s\"", volpath);
- up = test_usockfn(dbdir);
+ up = test_usockfn(volinfo);
if (up && up->pid) {
/* we already have a process, send our fd */
- if (send_cred(up->control_fd, rqstfd) < 0) {
+ if (send_fd(up->control_fd, rqstfd) < 0) {
/* FIXME */
return -1;
}
return 0;
}
- LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: no cnid_dbd for that volume yet. Starting one ...");
+ LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: no cnid_dbd for that volume yet");
time(&t);
if (!up) {
/* find an empty slot */
for (i = 0; i < MAXVOLS; i++) {
- if ( !srv[i].name ) {
+ if (srv[i].volinfo == NULL) {
up = &srv[i];
+ up->volinfo = volinfo;
+ retainvolinfo(volinfo);
up->tm = t;
up->count = 0;
- up->name = strdup(dbdir);
break;
}
}
LOG(log_error, logtype_cnid, "no free slot for cnid_dbd child. Configured maximum: %d. Do you have so many volumes?", MAXVOLS);
return -1;
}
- }
- else {
+ } else {
/* we have a slot but no process, check for respawn too fast */
if ( (t < (up->tm + TESTTIME)) /* We're in the respawn time window */
&&
/* there's a pb with the db inform child
* it will run recover, delete the db whatever
*/
- LOG(log_error, logtype_cnid, "try with -d %s", up->name);
- ret = execlp(dbdpn, dbdpn, "-d", dbdir, buf1, buf2, logconfig, NULL);
+ LOG(log_error, logtype_cnid, "try with -d %s", up->volinfo->v_path);
+ ret = execlp(dbdpn, dbdpn, "-d", volpath, buf1, buf2, logconfig, NULL);
}
else {
- ret = execlp(dbdpn, dbdpn, dbdir, buf1, buf2, logconfig, NULL);
+ ret = execlp(dbdpn, dbdpn, volpath, buf1, buf2, logconfig, NULL);
}
/* Yikes! We're still here, so exec failed... */
LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno));
}
/* ------------------ */
-static int set_dbdir(char *dbdir, int len)
+static int set_dbdir(char *dbdir)
{
+ int len;
struct stat st;
- if (!len)
- return -1;
+ len = strlen(dbdir);
if (stat(dbdir, &st) < 0 && mkdir(dbdir, 0755) < 0) {
LOG(log_error, logtype_cnid, "set_dbdir: mkdir failed for %s", dbdir);
/* ------------------ */
int main(int argc, char *argv[])
{
- char dbdir[MAXPATHLEN + 1];
+ char volpath[MAXPATHLEN + 1];
int len, actual_len;
pid_t pid;
int status;
char *dbdpn = _PATH_CNID_DBD;
char *host = DEFAULTHOST;
char *port = DEFAULTPORT;
- struct db_param *dbp;
int i;
int cc;
uid_t uid = 0;
char *loglevel = NULL;
char *logfile = NULL;
sigset_t set;
+ struct volinfo *volinfo;
set_processname("cnid_metad");
}
/* Check PID lockfile and become a daemon */
- switch(server_lock("cnid_metad", _PATH_CNID_METAD_LOCK, 0)) {
+ switch(server_lock("cnid_metad", _PATH_CNID_METAD_LOCK, debug)) {
case -1: /* error */
daemon_exit(EXITERR_SYS);
case 0: /* child */
if (rqstfd <= 0)
continue;
- /* TODO: Check out read errors, broken pipe etc. in libatalk. Is
- SIGIPE ignored there? Answer: Ignored for dsi, but not for asp ... */
- ret = readt(rqstfd, &len, sizeof(int), 1, 5);
+ ret = readt(rqstfd, &len, sizeof(int), 1, 4);
+
if (!ret) {
/* already close */
goto loop_end;
goto loop_end;
}
- actual_len = readt(rqstfd, dbdir, len, 1, 5);
+ actual_len = readt(rqstfd, volpath, len, 1, 5);
if (actual_len < 0) {
LOG(log_severe, logtype_cnid, "Read(2) error : %s", strerror(errno));
goto loop_end;
LOG(log_error, logtype_cnid, "error/short read (dir): %s", strerror(errno));
goto loop_end;
}
- dbdir[len] = '\0';
+ volpath[len] = '\0';
- if (set_dbdir(dbdir, len) < 0) {
+ /* Load .volinfo file */
+ if ((volinfo = allocvolinfo(volpath)) == NULL) {
+ LOG(log_severe, logtype_cnid, "allocvolinfo: %s", strerror(errno));
goto loop_end;
}
- if ((dbp = db_param_read(dbdir, METAD)) == NULL) {
- LOG(log_error, logtype_cnid, "Error reading config file");
+ if (set_dbdir(volinfo->v_dbpath) < 0) {
goto loop_end;
}
- maybe_start_dbd(dbdpn, dbdir, dbp->usock_file);
+
+ maybe_start_dbd(dbdpn, volinfo);
+
+ (void)closevolinfo(volinfo);
loop_end:
close(rqstfd);
/*
- * $Id: comm.c,v 1.6 2009-10-19 08:09:07 didg Exp $
- *
* Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
+ *
* All Rights Reserved. See COPYING.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-
-#ifdef HAVE_UNISTD_H
#include <unistd.h>
-#endif
-
#include <sys/param.h>
-#define _XPG4_2 1
-#include <sys/socket.h>
-
-#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
-#endif
-
-#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
-#endif
-
-#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
-#endif
-
-#ifdef HAVE_SYS_SOCKET_H
+#define _XPG4_2 1
#include <sys/socket.h>
-#endif
-
#include <sys/select.h>
-
#include <assert.h>
#include <time.h>
#include <atalk/logger.h>
+#include <atalk/util.h>
#include <atalk/cnid_dbd_private.h>
#include "db_param.h"
return;
}
-static int recv_cred(int fd)
-{
- int ret;
- struct msghdr msgh;
- struct iovec iov[1];
- struct cmsghdr *cmsgp = NULL;
- char buf[CMSG_SPACE(sizeof(int))];
- char dbuf[80];
-
- memset(&msgh,0,sizeof(msgh));
- memset(buf,0,sizeof(buf));
-
- msgh.msg_name = NULL;
- msgh.msg_namelen = 0;
-
- msgh.msg_iov = iov;
- msgh.msg_iovlen = 1;
-
- iov[0].iov_base = dbuf;
- iov[0].iov_len = sizeof(dbuf);
-
- msgh.msg_control = buf;
- msgh.msg_controllen = sizeof(buf);
-
- do {
- ret = recvmsg(fd ,&msgh,0);
- } while ( ret == -1 && errno == EINTR );
-
- if ( ret == -1 ) {
- return -1;
- }
-
- for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
- if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
- return *(int *) CMSG_DATA(cmsgp);
- }
- }
-
- if ( ret == sizeof (int) )
- errno = *(int *)dbuf; /* Rcvd errno */
- else
- errno = ENOENT; /* Default errno */
-
- return -1;
-}
/*
* Check for client requests. We keep up to fd_table_size open descriptors in
if (FD_ISSET(control_fd, &readfds)) {
int l = 0;
- fd = recv_cred(control_fd);
+ fd = recv_fd(control_fd, 0);
if (fd < 0) {
return -1;
}
if (!cur_fd)
return 0;
+
+ LOG(log_maxdebug, logtype_cnid, "comm_rcv: got data on fd %u", cur_fd);
+
+ if (setnonblock(cur_fd, 1) != 0) {
+ LOG(log_error, logtype_cnid, "comm_rcv: setnonblock: %s", strerror(errno));
+ return -1;
+ }
+
nametmp = rqst->name;
- if ((b = readt(cur_fd, rqst, sizeof(struct cnid_dbd_rqst), 1, 5))
+ if ((b = readt(cur_fd, rqst, sizeof(struct cnid_dbd_rqst), 1, CNID_DBD_TIMEOUT))
!= sizeof(struct cnid_dbd_rqst)) {
if (b)
LOG(log_error, logtype_cnid, "error reading message header: %s", strerror(errno));
return 0;
}
rqst->name = nametmp;
- if (rqst->namelen && readt(cur_fd, rqst->name, rqst->namelen, 1, 5) != rqst->namelen) {
+ if (rqst->namelen && readt(cur_fd, rqst->name, rqst->namelen, 1, CNID_DBD_TIMEOUT)
+ != rqst->namelen) {
LOG(log_error, logtype_cnid, "error reading message name: %s", strerror(errno));
invalidate_fd(cur_fd);
return 0;
needs zero terminated strings. */
rqst->name[rqst->namelen] = '\0';
+ LOG(log_maxdebug, logtype_cnid, "comm_rcv: got %u bytes", b + rqst->namelen);
+
return 1;
}
/*
- * $Id: comm.h,v 1.5 2009-10-19 08:09:07 didg Exp $
- *
* Copyright (C) Joerg Lenneis 2003
* All Rights Reserved. See COPYING.
*/
#ifndef CNID_DBD_COMM_H
#define CNID_DBD_COMM_H 1
+/* number of seconds to try reading in readt */
+#define CNID_DBD_TIMEOUT 1
#include <atalk/cnid_dbd_private.h>
#define MAXKEYLEN 64
#define DEFAULT_LOGFILE_AUTOREMOVE 1
-#define DEFAULT_CACHESIZE (8 * 1024)
+#define DEFAULT_CACHESIZE (8 * 1024) /* KB, so 8 MB */
+#define DEFAULT_MAXLOCKS 5000
+#define DEFAULT_MAXLOCKOBJS 5000
#define DEFAULT_FLUSH_FREQUENCY 1000
#define DEFAULT_FLUSH_INTERVAL 1800
#define DEFAULT_USOCK_FILE "usock"
{
dbp->logfile_autoremove = DEFAULT_LOGFILE_AUTOREMOVE;
dbp->cachesize = DEFAULT_CACHESIZE;
+ dbp->maxlocks = DEFAULT_MAXLOCKS;
+ dbp->maxlockobjs = DEFAULT_MAXLOCKOBJS;
dbp->flush_frequency = DEFAULT_FLUSH_FREQUENCY;
dbp->flush_interval = DEFAULT_FLUSH_INTERVAL;
if (make_pathname(dbp->usock_file, dir, DEFAULT_USOCK_FILE, usock_maxlen()) < 0) {
buffer overflow) nor elegant, we need to add support for whitespace in
filenames as well. */
-struct db_param *db_param_read(char *dir, enum identity id)
+struct db_param *db_param_read(char *dir)
{
FILE *fp;
static char key[MAXKEYLEN + 1];
LOG(log_info, logtype_cnid, "db_param: setting UNIX domain socket filename to %s", params.usock_file);
}
- /* Config for cnid_metad only */
- if ( id == METAD ) {
- /* Currently empty */
+ if (! strcmp(key, "fd_table_size")) {
+ params.fd_table_size = parse_int(val);
+ LOG(log_info, logtype_cnid, "db_param: setting max number of concurrent afpd connections per volume (fd_table_size) to %d", params.fd_table_size);
+ } else if (! strcmp(key, "logfile_autoremove")) {
+ params.logfile_autoremove = parse_int(val);
+ LOG(log_info, logtype_cnid, "db_param: setting logfile_autoremove to %d", params.logfile_autoremove);
+ } else if (! strcmp(key, "cachesize")) {
+ params.cachesize = parse_int(val);
+ LOG(log_info, logtype_cnid, "db_param: setting cachesize to %d", params.cachesize);
+ } else if (! strcmp(key, "maxlocks")) {
+ params.maxlocks = parse_int(val);
+ LOG(log_info, logtype_cnid, "db_param: setting maxlocks to %d", params.maxlocks);
+ } else if (! strcmp(key, "maxlockobjs")) {
+ params.maxlockobjs = parse_int(val);
+ LOG(log_info, logtype_cnid, "db_param: setting maxlockobjs to %d", params.maxlockobjs);
+ } else if (! strcmp(key, "flush_frequency")) {
+ params.flush_frequency = parse_int(val);
+ LOG(log_info, logtype_cnid, "db_param: setting flush_frequency to %d", params.flush_frequency);
+ } else if (! strcmp(key, "flush_interval")) {
+ params.flush_interval = parse_int(val);
+ LOG(log_info, logtype_cnid, "db_param: setting flush_interval to %d", params.flush_interval);
+ } else if (! strcmp(key, "idle_timeout")) {
+ params.idle_timeout = parse_int(val);
+ LOG(log_info, logtype_cnid, "db_param: setting idle timeout to %d", params.idle_timeout);
}
- /* Config for dbd only */
- else if (id == CNID_DBD ) {
- if (! strcmp(key, "fd_table_size")) {
- params.fd_table_size = parse_int(val);
- LOG(log_info, logtype_cnid, "db_param: setting max number of concurrent afpd connections per volume (fd_table_size) to %d", params.fd_table_size);
- } else if (! strcmp(key, "logfile_autoremove")) {
- params.logfile_autoremove = parse_int(val);
- LOG(log_info, logtype_cnid, "db_param: setting logfile_autoremove to %d", params.logfile_autoremove);
- } else if (! strcmp(key, "cachesize")) {
- params.cachesize = parse_int(val);
- LOG(log_info, logtype_cnid, "db_param: setting cachesize to %d", params.cachesize);
- } else if (! strcmp(key, "flush_frequency")) {
- params.flush_frequency = parse_int(val);
- LOG(log_info, logtype_cnid, "db_param: setting flush_frequency to %d", params.flush_frequency);
- } else if (! strcmp(key, "flush_interval")) {
- params.flush_interval = parse_int(val);
- LOG(log_info, logtype_cnid, "db_param: setting flush_interval to %d", params.flush_interval);
- } else if (! strcmp(key, "idle_timeout")) {
- params.idle_timeout = parse_int(val);
- LOG(log_info, logtype_cnid, "db_param: setting idle timeout to %d", params.idle_timeout);
- }
- }
if (parse_err)
break;
}
/*
- * $Id: db_param.h,v 1.6 2009-12-21 07:32:01 franklahm Exp $
- *
* Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
* All Rights Reserved. See COPYING.
*/
#include <sys/param.h>
#include <sys/cdefs.h>
-enum identity {
- METAD,
- CNID_DBD
-};
-
struct db_param {
char *dir;
int logfile_autoremove;
int cachesize; /* in KB */
+ int maxlocks;
+ int maxlockobjs;
int flush_interval;
int flush_frequency;
char usock_file[MAXPATHLEN + 1];
int max_vols;
};
-extern struct db_param * db_param_read (char *, enum identity);
-
+extern struct db_param *db_param_read (char *);
#endif /* CNID_DBD_DB_PARAM_H */
/*
- * $Id: dbd.h,v 1.7 2009-11-25 14:59:15 franklahm Exp $
- *
* Copyright (C) Joerg Lenneis 2003
- * Copyright (C) Frank Lahm 2009
+ * Copyright (C) Frank Lahm 2009, 2010
* All Rights Reserved. See COPYING.
*/
extern int add_cnid(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply);
extern int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply);
-extern int dbd_stamp(DBD *dbd);
extern int dbd_add(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int nolookup);
extern int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int roflag);
extern int dbd_get(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
extern int dbd_delete(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int idx);
extern int dbd_getstamp(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
extern int dbd_rebuild_add(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
+extern int dbd_search(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
extern int dbd_check_indexes(DBD *dbd, char *);
#endif /* CNID_DBD_DBD_H */
/*
- * $Id: dbd_add.c,v 1.8 2010-01-19 14:57:11 franklahm Exp $
- *
* Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
* All Rights Reserved. See COPYING.
*/
/* ---------------------- */
int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
{
+ static cnid_t id;
+ static char buf[ROOTINFO_DATALEN];
DBT rootinfo_key, rootinfo_data;
int rc;
- cnid_t hint, id;
-
+ cnid_t hint;
+
memset(&rootinfo_key, 0, sizeof(rootinfo_key));
memset(&rootinfo_data, 0, sizeof(rootinfo_data));
rootinfo_key.data = ROOTINFO_KEY;
rootinfo_key.size = ROOTINFO_KEYLEN;
- if ((rc = dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) <= 0) {
- rply->result = CNID_DBD_RES_ERR_DB;
- return -1;
+ if (id == 0) {
+ if ((rc = dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) < 0) {
+ rply->result = CNID_DBD_RES_ERR_DB;
+ return -1;
+ }
+ if (rc == 0) {
+ /* no rootinfo key yet */
+ memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
+ id = CNID_START - 1;
+ } else {
+ memcpy(buf, (char *)rootinfo_data.data, ROOTINFO_DATALEN);
+ memcpy(&hint, buf + CNID_TYPE_OFS, sizeof(hint));
+ id = ntohl(hint);
+ if (id < CNID_START - 1)
+ id = CNID_START - 1;
+ }
}
- memcpy(&hint, (char *)rootinfo_data.data +CNID_TYPE_OFS, sizeof(hint));
- id = ntohl(hint);
+
/* If we've hit the max CNID allowed, we return an error. CNID
* needs to be recycled before proceding. */
if (++id == CNID_INVALID) {
return -1;
}
-#if 0
- memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
-#endif
+ rootinfo_data.data = buf;
rootinfo_data.size = ROOTINFO_DATALEN;
hint = htonl(id);
- memcpy((char *)rootinfo_data.data +CNID_TYPE_OFS, &hint, sizeof(hint));
+ memcpy(buf + CNID_TYPE_OFS, &hint, sizeof(hint));
if (dbif_put(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0) < 0) {
rply->result = CNID_DBD_RES_ERR_DB;
return 0;
}
-/* ---------------
-*/
-int dbd_stamp(DBD *dbd)
-{
- DBT rootinfo_key, rootinfo_data;
- cnid_t hint;
- char buf[ROOTINFO_DATALEN];
- char stamp[CNID_DEV_LEN];
-
- memset(&rootinfo_key, 0, sizeof(rootinfo_key));
- memset(&rootinfo_data, 0, sizeof(rootinfo_data));
- rootinfo_key.data = ROOTINFO_KEY;
- rootinfo_key.size = ROOTINFO_KEYLEN;
-
- switch (dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) {
- case 0:
- hint = htonl(CNID_START);
- memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
- rootinfo_data.data = buf;
- rootinfo_data.size = ROOTINFO_DATALEN;
- if (dbif_stamp(dbd, stamp, CNID_DEV_LEN) < 0) {
- return -1;
- }
- memcpy((char *)rootinfo_data.data + CNID_TYPE_OFS, &hint, sizeof(hint));
- memcpy((char *)rootinfo_data.data + CNID_DEV_OFS, stamp, sizeof(stamp));
- if (dbif_put(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0) < 0) {
- return -1;
- }
- return 0;
-
- case 1: /* we already have one */
- return 0;
- default:
- return -1;
- }
- return -1;
-}
-
/* ------------------------ */
/* We need a nolookup version for `dbd` */
int dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int nolookup)
+
/*
* $Id: dbd_getstamp.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
*
memcpy(&rply->did, (char *) data.data + CNID_DID_OFS, sizeof(cnid_t));
- rply->namelen = data.size - CNID_NAME_OFS;
- rply->name = (char *)data.data + CNID_NAME_OFS;
+ rply->namelen = data.size;
+ rply->name = (char *)data.data;
LOG(log_debug, logtype_cnid, "dbd_resolve: Resolving CNID %u to did %u name %s",
ntohl(rqst->cnid), ntohl(rply->did), rply->name);
--- /dev/null
+/*
+ * Copyright (C) Frank Lahm 2010
+ * All Rights Reserved. See COPYING.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <errno.h>
+#include <netatalk/endian.h>
+#include <atalk/logger.h>
+#include <atalk/cnid_dbd_private.h>
+
+#include "dbif.h"
+#include "dbd.h"
+#include "pack.h"
+
+int dbd_search(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
+{
+ DBT key;
+ int results;
+ static char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)];
+
+ LOG(log_debug, logtype_cnid, "dbd_search(\"%s\"):", rqst->name);
+
+ memset(&key, 0, sizeof(key));
+ rply->name = resbuf;
+ rply->namelen = 0;
+
+ key.data = rqst->name;
+ key.size = rqst->namelen;
+
+ if ((results = dbif_search(dbd, &key, resbuf)) < 0) {
+ LOG(log_error, logtype_cnid, "dbd_search(\"%s\"): db error", rqst->name);
+ rply->result = CNID_DBD_RES_ERR_DB;
+ return -1;
+ }
+ if (results) {
+ LOG(log_debug, logtype_cnid, "dbd_search(\"%s\"): %d matches", rqst->name, results);
+ rply->namelen = results * sizeof(cnid_t);
+ rply->result = CNID_DBD_RES_OK;
+ } else {
+ LOG(log_debug, logtype_cnid, "dbd_search(\"%s\"): no matches", rqst->name);
+ rply->result = CNID_DBD_RES_NOTFOUND;
+ }
+
+ return 1;
+}
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif /* HAVE_SYS_TYPES_H */
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/cdefs.h>
#include <unistd.h>
-#include <atalk/logger.h>
+
#include <db.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+
#include "db_param.h"
#include "dbif.h"
#include "pack.h"
#define DB_ERRLOGFILE "db_errlog"
-static char *old_dbfiles[] = {"cnid.db", NULL};
-
-/* --------------- */
-static int upgrade_required(const DBD *dbd)
+/*!
+ * Get the db stamp which is the st_ctime of the file "cnid2.db" and store it in buffer
+ */
+static int dbif_stamp(DBD *dbd, void *buffer, int size)
{
- int i;
- int cwd = -1;
- int ret = 0;
- int found = 0;
struct stat st;
+ int rc,cwd;
- if ( ! dbd->db_filename)
- /* in memory db */
- return 0;
+ if (size < 8)
+ return -1;
/* Remember cwd */
if ((cwd = open(".", O_RDONLY)) < 0) {
/* chdir to db_envhome */
if ((chdir(dbd->db_envhome)) != 0) {
LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));
+ return -1;
+ }
+
+ if ((rc = stat(dbd->db_table[DBIF_CNID].name, &st)) < 0) {
+ LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(errno));
+ return -1;
+ }
+
+ LOG(log_maxdebug, logtype_cnid,"stamp: %s", asctime(localtime(&st.st_ctime)));
+
+ memset(buffer, 0, size);
+ memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
+
+ if ((fchdir(cwd)) != 0) {
+ LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * Inititialize rootinfo key (which has CNID 0 as key)
+ *
+ * This also "stamps" the database, which means storing st.st_ctime of the
+ * "cnid2.db" file in the rootinfo data at the DEV offset
+ *
+ * @param dbd (rw) database handle
+ * @param version (r) database version number
+ *
+ * @returns -1 on error, 0 on success
+ */
+static int dbif_init_rootinfo(DBD *dbd, int version)
+{
+ DBT key, data;
+ uint32_t v;
+ char buf[ROOTINFO_DATALEN];
+
+ LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
+
+ v = version;
+ v = htonl(v);
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = ROOTINFO_KEY;
+ key.size = ROOTINFO_KEYLEN;
+ data.data = buf;
+ data.size = ROOTINFO_DATALEN;
+
+ memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
+ memcpy(buf + CNID_DID_OFS, &v, sizeof(v));
+ if (dbif_stamp(dbd, buf + CNID_DEV_OFS, CNID_DEV_LEN) < 0)
+ return -1;
+
+ if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
+ return -1;
+ if (dbif_txn_commit(dbd) != 1) {
+ LOG(log_error, logtype_cnid, "dbif_init_rootinfo: cant commit txn");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * Return CNID database version number
+ *
+ * Returns version in *version
+ *
+ * @returns -1 on error, 0 if theres no rootinfo key yet, 1 if *version is returned
+ */
+static int dbif_getversion(DBD *dbd, uint32_t *version)
+{
+ DBT key, data;
+ int ret;
+
+ LOG(log_maxdebug, logtype_cnid, "dbif_getversion: reading version info");
+
+ *version = -1;
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = ROOTINFO_KEY;
+ key.size = ROOTINFO_KEYLEN;
+
+ switch (dbif_get(dbd, DBIF_CNID, &key, &data, 0)) {
+ case 1: /* found */
+ memcpy(version, (char *)data.data + CNID_DID_OFS, sizeof(uint32_t));
+ *version = ntohl(*version);
+ LOG(log_debug, logtype_cnid, "CNID database version %u", *version);
+ ret = 1;
+ break;
+ case 0: /* not found */
+ LOG(log_debug, logtype_cnid, "dbif_getversion: no version info found");
+ ret = 0;
+ break;
+ default:
+ LOG(log_error, logtype_cnid, "dbif_getversion: database error");
ret = -1;
- goto exit;
+ break;
}
- for (i = 0; old_dbfiles[i] != NULL; i++) {
- if ( !(stat(old_dbfiles[i], &st) < 0) ) {
- found++;
- continue;
- }
- if (errno != ENOENT) {
- LOG(log_error, logtype_cnid, "cnid_open: Checking %s gave %s", old_dbfiles[i], strerror(errno));
- found++;
- }
+ return ret;
+}
+
+/*!
+ * Set CNID database version number
+ *
+ * Initializes rootinfo key as neccessary
+ * @returns -1 on error, 0 on success
+ */
+static int dbif_setversion(DBD *dbd, uint32_t version)
+{
+ int ret;
+ DBT key, data;
+ uint32_t v;
+
+ LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
+
+ v = version;
+ v = htonl(v);
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = ROOTINFO_KEY;
+ key.size = ROOTINFO_KEYLEN;
+
+ if ((ret = dbif_get(dbd, DBIF_CNID, &key, &data, 0)) == -1)
+ return -1;
+ if (ret == 0) {
+ /* No rootinfo key yet, init it */
+ if (dbif_init_rootinfo(dbd, CNID_VERSION) != 0)
+ return -1;
+ /* Now try again */
+ if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) == -1)
+ return -1;
}
+ memcpy((char *)data.data + CNID_DID_OFS, &v, sizeof(v));
+ data.size = ROOTINFO_DATALEN;
+ if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
+ return -1;
-exit:
- if (cwd != -1) {
- if ((fchdir(cwd)) != 0) {
- LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));
- ret = -1;
- }
- close(cwd);
+ return 0;
+}
+
+/*!
+ * Upgrade CNID database versions, initialize rootinfo key as as necessary in dbif_setversion()
+ *
+ * For now this does nothing, as upgrading from ver. 0 to 1 is done in dbif_open
+ */
+#define UNINTIALIZED_DB UINT32_MAX
+static int dbif_upgrade(DBD *dbd)
+{
+ uint32_t version = CNID_VERSION_UNINTIALIZED_DB;
+
+ if (dbif_getversion(dbd, &version) == -1)
+ return -1;
+ if (version == CNID_VERSION_UNINTIALIZED_DB) {
+ version = CNID_VERSION;
+ if (dbif_setversion(dbd, CNID_VERSION) != 0)
+ return -1;
}
- return (ret < 0 ? ret : found);
+
+ /*
+ * Do upgrade stuff ...
+ */
+
+ /* Write current version to database */
+ if (version != CNID_VERSION) {
+ if (dbif_setversion(dbd, CNID_VERSION) != 0)
+ return -1;
+ }
+
+ LOG(log_debug, logtype_cnid, "Finished CNID database version upgrade check");
+
+ return 0;
}
/* --------------- */
return ret;
}
-/* --------------- */
-int dbif_stamp(DBD *dbd, void *buffer, int size)
-{
- struct stat st;
- int rc,cwd;
-
- if (size < 8)
- return -1;
-
- /* Remember cwd */
- if ((cwd = open(".", O_RDONLY)) < 0) {
- LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
- return -1;
- }
-
- /* chdir to db_envhome */
- if ((chdir(dbd->db_envhome)) != 0) {
- LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));
- return -1;
- }
-
- if ((rc = stat(dbd->db_table[DBIF_CNID].name, &st)) < 0) {
- LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(errno));
- return -1;
- }
- memset(buffer, 0, size);
- memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
-
- if ((fchdir(cwd)) != 0) {
- LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
/* --------------- */
DBD *dbif_init(const char *envhome, const char *filename)
{
dbd->db_table[DBIF_CNID].name = "cnid2.db";
dbd->db_table[DBIF_IDX_DEVINO].name = "devino.db";
dbd->db_table[DBIF_IDX_DIDNAME].name = "didname.db";
+ dbd->db_table[DBIF_IDX_NAME].name = "name.db";
dbd->db_table[DBIF_CNID].type = DB_BTREE;
dbd->db_table[DBIF_IDX_DEVINO].type = DB_BTREE;
dbd->db_table[DBIF_IDX_DIDNAME].type = DB_BTREE;
+ dbd->db_table[DBIF_IDX_NAME].type = DB_BTREE;
dbd->db_table[DBIF_CNID].openflags = DB_CREATE;
dbd->db_table[DBIF_IDX_DEVINO].openflags = DB_CREATE;
dbd->db_table[DBIF_IDX_DIDNAME].openflags = DB_CREATE;
+ dbd->db_table[DBIF_IDX_NAME].openflags = DB_CREATE;
+
+ dbd->db_table[DBIF_IDX_NAME].flags = DB_DUPSORT;
return dbd;
}
/*
We must open the db_env with an absolute pathname, as `dbd` keeps chdir'ing, which
breaks e.g. bdb logfile-rotation with relative pathnames.
- But still we use relative paths with upgrade_required() and the DB_ERRLOGFILE
+ But still we use relative paths with DB_ERRLOGFILE
in order to avoid creating absolute paths by copying. Both have no problem with
a relative path.
*/
{
int ret;
- /* Refuse to do anything if this is an old version of the CNID database */
- if (upgrade_required(dbd)) {
- LOG(log_error, logtype_cnid, "Found version 1 of the CNID database. Please upgrade to version 2");
- return -1;
- }
-
if ((ret = db_env_create(&dbd->db_env, 0))) {
LOG(log_error, logtype_cnid, "error creating DB environment: %s",
db_strerror(ret));
return -1;
}
+ if ((ret = dbd->db_env->set_lk_max_locks(dbd->db_env, dbp->maxlocks))) {
+ LOG(log_error, logtype_cnid, "error setting DB environment maxlocks to %i: %s",
+ 10000, db_strerror(ret));
+ dbd->db_env->close(dbd->db_env, 0);
+ dbd->db_env = NULL;
+ return -1;
+ }
+
+ if ((ret = dbd->db_env->set_lk_max_objects(dbd->db_env, dbp->maxlockobjs))) {
+ LOG(log_error, logtype_cnid, "error setting DB environment max lockobjects to %i: %s",
+ 10000, db_strerror(ret));
+ dbd->db_env->close(dbd->db_env, 0);
+ dbd->db_env = NULL;
+ return -1;
+ }
+
if ((ret = dbd->db_env->open(dbd->db_env, dbd->db_envhome, dbenv_oflags, 0))) {
LOG(log_error, logtype_cnid, "error opening DB environment after recovery: %s",
db_strerror(ret));
LOG(log_error, logtype_cnid, "error forcing checkpoint: %s", db_strerror(ret));
return -1;
}
- LOG(log_debug, logtype_cnid, "Finished CNID database upgrade check");
+ LOG(log_debug, logtype_cnid, "Finished BerkeleyBD upgrade check");
}
if ((fchdir(cwd)) != 0) {
}
if (reindex)
LOG(log_info, logtype_cnid, "... done.");
+
+ if (reindex)
+ LOG(log_info, logtype_cnid, "Reindexing name index...");
+
+ /*
+ * Upgrading from version 0 to 1 requires adding the name index below which
+ * must be done by specifying the DB_CREATE flag
+ */
+ uint32_t version = CNID_VERSION;
+ if (dbd->db_envhome && !reindex) {
+ if (dbif_getversion(dbd, &version) == -1)
+ return -1;
+ }
+
+ if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db,
+ dbd->db_txn,
+ dbd->db_table[DBIF_IDX_NAME].db,
+ idxname,
+ (reindex
+ ||
+ ((CNID_VERSION == CNID_VERSION_1) && (version == CNID_VERSION_0)))
+ ? DB_CREATE : 0)) != 0) {
+ LOG(log_error, logtype_cnid, "Failed to associate name index: %s", db_strerror(ret));
+ return -1;
+ }
+ if (reindex)
+ LOG(log_info, logtype_cnid, "... done.");
+
+ if ((dbd->db_envhome) && ((ret = dbif_upgrade(dbd)) != 0)) {
+ LOG(log_error, logtype_cnid, "Error upgrading CNID database to version %d", CNID_VERSION);
+ return -1;
+ }
return 0;
}
In order to support silent database upgrades:
destroy env at cnid_dbd shutdown.
*/
-int dbif_prep_upgrade(const char *path)
+int dbif_env_remove(const char *path)
{
int ret;
DBD *dbd;
key,
flags);
- if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD)
+ if (ret == DB_NOTFOUND) {
+ LOG(log_info, logtype_cnid, "key not found");
return 0;
+ }
if (ret) {
LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s",
dbd->db_table[dbi].name, db_strerror(ret));
return 1;
}
+/*!
+ * Search the database by name
+ *
+ * @param resbuf (w) buffer for search results CNIDs, maxsize is assumed to be
+ * DBD_MAX_SRCH_RSLTS * sizefof(cnid_t)
+ *
+ * @returns -1 on error, 0 when nothing found, else the number of matches
+ */
+int dbif_search(DBD *dbd, DBT *key, char *resbuf)
+{
+ int ret = 0;
+ int count = 0;
+ DBC *cursorp = NULL;
+ DBT pkey, data;
+ char *cnids = resbuf;
+ cnid_t cnid;
+ char *namebkp = key->data;
+ int namelenbkp = key->size;
+
+ memset(&pkey, 0, sizeof(DBT));
+ memset(&data, 0, sizeof(DBT));
+
+ /* Get a cursor */
+ ret = dbd->db_table[DBIF_IDX_NAME].db->cursor(dbd->db_table[DBIF_IDX_NAME].db,
+ NULL,
+ &cursorp,
+ 0);
+ if (ret != 0) {
+ LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(ret));
+ ret = -1;
+ goto exit;
+ }
+
+ ret = cursorp->pget(cursorp, key, &pkey, &data, DB_SET_RANGE);
+ while (count < DBD_MAX_SRCH_RSLTS && ret != DB_NOTFOUND) {
+ if (!((namelenbkp <= key->size) && (strncmp(namebkp, key->data, namelenbkp) == 0)))
+ break;
+ count++;
+ memcpy(cnids, pkey.data, sizeof(cnid_t));
+ memcpy(&cnid, pkey.data, sizeof(cnid_t));
+ cnids += sizeof(cnid_t);
+ LOG(log_debug, logtype_cnid, "match: CNID %" PRIu32, ntohl(cnid));
+
+ ret = cursorp->pget(cursorp, key, &pkey, &data, DB_NEXT);
+ }
+
+ ret = count;
+
+exit:
+ if (cursorp != NULL)
+ cursorp->close(cursorp);
+ return ret;
+}
+
int dbif_txn_begin(DBD *dbd)
{
int ret;
int dbif_dump(DBD *dbd, int dumpindexes)
{
int rc;
- uint32_t max = 0, count = 0, cnid, type, did, lastid;
+ uint32_t max = 0, count = 0, cnid, type, did, lastid, version;
uint64_t dev, ino;
time_t stamp;
DBC *cur;
/* Rootinfo node ? */
if (cnid == 0) {
- memcpy(&stamp, (char *)data.data + 4, sizeof(time_t));
- memcpy(&lastid, (char *)data.data + 20, sizeof(cnid_t));
+ memcpy(&stamp, (char *)data.data + CNID_DEV_OFS, sizeof(time_t));
+ memcpy(&lastid, (char *)data.data + CNID_TYPE_OFS, CNID_TYPE_LEN);
lastid = ntohl(lastid);
+ memcpy(&version, (char *)data.data + CNID_DID_OFS, CNID_DID_LEN);
+ version = ntohl(version);
+
strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
- printf("dbd stamp: 0x%08x (%s), next free CNID: %u\n", (unsigned int)stamp, timebuf, lastid + 1);
+ printf("CNID db version: %u, dbd stamp: 0x%08x (%s), next free CNID: %u\n",
+ version, (unsigned int)stamp, timebuf, lastid + 1);
} else {
/* dev */
memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8);
#include <atalk/adouble.h>
#include "db_param.h"
-#define DBIF_DB_CNT 3
+#define DBIF_DB_CNT 4
#define DBIF_CNID 0
#define DBIF_IDX_DEVINO 1
#define DBIF_IDX_DIDNAME 2
+#define DBIF_IDX_NAME 3
/* Structures */
typedef struct {
char *db_envhome;
char *db_filename;
FILE *db_errlog;
- db_table db_table[3];
+ db_table db_table[DBIF_DB_CNT];
} DBD;
/* Functions */
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);
extern int dbif_close(DBD *dbd);
-extern int dbif_prep_upgrade(const char *path);
+extern int dbif_env_remove(const char *path);
extern int dbif_get(DBD *, const int, DBT *, DBT *, u_int32_t);
extern int dbif_pget(DBD *, const int, DBT *, DBT *, DBT *, u_int32_t);
extern int dbif_put(DBD *, const int, DBT *, DBT *, u_int32_t);
extern int dbif_del(DBD *, const int, DBT *, u_int32_t);
extern int dbif_count(DBD *, const int, u_int32_t *);
-extern int dbif_stamp(DBD *, void *, int);
+extern int dbif_search(DBD *dbd, DBT *key, char *resbuf);
extern int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd);
extern int dbif_txn_begin(DBD *);
extern int dbif_txn_commit(DBD *);
/*
- * $Id: main.c,v 1.16 2009-11-25 14:59:15 franklahm Exp $
- *
* Copyright (C) Joerg Lenneis 2003
* Copyright (c) Frank Lahm 2009
* All Rights Reserved. See COPYING.
#include <netatalk/endian.h>
#include <atalk/cnid_dbd_private.h>
#include <atalk/logger.h>
+#include <atalk/volinfo.h>
#include "db_param.h"
#include "dbif.h"
*/
#define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
-static DBD *dbd;
+/* Global, needed by pack.c:idxname() */
+struct volinfo volinfo;
+static DBD *dbd;
static int exit_sig = 0;
static void sig_exit(int signo)
case CNID_DBD_OP_REBUILD_ADD:
ret = dbd_rebuild_add(dbd, &rqst, &rply);
break;
+ case CNID_DBD_OP_SEARCH:
+ ret = dbd_search(dbd, &rqst, &rply);
+ break;
default:
LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
ret = -1;
break;
}
-
+
if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
dbif_txn_abort(dbd);
return -1;
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));
struct db_param *dbp;
int err = 0;
int lockfd, ctrlfd, clntfd;
- char *dir, *logconfig;
+ char *logconfig;
set_processname("cnid_dbd");
exit(1);
}
- dir = argv[1];
ctrlfd = atoi(argv[2]);
clntfd = atoi(argv[3]);
logconfig = strdup(argv[4]);
setuplog(logconfig);
- switch_to_user(dir);
+ /* Load .volinfo file */
+ if (loadvolinfo(argv[1], &volinfo) == -1) {
+ LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
+ char dbpath[MAXPATHLEN+1];
+ if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
+ LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
+ exit(EXIT_FAILURE);
+ }
+ strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
+ strcat(dbpath, "/.AppleDB");
+
+ if (vol_load_charsets(&volinfo) == -1) {
+ LOG(log_error, logtype_cnid, "Error loading charsets!");
+ exit(EXIT_FAILURE);
+ }
+ LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
+
+ 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();
- LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
-
set_signal();
/* SIGINT and SIGTERM are always off, unless we are in pselect */
block_sigs_onoff(1);
- if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
+ if ((dbp = db_param_read(dbpath)) == NULL)
exit(1);
LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
- if (NULL == (dbd = dbif_init(".", "cnid2.db")))
+ if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
exit(2);
if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
}
LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
- if (dbd_stamp(dbd) < 0) {
- dbif_close(dbd);
- exit(5);
- }
- LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
-
if (comm_init(dbp, ctrlfd, clntfd) < 0) {
dbif_close(dbd);
exit(3);
if (dbif_close(dbd) < 0)
err++;
- if (dbif_prep_upgrade(dir) < 0)
+ if (dbif_env_remove(dbpath) < 0)
err++;
free_lock(lockfd);
/*
- * $Id: pack.c,v 1.6 2009-10-13 22:55:37 didg Exp $
- *
* Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
* All Rights Reserved. See COPYING.
*/
#include <netatalk/endian.h>
#include <string.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif /* HAVE_SYS_TYPES_H */
+#include <inttypes.h>
#include <sys/param.h>
#include <sys/cdefs.h>
#include <db.h>
+#include <atalk/unicode.h>
+#include <atalk/volinfo.h>
+#include <atalk/logger.h>
#include <atalk/cnid_dbd_private.h>
#include "pack.h"
+/* in main.c for `cnid_dbd` or cmd_dbd.c for `dbd` */
+extern struct volinfo volinfo;
+
/* --------------- */
/*
* This implementation is portable, but could probably be faster by using htonl
return (0);
}
+/* --------------- */
+int idxname(DB *dbp _U_, const DBT *pkey _U_, const DBT *pdata, DBT *skey)
+{
+ static char buffer[MAXPATHLEN +2];
+ uint16_t flags = CONV_TOLOWER;
+ memset(skey, 0, sizeof(DBT));
+
+ if (convert_charset(volinfo.v_volcharset,
+ volinfo.v_volcharset,
+ volinfo.v_maccharset,
+ (char *)pdata->data + CNID_NAME_OFS,
+ strlen((char *)pdata->data + CNID_NAME_OFS),
+ buffer,
+ MAXPATHLEN,
+ &flags) == (size_t)-1) {
+ LOG(log_error, logtype_cnid, "idxname: conversion error");
+ }
+
+ skey->data = buffer;
+ skey->size = strlen(skey->data);
+ return (0);
+}
+
/* The equivalent to make_cnid_data in the cnid library. Non re-entrant. We
differ from make_cnid_data in that we never return NULL, rqst->name cannot
ever cause start[] to overflow because name length is checked in libatalk. */
/*
- * $Id: pack.h,v 1.5 2009-05-04 09:09:43 franklahm Exp $
- *
* Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
* All Rights Reserved. See COPYING.
*/
extern unsigned char *pack_cnid_data(struct cnid_dbd_rqst *);
extern int didname(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
extern int devino(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
+extern int idxname(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
#endif /* CNID_DBD_PACK_H */
/*
- * $Id: usockfd.c,v 1.6 2009-11-05 14:38:07 franklahm Exp $
- *
* Copyright (C) Joerg Lenneis 2003
* All Rights Reserved. See COPYING.
*/
atalkinclude_HEADERS = \
adouble.h vfs.h aep.h afp.h asp.h atp.h boolean.h \
cnid.h compat.h ddp.h dsi.h ldapconfig.h list.h logger.h \
- nbp.h netddp.h pap.h paths.h rtmp.h server_child.h \
+ nbp.h netddp.h pap.h paths.h queue.h rtmp.h server_child.h \
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
+noinst_HEADERS = cnid_dbd_private.h cnid_private.h bstradd.h bstrlib.h errchk.h ftw.h
/*
- $Id: acl.h,v 1.1 2009-10-14 15:04:01 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include "config.h"
#endif /* HAVE_CONFIG_H */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
+
+#ifdef HAVE_SOLARIS_ACLS
#include <sys/acl.h>
-#endif /* HAVE_NFSv4_ACLS */
+#endif /* HAVE_SOLARIS_ACLS */
-/* Solaris NFSv4 ACL stuff */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_POSIX_ACLS
+#include <sys/types.h>
+#include <sys/acl.h>
+#endif /* HAVE_POSIX_ACLS */
+#ifdef HAVE_SOLARIS_ACLS
#define chmod nfsv4_chmod
-
extern int get_nfsv4_acl(const char *name, ace_t **retAces);
-extern int remove_acl(const char *name);
extern int strip_trivial_aces(ace_t **saces, int sacecount);
extern int strip_nontrivial_aces(ace_t **saces, int sacecount);
extern ace_t *concat_aces(ace_t *aces1, int ace1count, ace_t *aces2, int ace2count);
extern int nfsv4_chmod(char *name, mode_t mode);
-#endif /* HAVE_NFSv4_ACLS */
+#endif /* HAVE_SOLARIS_ACLS */
+
+extern int remove_acl_vfs(const char *name);
+#endif /* HAVE_ACLS */
-#endif /* ATALK_ACL_H */
+#endif /* ATALK_ACL_H */
need _XOPEN_SOURCE defined for pread.
*/
#if defined(HAVE_PREAD) && !defined(SOLARIS) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(TRU64)
-#ifdef _XOPEN_SOURCE
-#undef _XOPEN_SOURCE
-#endif
+#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif
+#endif
#include <sys/types.h>
#include <sys/stat.h>
#define AFPTRANS_ALL (AFPTRANS_DDP | AFPTRANS_TCP)
/* server flags */
-#define AFPSRVRINFO_COPY (1<<0) /* supports copyfile */
-#define AFPSRVRINFO_PASSWD (1<<1) /* supports change password */
+#define AFPSRVRINFO_COPY (1<<0) /* supports copyfile */
+#define AFPSRVRINFO_PASSWD (1<<1) /* supports change password */
#define AFPSRVRINFO_NOSAVEPASSWD (1<<2) /* don't allow save password */
#define AFPSRVRINFO_SRVMSGS (1<<3) /* supports server messages */
#define AFPSRVRINFO_SRVSIGNATURE (1<<4) /* supports server signature */
#define AFPSRVRINFO_TCPIP (1<<5) /* supports tcpip */
#define AFPSRVRINFO_SRVNOTIFY (1<<6) /* supports server notifications */
-
#define AFPSRVRINFO_SRVRECONNECT (1<<7) /* supports server reconnect */
#define AFPSRVRINFO_SRVRDIR (1<<8) /* supports directories service */
-
#define AFPSRVRINFO_SRVUTF8 (1<<9) /* supports UTF8 names AFP 3.1 */
#define AFPSRVRINFO_UUID (1<<10) /* supports UUIDs */
-#define AFPSRVRINFO_FASTBOZO (1<<15) /* fast copying */
+#define AFPSRVRINFO_EXTSLEEP (1<<11) /* supports extended sleep */
+#define AFPSRVRINFO_FASTBOZO (1<<15) /* fast copying */
#define AFP_OK 0
#define AFPERR_DID1 -4000 /* not an afp error DID is 1*/
AFPMESG_SERVER = 1
} afpmessage_t;
+/* extended sleep flag */
+#define AFPZZZ_EXT_SLEEP 1
+#define AFPZZZ_EXT_WAKEUP 2
+
+
/* AFP functions */
#define AFP_BYTELOCK 1
#define AFP_CLOSEVOL 2
#define AFP_SETACL 74
#define AFP_ACCESS 75
+/* more defines */
+#define REPLAYCACHE_SIZE 128
+
#endif
--- /dev/null
+/*
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*!
+ * @file
+ * Additional functions for bstrlib.
+ */
+
+#ifndef ATALK_BSTRADD_H
+#define ATALK_BSTRADD_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <atalk/bstrlib.h>
+
+#define cfrombstr(b) ((char *)((b)->data))
+
+/* strip slashes from end of a bstring */
+#define BSTRING_STRIP_SLASH(a) \
+ do { \
+ while (bchar((a), blength(a) - 1) == '/') \
+ bdelete((a), blength(a) - 1, 1); \
+ } while (0);
+
+typedef struct tagbstring static_bstring;
+
+extern bstring brefcstr(const char *str);
+extern int bunrefcstr(bstring b);
+
+extern struct bstrList *bstrListCreateMin(int min);
+extern int bstrListPush(struct bstrList *sl, bstring bs);
+extern bstring bstrListPop(struct bstrList *sl);
+extern bstring bjoinInv(const struct bstrList * bl, const_bstring sep);
+#endif /* ATALK_BSTRADD_H */
--- /dev/null
+/*
+ * This source file is part of the bstring string library. This code was
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source
+ * license and the GPL. Refer to the accompanying documentation for details
+ * on usage and license.
+ */
+
+/*!
+ * @file
+ * This file is the core module for implementing the bstring functions.
+ */
+
+#ifndef BSTRLIB_INCLUDE
+#define BSTRLIB_INCLUDE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+
+#if !defined (BSTRLIB_VSNP_OK) && !defined (BSTRLIB_NOVSNP)
+# if defined (__TURBOC__) && !defined (__BORLANDC__)
+# define BSTRLIB_NOVSNP
+# endif
+#endif
+
+#define BSTR_ERR (-1)
+#define BSTR_OK (0)
+#define BSTR_BS_BUFF_LENGTH_GET (0)
+
+typedef struct tagbstring * bstring;
+typedef const struct tagbstring * const_bstring;
+
+/* Copy functions */
+#define cstr2bstr bfromcstr
+extern bstring bfromcstr (const char * str);
+extern bstring bfromcstralloc (int mlen, const char * str);
+extern bstring blk2bstr (const void * blk, int len);
+extern char * bstr2cstr (const_bstring s, char z);
+extern int bcstrfree (char * s);
+extern bstring bstrcpy (const_bstring b1);
+extern int bassign (bstring a, const_bstring b);
+extern int bassignmidstr (bstring a, const_bstring b, int left, int len);
+extern int bassigncstr (bstring a, const char * str);
+extern int bassignblk (bstring a, const void * s, int len);
+
+/* Destroy function */
+extern int bdestroy (bstring b);
+
+/* Space allocation hinting functions */
+extern int balloc (bstring s, int len);
+extern int ballocmin (bstring b, int len);
+
+/* Substring extraction */
+extern bstring bmidstr (const_bstring b, int left, int len);
+
+/* Various standard manipulations */
+extern int bconcat (bstring b0, const_bstring b1);
+extern int bconchar (bstring b0, char c);
+extern int bcatcstr (bstring b, const char * s);
+extern int bcatblk (bstring b, const void * s, int len);
+extern int binsert (bstring s1, int pos, const_bstring s2, unsigned char fill);
+extern int binsertch (bstring s1, int pos, int len, unsigned char fill);
+extern int breplace (bstring b1, int pos, int len, const_bstring b2, unsigned char fill);
+extern int bdelete (bstring s1, int pos, int len);
+extern int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill);
+extern int btrunc (bstring b, int n);
+
+/* Scan/search functions */
+extern int bstricmp (const_bstring b0, const_bstring b1);
+extern int bstrnicmp (const_bstring b0, const_bstring b1, int n);
+extern int biseqcaseless (const_bstring b0, const_bstring b1);
+extern int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len);
+extern int biseq (const_bstring b0, const_bstring b1);
+extern int bisstemeqblk (const_bstring b0, const void * blk, int len);
+extern int biseqcstr (const_bstring b, const char * s);
+extern int biseqcstrcaseless (const_bstring b, const char * s);
+extern int bstrcmp (const_bstring b0, const_bstring b1);
+extern int bstrncmp (const_bstring b0, const_bstring b1, int n);
+extern int binstr (const_bstring s1, int pos, const_bstring s2);
+extern int binstrr (const_bstring s1, int pos, const_bstring s2);
+extern int binstrcaseless (const_bstring s1, int pos, const_bstring s2);
+extern int binstrrcaseless (const_bstring s1, int pos, const_bstring s2);
+extern int bstrchrp (const_bstring b, int c, int pos);
+extern int bstrrchrp (const_bstring b, int c, int pos);
+#define bstrchr(b,c) bstrchrp ((b), (c), 0)
+#define bstrrchr(b,c) bstrrchrp ((b), (c), blength(b)-1)
+extern int binchr (const_bstring b0, int pos, const_bstring b1);
+extern int binchrr (const_bstring b0, int pos, const_bstring b1);
+extern int bninchr (const_bstring b0, int pos, const_bstring b1);
+extern int bninchrr (const_bstring b0, int pos, const_bstring b1);
+extern int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos);
+extern int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos);
+
+/* List of string container functions */
+struct bstrList {
+ int qty, mlen;
+ bstring * entry;
+};
+extern struct bstrList * bstrListCreate (void);
+extern int bstrListDestroy (struct bstrList * sl);
+extern int bstrListAlloc (struct bstrList * sl, int msz);
+extern int bstrListAllocMin (struct bstrList * sl, int msz);
+
+/* String split and join functions */
+extern struct bstrList * bsplit (const_bstring str, unsigned char splitChar);
+extern struct bstrList * bsplits (const_bstring str, const_bstring splitStr);
+extern struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr);
+extern bstring bjoin (const struct bstrList * bl, const_bstring sep);
+extern int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+ int (* cb) (void * parm, int ofs, int len), void * parm);
+extern int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
+ int (* cb) (void * parm, int ofs, int len), void * parm);
+extern int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+ int (* cb) (void * parm, int ofs, int len), void * parm);
+
+/* Miscellaneous functions */
+extern int bpattern (bstring b, int len);
+extern int btoupper (bstring b);
+extern int btolower (bstring b);
+extern int bltrimws (bstring b);
+extern int brtrimws (bstring b);
+extern int btrimws (bstring b);
+
+#if !defined (BSTRLIB_NOVSNP)
+extern bstring bformat (const char * fmt, ...);
+extern int bformata (bstring b, const char * fmt, ...);
+extern int bassignformat (bstring b, const char * fmt, ...);
+extern int bvcformata (bstring b, int count, const char * fmt, va_list arglist);
+
+#define bvformata(ret, b, fmt, lastarg) { \
+bstring bstrtmp_b = (b); \
+const char * bstrtmp_fmt = (fmt); \
+int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \
+ for (;;) { \
+ va_list bstrtmp_arglist; \
+ va_start (bstrtmp_arglist, lastarg); \
+ bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \
+ va_end (bstrtmp_arglist); \
+ if (bstrtmp_r >= 0) { /* Everything went ok */ \
+ bstrtmp_r = BSTR_OK; \
+ break; \
+ } else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \
+ bstrtmp_r = BSTR_ERR; \
+ break; \
+ } \
+ bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \
+ } \
+ ret = bstrtmp_r; \
+}
+
+#endif
+
+typedef int (*bNgetc) (void *parm);
+typedef size_t (* bNread) (void *buff, size_t elsize, size_t nelem, void *parm);
+
+/* Input functions */
+extern bstring bgetstream (bNgetc getcPtr, void * parm, char terminator);
+extern bstring bread (bNread readPtr, void * parm);
+extern int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator);
+extern int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator);
+extern int breada (bstring b, bNread readPtr, void * parm);
+
+/* Stream functions */
+extern struct bStream * bsopen (bNread readPtr, void * parm);
+extern void * bsclose (struct bStream * s);
+extern int bsbufflength (struct bStream * s, int sz);
+extern int bsreadln (bstring b, struct bStream * s, char terminator);
+extern int bsreadlns (bstring r, struct bStream * s, const_bstring term);
+extern int bsread (bstring b, struct bStream * s, int n);
+extern int bsreadlna (bstring b, struct bStream * s, char terminator);
+extern int bsreadlnsa (bstring r, struct bStream * s, const_bstring term);
+extern int bsreada (bstring b, struct bStream * s, int n);
+extern int bsunread (struct bStream * s, const_bstring b);
+extern int bspeek (bstring r, const struct bStream * s);
+extern int bssplitscb (struct bStream * s, const_bstring splitStr,
+ int (* cb) (void * parm, int ofs, const_bstring entry), void * parm);
+extern int bssplitstrcb (struct bStream * s, const_bstring splitStr,
+ int (* cb) (void * parm, int ofs, const_bstring entry), void * parm);
+extern int bseof (const struct bStream * s);
+
+struct tagbstring {
+ int mlen;
+ int slen;
+ unsigned char * data;
+};
+
+/* Accessor macros */
+#define blengthe(b, e) (((b) == (void *)0 || (b)->slen < 0) ? (int)(e) : ((b)->slen))
+#define blength(b) (blengthe ((b), 0))
+#define bdataofse(b, o, e) (((b) == (void *)0 || (b)->data == (void*)0) ? (char *)(e) : ((char *)(b)->data) + (o))
+#define bdataofs(b, o) (bdataofse ((b), (o), (void *)0))
+#define bdatae(b, e) (bdataofse (b, 0, e))
+#define bdata(b) (bdataofs (b, 0))
+#define bchare(b, p, e) ((((unsigned)(p)) < (unsigned)blength(b)) ? ((b)->data[(p)]) : (e))
+#define bchar(b, p) bchare ((b), (p), '\0')
+
+/* Static constant string initialization macro */
+#define bsStaticMlen(q,m) {(m), (int) sizeof(q)-1, (unsigned char *) ("" q "")}
+#if defined(_MSC_VER)
+# define bsStatic(q) bsStaticMlen(q,-32)
+#endif
+#ifndef bsStatic
+# define bsStatic(q) bsStaticMlen(q,-__LINE__)
+#endif
+
+/* Static constant block parameter pair */
+#define bsStaticBlkParms(q) ((void *)("" q "")), ((int) sizeof(q)-1)
+
+/* Reference building macros */
+#define cstr2tbstr btfromcstr
+#define btfromcstr(t,s) { \
+ (t).data = (unsigned char *) (s); \
+ (t).slen = ((t).data) ? ((int) (strlen) ((char *)(t).data)) : 0; \
+ (t).mlen = -1; \
+}
+#define blk2tbstr(t,s,l) { \
+ (t).data = (unsigned char *) (s); \
+ (t).slen = l; \
+ (t).mlen = -1; \
+}
+#define btfromblk(t,s,l) blk2tbstr(t,s,l)
+#define bmid2tbstr(t,b,p,l) { \
+ const_bstring bstrtmp_s = (b); \
+ if (bstrtmp_s && bstrtmp_s->data && bstrtmp_s->slen >= 0) { \
+ int bstrtmp_left = (p); \
+ int bstrtmp_len = (l); \
+ if (bstrtmp_left < 0) { \
+ bstrtmp_len += bstrtmp_left; \
+ bstrtmp_left = 0; \
+ } \
+ if (bstrtmp_len > bstrtmp_s->slen - bstrtmp_left) \
+ bstrtmp_len = bstrtmp_s->slen - bstrtmp_left; \
+ if (bstrtmp_len <= 0) { \
+ (t).data = (unsigned char *)""; \
+ (t).slen = 0; \
+ } else { \
+ (t).data = bstrtmp_s->data + bstrtmp_left; \
+ (t).slen = bstrtmp_len; \
+ } \
+ } else { \
+ (t).data = (unsigned char *)""; \
+ (t).slen = 0; \
+ } \
+ (t).mlen = -__LINE__; \
+}
+#define btfromblkltrimws(t,s,l) { \
+ int bstrtmp_idx = 0, bstrtmp_len = (l); \
+ unsigned char * bstrtmp_s = (s); \
+ if (bstrtmp_s && bstrtmp_len >= 0) { \
+ for (; bstrtmp_idx < bstrtmp_len; bstrtmp_idx++) { \
+ if (!isspace (bstrtmp_s[bstrtmp_idx])) break; \
+ } \
+ } \
+ (t).data = bstrtmp_s + bstrtmp_idx; \
+ (t).slen = bstrtmp_len - bstrtmp_idx; \
+ (t).mlen = -__LINE__; \
+}
+#define btfromblkrtrimws(t,s,l) { \
+ int bstrtmp_len = (l) - 1; \
+ unsigned char * bstrtmp_s = (s); \
+ if (bstrtmp_s && bstrtmp_len >= 0) { \
+ for (; bstrtmp_len >= 0; bstrtmp_len--) { \
+ if (!isspace (bstrtmp_s[bstrtmp_len])) break; \
+ } \
+ } \
+ (t).data = bstrtmp_s; \
+ (t).slen = bstrtmp_len + 1; \
+ (t).mlen = -__LINE__; \
+}
+#define btfromblktrimws(t,s,l) { \
+ int bstrtmp_idx = 0, bstrtmp_len = (l) - 1; \
+ unsigned char * bstrtmp_s = (s); \
+ if (bstrtmp_s && bstrtmp_len >= 0) { \
+ for (; bstrtmp_idx <= bstrtmp_len; bstrtmp_idx++) { \
+ if (!isspace (bstrtmp_s[bstrtmp_idx])) break; \
+ } \
+ for (; bstrtmp_len >= bstrtmp_idx; bstrtmp_len--) { \
+ if (!isspace (bstrtmp_s[bstrtmp_len])) break; \
+ } \
+ } \
+ (t).data = bstrtmp_s + bstrtmp_idx; \
+ (t).slen = bstrtmp_len + 1 - bstrtmp_idx; \
+ (t).mlen = -__LINE__; \
+}
+
+/* Write protection macros */
+#define bwriteprotect(t) { if ((t).mlen >= 0) (t).mlen = -1; }
+#define bwriteallow(t) { if ((t).mlen == -1) (t).mlen = (t).slen + ((t).slen == 0); }
+#define biswriteprotected(t) ((t).mlen <= 0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
-/*
- * $Id: cnid.h,v 1.15 2010-03-31 09:47:32 franklahm Exp $
- *
+/*
* Copyright (c) 2003 the Netatalk Team
* Copyright (c) 2003 Rafal Lewczuk <rlewczuk@pronet.pl>
- *
+ * Copyright (c) 2010 Frank Lahm
+ *
* This program is free software; you can redistribute and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation version 2 of the License or later
*
*/
-/*
- * This file contains all generic CNID related stuff
+/*
+ * This file contains all generic CNID related stuff
* declarations. Included:
- * - CNID factory, which retrieves (eventually instantiates)
+ * - CNID factory, which retrieves (eventually instantiates)
* CNID objects on demand
* - selection of CNID backends (default, detected by volume)
* - full set of CNID operations needed by server core.
#define CNID_FLAG_BLOCK 0x08 /* block signals in update. */
#define CNID_FLAG_NODEV 0x10 /* don't use device number only inode */
#define CNID_FLAG_LAZY_INIT 0x20 /* */
-#define CNID_FLAG_MEMORY 0x40 /* this is a memory only db */
-#define CNID_FLAG_INODE 0x80 /* in cnid_add the inode is authoritative */
+#define CNID_FLAG_MEMORY 0x40 /* this is a memory only db */
+#define CNID_FLAG_INODE 0x80 /* in cnid_add the inode is authoritative */
#define CNID_INVALID 0
/* first valid ID */
* This is instance of CNID database object.
*/
struct _cnid_db {
-
- u_int32_t flags; /* Flags describing some CNID backend aspects. */
- char *volpath; /* Volume path this particular CNID db refers to. */
- void *_private; /* back-end speficic data */
-
- cnid_t (*cnid_add)(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
- char *name, const size_t, cnid_t hint);
- int (*cnid_delete)(struct _cnid_db *cdb, cnid_t id);
- cnid_t (*cnid_get)(struct _cnid_db *cdb, const cnid_t did, char *name, const size_t);
- cnid_t (*cnid_lookup)(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
- char *name, const size_t);
- cnid_t (*cnid_nextid)(struct _cnid_db *cdb);
- char *(*cnid_resolve)(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len);
- int (*cnid_update)(struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
- const cnid_t did, char *name, const size_t len);
- void (*cnid_close)(struct _cnid_db *cdb);
- int (*cnid_getstamp)(struct _cnid_db *cdb, void *buffer, const size_t len);
- cnid_t (*cnid_rebuild_add)(struct _cnid_db *, const struct stat *, const cnid_t,
- char *, const size_t, cnid_t);
+ u_int32_t flags; /* Flags describing some CNID backend aspects. */
+ char *volpath; /* Volume path this particular CNID db refers to. */
+ void *_private; /* back-end speficic data */
+
+ cnid_t (*cnid_add) (struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
+ char *name, const size_t, cnid_t hint);
+ int (*cnid_delete) (struct _cnid_db *cdb, cnid_t id);
+ cnid_t (*cnid_get) (struct _cnid_db *cdb, const cnid_t did, char *name, const size_t);
+ cnid_t (*cnid_lookup) (struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
+ char *name, const size_t);
+ cnid_t (*cnid_nextid) (struct _cnid_db *cdb);
+ char * (*cnid_resolve) (struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len);
+ int (*cnid_update) (struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
+ const cnid_t did, char *name, const size_t len);
+ void (*cnid_close) (struct _cnid_db *cdb);
+ int (*cnid_getstamp) (struct _cnid_db *cdb, void *buffer, const size_t len);
+ cnid_t (*cnid_rebuild_add) (struct _cnid_db *, const struct stat *, const cnid_t,
+ char *, const size_t, cnid_t);
+ int (*cnid_find) (struct _cnid_db *cdb, char *name, size_t namelen,
+ void *buffer, size_t buflen);
};
typedef struct _cnid_db cnid_db;
-/*
+/*
* Consolidation of args passedn from main cnid_open to modules cnid_XXX_open, so
* that it's easier to add aditional args as required.
*/
* CNID module - represents particular CNID implementation
*/
struct _cnid_module {
- char *name;
- struct list_head db_list; /* CNID modules are also stored on a bidirectional list. */
- struct _cnid_db *(*cnid_open)(struct cnid_open_args *args);
- u_int32_t flags; /* Flags describing some CNID backend aspects. */
+ char *name;
+ struct list_head db_list; /* CNID modules are also stored on a bidirectional list. */
+ struct _cnid_db *(*cnid_open)(struct cnid_open_args *args);
+ u_int32_t flags; /* Flags describing some CNID backend aspects. */
};
typedef struct _cnid_module cnid_module;
mode_t mask,
char *type,
int flags,
- const char *cnidsrv, /* Only for dbd */
- const char *cnidport); /* Only for dbd */
-
-cnid_t cnid_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
- char *name, const size_t len, cnid_t hint);
-
-int cnid_delete(struct _cnid_db *cdb, cnid_t id);
-
-cnid_t cnid_get (struct _cnid_db *cdb, const cnid_t did, char *name,const size_t len);
-
-int cnid_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len);
-
-cnid_t cnid_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
- char *name, const size_t len);
-
-char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len);
-
-int cnid_update (struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
- const cnid_t did, char *name, const size_t len);
-
+ const char *cnidsrv,
+ const char *cnidport);
+cnid_t cnid_add (struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
+ const char *name, const size_t len, cnid_t hint);
+int cnid_delete (struct _cnid_db *cdb, cnid_t id);
+cnid_t cnid_get (struct _cnid_db *cdb, const cnid_t did, char *name,const size_t len);
+int cnid_getstamp (struct _cnid_db *cdb, void *buffer, const size_t len);
+cnid_t cnid_lookup (struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
+ char *name, const size_t len);
+char *cnid_resolve (struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len);
+int cnid_update (struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
+ const cnid_t did, char *name, const size_t len);
cnid_t cnid_rebuild_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
char *name, const size_t len, cnid_t hint);
-
-
-/* This function closes a CNID database and frees all resources assigned to it. */
-void cnid_close(struct _cnid_db *db);
+int cnid_find (struct _cnid_db *cdb, const char *name, size_t namelen,
+ void *buffer, size_t buflen);
+void cnid_close (struct _cnid_db *db);
#endif
-
-/*
- * $Log: cnid.h,v $
- * Revision 1.15 2010-03-31 09:47:32 franklahm
- * clustering support: new per volume option cnidserver
- *
- * Revision 1.14 2009/11/28 13:09:25 didg
- * guard against confused DB returning junk values
- *
- * Revision 1.13 2009/11/24 12:18:19 didg
- * add a flag parameter to cnid open functions
- *
- * Revision 1.12 2005/09/07 15:23:21 didg
- *
- * lazy init dbd database, help with pre tiger OS and a lot of volumes.
- *
- * Revision 1.11 2005/05/03 14:55:12 didg
- *
- * remove gcc warning
- *
- * Revision 1.10 2005/04/28 20:49:51 bfernhomberg
- *
- * - merge branch-netatalk-afp-3x-dev, HEAD was tagged before
- *
- * Revision 1.9.6.8 2005/04/25 22:33:24 lenneis
- * Add a new interface to the cdb and dbd backends: cnid_rebuild_add. It
- * takes dev, ino, did, name and cnid and writes these values unconditionally
- * into the cnid database. To be used in a recovery tool that writes cnids
- * cached in AppleDouble files back into the database. Not used yet by
- * any daemons or command line utilities.
- *
- * Revision 1.9.6.7 2005/02/08 11:46:59 didg
- *
- * warnings fixes from 2.0 branch
- *
- * Revision 1.9.6.6 2004/02/22 18:36:37 didg
- *
- * small clean up
- *
- * Revision 1.9.6.5 2004/01/14 23:15:19 lenneis
- * Check if we can get a DB stamp sucessfully in afs_openvol and fail
- * the open if not.
- *
- * Revision 1.9.6.4 2004/01/10 07:19:31 bfernhomberg
- * add cnid_init prototype
- *
- * Revision 1.9.6.3 2004/01/03 22:42:55 didg
- *
- * better errors handling in afpd for dbd cnid.
- *
- * Revision 1.9.6.2 2004/01/03 22:21:09 didg
- *
- * add nodev volume option (always use 0 for device number).
- *
- * Revision 1.9.6.1 2003/09/09 16:42:20 didg
- *
- * big merge for db frontend and unicode.
- *
- * Revision 1.9.4.2 2003/06/11 15:29:11 rlewczuk
- * Removed obsolete parameter from cnid_add. Spotted by Didier.
- *
- * Revision 1.9.4.1 2003/05/29 07:53:19 rlewczuk
- * Selectable CNIDs. Some refactoring. Propably needs more of refactoring, mainly
- * a well designed API (current API is just an old cnid_* API enclosed in VMT).
- *
- */
-
#define CNID_DBD_OP_MANGLE_GET 0x0a
#define CNID_DBD_OP_GETSTAMP 0x0b
#define CNID_DBD_OP_REBUILD_ADD 0x0c
+#define CNID_DBD_OP_SEARCH 0x0d
#define CNID_DBD_RES_OK 0x00
#define CNID_DBD_RES_NOTFOUND 0x01
#define CNID_DBD_RES_ERR_DB 0x02
#define CNID_DBD_RES_ERR_MAX 0x03
#define CNID_DBD_RES_ERR_DUPLCNID 0x04
+#define CNID_DBD_RES_SRCH_CNT 0x05
+#define CNID_DBD_RES_SRCH_DONE 0x06
+
+#define DBD_MAX_SRCH_RSLTS 100
struct cnid_dbd_rqst {
int op;
cnid_t cnid;
dev_t dev;
ino_t ino;
- u_int32_t type;
+ uint32_t type;
cnid_t did;
- char *name;
+ char *name;
size_t namelen;
};
int result;
cnid_t cnid;
cnid_t did;
- char *name;
+ char *name;
size_t namelen;
};
#define CNID_INO_LEN 8
#define CNID_DEVINO_OFS CNID_LEN
-#define CNID_DEVINO_LEN (CNID_DEV_LEN +CNID_INO_LEN)
+#define CNID_DEVINO_LEN (CNID_DEV_LEN + CNID_INO_LEN)
-#define CNID_TYPE_OFS (CNID_DEVINO_OFS +CNID_DEVINO_LEN)
+#define CNID_TYPE_OFS (CNID_DEVINO_OFS + CNID_DEVINO_LEN)
#define CNID_TYPE_LEN 4
-#define CNID_DID_OFS (CNID_TYPE_OFS +CNID_TYPE_LEN)
+#define CNID_DID_OFS (CNID_TYPE_OFS + CNID_TYPE_LEN)
#define CNID_DID_LEN CNID_LEN
#define CNID_NAME_OFS (CNID_DID_OFS + CNID_DID_LEN)
#define ROOTINFO_KEYLEN 4
/*
- Rootinfo data:
- 4 unused bytes (cnid)
- 8 bytes, in first 4 bytes db stamp: struct stat.st_ctime of database file (dev)
- 8 unused bytes (inode)
+ Rootinfo data, fields as they are used by normal entries for CNIDs (for reference):
+ 4 bytes: CNID
+ 8 bytes: dev
+ 8 bytes: inode
4 bytes: is a file/directory (type)
- 4 unused bytes (did)
- 9 bytes name "RootInfo"
+ 4 bytes: DID
+ x bytes: name
+
+ Contents in Rootinfo entry:
+ 4 bytes: 0
+ 8 bytes: db stamp: struct stat.st_ctime of database file
+ 8 bytes: unused
+ 4 bytes: last used CNID
+ 4 bytes: version as htonl(uint32_t)
+ 9 bytes: name "RootInfo"
*/
#define ROOTINFO_DATA "\0\0\0\0" \
"\0\0\0\0\0\0\0\0" \
"RootInfo"
#define ROOTINFO_DATALEN (3*4 + 2*8 + 9)
+/*
+ * CNID version history:
+ * 0: up to Netatalk 2.1.x
+ * 1: starting with 2.2, additional name index, used in cnid_find
+ */
+#define CNID_VERSION_0 0
+#define CNID_VERSION_1 1
+#define CNID_VERSION_UNINTIALIZED_DB UINT32_MAX
+
+/* Current CNID version */
+#define CNID_VERSION CNID_VERSION_1
+
#endif
/*
- * $Id: directory.h,v 1.3 2010-01-10 10:58:25 franklahm Exp $
- *
* Copyright (c) 1990,1991 Regents of The University of Michigan.
* All Rights Reserved.
*
#include <sys/types.h>
#include <netatalk/endian.h>
#include <dirent.h>
+#include <stdint.h>
#include <atalk/cnid.h>
-#include <atalk/directory.h>
+#include <atalk/bstrlib.h>
+#include <atalk/queue.h>
/* setgid directories */
#ifndef DIRBITS
# endif /* AFS */
#endif /* DIRBITS */
-/* the did tree is now a red-black tree while the parent/child
- * tree is a circular doubly-linked list. how exciting. */
-struct dir {
- struct dir *d_left, *d_right, *d_back; /* for red-black tree */
- int d_color;
- struct dir *d_parent, *d_child; /* parent-child */
- struct dir *d_prev, *d_next; /* siblings */
- void *d_ofork; /* oforks using this directory. */
- u_int32_t d_did;
- int d_flags;
-
- time_t ctime; /* inode ctime */
- u_int32_t offcnt; /* offspring count */
+/* reserved directory id's */
+#define DIRDID_ROOT_PARENT htonl(1) /* parent directory of root */
+#define DIRDID_ROOT htonl(2) /* root directory */
- char *d_m_name; /* mac name */
- char *d_u_name; /* unix name */
- ucs2_t *d_m_name_ucs2; /* mac name as UCS2 */
+struct dir {
+ bstring d_fullpath; /* complete unix path to dir */
+ 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 */
+ 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 */
+ uint16_t d_vid; /* only needed in the dircache, because
+ we put all directories in one cache. */
};
struct path {
int m_type; /* mac name type (long name, unicode */
- char *m_name; /* mac name */
+ char *m_name; /* mac name */
char *u_name; /* unix name */
- cnid_t id; /* file id (only for getmetadata) */
+ cnid_t id; /* file id (only for getmetadata) */
struct dir *d_dir; /* */
int st_valid; /* does st_errno and st set */
int st_errno;
#endif
}
-
#endif /* ATALK_DIRECTORY_H */
dsi_proto protocol;
struct dsi_block header;
struct sockaddr_storage server, client;
-
struct itimerval timer;
-
- int in_write; /* in the middle of writing multiple packets, signal handlers
- * can't write to the socket
- */
+ int tickle; /* tickle count */
+ int in_write; /* in the middle of writing multiple packets,
+ signal handlers can't write to the socket */
int msg_request; /* pending message to the client */
int down_request; /* pending SIGUSR1 down in 5 mn */
size_t statuslen;
size_t datalen, cmdlen;
off_t read_count, write_count;
- int asleep; /* client won't reply AFP 0x7a ? */
- /* inited = initialized?, child = a child?, noreply = send reply? */
- char child, inited, noreply;
+ uint32_t flags; /* DSI flags like DSI_SLEEPING, DSI_DISCONNECTED */
const char *program;
int socket, serversock;
char srvloc_url[512];
#endif
+#ifdef USE_ZEROCONF
+ int zeroconf_registered;
+#endif
+
/* buffer for OSX deadlock */
char *buffer;
char *start;
/* DSI session options */
#define DSIOPT_SERVQUANT 0x00 /* server request quantum */
#define DSIOPT_ATTNQUANT 0x01 /* attention quantum */
+#define DSIOPT_REPLCSIZE 0x02 /* AFP replaycache size supported by the server (that's us) */
/* DSI Commands */
#define DSIFUNC_CLOSE 1 /* DSICloseSession */
/* default port number */
#define DSI_AFPOVERTCP_PORT 548
+/* DSI session State flags */
+#define DSI_DATA (1 << 0) /* we have received a DSI command */
+#define DSI_RUNNING (1 << 1) /* we have received a AFP command */
+#define DSI_SLEEPING (1 << 2) /* we're sleeping after FPZzz */
+#define DSI_EXTSLEEP (1 << 3) /* we're sleeping after FPZzz */
+#define DSI_DISCONNECTED (1 << 4) /* we're in diconnected state after a socket error */
+#define DSI_DIE (1 << 5) /* SIGUSR1, going down in 5 minutes */
+#define DSI_NOREPLY (1 << 6) /* in dsi_write we generate our own replies */
+#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 */
+
/* basic initialization: dsi_init.c */
extern DSI *dsi_init (const dsi_proto /*protocol*/,
const char * /*program*/,
extern void dsi_setstatus (DSI *, char *, const size_t);
/* in dsi_getsess.c */
-extern DSI *dsi_getsession (DSI *, server_child *, const int);
+extern afp_child_t *dsi_getsession (DSI *, server_child *, const int);
extern void dsi_kill (int);
extern int dsi_tickle (DSI *);
extern void dsi_getstatus (DSI *);
extern void dsi_close (DSI *);
-extern void dsi_sleep (DSI *, const int );
#define DSI_NOWAIT 1
/* low-level stream commands -- in dsi_stream.c */
#include <config.h>
#endif
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
#include <sys/acl.h>
#endif
--- /dev/null
+/*
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ */
+
+#ifndef ERRCHECK_H
+#define ERRCHECK_H
+
+#define EC_INIT int ret = 0
+#define EC_STATUS(a) ret = (a)
+#define EC_FAIL ret = -1; goto cleanup
+#define EC_CLEANUP cleanup
+#define EC_EXIT return ret
+
+/*
+ * Check out doc/DEVELOPER for more infos.
+ *
+ * We have these macros:
+ * EC_ZERO, EC_ZERO_LOG, EC_ZERO_LOGSTR, EC_ZERO_LOG_ERR, EC_ZERO_CUSTOM
+ * EC_NEG1, EC_NEG1_LOG, EC_NEG1_LOGSTR, EC_NEG1_LOG_ERR, EC_NEG1_CUSTOM
+ * EC_NULL, EC_NULL_LOG, EC_NULL_LOGSTR, EC_NULL_LOG_ERR, EC_NULL_CUSTOM
+ *
+ * A boileplate function template is:
+
+ int func(void)
+ {
+ EC_INIT;
+
+ ...your code here...
+
+ EC_STATUS(0);
+
+ EC_CLEANUP:
+ EC_EXIT;
+ }
+ */
+
+/* check for return val 0 which is ok, every other is an error, prints errno */
+#define EC_ZERO_LOG(a) \
+ do { \
+ if ((a) != 0) { \
+ LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+ ret = -1; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_ZERO_LOGSTR(a, b, ...) \
+ do { \
+ if ((a) != 0) { \
+ LOG(log_error, logtype_default, b, __VA_ARGS__); \
+ ret = -1; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_ZERO_LOG_ERR(a, b) \
+ do { \
+ if ((a) != 0) { \
+ LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+ ret = (b); \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_ZERO(a) \
+ do { \
+ if ((a) != 0) { \
+ ret = -1; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_ZERO_ERR(a,b ) \
+ do { \
+ if ((a) != 0) { \
+ ret = b; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+/* check for return val 0 which is ok, every other is an error, prints errno */
+#define EC_NEG1_LOG(a) \
+ do { \
+ if ((a) == -1) { \
+ LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+ ret = -1; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_NEG1_LOGSTR(a, b, ...) \
+ do { \
+ if ((a) == -1) { \
+ LOG(log_error, logtype_default, b, __VA_ARGS__); \
+ ret = -1; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_NEG1_LOG_ERR(a, b) \
+ do { \
+ if ((a) == -1) { \
+ LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+ ret = b; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_NEG1(a) \
+ do { \
+ if ((a) == -1) { \
+ ret = -1; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+/* check for return val != NULL, prints errno */
+#define EC_NULL_LOG(a) \
+ do { \
+ if ((a) == NULL) { \
+ LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+ ret = -1; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_NULL_LOGSTR(a, b, ...) \
+ do { \
+ if ((a) == NULL) { \
+ LOG(log_error, logtype_default, b , __VA_ARGS__); \
+ ret = -1; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_NULL_LOG_ERR(a, b) \
+ do { \
+ if ((a) == NULL) { \
+ LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+ ret = b; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define EC_NULL(a) \
+ do { \
+ if ((a) == NULL) { \
+ ret = -1; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#endif /* ERRCHECK_H */
--- /dev/null
+/* Copyright (C) 1992,1996-1999,2003,2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/*
+ * X/Open Portability Guide 4.2: ftw.h
+ */
+
+#ifndef _ATALK_FTW_H
+#define _ATALK_FTW_H 1
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* Values for the FLAG argument to the user function passed to `ftw'
+ and 'nftw'. */
+enum
+{
+ FTW_F, /* Regular file. */
+#define FTW_F FTW_F
+ FTW_D, /* Directory. */
+#define FTW_D FTW_D
+ FTW_DNR, /* Unreadable directory. */
+#define FTW_DNR FTW_DNR
+ FTW_NS, /* Unstatable file. */
+#define FTW_NS FTW_NS
+ FTW_SL, /* Symbolic link. */
+# define FTW_SL FTW_SL
+
+/* These flags are only passed from the `nftw' function. */
+ FTW_DP, /* Directory, all subdirs have been visited. */
+# define FTW_DP FTW_DP
+ FTW_SLN /* Symbolic link naming non-existing file. */
+# define FTW_SLN FTW_SLN
+};
+
+
+/* Flags for fourth argument of `nftw'. */
+enum
+{
+ FTW_PHYS = 1, /* Perform physical walk, ignore symlinks. */
+# define FTW_PHYS FTW_PHYS
+ FTW_MOUNT = 2, /* Report only files on same file system as the
+ argument. */
+# define FTW_MOUNT FTW_MOUNT
+ FTW_CHDIR = 4, /* Change to current directory while processing it. */
+# define FTW_CHDIR FTW_CHDIR
+ FTW_DEPTH = 8, /* Report files in directory before directory itself.*/
+# define FTW_DEPTH FTW_DEPTH
+ FTW_ACTIONRETVAL = 16 /* Assume callback to return FTW_* values instead of
+ zero to continue and non-zero to terminate. */
+# define FTW_ACTIONRETVAL FTW_ACTIONRETVAL
+};
+
+/* Return values from callback functions. */
+enum
+{
+ FTW_CONTINUE = 0, /* Continue with next sibling or for FTW_D with the
+ first child. */
+# define FTW_CONTINUE FTW_CONTINUE
+ FTW_STOP = 1, /* Return from `ftw' or `nftw' with FTW_STOP as return
+ value. */
+# define FTW_STOP FTW_STOP
+ FTW_SKIP_SUBTREE = 2, /* Only meaningful for FTW_D: Don't walk through the
+ subtree, instead just continue with its next
+ sibling. */
+# define FTW_SKIP_SUBTREE FTW_SKIP_SUBTREE
+ FTW_SKIP_SIBLINGS = 3,/* Continue with FTW_DP callback for current directory
+ (if FTW_DEPTH) and then its siblings. */
+# define FTW_SKIP_SIBLINGS FTW_SKIP_SIBLINGS
+};
+
+/* Structure used for fourth argument to callback function for `nftw'. */
+struct FTW
+ {
+ int base;
+ int level;
+ };
+
+/* Convenient types for callback functions. */
+typedef int (*nftw_func_t) (const char *filename,
+ const struct stat *status,
+ int flag,
+ struct FTW *info);
+#define NFTW_FUNC_T nftw_func_t
+
+typedef void (*dir_notification_func_t) (void);
+
+extern int nftw(const char *dir,
+ nftw_func_t func,
+ dir_notification_func_t up,
+ int descriptors,
+ int flag);
+
+#endif /* ATALK_FTW_H */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
#ifndef LDAPCONFIG_H
#define LDAPCONFIG_H
extern struct pref_array prefs_array[];
extern int ldap_config_valid;
-#endif
+#endif /* LDAPCONFIG_H */
-#endif
+#endif /* HAVE_ACLS */
--- /dev/null
+/*
+ Copyright (c) 2010 Frank Lahm
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef ATALK_QUEUE_H
+#define ATALK_QUEUE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+typedef struct qnode {
+ struct qnode *prev;
+ struct qnode *next;
+ void *data;
+} qnode_t;
+
+typedef qnode_t q_t;
+
+extern q_t *queue_init(void);
+extern void queue_destroy(q_t *q, void (*callback)(void *));
+#define queue_free(q) queue_destroy((q), free)
+extern qnode_t *enqueue(q_t *q, void *data);
+extern qnode_t *prequeue(q_t *q, void *data);
+extern void *dequeue(q_t *q);
+
+#endif /* ATALK_QUEUE_H */
int count, nsessions, nforks;
} server_child;
+typedef struct server_child_data {
+ 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 */
+ uint32_t idlen; /* clientid len (from the Mac client) */
+ char *clientid; /* clientid (from the Mac client) */
+ int ipc_fds[2]; /* socketpair for IPC bw */
+ struct server_child_data **prevp, *next;
+} afp_child_t;
+
+extern int parent_or_child;
+
/* server_child.c */
extern server_child *server_child_alloc (const int, const int);
-extern int server_child_add (server_child *, const int, const pid_t);
-extern int server_child_remove (server_child *, const int, const pid_t);
+extern afp_child_t *server_child_add (server_child *, int, pid_t, uint ipc_fds[2]);
+extern int server_child_remove (server_child *, const int, const pid_t);
extern void server_child_free (server_child *);
extern void server_child_kill (server_child *, const int, const int);
-extern void server_child_kill_one (server_child *children, const int forkid, const pid_t, const uid_t);
extern void server_child_kill_one_by_id (server_child *children, const int forkid, const pid_t pid, const uid_t,
const u_int32_t len, char *id, u_int32_t boottime);
-
+extern int server_child_transfer_session(server_child *children, int forkid, pid_t, uid_t, int, uint16_t);
extern void server_child_setup (server_child *, const int, void (*)(const pid_t));
extern void server_child_handler (server_child *);
extern void server_reset_signal (void);
+#ifndef ATALK_SERVER_IPC_H
+#define ATALK_SERVER_IPC_H
#include <atalk/server_child.h>
-#define IPC_KILLTOKEN 1
-#define IPC_GETSESSION 2
-
-void *server_ipc_create(void);
-int server_ipc_child(void *obj);
-int server_ipc_parent(void *obj);
-int server_ipc_read(server_child *children);
-int server_ipc_write(u_int16_t command, int len, void *token);
-
-
-
+#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);
+#endif /* IPC_GETSESSION_LOGIN */
-/*
- * $Id: util.h,v 1.21 2010-02-28 22:29:16 didg Exp $
- */
-
/*!
* @file
* Netatalk utility functions
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
+#include <poll.h>
#include <netatalk/at.h>
#include <atalk/unicode.h>
#define EXITERR_CONF 2 /* error in config files/cmd line parameters */
#define EXITERR_SYS 3 /* local system error */
+/* Print a SBT and exit */
+#define AFP_PANIC(why) \
+ do { \
+ netatalk_panic(why); \
+ abort(); \
+ } while(0);
+
+/* LOG assert errors */
+#ifndef NDEBUG
+#define AFP_ASSERT(b) \
+ do { \
+ if (!(b)) { \
+ AFP_PANIC(#b); \
+ } \
+ } while(0);
+#else
+#define AFP_ASSERT(b)
+#endif /* NDEBUG */
+
+#define STRCMP(a,b,c) (strcmp(a,c) b 0)
#ifdef WITH_SENDFILE
extern ssize_t sys_sendfile (int __out_fd, int __in_fd, off_t *__offset,size_t __count);
extern void bprint (char *, int);
extern int strdiacasecmp (const char *, const char *);
extern int strndiacasecmp (const char *, const char *, size_t);
-extern pid_t server_lock (char * /*program*/, char * /*file*/,
- int /*debug*/);
+extern pid_t server_lock (char * /*program*/, char * /*file*/, int /*debug*/);
extern void fault_setup (void (*fn)(void *));
+extern void netatalk_panic(const char *why);
#define server_unlock(x) (unlink(x))
/* strlcpy and strlcat are used by pam modules */
extern int setnonblock(int fd, int cmd);
extern ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, int timeout);
+extern ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout);
extern const char *getip_string(const struct sockaddr *sa);
extern unsigned int getip_port(const struct sockaddr *sa);
extern void apply_ip_mask(struct sockaddr *ai, int maskbits);
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};
+struct polldata {
+ enum fdtype fdtype; /* IPC fd or listening socket fd */
+ void *data; /* pointer to AFPconfig for listening socket and *
+ * pointer to afp_child_t for IPC fd */
+};
+
+extern void fdset_add_fd(struct pollfd **fdsetp,
+ struct polldata **polldatap,
+ int *fdset_usedp,
+ int *fdset_sizep,
+ int fd,
+ enum fdtype fdtype,
+ void *data);
+extern void fdset_del_fd(struct pollfd **fdsetp,
+ struct polldata **polldatap,
+ int *fdset_usedp,
+ int *fdset_sizep,
+ int fd);
+extern int send_fd(int socket, int fd);
+extern int recv_fd(int fd, int nonblocking);
+
/******************************************************************
* unix.c
*****************************************************************/
extern const char *getcwdpath(void);
+extern char *stripped_slashes_basename(char *p);
extern int lchdir(const char *dir);
-
+extern void randombytes(void *buf, int n);
#endif /* _ATALK_UTIL_H */
/*
- $Id: uuid.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $
Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#define UUID_BINSIZE 16
#define UUID_STRINGSIZE 36
-typedef char *uuidp_t;
-typedef char uuid_t[UUID_BINSIZE];
+typedef unsigned char *uuidp_t;
+typedef unsigned char atalk_uuid_t[UUID_BINSIZE];
-typedef enum {UUID_USER = 1, UUID_GROUP} uuidtype_t;
+typedef enum {UUID_USER = 1, UUID_GROUP, UUID_LOCAL} uuidtype_t;
extern char *uuidtype[];
/* afp_options.c needs these. defined in libatalk/ldap.c */
********************************************************/
extern int getuuidfromname( const char *name, uuidtype_t type, uuidp_t uuid);
-extern int getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type);
+extern int getnamefromuuid( const uuidp_t uuidp, char **name, uuidtype_t *type);
+
extern void localuuid_from_id(unsigned char *buf, uuidtype_t type, unsigned int id);
-extern int uuid_bin2string( uuidp_t uuidp, char **uuidstring);
+extern const char *uuid_bin2string(unsigned char *uuid);
extern void uuid_string2bin( const char *uuidstring, uuidp_t uuid);
#endif /* AFP_UUID_H */
#include <atalk/adouble.h>
#include <atalk/volume.h>
+#include <atalk/acl.h>
#define VFS_FUNC_ARGS_VALIDUPATH const struct vol *vol, const char *name
#define VFS_FUNC_VARS_VALIDUPATH vol, name
#define VFS_FUNC_ARGS_COPYFILE const struct vol *vol, int sfd, const char *src, const char *dst
#define VFS_FUNC_VARS_COPYFILE vol, sfd, src, dst
+#ifdef HAVE_SOLARIS_ACLS
#define VFS_FUNC_ARGS_ACL const struct vol *vol, const char *path, int cmd, int count, void *aces
#define VFS_FUNC_VARS_ACL vol, path, cmd, count, aces
+#endif
+#ifdef HAVE_POSIX_ACLS
+#define VFS_FUNC_ARGS_ACL const struct vol *vol, const char *path, acl_type_t type, int count, acl_t acl
+#define VFS_FUNC_VARS_ACL vol, path, type, count, acl
+#endif
#define VFS_FUNC_ARGS_REMOVE_ACL const struct vol *vol, const char *path, int dir
#define VFS_FUNC_VARS_REMOVE_ACL vol, path, dir
int (*vfs_renamefile) (VFS_FUNC_ARGS_RENAMEFILE);
int (*vfs_copyfile) (VFS_FUNC_ARGS_COPYFILE);
+#ifdef HAVE_ACLS
/* ACLs */
int (*vfs_acl) (VFS_FUNC_ARGS_ACL);
int (*vfs_remove_acl) (VFS_FUNC_ARGS_REMOVE_ACL);
+#endif
/* Extended Attributes */
int (*vfs_ea_getsize) (VFS_FUNC_ARGS_EA_GETSIZE);
-/*
- * $Id: volinfo.h,v 1.10 2009-12-03 12:47:37 franklahm Exp $
- */
-
#ifndef _ATALK_VOLINFO_H
#define _ATALK_VOLINFO_H 1
} vol_opt_name_t;
struct volinfo {
+ int retaincount;
+ int malloced;
+
char *v_name;
char *v_path;
int v_flags;
int v_vfs_ea;
char *(*ad_path)(const char *, int);
char *v_dbd_host;
- int v_dbd_port;
+ char *v_dbd_port;
};
+struct volinfo *allocvolinfo(char *path);
extern int loadvolinfo(char *path, struct volinfo *vol);
+extern void retainvolinfo(struct volinfo *vol);
+extern int closevolinfo(struct volinfo *vol);
extern int savevolinfo(const struct vol *vol, const char *Cnid_srv, const char *Cnid_port);
extern int vol_load_charsets(struct volinfo *vol);
/*
- * $Id: volume.h,v 1.17 2010-04-10 08:24:54 franklahm Exp $
+ * $Id: volume.h,v 1.16 2010/03/31 09:47:32 franklahm Exp $
*
* Copyright (c) 1990,1994 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
u_int16_t v_vid;
int v_flags;
char *v_path;
- struct dir *v_dir, *v_root;
- struct dir *v_curdir; /* cache */
- hash_t *v_hash;
+ struct dir *v_root;
time_t v_mtime;
charset_t v_volcharset;
char *v_postexec;
int v_root_preexec_close;
int v_preexec_close;
-
+ char *v_uuid; /* For TimeMachine zeroconf record */
#ifdef FORCE_UIDGID
char *v_forceuid;
char *v_forcegid;
/*
Flags that alter volume behaviour.
- Keep in sync with include/atalk/volinfo.h and libatalk/util/volinfo.c
+ Keep in sync with libatalk/util/volinfo.c
*/
#define AFPVOL_A2VOL (1 << 5) /* prodos volume */
#define AFPVOL_CRLF (1 << 6) /* cr/lf translation */
#define AFPVOL_CACHE (1 << 21) /* Use adouble v2 CNID caching. Default: yes */
#define AFPVOL_INV_DOTS (1 << 22) /* dots files are invisible */
-#define AFPVOL_TM (1 << 24) /* Supports TimeMachine */
-#define AFPVOL_ACLS (1 << 25) /* Volume supports ACLS */
+#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
/* Extended Attributes vfs indirection */
#define AFPVOL_EA_NONE 0 /* No EAs */
#define VOLPBIT_XBTOTAL 10
#define VOLPBIT_BSIZE 11 /* block size */
-#ifdef AFP3x
#define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
-#endif
#define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? 1 : 0)
#define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
# Makefile.am for libatalk/
-SUBDIRS = acl adouble asp atp compat cnid dsi nbp netddp tdb util unicode vfs
+SUBDIRS = acl adouble asp atp bstring compat cnid dsi nbp netddp tdb util unicode vfs
lib_LTLIBRARIES = libatalk.la
libatalk_la_SOURCES = dummy.c
libatalk_la_LIBADD = \
+ acl/libacl.la \
adouble/libadouble.la \
asp/libasp.la \
atp/libatp.la \
+ bstring/libbstring.la \
compat/libcompat.la \
dsi/libdsi.la \
nbp/libnbp.la \
util/libutil.la \
tdb/libtdb.la \
unicode/libunicode.la \
- vfs/libvfs.la @LIBATALK_ACLS@
+ vfs/libvfs.la
libatalk_la_DEPENDENCIES = \
+ acl/libacl.la \
adouble/libadouble.la \
asp/libasp.la \
atp/libatp.la \
+ bstring/libbstring.la \
compat/libcompat.la \
dsi/libdsi.la \
nbp/libnbp.la \
util/libutil.la \
tdb/libtdb.la \
unicode/libunicode.la \
- vfs/libvfs.la @LIBATALK_ACLS@
+ vfs/libvfs.la
libatalk_la_LDFLAGS = -static
noinst_HEADERS = aclldap.h cache.h
-if USE_NFSv4_ACLS
-
noinst_LTLIBRARIES = libacl.la
-libacl_la_SOURCES = \
- ldap.c \
- uuid.c \
- cache.c \
- ldap_config.c \
- unix.c
-
-libacl_la_LDFLAGS = -lldap
+libacl_la_SOURCES = cache.c unix.c uuid.c
+libacl_la_LDFLAGS =
+if HAVE_LDAP
+libacl_la_SOURCES += ldap.c ldap_config.c
+libacl_la_LDFLAGS += -lldap
endif
/*
- $Id: aclldap.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $
Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
* Interface
********************************************************/
-/*
- * name: give me his name
- * type: and type of USER or GROUP
- * uuid_string: returns pointer to allocated string
- * returns 0 on success !=0 on errror
- */
extern int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string);
-
-/*
- * uuipd: give me his uuid
- * name: returns pointer to allocated string
- * type: returns type: USER or GROUP
- * returns 0 on success !=0 on errror
- */
-extern int ldap_getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type);
+extern int ldap_getnamefromuuid( const char *uuidstr, char **name, uuidtype_t *type);
#endif /* ACLLDAP_H */
int i;
int ret = 0;
cacheduser_t *entry;
- char *uuidstring = NULL;
char timestr[200];
struct tm *tmp = NULL;
for ( i=0 ; i<256; i++) {
if ((entry = namecache[i]) != NULL) {
do {
- uuid_bin2string(entry->uuid, &uuidstring);
tmp = localtime(&entry->creationtime);
if (tmp == NULL)
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, uuidstring, uuidtype[entry->type], timestr);
- free(uuidstring);
+ i, entry->name, uuid_bin2string(entry->uuid), uuidtype[entry->type], timestr);
} while ((entry = entry->next) != NULL);
}
}
for ( i=0; i<256; i++) {
if ((entry = uuidcache[i]) != NULL) {
do {
- uuid_bin2string(entry->uuid, &uuidstring);
+
tmp = localtime(&entry->creationtime);
if (tmp == 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, uuidstring, entry->name, uuidtype[entry->type], timestr);
- free(uuidstring);
+ i, uuid_bin2string(entry->uuid), entry->name, uuidtype[entry->type], timestr);
} while ((entry = entry->next) != NULL);
}
}
return index;
}
-/* hash uuid_t into unsigned char */
+/* hash atalk_uuid_t into unsigned char */
static unsigned char hashuuid(uuidp_t uuid) {
unsigned char index = 83;
int i;
/*
- $Id: ldap.c,v 1.7 2010-04-23 11:37:06 franklahm Exp $
Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include "config.h"
#endif /* HAVE_CONFIG_H */
+#ifdef HAVE_LDAP
+
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include <errno.h>
+#define LDAP_DEPRECATED 1
#include <ldap.h>
#include <atalk/logger.h>
* Interface
********************************************************/
-/*
- * returns allocated storage in uuid_string, caller must free it
- * returns 0 on success, -1 on error or not found
+/*!
+ * Search UUID for name in LDAP
+ *
+ * Caller must free uuid_string when done with it
+ *
+ * @param name (r) name to search
+ * @param type (r) type of USER or GROUP
+ * @param uuid_string (w) result as pointer to allocated UUID-string
+ *
+ * @returns 0 on success, -1 on error or not found
*/
int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string) {
int ret;
char *attributes[] = { ldap_uuid_attr, NULL};
char *ldap_attr;
+ if (!ldap_config_valid)
+ return -1;
+
/* make filter */
if (type == UUID_GROUP)
ldap_attr = ldap_group_attr;
* LDAP search wrapper
* returns allocated storage in name, caller must free it
* returns 0 on success, -1 on error or not found
+ *
+ * @param uuidstr (r) uuid to search as ascii string
+ * @param name (w) return pointer to name as allocated string
+ * @param type (w) return type: USER or GROUP
+ *
+ * returns 0 on success, -1 on errror
*/
-int ldap_getnamefromuuid( char *uuidstr, char **name, uuidtype_t *type) {
+int ldap_getnamefromuuid( const char *uuidstr, char **name, uuidtype_t *type) {
int ret;
int len;
char filter[256]; /* this should really be enough. we dont want to malloc everything! */
char *attributes[] = { NULL, NULL};
+ if (!ldap_config_valid)
+ return -1;
+
/* make filter */
len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
if (len >= 256 || len == -1) {
return -1;
}
+#endif /* HAVE_LDAP */
/*
- $Id: ldap_config.c,v 1.4 2009-11-28 11:10:37 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include "config.h"
#endif /* HAVE_CONFIG_H */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_LDAP
#include <stdio.h>
+#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
while(ldap_prefs[i].pref != NULL) {
if ( ldap_prefs[i].valid != 0) {
- LOG(log_error, logtype_afpd,"afp_ldap.conf: Missing option: \"%s\"", ldap_prefs[i].name);
+ LOG(log_debug, logtype_afpd,"afp_ldap.conf: Missing option: \"%s\"", ldap_prefs[i].name);
ldap_config_valid = 0;
break;
}
if (ldap_config_valid) {
if (ldap_auth_method == LDAP_AUTH_NONE)
- LOG(log_debug, logtype_afpd,"ldappref: Pref is ok. Using anonymous bind.");
+ LOG(log_debug, logtype_afpd,"afp_ldap.conf: Using anonymous bind.");
else if (ldap_auth_method == LDAP_AUTH_SIMPLE)
- LOG(log_debug, logtype_afpd,"ldappref: Pref is ok. Using simple bind.");
+ LOG(log_debug, logtype_afpd,"afp_ldap.conf: Using simple bind.");
else {
ldap_config_valid = 0;
- LOG(log_error, logtype_afpd,"ldappref: Pref not ok. SASL not yet supported.");
+ LOG(log_error, logtype_afpd,"afp_ldap.conf: SASL not yet supported.");
}
} else
- LOG(log_error, logtype_afpd,"ldappref: Pref is not ok.");
+ LOG(log_info, logtype_afpd,"afp_ldap.conf: not used");
fclose(f);
return 0;
}
-#endif
+#endif /* HAVE_LDAP */
#include "config.h"
#endif /* HAVE_CONFIG_H */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
#include <unistd.h>
#include <sys/types.h>
*retAces = NULL;
/* Only call acl() for regular files and directories, otherwise just return 0 */
- if (lstat(name, &st) != 0)
+ if (lstat(name, &st) != 0) {
+ LOG(log_warning, logtype_afpd, "get_nfsv4_acl(\"%s/%s\"): %s", getcwdpath(), name, strerror(errno));
return -1;
- if ( ! (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)))
+ }
+
+ if (S_ISLNK(st.st_mode))
+ /* sorry, no ACLs for symlinks */
return 0;
- if ((ace_count = acl(name, ACE_GETACLCNT, 0, NULL)) == 0)
+ if ( ! (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) {
+ LOG(log_warning, logtype_afpd, "get_nfsv4_acl(\"%s/%s\"): special", getcwdpath(), name);
return 0;
+ }
+
+ if ((ace_count = acl(name, ACE_GETACLCNT, 0, NULL)) == 0) {
+ LOG(log_warning, logtype_afpd, "get_nfsv4_acl(\"%s/%s\"): 0 ACEs", getcwdpath(), name);
+ return 0;
+ }
if (ace_count == -1) {
LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl('%s/%s', ACE_GETACLCNT): ace_count %i, error: %s",
if (cacl) free(cacl);
LOG(log_debug, logtype_afpd, "nfsv4_chmod(\"%s/%s\", %04o): result: %u",
- ret, getcwdpath(), name, mode);
+ getcwdpath(), name, mode, ret);
return ret;
}
-
-#endif /* HAVE_NFSv4_ACLS */
+#endif /* HAVE_SOLARIS_ACLS */
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
#include <atalk/logger.h>
#include <atalk/afp.h>
#include "aclldap.h"
#include "cache.h"
-char *uuidtype[] = {"NULL","USER", "GROUP"};
+char *uuidtype[] = {"NULL","USER", "GROUP", "LOCAL"};
/********************************************************
* Public helper function
}
-/*
- * convert 16 byte binary uuid to neat ascii represantation including dashes
- * string is allocated and pointer returned. caller must freee.
+/*!
+ * Convert 16 byte binary uuid to neat ascii represantation including dashes.
+ *
+ * Returns pointer to static buffer.
*/
-int uuid_bin2string( uuidp_t uuid, char **uuidstring) {
- char ascii[16] = { "0123456789ABCDEF" };
- int nibble = 1;
+const char *uuid_bin2string(unsigned char *uuid) {
+ static char uuidstring[UUID_STRINGSIZE + 1];
+
int i = 0;
unsigned char c;
- char *s;
-
- *uuidstring = calloc(1, UUID_STRINGSIZE + 1);
- if (*uuidstring == NULL) {
- LOG(log_error, logtype_default, "uuid_bin2string: %s: error calloc'ing",strerror(errno));
- return -1;
- }
- s = *uuidstring;
while (i < UUID_STRINGSIZE) {
c = *uuid;
- if (nibble)
- c = c >> 4;
- else {
- c &= 0x0f;
- uuid++;
- }
- s[i] = ascii[c];
- nibble ^= 1;
- i++;
+ uuid++;
+ sprintf(uuidstring + i, "%02X", c);
+ i += 2;
if (i==8 || i==13 || i==18 || i==23)
- s[i++] = '-';
+ uuidstring[i++] = '-';
}
- return 0;
+ uuidstring[i] = 0;
+ return uuidstring;
}
/********************************************************
*/
int getuuidfromname( const char *name, uuidtype_t type, uuidp_t uuid) {
int ret = 0;
+#ifdef HAVE_LDAP
char *uuid_string = NULL;
-
+#endif
ret = search_cachebyname( name, type, uuid);
- if (ret == 0) { /* found in cache */
-#ifdef DEBUG
- uuid_bin2string( uuid, &uuid_string);
+ if (ret == 0) {
+ /* found in cache */
LOG(log_debug, logtype_afpd, "getuuidfromname{cache}: name: %s, type: %s -> UUID: %s",
- name, uuidtype[type], uuid_string);
-#else
- LOG(log_debug, logtype_afpd, "getuuidfromname{cache}: name: %s, type: %s",
- name, uuidtype[type]);
+ name, uuidtype[type], uuid_bin2string(uuid));
+ } 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));
+ } else {
+ LOG(log_debug, logtype_afpd, "getuuidfromname(\"%s\",t:%u): no result from ldap search",
+ name, type);
+ }
#endif
- } else { /* if not found in cache */
- ret = ldap_getuuidfromname( name, type, &uuid_string);
if (ret != 0) {
- LOG(log_info, logtype_afpd, "getuuidfromname: no result from ldap_getuuidfromname");
- goto cleanup;
+ /* Build a local UUID */
+ if (type == UUID_USER) {
+ struct passwd *pwd;
+ if ((pwd = getpwnam(name)) == NULL) {
+ LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
+ name, uuidtype[type]);
+ goto cleanup;
+ }
+ 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;
+ }
+ 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));
}
- uuid_string2bin( uuid_string, uuid);
+ ret = 0;
add_cachebyname( name, uuid, type, 0);
- LOG(log_debug, logtype_afpd, "getuuidfromname{LDAP}: name: %s, type: %s -> UUID: %s",name, uuidtype[type], uuid_string);
}
cleanup:
- free(uuid_string);
+#ifdef HAVE_LDAP
+ if (uuid_string) free(uuid_string);
+#endif
return ret;
}
-/*
+
+/*
* uuidp: pointer to a uuid
* name: returns allocated buffer from ldap_getnamefromuuid
- * type: returns USER or GROUP
+ * type: returns USER, GROUP or LOCAL
* return 0 on success !=0 on errror
*
* Caller must free name appropiately.
*/
-int getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type) {
+int getnamefromuuid(const uuidp_t uuidp, char **name, uuidtype_t *type) {
int ret;
- char *uuid_string = NULL;
ret = search_cachebyuuid( uuidp, name, type);
- if (ret == 0) { /* found in cache */
-#ifdef DEBUG
- uuid_bin2string( uuidp, &uuid_string);
+ if (ret == 0) {
+ /* found in cache */
LOG(log_debug9, logtype_afpd, "getnamefromuuid{cache}: UUID: %s -> name: %s, type:%s",
- uuid_string, *name, uuidtype[*type]);
- free(uuid_string);
- uuid_string = NULL;
-#endif
- } else { /* if not found in cache */
- uuid_bin2string( uuidp, &uuid_string);
- ret = ldap_getnamefromuuid( uuid_string, name, type);
+ 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;
+ }
+
+#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_string);
+ 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_string, *name, uuidtype[*type]);
+ uuid_bin2string(uuidp), *name, uuidtype[*type]);
+#endif
}
cleanup:
- free(uuid_string);
return ret;
}
&(atp_sockaddr(asp->asp_atp)->sat_addr))) == NULL)
return NULL;
+ int dummy[2];
switch ((pid = fork())) {
case 0 : /* child */
server_reset_signal();
break;
default : /* parent process */
- /* we need atomic setting or pb with tickle_handler
- */
- switch (server_child_add(children, CHILD_ASPFORK, pid)) {
- case 0: /* added child */
- if ((asp_ac_tmp = (struct asp_child *)
- malloc(sizeof(struct asp_child)))) {
- asp_ac_tmp->ac_pid = pid;
- asp_ac_tmp->ac_state = ACSTATE_OK;
- asp_ac_tmp->ac_sat = sat;
- asp_ac_tmp->ac_sat.sat_port = asp->cmdbuf[1];
-
- asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
- asp->cmdbuf[1] = sid;
- set_asp_ac(sid, asp_ac_tmp);
- asperr = ASPERR_OK;
- break;
- } /* fall through if malloc fails */
- case -1: /* bad error */
+ /* we need atomic setting or pb with tickle_handler */
+ if (server_child_add(children, CHILD_ASPFORK, pid, dummy)) {
+ if ((asp_ac_tmp = malloc(sizeof(struct asp_child))) == NULL) {
+ kill(pid, SIGQUIT);
+ break;
+ }
+ asp_ac_tmp->ac_pid = pid;
+ asp_ac_tmp->ac_state = ACSTATE_OK;
+ asp_ac_tmp->ac_sat = sat;
+ asp_ac_tmp->ac_sat.sat_port = asp->cmdbuf[1];
+
+ asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
+ asp->cmdbuf[1] = sid;
+ set_asp_ac(sid, asp_ac_tmp);
+ asperr = ASPERR_OK;
+ break;
+ } else {
kill(pid, SIGQUIT);
break;
- default: /* non-fatal error */
- break;
- }
+ }
atp_close(atp);
break;
}
--- /dev/null
+Makefile
+Makefile.in
+*.lo
+*.la
+.deps
+.libs
--- /dev/null
+# Makefile.am for libatalk/adouble/
+
+noinst_LTLIBRARIES = libbstring.la
+
+libbstring_la_SOURCES = bstrlib.c bstradd.c
+
--- /dev/null
+/*
+ $Id: bstradd.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*!
+ * @file
+ * Additional functions for bstrlib
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <atalk/bstrlib.h>
+
+/* Optionally include a mechanism for debugging memory */
+
+#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG)
+#include "memdbg.h"
+#endif
+
+#ifndef bstr__alloc
+#define bstr__alloc(x) malloc (x)
+#endif
+
+#ifndef bstr__free
+#define bstr__free(p) free (p)
+#endif
+
+#ifndef bstr__realloc
+#define bstr__realloc(p,x) realloc ((p), (x))
+#endif
+
+#ifndef bstr__memcpy
+#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
+#endif
+
+#ifndef bstr__memmove
+#define bstr__memmove(d,s,l) memmove ((d), (s), (l))
+#endif
+
+#ifndef bstr__memset
+#define bstr__memset(d,c,l) memset ((d), (c), (l))
+#endif
+
+#ifndef bstr__memcmp
+#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
+#endif
+
+#ifndef bstr__memchr
+#define bstr__memchr(s,c,l) memchr ((s), (c), (l))
+#endif
+
+/*************************************************************************
+ * Stuff for making bstrings referencing c-strings
+ ************************************************************************/
+
+/*!
+ * @brief Create a bstring referencing "str"
+ *
+ * This is usefull if a well know code path uses string, often doing strlen on string.
+ * By converting to bstring which carries the strlen, the repeated computation can be avoided.
+ */
+bstring brefcstr (char *str) {
+ bstring b;
+ int i;
+ size_t j;
+
+ if (str == NULL)
+ return NULL;
+ j = strlen(str);
+
+ b = (bstring)bstr__alloc(sizeof(struct tagbstring));
+ if (NULL == b)
+ return NULL;
+
+ b->slen = (int) j;
+ b->mlen = -1;
+ b->data = str;
+
+ return b;
+}
+
+/*!
+ * @brief Free up the bstring, WITHOUT freeing the pointed to c-string!
+ */
+int bunrefcstr (bstring b) {
+ if (b == NULL || b->slen < 0 || b->mlen > 0 || b->data == NULL)
+ return BSTR_ERR;
+
+ /* In case there is any stale usage, there is one more chance to
+ notice this error. */
+
+ b->slen = -1;
+ b->mlen = -__LINE__;
+ b->data = NULL;
+
+ bstr__free (b);
+ return BSTR_OK;
+}
+
+/*************************************************************************
+ * stuff for bstrList
+ ************************************************************************/
+
+/*!
+ * @brief Create an empty list with preallocated storage for at least 'min' members
+ */
+struct bstrList *bstrListCreateMin(int min)
+{
+ struct bstrList *sl = NULL;
+
+ if ((sl = bstrListCreate()) == NULL)
+ return NULL;
+
+ if ((bstrListAlloc(sl, min)) != BSTR_OK) {
+ bstrListDestroy(sl);
+ return NULL;
+ }
+
+ return sl;
+}
+
+/*!
+ * @brief Push a bstring to the end of a list
+ */
+int bstrListPush(struct bstrList *sl, bstring bs)
+{
+ if (sl->qty == sl->mlen) {
+ if ((bstrListAlloc(sl, sl->qty + 1)) != BSTR_OK)
+ return BSTR_ERR;
+ }
+
+ sl->entry[sl->qty] = bs;
+ sl->qty++;
+ return BSTR_OK;
+}
+
+/*!
+ * @brief Pop a bstring from the end of a list
+ */
+bstring bstrListPop(struct bstrList *sl)
+{
+ return NULL;
+}
+
+/*!
+ * @brief Inverse bjoin
+ */
+bstring bjoinInv(const struct bstrList * bl, const_bstring sep) {
+ bstring b;
+ int i, j, c, v;
+
+ if (bl == NULL || bl->qty < 0)
+ return NULL;
+ if (sep != NULL && (sep->slen < 0 || sep->data == NULL))
+ return NULL;
+
+ for (i = 0, c = 1; i < bl->qty; i++) {
+ v = bl->entry[i]->slen;
+ if (v < 0)
+ return NULL;/* Invalid input */
+ c += v;
+ if (c < 0)
+ return NULL;/* Wrap around ?? */
+ }
+
+ if (sep != NULL)
+ c += (bl->qty - 1) * sep->slen;
+
+ b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+ if (NULL == b)
+ return NULL; /* Out of memory */
+ b->data = (unsigned char *) bstr__alloc (c);
+ if (b->data == NULL) {
+ bstr__free (b);
+ return NULL;
+ }
+
+ b->mlen = c;
+ b->slen = c-1;
+
+ for (i = bl->qty - 1, c = 0, j = 0; i >= 0; i--, j++) {
+ if (j > 0 && sep != NULL) {
+ bstr__memcpy (b->data + c, sep->data, sep->slen);
+ c += sep->slen;
+ }
+ v = bl->entry[i]->slen;
+ bstr__memcpy (b->data + c, bl->entry[i]->data, v);
+ c += v;
+ }
+ b->data[c] = (unsigned char) '\0';
+ return b;
+}
--- /dev/null
+/*
+ * This source file is part of the bstring string library. This code was
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source
+ * license and the GPL. Refer to the accompanying documentation for details
+ * on usage and license.
+ */
+
+/*
+ * bstrlib.c
+ *
+ * This file is the core module for implementing the bstring functions.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <atalk/bstrlib.h>
+
+/* Optionally include a mechanism for debugging memory */
+
+#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG)
+#include "memdbg.h"
+#endif
+
+#ifndef bstr__alloc
+#define bstr__alloc(x) malloc (x)
+#endif
+
+#ifndef bstr__free
+#define bstr__free(p) free (p)
+#endif
+
+#ifndef bstr__realloc
+#define bstr__realloc(p,x) realloc ((p), (x))
+#endif
+
+#ifndef bstr__memcpy
+#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
+#endif
+
+#ifndef bstr__memmove
+#define bstr__memmove(d,s,l) memmove ((d), (s), (l))
+#endif
+
+#ifndef bstr__memset
+#define bstr__memset(d,c,l) memset ((d), (c), (l))
+#endif
+
+#ifndef bstr__memcmp
+#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
+#endif
+
+#ifndef bstr__memchr
+#define bstr__memchr(s,c,l) memchr ((s), (c), (l))
+#endif
+
+/* Just a length safe wrapper for memmove. */
+
+#define bBlockCopy(D,S,L) { if ((L) > 0) bstr__memmove ((D),(S),(L)); }
+
+/* Compute the snapped size for a given requested size. By snapping to powers
+ of 2 like this, repeated reallocations are avoided. */
+static int snapUpSize (int i) {
+ if (i < 8) {
+ i = 8;
+ } else {
+ unsigned int j;
+ j = (unsigned int) i;
+
+ j |= (j >> 1);
+ j |= (j >> 2);
+ j |= (j >> 4);
+ j |= (j >> 8); /* Ok, since int >= 16 bits */
+#if (UINT_MAX != 0xffff)
+ j |= (j >> 16); /* For 32 bit int systems */
+#if (UINT_MAX > 0xffffffffUL)
+ j |= (j >> 32); /* For 64 bit int systems */
+#endif
+#endif
+ /* Least power of two greater than i */
+ j++;
+ if ((int) j >= i) i = (int) j;
+ }
+ return i;
+}
+
+/* int balloc (bstring b, int len)
+ *
+ * Increase the size of the memory backing the bstring b to at least len.
+ */
+int balloc (bstring b, int olen) {
+ int len;
+ if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 ||
+ b->mlen < b->slen || olen <= 0) {
+ return BSTR_ERR;
+ }
+
+ if (olen >= b->mlen) {
+ unsigned char * x;
+
+ if ((len = snapUpSize (olen)) <= b->mlen) return BSTR_OK;
+
+ /* Assume probability of a non-moving realloc is 0.125 */
+ if (7 * b->mlen < 8 * b->slen) {
+
+ /* If slen is close to mlen in size then use realloc to reduce
+ the memory defragmentation */
+
+ reallocStrategy:;
+
+ x = (unsigned char *) bstr__realloc (b->data, (size_t) len);
+ if (x == NULL) {
+
+ /* Since we failed, try allocating the tighest possible
+ allocation */
+
+ if (NULL == (x = (unsigned char *) bstr__realloc (b->data, (size_t) (len = olen)))) {
+ return BSTR_ERR;
+ }
+ }
+ } else {
+
+ /* If slen is not close to mlen then avoid the penalty of copying
+ the extra bytes that are allocated, but not considered part of
+ the string */
+
+ if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) {
+
+ /* Perhaps there is no available memory for the two
+ allocations to be in memory at once */
+
+ goto reallocStrategy;
+
+ } else {
+ if (b->slen) bstr__memcpy ((char *) x, (char *) b->data, (size_t) b->slen);
+ bstr__free (b->data);
+ }
+ }
+ b->data = x;
+ b->mlen = len;
+ b->data[b->slen] = (unsigned char) '\0';
+ }
+
+ return BSTR_OK;
+}
+
+/* int ballocmin (bstring b, int len)
+ *
+ * Set the size of the memory backing the bstring b to len or b->slen+1,
+ * whichever is larger. Note that repeated use of this function can degrade
+ * performance.
+ */
+int ballocmin (bstring b, int len) {
+ unsigned char * s;
+
+ if (b == NULL || b->data == NULL || (b->slen+1) < 0 || b->mlen <= 0 ||
+ b->mlen < b->slen || len <= 0) {
+ return BSTR_ERR;
+ }
+
+ if (len < b->slen + 1) len = b->slen + 1;
+
+ if (len != b->mlen) {
+ s = (unsigned char *) bstr__realloc (b->data, (size_t) len);
+ if (NULL == s) return BSTR_ERR;
+ s[b->slen] = (unsigned char) '\0';
+ b->data = s;
+ b->mlen = len;
+ }
+
+ return BSTR_OK;
+}
+
+/* bstring bfromcstr (const char * str)
+ *
+ * Create a bstring which contains the contents of the '\0' terminated char *
+ * buffer str.
+ */
+bstring bfromcstr (const char * str) {
+bstring b;
+int i;
+size_t j;
+
+ if (str == NULL) return NULL;
+ j = (strlen) (str);
+ i = snapUpSize ((int) (j + (2 - (j != 0))));
+ if (i <= (int) j) return NULL;
+
+ b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+ if (NULL == b) return NULL;
+ b->slen = (int) j;
+ if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
+ bstr__free (b);
+ return NULL;
+ }
+
+ bstr__memcpy (b->data, str, j+1);
+ return b;
+}
+
+/* bstring bfromcstralloc (int mlen, const char * str)
+ *
+ * Create a bstring which contains the contents of the '\0' terminated char *
+ * buffer str. The memory buffer backing the string is at least len
+ * characters in length.
+ */
+bstring bfromcstralloc (int mlen, const char * str) {
+bstring b;
+int i;
+size_t j;
+
+ if (str == NULL) return NULL;
+ j = (strlen) (str);
+ i = snapUpSize ((int) (j + (2 - (j != 0))));
+ if (i <= (int) j) return NULL;
+
+ b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+ if (b == NULL) return NULL;
+ b->slen = (int) j;
+ if (i < mlen) i = mlen;
+
+ if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
+ bstr__free (b);
+ return NULL;
+ }
+
+ bstr__memcpy (b->data, str, j+1);
+ return b;
+}
+
+/* bstring blk2bstr (const void * blk, int len)
+ *
+ * Create a bstring which contains the content of the block blk of length
+ * len.
+ */
+bstring blk2bstr (const void * blk, int len) {
+bstring b;
+int i;
+
+ if (blk == NULL || len < 0) return NULL;
+ b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+ if (b == NULL) return NULL;
+ b->slen = len;
+
+ i = len + (2 - (len != 0));
+ i = snapUpSize (i);
+
+ b->mlen = i;
+
+ b->data = (unsigned char *) bstr__alloc ((size_t) b->mlen);
+ if (b->data == NULL) {
+ bstr__free (b);
+ return NULL;
+ }
+
+ if (len > 0) bstr__memcpy (b->data, blk, (size_t) len);
+ b->data[len] = (unsigned char) '\0';
+
+ return b;
+}
+
+/* char * bstr2cstr (const_bstring s, char z)
+ *
+ * Create a '\0' terminated char * buffer which is equal to the contents of
+ * the bstring s, except that any contained '\0' characters are converted
+ * to the character in z. This returned value should be freed with a
+ * bcstrfree () call, by the calling application.
+ */
+char * bstr2cstr (const_bstring b, char z) {
+int i, l;
+char * r;
+
+ if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+ l = b->slen;
+ r = (char *) bstr__alloc ((size_t) (l + 1));
+ if (r == NULL) return r;
+
+ for (i=0; i < l; i ++) {
+ r[i] = (char) ((b->data[i] == '\0') ? z : (char) (b->data[i]));
+ }
+
+ r[l] = (unsigned char) '\0';
+
+ return r;
+}
+
+/* int bcstrfree (char * s)
+ *
+ * Frees a C-string generated by bstr2cstr (). This is normally unnecessary
+ * since it just wraps a call to bstr__free (), however, if bstr__alloc ()
+ * and bstr__free () have been redefined as a macros within the bstrlib
+ * module (via defining them in memdbg.h after defining
+ * BSTRLIB_MEMORY_DEBUG) with some difference in behaviour from the std
+ * library functions, then this allows a correct way of freeing the memory
+ * that allows higher level code to be independent from these macro
+ * redefinitions.
+ */
+int bcstrfree (char * s) {
+ if (s) {
+ bstr__free (s);
+ return BSTR_OK;
+ }
+ return BSTR_ERR;
+}
+
+/* int bconcat (bstring b0, const_bstring b1)
+ *
+ * Concatenate the bstring b1 to the bstring b0.
+ */
+int bconcat (bstring b0, const_bstring b1) {
+int len, d;
+bstring aux = (bstring) b1;
+
+ if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL) return BSTR_ERR;
+
+ d = b0->slen;
+ len = b1->slen;
+ if ((d | (b0->mlen - d) | len | (d + len)) < 0) return BSTR_ERR;
+
+ if (b0->mlen <= d + len + 1) {
+ ptrdiff_t pd = b1->data - b0->data;
+ if (0 <= pd && pd < b0->mlen) {
+ if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
+ }
+ if (balloc (b0, d + len + 1) != BSTR_OK) {
+ if (aux != b1) bdestroy (aux);
+ return BSTR_ERR;
+ }
+ }
+
+ bBlockCopy (&b0->data[d], &aux->data[0], (size_t) len);
+ b0->data[d + len] = (unsigned char) '\0';
+ b0->slen = d + len;
+ if (aux != b1) bdestroy (aux);
+ return BSTR_OK;
+}
+
+/* int bconchar (bstring b, char c)
+/ *
+ * Concatenate the single character c to the bstring b.
+ */
+int bconchar (bstring b, char c) {
+int d;
+
+ if (b == NULL) return BSTR_ERR;
+ d = b->slen;
+ if ((d | (b->mlen - d)) < 0 || balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+ b->data[d] = (unsigned char) c;
+ b->data[d + 1] = (unsigned char) '\0';
+ b->slen++;
+ return BSTR_OK;
+}
+
+/* int bcatcstr (bstring b, const char * s)
+ *
+ * Concatenate a char * string to a bstring.
+ */
+int bcatcstr (bstring b, const char * s) {
+char * d;
+int i, l;
+
+ if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
+ || b->mlen <= 0 || s == NULL) return BSTR_ERR;
+
+ /* Optimistically concatenate directly */
+ l = b->mlen - b->slen;
+ d = (char *) &b->data[b->slen];
+ for (i=0; i < l; i++) {
+ if ((*d++ = *s++) == '\0') {
+ b->slen += i;
+ return BSTR_OK;
+ }
+ }
+ b->slen += i;
+
+ /* Need to explicitely resize and concatenate tail */
+ return bcatblk (b, (const void *) s, (int) strlen (s));
+}
+
+/* int bcatblk (bstring b, const void * s, int len)
+ *
+ * Concatenate a fixed length buffer to a bstring.
+ */
+int bcatblk (bstring b, const void * s, int len) {
+int nl;
+
+ if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
+ || b->mlen <= 0 || s == NULL || len < 0) return BSTR_ERR;
+
+ if (0 > (nl = b->slen + len)) return BSTR_ERR; /* Overflow? */
+ if (b->mlen <= nl && 0 > balloc (b, nl + 1)) return BSTR_ERR;
+
+ bBlockCopy (&b->data[b->slen], s, (size_t) len);
+ b->slen = nl;
+ b->data[nl] = (unsigned char) '\0';
+ return BSTR_OK;
+}
+
+/* bstring bstrcpy (const_bstring b)
+ *
+ * Create a copy of the bstring b.
+ */
+bstring bstrcpy (const_bstring b) {
+bstring b0;
+int i,j;
+
+ /* Attempted to copy an invalid string? */
+ if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+
+ b0 = (bstring) bstr__alloc (sizeof (struct tagbstring));
+ if (b0 == NULL) {
+ /* Unable to allocate memory for string header */
+ return NULL;
+ }
+
+ i = b->slen;
+ j = snapUpSize (i + 1);
+
+ b0->data = (unsigned char *) bstr__alloc (j);
+ if (b0->data == NULL) {
+ j = i + 1;
+ b0->data = (unsigned char *) bstr__alloc (j);
+ if (b0->data == NULL) {
+ /* Unable to allocate memory for string data */
+ bstr__free (b0);
+ return NULL;
+ }
+ }
+
+ b0->mlen = j;
+ b0->slen = i;
+
+ if (i) bstr__memcpy ((char *) b0->data, (char *) b->data, i);
+ b0->data[b0->slen] = (unsigned char) '\0';
+
+ return b0;
+}
+
+/* int bassign (bstring a, const_bstring b)
+ *
+ * Overwrite the string a with the contents of string b.
+ */
+int bassign (bstring a, const_bstring b) {
+ if (b == NULL || b->data == NULL || b->slen < 0)
+ return BSTR_ERR;
+ if (b->slen != 0) {
+ if (balloc (a, b->slen) != BSTR_OK) return BSTR_ERR;
+ bstr__memmove (a->data, b->data, b->slen);
+ } else {
+ if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+ a->slen < 0 || a->mlen == 0)
+ return BSTR_ERR;
+ }
+ a->data[b->slen] = (unsigned char) '\0';
+ a->slen = b->slen;
+ return BSTR_OK;
+}
+
+/* int bassignmidstr (bstring a, const_bstring b, int left, int len)
+ *
+ * Overwrite the string a with the middle of contents of string b
+ * starting from position left and running for a length len. left and
+ * len are clamped to the ends of b as with the function bmidstr.
+ */
+int bassignmidstr (bstring a, const_bstring b, int left, int len) {
+ if (b == NULL || b->data == NULL || b->slen < 0)
+ return BSTR_ERR;
+
+ if (left < 0) {
+ len += left;
+ left = 0;
+ }
+
+ if (len > b->slen - left) len = b->slen - left;
+
+ if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+ a->slen < 0 || a->mlen == 0)
+ return BSTR_ERR;
+
+ if (len > 0) {
+ if (balloc (a, len) != BSTR_OK) return BSTR_ERR;
+ bstr__memmove (a->data, b->data + left, len);
+ a->slen = len;
+ } else {
+ a->slen = 0;
+ }
+ a->data[a->slen] = (unsigned char) '\0';
+ return BSTR_OK;
+}
+
+/* int bassigncstr (bstring a, const char * str)
+ *
+ * Overwrite the string a with the contents of char * string str. Note that
+ * the bstring a must be a well defined and writable bstring. If an error
+ * occurs BSTR_ERR is returned however a may be partially overwritten.
+ */
+int bassigncstr (bstring a, const char * str) {
+int i;
+size_t len;
+ if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+ a->slen < 0 || a->mlen == 0 || NULL == str)
+ return BSTR_ERR;
+
+ for (i=0; i < a->mlen; i++) {
+ if ('\0' == (a->data[i] = str[i])) {
+ a->slen = i;
+ return BSTR_OK;
+ }
+ }
+
+ a->slen = i;
+ len = strlen (str + i);
+ if (len > INT_MAX || i + len + 1 > INT_MAX ||
+ 0 > balloc (a, (int) (i + len + 1))) return BSTR_ERR;
+ bBlockCopy (a->data + i, str + i, (size_t) len + 1);
+ a->slen += (int) len;
+ return BSTR_OK;
+}
+
+/* int bassignblk (bstring a, const void * s, int len)
+ *
+ * Overwrite the string a with the contents of the block (s, len). Note that
+ * the bstring a must be a well defined and writable bstring. If an error
+ * occurs BSTR_ERR is returned and a is not overwritten.
+ */
+int bassignblk (bstring a, const void * s, int len) {
+ if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+ a->slen < 0 || a->mlen == 0 || NULL == s || len + 1 < 1)
+ return BSTR_ERR;
+ if (len + 1 > a->mlen && 0 > balloc (a, len + 1)) return BSTR_ERR;
+ bBlockCopy (a->data, s, (size_t) len);
+ a->data[len] = (unsigned char) '\0';
+ a->slen = len;
+ return BSTR_OK;
+}
+
+/* int btrunc (bstring b, int n)
+ *
+ * Truncate the bstring to at most n characters.
+ */
+int btrunc (bstring b, int n) {
+ if (n < 0 || b == NULL || b->data == NULL || b->mlen < b->slen ||
+ b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+ if (b->slen > n) {
+ b->slen = n;
+ b->data[n] = (unsigned char) '\0';
+ }
+ return BSTR_OK;
+}
+
+#define upcase(c) (toupper ((unsigned char) c))
+#define downcase(c) (tolower ((unsigned char) c))
+#define wspace(c) (isspace ((unsigned char) c))
+
+/* int btoupper (bstring b)
+ *
+ * Convert contents of bstring to upper case.
+ */
+int btoupper (bstring b) {
+int i, len;
+ if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+ b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+ for (i=0, len = b->slen; i < len; i++) {
+ b->data[i] = (unsigned char) upcase (b->data[i]);
+ }
+ return BSTR_OK;
+}
+
+/* int btolower (bstring b)
+ *
+ * Convert contents of bstring to lower case.
+ */
+int btolower (bstring b) {
+int i, len;
+ if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+ b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+ for (i=0, len = b->slen; i < len; i++) {
+ b->data[i] = (unsigned char) downcase (b->data[i]);
+ }
+ return BSTR_OK;
+}
+
+/* int bstricmp (const_bstring b0, const_bstring b1)
+ *
+ * Compare two strings without differentiating between case. The return
+ * value is the difference of the values of the characters where the two
+ * strings first differ after lower case transformation, otherwise 0 is
+ * returned indicating that the strings are equal. If the lengths are
+ * different, then a difference from 0 is given, but if the first extra
+ * character is '\0', then it is taken to be the value UCHAR_MAX+1.
+ */
+int bstricmp (const_bstring b0, const_bstring b1) {
+int i, v, n;
+
+ if (bdata (b0) == NULL || b0->slen < 0 ||
+ bdata (b1) == NULL || b1->slen < 0) return SHRT_MIN;
+ if ((n = b0->slen) > b1->slen) n = b1->slen;
+ else if (b0->slen == b1->slen && b0->data == b1->data) return BSTR_OK;
+
+ for (i = 0; i < n; i ++) {
+ v = (char) downcase (b0->data[i])
+ - (char) downcase (b1->data[i]);
+ if (0 != v) return v;
+ }
+
+ if (b0->slen > n) {
+ v = (char) downcase (b0->data[n]);
+ if (v) return v;
+ return UCHAR_MAX + 1;
+ }
+ if (b1->slen > n) {
+ v = - (char) downcase (b1->data[n]);
+ if (v) return v;
+ return - (int) (UCHAR_MAX + 1);
+ }
+ return BSTR_OK;
+}
+
+/* int bstrnicmp (const_bstring b0, const_bstring b1, int n)
+ *
+ * Compare two strings without differentiating between case for at most n
+ * characters. If the position where the two strings first differ is
+ * before the nth position, the return value is the difference of the values
+ * of the characters, otherwise 0 is returned. If the lengths are different
+ * and less than n characters, then a difference from 0 is given, but if the
+ * first extra character is '\0', then it is taken to be the value
+ * UCHAR_MAX+1.
+ */
+int bstrnicmp (const_bstring b0, const_bstring b1, int n) {
+int i, v, m;
+
+ if (bdata (b0) == NULL || b0->slen < 0 ||
+ bdata (b1) == NULL || b1->slen < 0 || n < 0) return SHRT_MIN;
+ m = n;
+ if (m > b0->slen) m = b0->slen;
+ if (m > b1->slen) m = b1->slen;
+
+ if (b0->data != b1->data) {
+ for (i = 0; i < m; i ++) {
+ v = (char) downcase (b0->data[i]);
+ v -= (char) downcase (b1->data[i]);
+ if (v != 0) return b0->data[i] - b1->data[i];
+ }
+ }
+
+ if (n == m || b0->slen == b1->slen) return BSTR_OK;
+
+ if (b0->slen > m) {
+ v = (char) downcase (b0->data[m]);
+ if (v) return v;
+ return UCHAR_MAX + 1;
+ }
+
+ v = - (char) downcase (b1->data[m]);
+ if (v) return v;
+ return - (int) (UCHAR_MAX + 1);
+}
+
+/* int biseqcaseless (const_bstring b0, const_bstring b1)
+ *
+ * Compare two strings for equality without differentiating between case.
+ * If the strings differ other than in case, 0 is returned, if the strings
+ * are the same, 1 is returned, if there is an error, -1 is returned. If
+ * the length of the strings are different, this function is O(1). '\0'
+ * termination characters are not treated in any special way.
+ */
+int biseqcaseless (const_bstring b0, const_bstring b1) {
+int i, n;
+
+ if (bdata (b0) == NULL || b0->slen < 0 ||
+ bdata (b1) == NULL || b1->slen < 0) return BSTR_ERR;
+ if (b0->slen != b1->slen) return BSTR_OK;
+ if (b0->data == b1->data || b0->slen == 0) return 1;
+ for (i=0, n=b0->slen; i < n; i++) {
+ if (b0->data[i] != b1->data[i]) {
+ unsigned char c = (unsigned char) downcase (b0->data[i]);
+ if (c != (unsigned char) downcase (b1->data[i])) return 0;
+ }
+ }
+ return 1;
+}
+
+/* int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len)
+ *
+ * Compare beginning of string b0 with a block of memory of length len
+ * without differentiating between case for equality. If the beginning of b0
+ * differs from the memory block other than in case (or if b0 is too short),
+ * 0 is returned, if the strings are the same, 1 is returned, if there is an
+ * error, -1 is returned. '\0' characters are not treated in any special
+ * way.
+ */
+int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) {
+int i;
+
+ if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
+ return BSTR_ERR;
+ if (b0->slen < len) return BSTR_OK;
+ if (b0->data == (const unsigned char *) blk || len == 0) return 1;
+
+ for (i = 0; i < len; i ++) {
+ if (b0->data[i] != ((const unsigned char *) blk)[i]) {
+ if (downcase (b0->data[i]) !=
+ downcase (((const unsigned char *) blk)[i])) return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * int bltrimws (bstring b)
+ *
+ * Delete whitespace contiguous from the left end of the string.
+ */
+int bltrimws (bstring b) {
+int i, len;
+
+ if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+ b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+ for (len = b->slen, i = 0; i < len; i++) {
+ if (!wspace (b->data[i])) {
+ return bdelete (b, 0, i);
+ }
+ }
+
+ b->data[0] = (unsigned char) '\0';
+ b->slen = 0;
+ return BSTR_OK;
+}
+
+/*
+ * int brtrimws (bstring b)
+ *
+ * Delete whitespace contiguous from the right end of the string.
+ */
+int brtrimws (bstring b) {
+int i;
+
+ if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+ b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+ for (i = b->slen - 1; i >= 0; i--) {
+ if (!wspace (b->data[i])) {
+ if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
+ b->slen = i + 1;
+ return BSTR_OK;
+ }
+ }
+
+ b->data[0] = (unsigned char) '\0';
+ b->slen = 0;
+ return BSTR_OK;
+}
+
+/*
+ * int btrimws (bstring b)
+ *
+ * Delete whitespace contiguous from both ends of the string.
+ */
+int btrimws (bstring b) {
+int i, j;
+
+ if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+ b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+ for (i = b->slen - 1; i >= 0; i--) {
+ if (!wspace (b->data[i])) {
+ if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
+ b->slen = i + 1;
+ for (j = 0; wspace (b->data[j]); j++) {}
+ return bdelete (b, 0, j);
+ }
+ }
+
+ b->data[0] = (unsigned char) '\0';
+ b->slen = 0;
+ return BSTR_OK;
+}
+
+/* int biseq (const_bstring b0, const_bstring b1)
+ *
+ * Compare the string b0 and b1. If the strings differ, 0 is returned, if
+ * the strings are the same, 1 is returned, if there is an error, -1 is
+ * returned. If the length of the strings are different, this function is
+ * O(1). '\0' termination characters are not treated in any special way.
+ */
+int biseq (const_bstring b0, const_bstring b1) {
+ if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
+ b0->slen < 0 || b1->slen < 0) return BSTR_ERR;
+ if (b0->slen != b1->slen) return BSTR_OK;
+ if (b0->data == b1->data || b0->slen == 0) return 1;
+ return !bstr__memcmp (b0->data, b1->data, b0->slen);
+}
+
+/* int bisstemeqblk (const_bstring b0, const void * blk, int len)
+ *
+ * Compare beginning of string b0 with a block of memory of length len for
+ * equality. If the beginning of b0 differs from the memory block (or if b0
+ * is too short), 0 is returned, if the strings are the same, 1 is returned,
+ * if there is an error, -1 is returned. '\0' characters are not treated in
+ * any special way.
+ */
+int bisstemeqblk (const_bstring b0, const void * blk, int len) {
+int i;
+
+ if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
+ return BSTR_ERR;
+ if (b0->slen < len) return BSTR_OK;
+ if (b0->data == (const unsigned char *) blk || len == 0) return 1;
+
+ for (i = 0; i < len; i ++) {
+ if (b0->data[i] != ((const unsigned char *) blk)[i]) return BSTR_OK;
+ }
+ return 1;
+}
+
+/* int biseqcstr (const_bstring b, const char *s)
+ *
+ * Compare the bstring b and char * string s. The C string s must be '\0'
+ * terminated at exactly the length of the bstring b, and the contents
+ * between the two must be identical with the bstring b with no '\0'
+ * characters for the two contents to be considered equal. This is
+ * equivalent to the condition that their current contents will be always be
+ * equal when comparing them in the same format after converting one or the
+ * other. If the strings are equal 1 is returned, if they are unequal 0 is
+ * returned and if there is a detectable error BSTR_ERR is returned.
+ */
+int biseqcstr (const_bstring b, const char * s) {
+int i;
+ if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
+ for (i=0; i < b->slen; i++) {
+ if (s[i] == '\0' || b->data[i] != (unsigned char) s[i]) return BSTR_OK;
+ }
+ return s[i] == '\0';
+}
+
+/* int biseqcstrcaseless (const_bstring b, const char *s)
+ *
+ * Compare the bstring b and char * string s. The C string s must be '\0'
+ * terminated at exactly the length of the bstring b, and the contents
+ * between the two must be identical except for case with the bstring b with
+ * no '\0' characters for the two contents to be considered equal. This is
+ * equivalent to the condition that their current contents will be always be
+ * equal ignoring case when comparing them in the same format after
+ * converting one or the other. If the strings are equal, except for case,
+ * 1 is returned, if they are unequal regardless of case 0 is returned and
+ * if there is a detectable error BSTR_ERR is returned.
+ */
+int biseqcstrcaseless (const_bstring b, const char * s) {
+int i;
+ if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
+ for (i=0; i < b->slen; i++) {
+ if (s[i] == '\0' ||
+ (b->data[i] != (unsigned char) s[i] &&
+ downcase (b->data[i]) != (unsigned char) downcase (s[i])))
+ return BSTR_OK;
+ }
+ return s[i] == '\0';
+}
+
+/* int bstrcmp (const_bstring b0, const_bstring b1)
+ *
+ * Compare the string b0 and b1. If there is an error, SHRT_MIN is returned,
+ * otherwise a value less than or greater than zero, indicating that the
+ * string pointed to by b0 is lexicographically less than or greater than
+ * the string pointed to by b1 is returned. If the the string lengths are
+ * unequal but the characters up until the length of the shorter are equal
+ * then a value less than, or greater than zero, indicating that the string
+ * pointed to by b0 is shorter or longer than the string pointed to by b1 is
+ * returned. 0 is returned if and only if the two strings are the same. If
+ * the length of the strings are different, this function is O(n). Like its
+ * standard C library counter part strcmp, the comparison does not proceed
+ * past any '\0' termination characters encountered.
+ */
+int bstrcmp (const_bstring b0, const_bstring b1) {
+int i, v, n;
+
+ if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
+ b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
+ n = b0->slen; if (n > b1->slen) n = b1->slen;
+ if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0))
+ return BSTR_OK;
+
+ for (i = 0; i < n; i ++) {
+ v = ((char) b0->data[i]) - ((char) b1->data[i]);
+ if (v != 0) return v;
+ if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
+ }
+
+ if (b0->slen > n) return 1;
+ if (b1->slen > n) return -1;
+ return BSTR_OK;
+}
+
+/* int bstrncmp (const_bstring b0, const_bstring b1, int n)
+ *
+ * Compare the string b0 and b1 for at most n characters. If there is an
+ * error, SHRT_MIN is returned, otherwise a value is returned as if b0 and
+ * b1 were first truncated to at most n characters then bstrcmp was called
+ * with these new strings are paremeters. If the length of the strings are
+ * different, this function is O(n). Like its standard C library counter
+ * part strcmp, the comparison does not proceed past any '\0' termination
+ * characters encountered.
+ */
+int bstrncmp (const_bstring b0, const_bstring b1, int n) {
+int i, v, m;
+
+ if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
+ b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
+ m = n;
+ if (m > b0->slen) m = b0->slen;
+ if (m > b1->slen) m = b1->slen;
+
+ if (b0->data != b1->data) {
+ for (i = 0; i < m; i ++) {
+ v = ((char) b0->data[i]) - ((char) b1->data[i]);
+ if (v != 0) return v;
+ if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
+ }
+ }
+
+ if (n == m || b0->slen == b1->slen) return BSTR_OK;
+
+ if (b0->slen > m) return 1;
+ return -1;
+}
+
+/* bstring bmidstr (const_bstring b, int left, int len)
+ *
+ * Create a bstring which is the substring of b starting from position left
+ * and running for a length len (clamped by the end of the bstring b.) If
+ * b is detectably invalid, then NULL is returned. The section described
+ * by (left, len) is clamped to the boundaries of b.
+ */
+bstring bmidstr (const_bstring b, int left, int len) {
+
+ if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+
+ if (left < 0) {
+ len += left;
+ left = 0;
+ }
+
+ if (len > b->slen - left) len = b->slen - left;
+
+ if (len <= 0) return bfromcstr ("");
+ return blk2bstr (b->data + left, len);
+}
+
+/* int bdelete (bstring b, int pos, int len)
+ *
+ * Removes characters from pos to pos+len-1 inclusive and shifts the tail of
+ * the bstring starting from pos+len to pos. len must be positive for this
+ * call to have any effect. The section of the string described by (pos,
+ * len) is clamped to boundaries of the bstring b.
+ */
+int bdelete (bstring b, int pos, int len) {
+ /* Clamp to left side of bstring */
+ if (pos < 0) {
+ len += pos;
+ pos = 0;
+ }
+
+ if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 ||
+ b->mlen < b->slen || b->mlen <= 0)
+ return BSTR_ERR;
+ if (len > 0 && pos < b->slen) {
+ if (pos + len >= b->slen) {
+ b->slen = pos;
+ } else {
+ bBlockCopy ((char *) (b->data + pos),
+ (char *) (b->data + pos + len),
+ b->slen - (pos+len));
+ b->slen -= len;
+ }
+ b->data[b->slen] = (unsigned char) '\0';
+ }
+ return BSTR_OK;
+}
+
+/* int bdestroy (bstring b)
+ *
+ * Free up the bstring. Note that if b is detectably invalid or not writable
+ * then no action is performed and BSTR_ERR is returned. Like a freed memory
+ * allocation, dereferences, writes or any other action on b after it has
+ * been bdestroyed is undefined.
+ */
+int bdestroy (bstring b) {
+ if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen ||
+ b->data == NULL)
+ return BSTR_ERR;
+
+ bstr__free (b->data);
+
+ /* In case there is any stale usage, there is one more chance to
+ notice this error. */
+
+ b->slen = -1;
+ b->mlen = -__LINE__;
+ b->data = NULL;
+
+ bstr__free (b);
+ return BSTR_OK;
+}
+
+/* int binstr (const_bstring b1, int pos, const_bstring b2)
+ *
+ * Search for the bstring b2 in b1 starting from position pos, and searching
+ * forward. If it is found then return with the first position where it is
+ * found, otherwise return BSTR_ERR. Note that this is just a brute force
+ * string searcher that does not attempt clever things like the Boyer-Moore
+ * search algorithm. Because of this there are many degenerate cases where
+ * this can take much longer than it needs to.
+ */
+int binstr (const_bstring b1, int pos, const_bstring b2) {
+int j, ii, ll, lf;
+unsigned char * d0;
+unsigned char c0;
+register unsigned char * d1;
+register unsigned char c1;
+register int i;
+
+ if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+ b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+ if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR;
+ if (b1->slen < pos || pos < 0) return BSTR_ERR;
+ if (b2->slen == 0) return pos;
+
+ /* No space to find such a string? */
+ if ((lf = b1->slen - b2->slen + 1) <= pos) return BSTR_ERR;
+
+ /* An obvious alias case */
+ if (b1->data == b2->data && pos == 0) return 0;
+
+ i = pos;
+
+ d0 = b2->data;
+ d1 = b1->data;
+ ll = b2->slen;
+
+ /* Peel off the b2->slen == 1 case */
+ c0 = d0[0];
+ if (1 == ll) {
+ for (;i < lf; i++) if (c0 == d1[i]) return i;
+ return BSTR_ERR;
+ }
+
+ c1 = c0;
+ j = 0;
+ lf = b1->slen - 1;
+
+ ii = -1;
+ if (i < lf) do {
+ /* Unrolled current character test */
+ if (c1 != d1[i]) {
+ if (c1 != d1[1+i]) {
+ i += 2;
+ continue;
+ }
+ i++;
+ }
+
+ /* Take note if this is the start of a potential match */
+ if (0 == j) ii = i;
+
+ /* Shift the test character down by one */
+ j++;
+ i++;
+
+ /* If this isn't past the last character continue */
+ if (j < ll) {
+ c1 = d0[j];
+ continue;
+ }
+
+ N0:;
+
+ /* If no characters mismatched, then we matched */
+ if (i == ii+j) return ii;
+
+ /* Shift back to the beginning */
+ i -= j;
+ j = 0;
+ c1 = c0;
+ } while (i < lf);
+
+ /* Deal with last case if unrolling caused a misalignment */
+ if (i == lf && ll == j+1 && c1 == d1[i]) goto N0;
+
+ return BSTR_ERR;
+}
+
+/* int binstrr (const_bstring b1, int pos, const_bstring b2)
+ *
+ * Search for the bstring b2 in b1 starting from position pos, and searching
+ * backward. If it is found then return with the first position where it is
+ * found, otherwise return BSTR_ERR. Note that this is just a brute force
+ * string searcher that does not attempt clever things like the Boyer-Moore
+ * search algorithm. Because of this there are many degenerate cases where
+ * this can take much longer than it needs to.
+ */
+int binstrr (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l;
+unsigned char * d0, * d1;
+
+ if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+ b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+ if (b1->slen == pos && b2->slen == 0) return pos;
+ if (b1->slen < pos || pos < 0) return BSTR_ERR;
+ if (b2->slen == 0) return pos;
+
+ /* Obvious alias case */
+ if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0;
+
+ i = pos;
+ if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
+
+ /* If no space to find such a string then snap back */
+ if (l + 1 <= i) i = l;
+ j = 0;
+
+ d0 = b2->data;
+ d1 = b1->data;
+ l = b2->slen;
+
+ for (;;) {
+ if (d0[j] == d1[i + j]) {
+ j ++;
+ if (j >= l) return i;
+ } else {
+ i --;
+ if (i < 0) break;
+ j=0;
+ }
+ }
+
+ return BSTR_ERR;
+}
+
+/* int binstrcaseless (const_bstring b1, int pos, const_bstring b2)
+ *
+ * Search for the bstring b2 in b1 starting from position pos, and searching
+ * forward but without regard to case. If it is found then return with the
+ * first position where it is found, otherwise return BSTR_ERR. Note that
+ * this is just a brute force string searcher that does not attempt clever
+ * things like the Boyer-Moore search algorithm. Because of this there are
+ * many degenerate cases where this can take much longer than it needs to.
+ */
+int binstrcaseless (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l, ll;
+unsigned char * d0, * d1;
+
+ if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+ b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+ if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR;
+ if (b1->slen < pos || pos < 0) return BSTR_ERR;
+ if (b2->slen == 0) return pos;
+
+ l = b1->slen - b2->slen + 1;
+
+ /* No space to find such a string? */
+ if (l <= pos) return BSTR_ERR;
+
+ /* An obvious alias case */
+ if (b1->data == b2->data && pos == 0) return BSTR_OK;
+
+ i = pos;
+ j = 0;
+
+ d0 = b2->data;
+ d1 = b1->data;
+ ll = b2->slen;
+
+ for (;;) {
+ if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
+ j ++;
+ if (j >= ll) return i;
+ } else {
+ i ++;
+ if (i >= l) break;
+ j=0;
+ }
+ }
+
+ return BSTR_ERR;
+}
+
+/* int binstrrcaseless (const_bstring b1, int pos, const_bstring b2)
+ *
+ * Search for the bstring b2 in b1 starting from position pos, and searching
+ * backward but without regard to case. If it is found then return with the
+ * first position where it is found, otherwise return BSTR_ERR. Note that
+ * this is just a brute force string searcher that does not attempt clever
+ * things like the Boyer-Moore search algorithm. Because of this there are
+ * many degenerate cases where this can take much longer than it needs to.
+ */
+int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l;
+unsigned char * d0, * d1;
+
+ if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+ b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+ if (b1->slen == pos && b2->slen == 0) return pos;
+ if (b1->slen < pos || pos < 0) return BSTR_ERR;
+ if (b2->slen == 0) return pos;
+
+ /* Obvious alias case */
+ if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return BSTR_OK;
+
+ i = pos;
+ if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
+
+ /* If no space to find such a string then snap back */
+ if (l + 1 <= i) i = l;
+ j = 0;
+
+ d0 = b2->data;
+ d1 = b1->data;
+ l = b2->slen;
+
+ for (;;) {
+ if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
+ j ++;
+ if (j >= l) return i;
+ } else {
+ i --;
+ if (i < 0) break;
+ j=0;
+ }
+ }
+
+ return BSTR_ERR;
+}
+
+
+/* int bstrchrp (const_bstring b, int c, int pos)
+ *
+ * Search for the character c in b forwards from the position pos
+ * (inclusive).
+ */
+int bstrchrp (const_bstring b, int c, int pos) {
+unsigned char * p;
+
+ if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
+ p = (unsigned char *) bstr__memchr ((b->data + pos), (unsigned char) c, (b->slen - pos));
+ if (p) return (int) (p - b->data);
+ return BSTR_ERR;
+}
+
+/* int bstrrchrp (const_bstring b, int c, int pos)
+ *
+ * Search for the character c in b backwards from the position pos in string
+ * (inclusive).
+ */
+int bstrrchrp (const_bstring b, int c, int pos) {
+int i;
+
+ if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
+ for (i=pos; i >= 0; i--) {
+ if (b->data[i] == (unsigned char) c) return i;
+ }
+ return BSTR_ERR;
+}
+
+#if !defined (BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF)
+#define LONG_LOG_BITS_QTY (3)
+#define LONG_BITS_QTY (1 << LONG_LOG_BITS_QTY)
+#define LONG_TYPE unsigned char
+
+#define CFCLEN ((1 << CHAR_BIT) / LONG_BITS_QTY)
+struct charField { LONG_TYPE content[CFCLEN]; };
+#define testInCharField(cf,c) ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1))))
+#define setInCharField(cf,idx) { \
+ unsigned int c = (unsigned int) (idx); \
+ (cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE) (1ul << (c & (LONG_BITS_QTY-1))); \
+}
+
+#else
+
+#define CFCLEN (1 << CHAR_BIT)
+struct charField { unsigned char content[CFCLEN]; };
+#define testInCharField(cf,c) ((cf)->content[(unsigned char) (c)])
+#define setInCharField(cf,idx) (cf)->content[(unsigned int) (idx)] = ~0
+
+#endif
+
+/* Convert a bstring to charField */
+static int buildCharField (struct charField * cf, const_bstring b) {
+int i;
+ if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR;
+ memset ((void *) cf->content, 0, sizeof (struct charField));
+ for (i=0; i < b->slen; i++) {
+ setInCharField (cf, b->data[i]);
+ }
+ return BSTR_OK;
+}
+
+static void invertCharField (struct charField * cf) {
+int i;
+ for (i=0; i < CFCLEN; i++) cf->content[i] = ~cf->content[i];
+}
+
+/* Inner engine for binchr */
+static int binchrCF (const unsigned char * data, int len, int pos, const struct charField * cf) {
+int i;
+ for (i=pos; i < len; i++) {
+ unsigned char c = (unsigned char) data[i];
+ if (testInCharField (cf, c)) return i;
+ }
+ return BSTR_ERR;
+}
+
+/* int binchr (const_bstring b0, int pos, const_bstring b1);
+ *
+ * Search for the first position in b0 starting from pos or after, in which
+ * one of the characters in b1 is found and return it. If such a position
+ * does not exist in b0, then BSTR_ERR is returned.
+ */
+int binchr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+ if (pos < 0 || b0 == NULL || b0->data == NULL ||
+ b0->slen <= pos) return BSTR_ERR;
+ if (1 == b1->slen) return bstrchrp (b0, b1->data[0], pos);
+ if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
+ return binchrCF (b0->data, b0->slen, pos, &chrs);
+}
+
+/* Inner engine for binchrr */
+static int binchrrCF (const unsigned char * data, int pos, const struct charField * cf) {
+int i;
+ for (i=pos; i >= 0; i--) {
+ unsigned int c = (unsigned int) data[i];
+ if (testInCharField (cf, c)) return i;
+ }
+ return BSTR_ERR;
+}
+
+/* int binchrr (const_bstring b0, int pos, const_bstring b1);
+ *
+ * Search for the last position in b0 no greater than pos, in which one of
+ * the characters in b1 is found and return it. If such a position does not
+ * exist in b0, then BSTR_ERR is returned.
+ */
+int binchrr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+ if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL ||
+ b0->slen < pos) return BSTR_ERR;
+ if (pos == b0->slen) pos--;
+ if (1 == b1->slen) return bstrrchrp (b0, b1->data[0], pos);
+ if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
+ return binchrrCF (b0->data, pos, &chrs);
+}
+
+/* int bninchr (const_bstring b0, int pos, const_bstring b1);
+ *
+ * Search for the first position in b0 starting from pos or after, in which
+ * none of the characters in b1 is found and return it. If such a position
+ * does not exist in b0, then BSTR_ERR is returned.
+ */
+int bninchr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+ if (pos < 0 || b0 == NULL || b0->data == NULL ||
+ b0->slen <= pos) return BSTR_ERR;
+ if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
+ invertCharField (&chrs);
+ return binchrCF (b0->data, b0->slen, pos, &chrs);
+}
+
+/* int bninchrr (const_bstring b0, int pos, const_bstring b1);
+ *
+ * Search for the last position in b0 no greater than pos, in which none of
+ * the characters in b1 is found and return it. If such a position does not
+ * exist in b0, then BSTR_ERR is returned.
+ */
+int bninchrr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+ if (pos < 0 || b0 == NULL || b0->data == NULL ||
+ b0->slen < pos) return BSTR_ERR;
+ if (pos == b0->slen) pos--;
+ if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
+ invertCharField (&chrs);
+ return binchrrCF (b0->data, pos, &chrs);
+}
+
+/* int bsetstr (bstring b0, int pos, bstring b1, unsigned char fill)
+ *
+ * Overwrite the string b0 starting at position pos with the string b1. If
+ * the position pos is past the end of b0, then the character "fill" is
+ * appended as necessary to make up the gap between the end of b0 and pos.
+ * If b1 is NULL, it behaves as if it were a 0-length string.
+ */
+int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill) {
+int d, newlen;
+ptrdiff_t pd;
+bstring aux = (bstring) b1;
+
+ if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data ||
+ b0->mlen < b0->slen || b0->mlen <= 0) return BSTR_ERR;
+ if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) return BSTR_ERR;
+
+ d = pos;
+
+ /* Aliasing case */
+ if (NULL != aux) {
+ if ((pd = (ptrdiff_t) (b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) {
+ if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
+ }
+ d += aux->slen;
+ }
+
+ /* Increase memory size if necessary */
+ if (balloc (b0, d + 1) != BSTR_OK) {
+ if (aux != b1) bdestroy (aux);
+ return BSTR_ERR;
+ }
+
+ newlen = b0->slen;
+
+ /* Fill in "fill" character as necessary */
+ if (pos > newlen) {
+ bstr__memset (b0->data + b0->slen, (int) fill, (size_t) (pos - b0->slen));
+ newlen = pos;
+ }
+
+ /* Copy b1 to position pos in b0. */
+ if (aux != NULL) {
+ bBlockCopy ((char *) (b0->data + pos), (char *) aux->data, aux->slen);
+ if (aux != b1) bdestroy (aux);
+ }
+
+ /* Indicate the potentially increased size of b0 */
+ if (d > newlen) newlen = d;
+
+ b0->slen = newlen;
+ b0->data[newlen] = (unsigned char) '\0';
+
+ return BSTR_OK;
+}
+
+/* int binsert (bstring b1, int pos, bstring b2, unsigned char fill)
+ *
+ * Inserts the string b2 into b1 at position pos. If the position pos is
+ * past the end of b1, then the character "fill" is appended as necessary to
+ * make up the gap between the end of b1 and pos. Unlike bsetstr, binsert
+ * does not allow b2 to be NULL.
+ */
+int binsert (bstring b1, int pos, const_bstring b2, unsigned char fill) {
+int d, l;
+ptrdiff_t pd;
+bstring aux = (bstring) b2;
+
+ if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 ||
+ b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) return BSTR_ERR;
+
+ /* Aliasing case */
+ if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->mlen) {
+ if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
+ }
+
+ /* Compute the two possible end pointers */
+ d = b1->slen + aux->slen;
+ l = pos + aux->slen;
+ if ((d|l) < 0) return BSTR_ERR;
+
+ if (l > d) {
+ /* Inserting past the end of the string */
+ if (balloc (b1, l + 1) != BSTR_OK) {
+ if (aux != b2) bdestroy (aux);
+ return BSTR_ERR;
+ }
+ bstr__memset (b1->data + b1->slen, (int) fill, (size_t) (pos - b1->slen));
+ b1->slen = l;
+ } else {
+ /* Inserting in the middle of the string */
+ if (balloc (b1, d + 1) != BSTR_OK) {
+ if (aux != b2) bdestroy (aux);
+ return BSTR_ERR;
+ }
+ bBlockCopy (b1->data + l, b1->data + pos, d - l);
+ b1->slen = d;
+ }
+ bBlockCopy (b1->data + pos, aux->data, aux->slen);
+ b1->data[b1->slen] = (unsigned char) '\0';
+ if (aux != b2) bdestroy (aux);
+ return BSTR_OK;
+}
+
+/* int breplace (bstring b1, int pos, int len, bstring b2,
+ * unsigned char fill)
+ *
+ * Replace a section of a string from pos for a length len with the string b2.
+ * fill is used is pos > b1->slen.
+ */
+int breplace (bstring b1, int pos, int len, const_bstring b2,
+ unsigned char fill) {
+int pl, ret;
+ptrdiff_t pd;
+bstring aux = (bstring) b2;
+
+ if (pos < 0 || len < 0 || (pl = pos + len) < 0 || b1 == NULL ||
+ b2 == NULL || b1->data == NULL || b2->data == NULL ||
+ b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen ||
+ b1->mlen <= 0) return BSTR_ERR;
+
+ /* Straddles the end? */
+ if (pl >= b1->slen) {
+ if ((ret = bsetstr (b1, pos, b2, fill)) < 0) return ret;
+ if (pos + b2->slen < b1->slen) {
+ b1->slen = pos + b2->slen;
+ b1->data[b1->slen] = (unsigned char) '\0';
+ }
+ return ret;
+ }
+
+ /* Aliasing case */
+ if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->slen) {
+ if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
+ }
+
+ if (aux->slen > len) {
+ if (balloc (b1, b1->slen + aux->slen - len) != BSTR_OK) {
+ if (aux != b2) bdestroy (aux);
+ return BSTR_ERR;
+ }
+ }
+
+ if (aux->slen != len) bstr__memmove (b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len));
+ bstr__memcpy (b1->data + pos, aux->data, aux->slen);
+ b1->slen += aux->slen - len;
+ b1->data[b1->slen] = (unsigned char) '\0';
+ if (aux != b2) bdestroy (aux);
+ return BSTR_OK;
+}
+
+/* int bfindreplace (bstring b, const_bstring find, const_bstring repl,
+ * int pos)
+ *
+ * Replace all occurrences of a find string with a replace string after a
+ * given point in a bstring.
+ */
+
+typedef int (*instr_fnptr) (const_bstring s1, int pos, const_bstring s2);
+
+static int findreplaceengine (bstring b, const_bstring find, const_bstring repl, int pos, instr_fnptr instr) {
+int i, ret, slen, mlen, delta, acc;
+int * d;
+int static_d[32];
+ptrdiff_t pd;
+bstring auxf = (bstring) find;
+bstring auxr = (bstring) repl;
+
+ if (b == NULL || b->data == NULL || find == NULL ||
+ find->data == NULL || repl == NULL || repl->data == NULL ||
+ pos < 0 || find->slen <= 0 || b->mlen < 0 || b->slen > b->mlen ||
+ b->mlen <= 0 || b->slen < 0 || repl->slen < 0) return BSTR_ERR;
+ if (pos > b->slen - find->slen) return BSTR_OK;
+
+ /* Alias with find string */
+ pd = (ptrdiff_t) (find->data - b->data);
+ if ((ptrdiff_t) (pos - find->slen) < pd && pd < (ptrdiff_t) b->slen) {
+ if (NULL == (auxf = bstrcpy (find))) return BSTR_ERR;
+ }
+
+ /* Alias with repl string */
+ pd = (ptrdiff_t) (repl->data - b->data);
+ if ((ptrdiff_t) (pos - repl->slen) < pd && pd < (ptrdiff_t) b->slen) {
+ if (NULL == (auxr = bstrcpy (repl))) {
+ if (auxf != find) bdestroy (auxf);
+ return BSTR_ERR;
+ }
+ }
+
+ delta = auxf->slen - auxr->slen;
+
+ /* in-place replacement since find and replace strings are of equal
+ length */
+ if (delta == 0) {
+ while ((pos = instr (b, pos, auxf)) >= 0) {
+ bstr__memcpy (b->data + pos, auxr->data, auxr->slen);
+ pos += auxf->slen;
+ }
+ if (auxf != find) bdestroy (auxf);
+ if (auxr != repl) bdestroy (auxr);
+ return BSTR_OK;
+ }
+
+ /* shrinking replacement since auxf->slen > auxr->slen */
+ if (delta > 0) {
+ acc = 0;
+
+ while ((i = instr (b, pos, auxf)) >= 0) {
+ if (acc && i > pos)
+ bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
+ if (auxr->slen)
+ bstr__memcpy (b->data + i - acc, auxr->data, auxr->slen);
+ acc += delta;
+ pos = i + auxf->slen;
+ }
+
+ if (acc) {
+ i = b->slen;
+ if (i > pos)
+ bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
+ b->slen -= acc;
+ b->data[b->slen] = (unsigned char) '\0';
+ }
+
+ if (auxf != find) bdestroy (auxf);
+ if (auxr != repl) bdestroy (auxr);
+ return BSTR_OK;
+ }
+
+ /* expanding replacement since find->slen < repl->slen. Its a lot
+ more complicated. */
+
+ mlen = 32;
+ d = (int *) static_d; /* Avoid malloc for trivial cases */
+ acc = slen = 0;
+
+ while ((pos = instr (b, pos, auxf)) >= 0) {
+ if (slen + 1 >= mlen) {
+ int sl;
+ int * t;
+ mlen += mlen;
+ sl = sizeof (int *) * mlen;
+ if (static_d == d) d = NULL;
+ if (sl < mlen || NULL == (t = (int *) bstr__realloc (d, sl))) {
+ ret = BSTR_ERR;
+ goto done;
+ }
+ if (NULL == d) bstr__memcpy (t, static_d, sizeof (static_d));
+ d = t;
+ }
+ d[slen] = pos;
+ slen++;
+ acc -= delta;
+ pos += auxf->slen;
+ if (pos < 0 || acc < 0) {
+ ret = BSTR_ERR;
+ goto done;
+ }
+ }
+ d[slen] = b->slen;
+
+ if (BSTR_OK == (ret = balloc (b, b->slen + acc + 1))) {
+ b->slen += acc;
+ for (i = slen-1; i >= 0; i--) {
+ int s, l;
+ s = d[i] + auxf->slen;
+ l = d[i+1] - s;
+ if (l) {
+ bstr__memmove (b->data + s + acc, b->data + s, l);
+ }
+ if (auxr->slen) {
+ bstr__memmove (b->data + s + acc - auxr->slen,
+ auxr->data, auxr->slen);
+ }
+ acc += delta;
+ }
+ b->data[b->slen] = (unsigned char) '\0';
+ }
+
+ done:;
+ if (static_d == d) d = NULL;
+ bstr__free (d);
+ if (auxf != find) bdestroy (auxf);
+ if (auxr != repl) bdestroy (auxr);
+ return ret;
+}
+
+/* int bfindreplace (bstring b, const_bstring find, const_bstring repl,
+ * int pos)
+ *
+ * Replace all occurrences of a find string with a replace string after a
+ * given point in a bstring.
+ */
+int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos) {
+ return findreplaceengine (b, find, repl, pos, binstr);
+}
+
+/* int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl,
+ * int pos)
+ *
+ * Replace all occurrences of a find string, ignoring case, with a replace
+ * string after a given point in a bstring.
+ */
+int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos) {
+ return findreplaceengine (b, find, repl, pos, binstrcaseless);
+}
+
+/* int binsertch (bstring b, int pos, int len, unsigned char fill)
+ *
+ * Inserts the character fill repeatedly into b at position pos for a
+ * length len. If the position pos is past the end of b, then the
+ * character "fill" is appended as necessary to make up the gap between the
+ * end of b and the position pos + len.
+ */
+int binsertch (bstring b, int pos, int len, unsigned char fill) {
+int d, l, i;
+
+ if (pos < 0 || b == NULL || b->slen < 0 || b->mlen < b->slen ||
+ b->mlen <= 0 || len < 0) return BSTR_ERR;
+
+ /* Compute the two possible end pointers */
+ d = b->slen + len;
+ l = pos + len;
+ if ((d|l) < 0) return BSTR_ERR;
+
+ if (l > d) {
+ /* Inserting past the end of the string */
+ if (balloc (b, l + 1) != BSTR_OK) return BSTR_ERR;
+ pos = b->slen;
+ b->slen = l;
+ } else {
+ /* Inserting in the middle of the string */
+ if (balloc (b, d + 1) != BSTR_OK) return BSTR_ERR;
+ for (i = d - 1; i >= l; i--) {
+ b->data[i] = b->data[i - len];
+ }
+ b->slen = d;
+ }
+
+ for (i=pos; i < l; i++) b->data[i] = fill;
+ b->data[b->slen] = (unsigned char) '\0';
+ return BSTR_OK;
+}
+
+/* int bpattern (bstring b, int len)
+ *
+ * Replicate the bstring, b in place, end to end repeatedly until it
+ * surpasses len characters, then chop the result to exactly len characters.
+ * This function operates in-place. The function will return with BSTR_ERR
+ * if b is NULL or of length 0, otherwise BSTR_OK is returned.
+ */
+int bpattern (bstring b, int len) {
+int i, d;
+
+ d = blength (b);
+ if (d <= 0 || len < 0 || balloc (b, len + 1) != BSTR_OK) return BSTR_ERR;
+ if (len > 0) {
+ if (d == 1) return bsetstr (b, len, NULL, b->data[0]);
+ for (i = d; i < len; i++) b->data[i] = b->data[i - d];
+ }
+ b->data[len] = (unsigned char) '\0';
+ b->slen = len;
+ return BSTR_OK;
+}
+
+#define BS_BUFF_SZ (1024)
+
+/* int breada (bstring b, bNread readPtr, void * parm)
+ *
+ * Use a finite buffer fread-like function readPtr to concatenate to the
+ * bstring b the entire contents of file-like source data in a roughly
+ * efficient way.
+ */
+int breada (bstring b, bNread readPtr, void * parm) {
+int i, l, n;
+
+ if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+ b->mlen <= 0 || readPtr == NULL) return BSTR_ERR;
+
+ i = b->slen;
+ for (n=i+16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) {
+ if (BSTR_OK != balloc (b, n + 1)) return BSTR_ERR;
+ l = (int) readPtr ((void *) (b->data + i), 1, n - i, parm);
+ i += l;
+ b->slen = i;
+ if (i < n) break;
+ }
+
+ b->data[i] = (unsigned char) '\0';
+ return BSTR_OK;
+}
+
+/* bstring bread (bNread readPtr, void * parm)
+ *
+ * Use a finite buffer fread-like function readPtr to create a bstring
+ * filled with the entire contents of file-like source data in a roughly
+ * efficient way.
+ */
+bstring bread (bNread readPtr, void * parm) {
+bstring buff;
+
+ if (0 > breada (buff = bfromcstr (""), readPtr, parm)) {
+ bdestroy (buff);
+ return NULL;
+ }
+ return buff;
+}
+
+/* int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator)
+ *
+ * Use an fgetc-like single character stream reading function (getcPtr) to
+ * obtain a sequence of characters which are concatenated to the end of the
+ * bstring b. The stream read is terminated by the passed in terminator
+ * parameter.
+ *
+ * If getcPtr returns with a negative number, or the terminator character
+ * (which is appended) is read, then the stream reading is halted and the
+ * function returns with a partial result in b. If there is an empty partial
+ * result, 1 is returned. If no characters are read, or there is some other
+ * detectable error, BSTR_ERR is returned.
+ */
+int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) {
+int c, d, e;
+
+ if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+ b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
+ d = 0;
+ e = b->mlen - 2;
+
+ while ((c = getcPtr (parm)) >= 0) {
+ if (d > e) {
+ b->slen = d;
+ if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+ e = b->mlen - 2;
+ }
+ b->data[d] = (unsigned char) c;
+ d++;
+ if (c == terminator) break;
+ }
+
+ b->data[d] = (unsigned char) '\0';
+ b->slen = d;
+
+ return d == 0 && c < 0;
+}
+
+/* int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator)
+ *
+ * Use an fgetc-like single character stream reading function (getcPtr) to
+ * obtain a sequence of characters which are concatenated to the end of the
+ * bstring b. The stream read is terminated by the passed in terminator
+ * parameter.
+ *
+ * If getcPtr returns with a negative number, or the terminator character
+ * (which is appended) is read, then the stream reading is halted and the
+ * function returns with a partial result concatentated to b. If there is
+ * an empty partial result, 1 is returned. If no characters are read, or
+ * there is some other detectable error, BSTR_ERR is returned.
+ */
+int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) {
+int c, d, e;
+
+ if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+ b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
+ d = b->slen;
+ e = b->mlen - 2;
+
+ while ((c = getcPtr (parm)) >= 0) {
+ if (d > e) {
+ b->slen = d;
+ if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+ e = b->mlen - 2;
+ }
+ b->data[d] = (unsigned char) c;
+ d++;
+ if (c == terminator) break;
+ }
+
+ b->data[d] = (unsigned char) '\0';
+ b->slen = d;
+
+ return d == 0 && c < 0;
+}
+
+/* bstring bgetstream (bNgetc getcPtr, void * parm, char terminator)
+ *
+ * Use an fgetc-like single character stream reading function (getcPtr) to
+ * obtain a sequence of characters which are concatenated into a bstring.
+ * The stream read is terminated by the passed in terminator function.
+ *
+ * If getcPtr returns with a negative number, or the terminator character
+ * (which is appended) is read, then the stream reading is halted and the
+ * result obtained thus far is returned. If no characters are read, or
+ * there is some other detectable error, NULL is returned.
+ */
+bstring bgetstream (bNgetc getcPtr, void * parm, char terminator) {
+bstring buff;
+
+ if (0 > bgetsa (buff = bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) {
+ bdestroy (buff);
+ buff = NULL;
+ }
+ return buff;
+}
+
+struct bStream {
+ bstring buff; /* Buffer for over-reads */
+ void * parm; /* The stream handle for core stream */
+ bNread readFnPtr; /* fread compatible fnptr for core stream */
+ int isEOF; /* track file's EOF state */
+ int maxBuffSz;
+};
+
+/* struct bStream * bsopen (bNread readPtr, void * parm)
+ *
+ * Wrap a given open stream (described by a fread compatible function
+ * pointer and stream handle) into an open bStream suitable for the bstring
+ * library streaming functions.
+ */
+struct bStream * bsopen (bNread readPtr, void * parm) {
+struct bStream * s;
+
+ if (readPtr == NULL) return NULL;
+ s = (struct bStream *) bstr__alloc (sizeof (struct bStream));
+ if (s == NULL) return NULL;
+ s->parm = parm;
+ s->buff = bfromcstr ("");
+ s->readFnPtr = readPtr;
+ s->maxBuffSz = BS_BUFF_SZ;
+ s->isEOF = 0;
+ return s;
+}
+
+/* int bsbufflength (struct bStream * s, int sz)
+ *
+ * Set the length of the buffer used by the bStream. If sz is zero, the
+ * length is not set. This function returns with the previous length.
+ */
+int bsbufflength (struct bStream * s, int sz) {
+int oldSz;
+ if (s == NULL || sz < 0) return BSTR_ERR;
+ oldSz = s->maxBuffSz;
+ if (sz > 0) s->maxBuffSz = sz;
+ return oldSz;
+}
+
+int bseof (const struct bStream * s) {
+ if (s == NULL || s->readFnPtr == NULL) return BSTR_ERR;
+ return s->isEOF && (s->buff->slen == 0);
+}
+
+/* void * bsclose (struct bStream * s)
+ *
+ * Close the bStream, and return the handle to the stream that was originally
+ * used to open the given stream.
+ */
+void * bsclose (struct bStream * s) {
+void * parm;
+ if (s == NULL) return NULL;
+ s->readFnPtr = NULL;
+ if (s->buff) bdestroy (s->buff);
+ s->buff = NULL;
+ parm = s->parm;
+ s->parm = NULL;
+ s->isEOF = 1;
+ bstr__free (s);
+ return parm;
+}
+
+/* int bsreadlna (bstring r, struct bStream * s, char terminator)
+ *
+ * Read a bstring terminated by the terminator character or the end of the
+ * stream from the bStream (s) and return it into the parameter r. This
+ * function may read additional characters from the core stream that are not
+ * returned, but will be retained for subsequent read operations.
+ */
+int bsreadlna (bstring r, struct bStream * s, char terminator) {
+int i, l, ret, rlo;
+char * b;
+struct tagbstring x;
+
+ if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 ||
+ r->slen < 0 || r->mlen < r->slen) return BSTR_ERR;
+ l = s->buff->slen;
+ if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+ b = (char *) s->buff->data;
+ x.data = (unsigned char *) b;
+
+ /* First check if the current buffer holds the terminator */
+ b[l] = terminator; /* Set sentinel */
+ for (i=0; b[i] != terminator; i++) ;
+ if (i < l) {
+ x.slen = i + 1;
+ ret = bconcat (r, &x);
+ s->buff->slen = l;
+ if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
+ return BSTR_OK;
+ }
+
+ rlo = r->slen;
+
+ /* If not then just concatenate the entire buffer to the output */
+ x.slen = l;
+ if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
+
+ /* Perform direct in-place reads into the destination to allow for
+ the minimum of data-copies */
+ for (;;) {
+ if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
+ b = (char *) (r->data + r->slen);
+ l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
+ if (l <= 0) {
+ r->data[r->slen] = (unsigned char) '\0';
+ s->buff->slen = 0;
+ s->isEOF = 1;
+ /* If nothing was read return with an error message */
+ return BSTR_ERR & -(r->slen == rlo);
+ }
+ b[l] = terminator; /* Set sentinel */
+ for (i=0; b[i] != terminator; i++) ;
+ if (i < l) break;
+ r->slen += l;
+ }
+
+ /* Terminator found, push over-read back to buffer */
+ i++;
+ r->slen += i;
+ s->buff->slen = l - i;
+ bstr__memcpy (s->buff->data, b + i, l - i);
+ r->data[r->slen] = (unsigned char) '\0';
+ return BSTR_OK;
+}
+
+/* int bsreadlnsa (bstring r, struct bStream * s, bstring term)
+ *
+ * Read a bstring terminated by any character in the term string or the end
+ * of the stream from the bStream (s) and return it into the parameter r.
+ * This function may read additional characters from the core stream that
+ * are not returned, but will be retained for subsequent read operations.
+ */
+int bsreadlnsa (bstring r, struct bStream * s, const_bstring term) {
+int i, l, ret, rlo;
+unsigned char * b;
+struct tagbstring x;
+struct charField cf;
+
+ if (s == NULL || s->buff == NULL || r == NULL || term == NULL ||
+ term->data == NULL || r->mlen <= 0 || r->slen < 0 ||
+ r->mlen < r->slen) return BSTR_ERR;
+ if (term->slen == 1) return bsreadlna (r, s, term->data[0]);
+ if (term->slen < 1 || buildCharField (&cf, term)) return BSTR_ERR;
+
+ l = s->buff->slen;
+ if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+ b = (unsigned char *) s->buff->data;
+ x.data = b;
+
+ /* First check if the current buffer holds the terminator */
+ b[l] = term->data[0]; /* Set sentinel */
+ for (i=0; !testInCharField (&cf, b[i]); i++) ;
+ if (i < l) {
+ x.slen = i + 1;
+ ret = bconcat (r, &x);
+ s->buff->slen = l;
+ if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
+ return BSTR_OK;
+ }
+
+ rlo = r->slen;
+
+ /* If not then just concatenate the entire buffer to the output */
+ x.slen = l;
+ if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
+
+ /* Perform direct in-place reads into the destination to allow for
+ the minimum of data-copies */
+ for (;;) {
+ if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
+ b = (unsigned char *) (r->data + r->slen);
+ l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
+ if (l <= 0) {
+ r->data[r->slen] = (unsigned char) '\0';
+ s->buff->slen = 0;
+ s->isEOF = 1;
+ /* If nothing was read return with an error message */
+ return BSTR_ERR & -(r->slen == rlo);
+ }
+
+ b[l] = term->data[0]; /* Set sentinel */
+ for (i=0; !testInCharField (&cf, b[i]); i++) ;
+ if (i < l) break;
+ r->slen += l;
+ }
+
+ /* Terminator found, push over-read back to buffer */
+ i++;
+ r->slen += i;
+ s->buff->slen = l - i;
+ bstr__memcpy (s->buff->data, b + i, l - i);
+ r->data[r->slen] = (unsigned char) '\0';
+ return BSTR_OK;
+}
+
+/* int bsreada (bstring r, struct bStream * s, int n)
+ *
+ * Read a bstring of length n (or, if it is fewer, as many bytes as is
+ * remaining) from the bStream. This function may read additional
+ * characters from the core stream that are not returned, but will be
+ * retained for subsequent read operations. This function will not read
+ * additional characters from the core stream beyond virtual stream pointer.
+ */
+int bsreada (bstring r, struct bStream * s, int n) {
+int l, ret, orslen;
+char * b;
+struct tagbstring x;
+
+ if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
+ || r->slen < 0 || r->mlen < r->slen || n <= 0) return BSTR_ERR;
+
+ n += r->slen;
+ if (n <= 0) return BSTR_ERR;
+
+ l = s->buff->slen;
+
+ orslen = r->slen;
+
+ if (0 == l) {
+ if (s->isEOF) return BSTR_ERR;
+ if (r->mlen > n) {
+ l = (int) s->readFnPtr (r->data + r->slen, 1, n - r->slen, s->parm);
+ if (0 >= l || l > n - r->slen) {
+ s->isEOF = 1;
+ return BSTR_ERR;
+ }
+ r->slen += l;
+ r->data[r->slen] = (unsigned char) '\0';
+ return 0;
+ }
+ }
+
+ if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+ b = (char *) s->buff->data;
+ x.data = (unsigned char *) b;
+
+ do {
+ if (l + r->slen >= n) {
+ x.slen = n - r->slen;
+ ret = bconcat (r, &x);
+ s->buff->slen = l;
+ if (BSTR_OK == ret) bdelete (s->buff, 0, x.slen);
+ return BSTR_ERR & -(r->slen == orslen);
+ }
+
+ x.slen = l;
+ if (BSTR_OK != bconcat (r, &x)) break;
+
+ l = n - r->slen;
+ if (l > s->maxBuffSz) l = s->maxBuffSz;
+
+ l = (int) s->readFnPtr (b, 1, l, s->parm);
+
+ } while (l > 0);
+ if (l < 0) l = 0;
+ if (l == 0) s->isEOF = 1;
+ s->buff->slen = l;
+ return BSTR_ERR & -(r->slen == orslen);
+}
+
+/* int bsreadln (bstring r, struct bStream * s, char terminator)
+ *
+ * Read a bstring terminated by the terminator character or the end of the
+ * stream from the bStream (s) and return it into the parameter r. This
+ * function may read additional characters from the core stream that are not
+ * returned, but will be retained for subsequent read operations.
+ */
+int bsreadln (bstring r, struct bStream * s, char terminator) {
+ if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0)
+ return BSTR_ERR;
+ if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+ r->slen = 0;
+ return bsreadlna (r, s, terminator);
+}
+
+/* int bsreadlns (bstring r, struct bStream * s, bstring term)
+ *
+ * Read a bstring terminated by any character in the term string or the end
+ * of the stream from the bStream (s) and return it into the parameter r.
+ * This function may read additional characters from the core stream that
+ * are not returned, but will be retained for subsequent read operations.
+ */
+int bsreadlns (bstring r, struct bStream * s, const_bstring term) {
+ if (s == NULL || s->buff == NULL || r == NULL || term == NULL
+ || term->data == NULL || r->mlen <= 0) return BSTR_ERR;
+ if (term->slen == 1) return bsreadln (r, s, term->data[0]);
+ if (term->slen < 1) return BSTR_ERR;
+ if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+ r->slen = 0;
+ return bsreadlnsa (r, s, term);
+}
+
+/* int bsread (bstring r, struct bStream * s, int n)
+ *
+ * Read a bstring of length n (or, if it is fewer, as many bytes as is
+ * remaining) from the bStream. This function may read additional
+ * characters from the core stream that are not returned, but will be
+ * retained for subsequent read operations. This function will not read
+ * additional characters from the core stream beyond virtual stream pointer.
+ */
+int bsread (bstring r, struct bStream * s, int n) {
+ if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
+ || n <= 0) return BSTR_ERR;
+ if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+ r->slen = 0;
+ return bsreada (r, s, n);
+}
+
+/* int bsunread (struct bStream * s, const_bstring b)
+ *
+ * Insert a bstring into the bStream at the current position. These
+ * characters will be read prior to those that actually come from the core
+ * stream.
+ */
+int bsunread (struct bStream * s, const_bstring b) {
+ if (s == NULL || s->buff == NULL) return BSTR_ERR;
+ return binsert (s->buff, 0, b, (unsigned char) '?');
+}
+
+/* int bspeek (bstring r, const struct bStream * s)
+ *
+ * Return the currently buffered characters from the bStream that will be
+ * read prior to reads from the core stream.
+ */
+int bspeek (bstring r, const struct bStream * s) {
+ if (s == NULL || s->buff == NULL) return BSTR_ERR;
+ return bassign (r, s->buff);
+}
+
+/* bstring bjoin (const struct bstrList * bl, const_bstring sep);
+ *
+ * Join the entries of a bstrList into one bstring by sequentially
+ * concatenating them with the sep string in between. If there is an error
+ * NULL is returned, otherwise a bstring with the correct result is returned.
+ */
+bstring bjoin (const struct bstrList * bl, const_bstring sep) {
+bstring b;
+int i, c, v;
+
+ if (bl == NULL || bl->qty < 0) return NULL;
+ if (sep != NULL && (sep->slen < 0 || sep->data == NULL)) return NULL;
+
+ for (i = 0, c = 1; i < bl->qty; i++) {
+ v = bl->entry[i]->slen;
+ if (v < 0) return NULL; /* Invalid input */
+ c += v;
+ if (c < 0) return NULL; /* Wrap around ?? */
+ }
+
+ if (sep != NULL) c += (bl->qty - 1) * sep->slen;
+
+ b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+ if (NULL == b) return NULL; /* Out of memory */
+ b->data = (unsigned char *) bstr__alloc (c);
+ if (b->data == NULL) {
+ bstr__free (b);
+ return NULL;
+ }
+
+ b->mlen = c;
+ b->slen = c-1;
+
+ for (i = 0, c = 0; i < bl->qty; i++) {
+ if (i > 0 && sep != NULL) {
+ bstr__memcpy (b->data + c, sep->data, sep->slen);
+ c += sep->slen;
+ }
+ v = bl->entry[i]->slen;
+ bstr__memcpy (b->data + c, bl->entry[i]->data, v);
+ c += v;
+ }
+ b->data[c] = (unsigned char) '\0';
+ return b;
+}
+
+#define BSSSC_BUFF_LEN (256)
+
+/* int bssplitscb (struct bStream * s, const_bstring splitStr,
+ * int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
+ *
+ * Iterate the set of disjoint sequential substrings read from a stream
+ * divided by any of the characters in splitStr. An empty splitStr causes
+ * the whole stream to be iterated once.
+ *
+ * Note: At the point of calling the cb function, the bStream pointer is
+ * pointed exactly at the position right after having read the split
+ * character. The cb function can act on the stream by causing the bStream
+ * pointer to move, and bssplitscb will continue by starting the next split
+ * at the position of the pointer after the return from cb.
+ *
+ * However, if the cb causes the bStream s to be destroyed then the cb must
+ * return with a negative value, otherwise bssplitscb will continue in an
+ * undefined manner.
+ */
+int bssplitscb (struct bStream * s, const_bstring splitStr,
+ int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
+struct charField chrs;
+bstring buff;
+int i, p, ret;
+
+ if (cb == NULL || s == NULL || s->readFnPtr == NULL
+ || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+ if (NULL == (buff = bfromcstr (""))) return BSTR_ERR;
+
+ if (splitStr->slen == 0) {
+ while (bsreada (buff, s, BSSSC_BUFF_LEN) >= 0) ;
+ if ((ret = cb (parm, 0, buff)) > 0)
+ ret = 0;
+ } else {
+ buildCharField (&chrs, splitStr);
+ ret = p = i = 0;
+ for (;;) {
+ if (i >= buff->slen) {
+ bsreada (buff, s, BSSSC_BUFF_LEN);
+ if (i >= buff->slen) {
+ if (0 < (ret = cb (parm, p, buff))) ret = 0;
+ break;
+ }
+ }
+ if (testInCharField (&chrs, buff->data[i])) {
+ struct tagbstring t;
+ unsigned char c;
+
+ blk2tbstr (t, buff->data + i + 1, buff->slen - (i + 1));
+ if ((ret = bsunread (s, &t)) < 0) break;
+ buff->slen = i;
+ c = buff->data[i];
+ buff->data[i] = (unsigned char) '\0';
+ if ((ret = cb (parm, p, buff)) < 0) break;
+ buff->data[i] = c;
+ buff->slen = 0;
+ p += i + 1;
+ i = -1;
+ }
+ i++;
+ }
+ }
+
+ bdestroy (buff);
+ return ret;
+}
+
+/* int bssplitstrcb (struct bStream * s, const_bstring splitStr,
+ * int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
+ *
+ * Iterate the set of disjoint sequential substrings read from a stream
+ * divided by the entire substring splitStr. An empty splitStr causes
+ * each character of the stream to be iterated.
+ *
+ * Note: At the point of calling the cb function, the bStream pointer is
+ * pointed exactly at the position right after having read the split
+ * character. The cb function can act on the stream by causing the bStream
+ * pointer to move, and bssplitscb will continue by starting the next split
+ * at the position of the pointer after the return from cb.
+ *
+ * However, if the cb causes the bStream s to be destroyed then the cb must
+ * return with a negative value, otherwise bssplitscb will continue in an
+ * undefined manner.
+ */
+int bssplitstrcb (struct bStream * s, const_bstring splitStr,
+ int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
+bstring buff;
+int i, p, ret;
+
+ if (cb == NULL || s == NULL || s->readFnPtr == NULL
+ || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+ if (splitStr->slen == 1) return bssplitscb (s, splitStr, cb, parm);
+
+ if (NULL == (buff = bfromcstr (""))) return BSTR_ERR;
+
+ if (splitStr->slen == 0) {
+ for (i=0; bsreada (buff, s, BSSSC_BUFF_LEN) >= 0; i++) {
+ if ((ret = cb (parm, 0, buff)) < 0) {
+ bdestroy (buff);
+ return ret;
+ }
+ buff->slen = 0;
+ }
+ return BSTR_OK;
+ } else {
+ ret = p = i = 0;
+ for (i=p=0;;) {
+ if ((ret = binstr (buff, 0, splitStr)) >= 0) {
+ struct tagbstring t;
+ blk2tbstr (t, buff->data, ret);
+ i = ret + splitStr->slen;
+ if ((ret = cb (parm, p, &t)) < 0) break;
+ p += i;
+ bdelete (buff, 0, i);
+ } else {
+ bsreada (buff, s, BSSSC_BUFF_LEN);
+ if (bseof (s)) {
+ if ((ret = cb (parm, p, buff)) > 0) ret = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ bdestroy (buff);
+ return ret;
+}
+
+/* int bstrListCreate (void)
+ *
+ * Create a bstrList.
+ */
+struct bstrList * bstrListCreate (void) {
+struct bstrList * sl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+ if (sl) {
+ sl->entry = (bstring *) bstr__alloc (1*sizeof (bstring));
+ if (!sl->entry) {
+ bstr__free (sl);
+ sl = NULL;
+ } else {
+ sl->qty = 0;
+ sl->mlen = 1;
+ }
+ }
+ return sl;
+}
+
+/* int bstrListDestroy (struct bstrList * sl)
+ *
+ * Destroy a bstrList that has been created by bsplit, bsplits or bstrListCreate.
+ */
+int bstrListDestroy (struct bstrList * sl) {
+int i;
+ if (sl == NULL || sl->qty < 0) return BSTR_ERR;
+ for (i=0; i < sl->qty; i++) {
+ if (sl->entry[i]) {
+ bdestroy (sl->entry[i]);
+ sl->entry[i] = NULL;
+ }
+ }
+ sl->qty = -1;
+ sl->mlen = -1;
+ bstr__free (sl->entry);
+ sl->entry = NULL;
+ bstr__free (sl);
+ return BSTR_OK;
+}
+
+/* int bstrListAlloc (struct bstrList * sl, int msz)
+ *
+ * Ensure that there is memory for at least msz number of entries for the
+ * list.
+ */
+int bstrListAlloc (struct bstrList * sl, int msz) {
+bstring * l;
+int smsz;
+size_t nsz;
+ if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
+ if (sl->mlen >= msz) return BSTR_OK;
+ smsz = snapUpSize (msz);
+ nsz = ((size_t) smsz) * sizeof (bstring);
+ if (nsz < (size_t) smsz) return BSTR_ERR;
+ l = (bstring *) bstr__realloc (sl->entry, nsz);
+ if (!l) {
+ smsz = msz;
+ nsz = ((size_t) smsz) * sizeof (bstring);
+ l = (bstring *) bstr__realloc (sl->entry, nsz);
+ if (!l) return BSTR_ERR;
+ }
+ sl->mlen = smsz;
+ sl->entry = l;
+ return BSTR_OK;
+}
+
+/* int bstrListAllocMin (struct bstrList * sl, int msz)
+ *
+ * Try to allocate the minimum amount of memory for the list to include at
+ * least msz entries or sl->qty whichever is greater.
+ */
+int bstrListAllocMin (struct bstrList * sl, int msz) {
+bstring * l;
+size_t nsz;
+ if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
+ if (msz < sl->qty) msz = sl->qty;
+ if (sl->mlen == msz) return BSTR_OK;
+ nsz = ((size_t) msz) * sizeof (bstring);
+ if (nsz < (size_t) msz) return BSTR_ERR;
+ l = (bstring *) bstr__realloc (sl->entry, nsz);
+ if (!l) return BSTR_ERR;
+ sl->mlen = msz;
+ sl->entry = l;
+ return BSTR_OK;
+}
+
+/* int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+ * int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ * Iterate the set of disjoint sequential substrings over str divided by the
+ * character in splitChar.
+ *
+ * Note: Non-destructive modification of str from within the cb function
+ * while performing this split is not undefined. bsplitcb behaves in
+ * sequential lock step with calls to cb. I.e., after returning from a cb
+ * that return a non-negative integer, bsplitcb continues from the position
+ * 1 character after the last detected split character and it will halt
+ * immediately if the length of str falls below this point. However, if the
+ * cb function destroys str, then it *must* return with a negative value,
+ * otherwise bsplitcb will continue in an undefined manner.
+ */
+int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+ int (* cb) (void * parm, int ofs, int len), void * parm) {
+int i, p, ret;
+
+ if (cb == NULL || str == NULL || pos < 0 || pos > str->slen)
+ return BSTR_ERR;
+
+ p = pos;
+ do {
+ for (i=p; i < str->slen; i++) {
+ if (str->data[i] == splitChar) break;
+ }
+ if ((ret = cb (parm, p, i - p)) < 0) return ret;
+ p = i + 1;
+ } while (p <= str->slen);
+ return BSTR_OK;
+}
+
+/* int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
+ * int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ * Iterate the set of disjoint sequential substrings over str divided by any
+ * of the characters in splitStr. An empty splitStr causes the whole str to
+ * be iterated once.
+ *
+ * Note: Non-destructive modification of str from within the cb function
+ * while performing this split is not undefined. bsplitscb behaves in
+ * sequential lock step with calls to cb. I.e., after returning from a cb
+ * that return a non-negative integer, bsplitscb continues from the position
+ * 1 character after the last detected split character and it will halt
+ * immediately if the length of str falls below this point. However, if the
+ * cb function destroys str, then it *must* return with a negative value,
+ * otherwise bsplitscb will continue in an undefined manner.
+ */
+int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
+ int (* cb) (void * parm, int ofs, int len), void * parm) {
+struct charField chrs;
+int i, p, ret;
+
+ if (cb == NULL || str == NULL || pos < 0 || pos > str->slen
+ || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+ if (splitStr->slen == 0) {
+ if ((ret = cb (parm, 0, str->slen)) > 0) ret = 0;
+ return ret;
+ }
+
+ if (splitStr->slen == 1)
+ return bsplitcb (str, splitStr->data[0], pos, cb, parm);
+
+ buildCharField (&chrs, splitStr);
+
+ p = pos;
+ do {
+ for (i=p; i < str->slen; i++) {
+ if (testInCharField (&chrs, str->data[i])) break;
+ }
+ if ((ret = cb (parm, p, i - p)) < 0) return ret;
+ p = i + 1;
+ } while (p <= str->slen);
+ return BSTR_OK;
+}
+
+/* int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+ * int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ * Iterate the set of disjoint sequential substrings over str divided by the
+ * substring splitStr. An empty splitStr causes the whole str to be
+ * iterated once.
+ *
+ * Note: Non-destructive modification of str from within the cb function
+ * while performing this split is not undefined. bsplitstrcb behaves in
+ * sequential lock step with calls to cb. I.e., after returning from a cb
+ * that return a non-negative integer, bsplitscb continues from the position
+ * 1 character after the last detected split character and it will halt
+ * immediately if the length of str falls below this point. However, if the
+ * cb function destroys str, then it *must* return with a negative value,
+ * otherwise bsplitscb will continue in an undefined manner.
+ */
+int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+ int (* cb) (void * parm, int ofs, int len), void * parm) {
+int i, p, ret;
+
+ if (cb == NULL || str == NULL || pos < 0 || pos > str->slen
+ || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+ if (0 == splitStr->slen) {
+ for (i=pos; i < str->slen; i++) {
+ if ((ret = cb (parm, i, 1)) < 0) return ret;
+ }
+ return BSTR_OK;
+ }
+
+ if (splitStr->slen == 1)
+ return bsplitcb (str, splitStr->data[0], pos, cb, parm);
+
+ for (i=p=pos; i <= str->slen - splitStr->slen; i++) {
+ if (0 == bstr__memcmp (splitStr->data, str->data + i, splitStr->slen)) {
+ if ((ret = cb (parm, p, i - p)) < 0) return ret;
+ i += splitStr->slen;
+ p = i;
+ }
+ }
+ if ((ret = cb (parm, p, str->slen - p)) < 0) return ret;
+ return BSTR_OK;
+}
+
+struct genBstrList {
+ bstring b;
+ struct bstrList * bl;
+};
+
+static int bscb (void * parm, int ofs, int len) {
+struct genBstrList * g = (struct genBstrList *) parm;
+ if (g->bl->qty >= g->bl->mlen) {
+ int mlen = g->bl->mlen * 2;
+ bstring * tbl;
+
+ while (g->bl->qty >= mlen) {
+ if (mlen < g->bl->mlen) return BSTR_ERR;
+ mlen += mlen;
+ }
+
+ tbl = (bstring *) bstr__realloc (g->bl->entry, sizeof (bstring) * mlen);
+ if (tbl == NULL) return BSTR_ERR;
+
+ g->bl->entry = tbl;
+ g->bl->mlen = mlen;
+ }
+
+ g->bl->entry[g->bl->qty] = bmidstr (g->b, ofs, len);
+ g->bl->qty++;
+ return BSTR_OK;
+}
+
+/* struct bstrList * bsplit (const_bstring str, unsigned char splitChar)
+ *
+ * Create an array of sequential substrings from str divided by the character
+ * splitChar.
+ */
+struct bstrList * bsplit (const_bstring str, unsigned char splitChar) {
+struct genBstrList g;
+
+ if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
+
+ g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+ if (g.bl == NULL) return NULL;
+ g.bl->mlen = 4;
+ g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+ if (NULL == g.bl->entry) {
+ bstr__free (g.bl);
+ return NULL;
+ }
+
+ g.b = (bstring) str;
+ g.bl->qty = 0;
+ if (bsplitcb (str, splitChar, 0, bscb, &g) < 0) {
+ bstrListDestroy (g.bl);
+ return NULL;
+ }
+ return g.bl;
+}
+
+/* struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr)
+ *
+ * Create an array of sequential substrings from str divided by the entire
+ * substring splitStr.
+ */
+struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) {
+struct genBstrList g;
+
+ if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
+
+ g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+ if (g.bl == NULL) return NULL;
+ g.bl->mlen = 4;
+ g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+ if (NULL == g.bl->entry) {
+ bstr__free (g.bl);
+ return NULL;
+ }
+
+ g.b = (bstring) str;
+ g.bl->qty = 0;
+ if (bsplitstrcb (str, splitStr, 0, bscb, &g) < 0) {
+ bstrListDestroy (g.bl);
+ return NULL;
+ }
+ return g.bl;
+}
+
+/* struct bstrList * bsplits (const_bstring str, bstring splitStr)
+ *
+ * Create an array of sequential substrings from str divided by any of the
+ * characters in splitStr. An empty splitStr causes a single entry bstrList
+ * containing a copy of str to be returned.
+ */
+struct bstrList * bsplits (const_bstring str, const_bstring splitStr) {
+struct genBstrList g;
+
+ if ( str == NULL || str->slen < 0 || str->data == NULL ||
+ splitStr == NULL || splitStr->slen < 0 || splitStr->data == NULL)
+ return NULL;
+
+ g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+ if (g.bl == NULL) return NULL;
+ g.bl->mlen = 4;
+ g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+ if (NULL == g.bl->entry) {
+ bstr__free (g.bl);
+ return NULL;
+ }
+ g.b = (bstring) str;
+ g.bl->qty = 0;
+
+ if (bsplitscb (str, splitStr, 0, bscb, &g) < 0) {
+ bstrListDestroy (g.bl);
+ return NULL;
+ }
+ return g.bl;
+}
+
+#if defined (__TURBOC__) && !defined (__BORLANDC__)
+# ifndef BSTRLIB_NOVSNP
+# define BSTRLIB_NOVSNP
+# endif
+#endif
+
+/* Give WATCOM C/C++, MSVC some latitude for their non-support of vsnprintf */
+#if defined(__WATCOMC__) || defined(_MSC_VER)
+#define exvsnprintf(r,b,n,f,a) {r = _vsnprintf (b,n,f,a);}
+#else
+#ifdef BSTRLIB_NOVSNP
+/* This is just a hack. If you are using a system without a vsnprintf, it is
+ not recommended that bformat be used at all. */
+#define exvsnprintf(r,b,n,f,a) {vsprintf (b,f,a); r = -1;}
+#define START_VSNBUFF (256)
+#else
+
+#ifdef __GNUC__
+/* Something is making gcc complain about this prototype not being here, so
+ I've just gone ahead and put it in. */
+extern int vsnprintf (char *buf, size_t count, const char *format, va_list arg);
+#endif
+
+#define exvsnprintf(r,b,n,f,a) {r = vsnprintf (b,n,f,a);}
+#endif
+#endif
+
+#if !defined (BSTRLIB_NOVSNP)
+
+#ifndef START_VSNBUFF
+#define START_VSNBUFF (16)
+#endif
+
+/* On IRIX vsnprintf returns n-1 when the operation would overflow the target
+ buffer, WATCOM and MSVC both return -1, while C99 requires that the
+ returned value be exactly what the length would be if the buffer would be
+ large enough. This leads to the idea that if the return value is larger
+ than n, then changing n to the return value will reduce the number of
+ iterations required. */
+
+/* int bformata (bstring b, const char * fmt, ...)
+ *
+ * After the first parameter, it takes the same parameters as printf (), but
+ * rather than outputting results to stdio, it appends the results to
+ * a bstring which contains what would have been output. Note that if there
+ * is an early generation of a '\0' character, the bstring will be truncated
+ * to this end point.
+ */
+int bformata (bstring b, const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+ if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0
+ || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+ /* Since the length is not determinable beforehand, a search is
+ performed using the truncating "vsnprintf" call (to avoid buffer
+ overflows) on increasing potential sizes for the output result. */
+
+ if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+ if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+ n = 1;
+ if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
+ }
+
+ for (;;) {
+ va_start (arglist, fmt);
+ exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+ va_end (arglist);
+
+ buff->data[n] = (unsigned char) '\0';
+ buff->slen = (int) (strlen) ((char *) buff->data);
+
+ if (buff->slen < n) break;
+
+ if (r > n) n = r; else n += n;
+
+ if (BSTR_OK != balloc (buff, n + 2)) {
+ bdestroy (buff);
+ return BSTR_ERR;
+ }
+ }
+
+ r = bconcat (b, buff);
+ bdestroy (buff);
+ return r;
+}
+
+/* int bassignformat (bstring b, const char * fmt, ...)
+ *
+ * After the first parameter, it takes the same parameters as printf (), but
+ * rather than outputting results to stdio, it outputs the results to
+ * the bstring parameter b. Note that if there is an early generation of a
+ * '\0' character, the bstring will be truncated to this end point.
+ */
+int bassignformat (bstring b, const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+ if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0
+ || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+ /* Since the length is not determinable beforehand, a search is
+ performed using the truncating "vsnprintf" call (to avoid buffer
+ overflows) on increasing potential sizes for the output result. */
+
+ if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+ if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+ n = 1;
+ if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
+ }
+
+ for (;;) {
+ va_start (arglist, fmt);
+ exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+ va_end (arglist);
+
+ buff->data[n] = (unsigned char) '\0';
+ buff->slen = (int) (strlen) ((char *) buff->data);
+
+ if (buff->slen < n) break;
+
+ if (r > n) n = r; else n += n;
+
+ if (BSTR_OK != balloc (buff, n + 2)) {
+ bdestroy (buff);
+ return BSTR_ERR;
+ }
+ }
+
+ r = bassign (b, buff);
+ bdestroy (buff);
+ return r;
+}
+
+/* bstring bformat (const char * fmt, ...)
+ *
+ * Takes the same parameters as printf (), but rather than outputting results
+ * to stdio, it forms a bstring which contains what would have been output.
+ * Note that if there is an early generation of a '\0' character, the
+ * bstring will be truncated to this end point.
+ */
+bstring bformat (const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+ if (fmt == NULL) return NULL;
+
+ /* Since the length is not determinable beforehand, a search is
+ performed using the truncating "vsnprintf" call (to avoid buffer
+ overflows) on increasing potential sizes for the output result. */
+
+ if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+ if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+ n = 1;
+ if (NULL == (buff = bfromcstralloc (n + 2, ""))) return NULL;
+ }
+
+ for (;;) {
+ va_start (arglist, fmt);
+ exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+ va_end (arglist);
+
+ buff->data[n] = (unsigned char) '\0';
+ buff->slen = (int) (strlen) ((char *) buff->data);
+
+ if (buff->slen < n) break;
+
+ if (r > n) n = r; else n += n;
+
+ if (BSTR_OK != balloc (buff, n + 2)) {
+ bdestroy (buff);
+ return NULL;
+ }
+ }
+
+ return buff;
+}
+
+/* int bvcformata (bstring b, int count, const char * fmt, va_list arglist)
+ *
+ * The bvcformata function formats data under control of the format control
+ * string fmt and attempts to append the result to b. The fmt parameter is
+ * the same as that of the printf function. The variable argument list is
+ * replaced with arglist, which has been initialized by the va_start macro.
+ * The size of the output is upper bounded by count. If the required output
+ * exceeds count, the string b is not augmented with any contents and a value
+ * below BSTR_ERR is returned. If a value below -count is returned then it
+ * is recommended that the negative of this value be used as an update to the
+ * count in a subsequent pass. On other errors, such as running out of
+ * memory, parameter errors or numeric wrap around BSTR_ERR is returned.
+ * BSTR_OK is returned when the output is successfully generated and
+ * appended to b.
+ *
+ * Note: There is no sanity checking of arglist, and this function is
+ * destructive of the contents of b from the b->slen point onward. If there
+ * is an early generation of a '\0' character, the bstring will be truncated
+ * to this end point.
+ */
+int bvcformata (bstring b, int count, const char * fmt, va_list arg) {
+int n, r, l;
+
+ if (b == NULL || fmt == NULL || count <= 0 || b->data == NULL
+ || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+ if (count > (n = b->slen + count) + 2) return BSTR_ERR;
+ if (BSTR_OK != balloc (b, n + 2)) return BSTR_ERR;
+
+ exvsnprintf (r, (char *) b->data + b->slen, count + 2, fmt, arg);
+
+ /* Did the operation complete successfully within bounds? */
+
+ if (n >= (l = b->slen + (int) (strlen) ((const char *) b->data + b->slen))) {
+ b->slen = l;
+ return BSTR_OK;
+ }
+
+ /* Abort, since the buffer was not large enough. The return value
+ tries to help set what the retry length should be. */
+
+ b->data[b->slen] = '\0';
+ if (r > count+1) l = r; else {
+ l = count+count;
+ if (count > l) l = INT_MAX;
+ }
+ n = -l;
+ if (n > BSTR_ERR-1) n = BSTR_ERR-1;
+ return n;
+}
+
+#endif
cnid_cdb_rebuild_add.c \
cnid_cdb.h
libcnid_cdb_la_CFLAGS = @BDB_CFLAGS@
-libcnid_cdb_la_LIBADD = @BDB_LIBS@
+libcnid_cdb_la_LIBADD = @BDB_LIBS@ @PTHREAD_LIBS@
if USE_CDB_BACKEND
noinst_LTLIBRARIES = libcnid_cdb.la
/*
-
- * $Id: cnid_cdb_open.c,v 1.5 2010-03-31 09:47:32 franklahm Exp $
- *
* Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
* All Rights Reserved. See COPYRIGHT.
*
#endif /* HAVE_CONFIG_H */
#ifdef CNID_BACKEND_CDB
+
+#include <atalk/cnid_private.h>
#include "cnid_cdb_private.h"
#ifndef MIN
#define DBHOMELEN 8
#define DBLEN 10
-/* we version the did/name database so that we can change the format
- * if necessary. the key is in the form of a did/name pair. in this case,
- * we use 0/0. */
-#define DBVERSION_KEY "\0\0\0\0\0"
-#define DBVERSION_KEYLEN 5
-#define DBVERSION1 0x00000001U
-#define DBVERSION DBVERSION1
-
#define DBOPTIONS (DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL)
#define MAXITER 0xFFFF /* maximum number of simultaneously open CNID
goto fail_appinit;
}
-#if 0
- DBT key, pkey, data;
/* ---------------------- */
- /* Check for version. This way we can update the database if we need
- * to change the format in any way. */
- memset(&key, 0, sizeof(key));
- memset(&pkey, 0, sizeof(DBT));
- memset(&data, 0, sizeof(data));
- key.data = DBVERSION_KEY;
- key.size = DBVERSION_KEYLEN;
+ /* Check for version. "cdb" only supports CNID_VERSION_0, cf cnid_private.h */
- if ((rc = db->db_didname->pget(db->db_didname, NULL, &key, &pkey, &data, 0)) != 0) {
- int ret;
- {
- u_int32_t version = htonl(DBVERSION);
+ DBT key, data;
+ uint32_t version;
- data.data = &version;
- data.size = sizeof(version);
- }
- if ((ret = db->db_didname->put(db->db_cnid, NULL, &key, &data,
- DB_NOOVERWRITE))) {
- LOG(log_error, logtype_default, "cnid_open: Error putting new version: %s",
- db_strerror(ret));
- db->db_didname->close(db->db_didname, 0);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = ROOTINFO_KEY;
+ key.size = ROOTINFO_KEYLEN;
+
+ if ((rc = db->db_cnid->get(db->db_cnid, NULL, &key, &data, 0)) == 0) {
+ /* If not found, ignore it */
+ memcpy(&version, data.data + CNID_DID_OFS, sizeof(version));
+ version = ntohl(version);
+ LOG(log_debug, logtype_default, "CNID db version %u", version);
+ if (version != CNID_VERSION_0) {
+ LOG(log_error, logtype_default, "Unsupported CNID db version %u, use CNID backend \"dbd\"", version);
goto fail_appinit;
}
}
-#endif
-
- /* TODO In the future we might check for version number here. */
-#if 0
- memcpy(&version, data.data, sizeof(version));
- if (version != ntohl(DBVERSION)) {
- /* Do stuff here. */
- }
-#endif /* 0 */
db_env_set_func_yield(my_yield);
return cdb;
/*
- * $Id: cnid.c,v 1.13 2010-03-31 09:47:32 franklahm Exp $
- *
* Copyright (c) 2003 the Netatalk Team
* Copyright (c) 2003 Rafal Lewczuk <rlewczuk@pronet.pl>
*
static void block_signal( u_int32_t flags)
{
if ((flags & CNID_FLAG_BLOCK)) {
- sigprocmask(SIG_BLOCK, &sigblockset, NULL);
+ pthread_sigmask(SIG_BLOCK, &sigblockset, NULL);
}
}
static void unblock_signal(u_int32_t flags)
{
if ((flags & CNID_FLAG_BLOCK)) {
- sigprocmask(SIG_UNBLOCK, &sigblockset, NULL);
+ pthread_sigmask(SIG_UNBLOCK, &sigblockset, NULL);
}
}
/* --------------- */
cnid_t cnid_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
- char *name, const size_t len, cnid_t hint)
+ const char *name, const size_t len, cnid_t hint)
{
cnid_t ret;
/* --------------- */
cnid_t cnid_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
- char *name, const size_t len)
+ char *name, const size_t len)
{
-cnid_t ret;
+ cnid_t ret;
block_signal(cdb->flags);
ret = valide(cdb->cnid_lookup(cdb, st, did, name, len));
return ret;
}
+/* --------------- */
+int cnid_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen)
+{
+ int ret;
+
+ if (cdb->cnid_find == NULL) {
+ LOG(log_error, logtype_cnid, "cnid_find not supported by CNID backend");
+ return -1;
+ }
+
+ block_signal(cdb->flags);
+ ret = cdb->cnid_find(cdb, name, namelen, buffer, buflen);
+ unblock_signal(cdb->flags);
+ return ret;
+}
+
/* --------------- */
char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
{
strerror(errno));
}
close(sock);
- sock=-1;
+ sock = -1;
continue;
}
} else {
cdb->cnid_delete = cnid_dbd_delete;
cdb->cnid_get = cnid_dbd_get;
cdb->cnid_lookup = cnid_dbd_lookup;
+ cdb->cnid_find = cnid_dbd_find;
cdb->cnid_nextid = NULL;
cdb->cnid_resolve = cnid_dbd_resolve;
cdb->cnid_getstamp = cnid_dbd_getstamp;
rqst.op = CNID_DBD_OP_RESOLVE;
rqst.cnid = *id;
- /* This mimicks the behaviour of the "regular" cnid_resolve. So far,
- nobody uses the content of buffer. It only provides space for the
- name in the caller. */
- rply.name = (char *)buffer + CNID_HEADER_LEN;
- rply.namelen = len - CNID_HEADER_LEN;
+ /* Pass buffer to transmit so it can stuff the reply data there */
+ rply.name = (char *)buffer;
+ rply.namelen = len;
if (transmit(db, &rqst, &rply) < 0) {
errno = CNID_ERR_DB;
switch (rply.result) {
case CNID_DBD_RES_OK:
*id = rply.did;
- name = rply.name;
+ name = rply.name + CNID_NAME_OFS;
LOG(log_debug, logtype_cnid, "cnid_dbd_resolve: resolved did: %u, name: '%s'", ntohl(*id), name);
break;
case CNID_DBD_RES_NOTFOUND:
return id;
}
+/* ---------------------- */
+int cnid_dbd_find(struct _cnid_db *cdb, char *name, size_t namelen, void *buffer, size_t buflen)
+{
+ CNID_private *db;
+ struct cnid_dbd_rqst rqst;
+ struct cnid_dbd_rply rply;
+ int count;
+
+ if (!cdb || !(db = cdb->_private) || !name) {
+ LOG(log_error, logtype_cnid, "cnid_find: Parameter error");
+ errno = CNID_ERR_PARAM;
+ return CNID_INVALID;
+ }
+
+ if (namelen > MAXPATHLEN) {
+ LOG(log_error, logtype_cnid, "cnid_find: Path name is too long");
+ errno = CNID_ERR_PATH;
+ return CNID_INVALID;
+ }
+
+ LOG(log_debug, logtype_cnid, "cnid_find(\"%s\")", name);
+
+ RQST_RESET(&rqst);
+ rqst.op = CNID_DBD_OP_SEARCH;
+
+ rqst.name = name;
+ rqst.namelen = namelen;
+
+ rply.name = buffer;
+ rply.namelen = buflen;
+
+ if (transmit(db, &rqst, &rply) < 0) {
+ errno = CNID_ERR_DB;
+ return CNID_INVALID;
+ }
+
+ switch (rply.result) {
+ case CNID_DBD_RES_OK:
+ count = rply.namelen / sizeof(cnid_t);
+ LOG(log_debug, logtype_cnid, "cnid_find: got %d matches", count);
+ break;
+ case CNID_DBD_RES_NOTFOUND:
+ count = 0;
+ break;
+ case CNID_DBD_RES_ERR_DB:
+ errno = CNID_ERR_DB;
+ count = -1;
+ break;
+ default:
+ abort();
+ }
+
+ return count;
+}
+
/* ---------------------- */
int cnid_dbd_update(struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
const cnid_t did, char *name, const size_t len)
/*
- * $Id: cnid_dbd.h,v 1.6 2010-03-31 09:47:32 franklahm Exp $
- *
* Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
* All Rights Reserved. See COPYING.
*/
extern struct _cnid_module cnid_dbd_module;
extern struct _cnid_db *cnid_dbd_open (struct cnid_open_args *args);
-extern void cnid_dbd_close (struct _cnid_db *);
-extern cnid_t cnid_dbd_add (struct _cnid_db *, const struct stat *, const cnid_t,
- char *, const size_t, cnid_t);
-extern cnid_t cnid_dbd_get (struct _cnid_db *, const cnid_t, char *, const size_t);
-extern char *cnid_dbd_resolve (struct _cnid_db *, cnid_t *, void *, size_t );
-extern int cnid_dbd_getstamp (struct _cnid_db *, void *, const size_t );
-extern cnid_t cnid_dbd_lookup (struct _cnid_db *, const struct stat *, const cnid_t,
- char *, const size_t);
-extern int cnid_dbd_update (struct _cnid_db *, const cnid_t, const struct stat *,
- const cnid_t, char *, size_t);
-extern int cnid_dbd_delete (struct _cnid_db *, const cnid_t);
-extern cnid_t cnid_dbd_rebuild_add (struct _cnid_db *, const struct stat *,
- const cnid_t, char *, const size_t, cnid_t);
+extern void cnid_dbd_close (struct _cnid_db *);
+extern cnid_t cnid_dbd_add (struct _cnid_db *, const struct stat *, const cnid_t,
+ char *, const size_t, cnid_t);
+extern cnid_t cnid_dbd_get (struct _cnid_db *, const cnid_t, char *, const size_t);
+extern char *cnid_dbd_resolve (struct _cnid_db *, cnid_t *, void *, size_t );
+extern int cnid_dbd_getstamp (struct _cnid_db *, void *, const size_t );
+extern cnid_t cnid_dbd_lookup (struct _cnid_db *, const struct stat *, const cnid_t,
+ char *, const size_t);
+extern int cnid_dbd_find (struct _cnid_db *cdb, char *name, size_t namelen,
+ void *buffer, size_t buflen);
+extern int cnid_dbd_update (struct _cnid_db *, const cnid_t, const struct stat *,
+ const cnid_t, char *, size_t);
+extern int cnid_dbd_delete (struct _cnid_db *, const cnid_t);
+extern cnid_t cnid_dbd_rebuild_add(struct _cnid_db *, const struct stat *,
+ const cnid_t, char *, const size_t, cnid_t);
/* FIXME: These functions could be static in cnid_dbd.c */
tvp = 0;
if (mask != 0) {
- rv = sigprocmask(SIG_SETMASK, mask, &omask);
+ rv = pthread_sigmask(SIG_SETMASK, mask, &omask);
if (rv != 0)
return rv;
}
rv = select(count, rfds, wfds, efds, tvp);
if (mask != 0) {
sverrno = errno;
- sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
+ pthread_sigmask(SIG_SETMASK, &omask, (sigset_t *)0);
errno = sverrno;
}
u_int32_t len, nlen;
u_int16_t id;
- if (dsi->asleep)
+ if (dsi->flags & DSI_SLEEPING)
return 1;
if (dsi->in_write) {
void dsi_close(DSI *dsi)
{
/* server generated. need to set all the fields. */
- if (!dsi->asleep) {
+ if (!(dsi->flags & DSI_SLEEPING)) {
dsi->header.dsi_flags = DSIFL_REQUEST;
dsi->header.dsi_command = DSIFUNC_CLOSE;
dsi->header.dsi_requestID = htons(dsi_serverID(dsi));
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-#ifdef HAVE_UNISTD_H
#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
/* POSIX.1 sys/wait.h check */
#include <sys/types.h>
#include <atalk/dsi.h>
#include <atalk/server_child.h>
-static server_child *children = NULL;
-
-void dsi_kill(int sig)
-{
- if (children)
- server_child_kill(children, CHILD_DSIFORK, sig);
-}
-
/* hand off the command. return child connection to the main program */
-DSI *dsi_getsession(DSI *dsi, server_child *serv_children,
- const int tickleval)
+afp_child_t *dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval)
{
pid_t pid;
-
- /* do a couple things on first entry */
- if (!dsi->inited) {
- if (!(children = serv_children))
- return NULL;
- dsi->inited = 1;
+ unsigned int ipc_fds[2];
+ afp_child_t *child;
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, ipc_fds) < 0) {
+ LOG(log_error, logtype_afpd, "dsi_getsess: %s", strerror(errno));
+ exit( EXITERR_CLNT );
}
-
- switch (pid = dsi->proto_open(dsi)) {
+
+ if (setnonblock(ipc_fds[0], 1) != 0 || setnonblock(ipc_fds[1], 1) != 0) {
+ LOG(log_error, logtype_afpd, "dsi_getsess: setnonblock: %s", strerror(errno));
+ exit(EXITERR_CLNT);
+ }
+
+ switch (pid = dsi->proto_open(dsi)) { /* in libatalk/dsi/dsi_tcp.c */
case -1:
/* if we fail, just return. it might work later */
LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
- return dsi;
+ return NULL;
case 0: /* child. mostly handled below. */
- dsi->child = 1;
break;
default: /* parent */
/* using SIGQUIT is hokey, but the child might not have
* re-established its signal handler for SIGTERM yet. */
- if (server_child_add(children, CHILD_DSIFORK, pid) < 0) {
+ if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds)) < 0) {
LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
dsi->header.dsi_flags = DSIFL_REPLY;
dsi->header.dsi_code = DSIERR_SERVBUSY;
dsi->header.dsi_code = DSIERR_OK;
kill(pid, SIGQUIT);
}
-
dsi->proto_close(dsi);
- return dsi;
+ return child;
}
/* child: check number of open connections. this is one off the
* actual count. */
- if ((children->count >= children->nsessions) &&
+ if ((serv_children->count >= serv_children->nsessions) &&
(dsi->header.dsi_command == DSIFUNC_OPEN)) {
LOG(log_info, logtype_dsi, "dsi_getsess: too many connections");
dsi->header.dsi_flags = DSIFL_REPLY;
/* get rid of some stuff */
close(dsi->serversock);
- server_child_free(children);
- children = NULL;
+ server_child_free(serv_children);
switch (dsi->header.dsi_command) {
case DSIFUNC_STAT: /* send off status and return */
dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0;
signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */
dsi_opensession(dsi);
- return dsi;
+ if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
+ exit(EXITERR_SYS);
+ child->ipc_fds[1] = ipc_fds[1];
+ return child;
break;
default: /* just close */
void dsi_opensession(DSI *dsi)
{
u_int32_t i = 0; /* this serves double duty. it must be 4-bytes long */
+ int offs;
/* parse options */
while (i < dsi->cmdlen) {
dsi->header.dsi_flags = DSIFL_REPLY;
dsi->header.dsi_code = 0;
/* dsi->header.dsi_command = DSIFUNC_OPEN;*/
- dsi->cmdlen = 2 + sizeof(i); /* length of data. dsi_send uses it. */
+
+ dsi->cmdlen = 2 * (2 + sizeof(i)); /* length of data. dsi_send uses it. */
+
+ /* DSI Option Server Request Quantum */
dsi->commands[0] = DSIOPT_SERVQUANT;
dsi->commands[1] = sizeof(i);
i = htonl(( dsi->server_quantum < DSI_SERVQUANT_MIN ||
DSI_SERVQUANT_DEF : dsi->server_quantum);
memcpy(dsi->commands + 2, &i, sizeof(i));
+ /* AFP replaycache size option */
+ offs = 2 + sizeof(i);
+ dsi->commands[offs] = DSIOPT_REPLCSIZE;
+ dsi->commands[offs+1] = sizeof(i);
+ i = htonl(REPLAYCACHE_SIZE);
+ memcpy(dsi->commands + offs + 2, &i, sizeof(i));
dsi_send(dsi);
}
const size_t size, const int err)
{
- dsi->noreply = 1; /* we will handle our own replies */
+ dsi->flags |= DSI_NOREPLY; /* we will handle our own replies */
dsi->header.dsi_flags = DSIFL_REPLY;
/*dsi->header.dsi_command = DSIFUNC_CMD;*/
dsi->header.dsi_len = htonl(size);
{
size_t stored;
ssize_t len;
-
+
stored = 0;
while (stored < length) {
len = buf_read(dsi, (u_int8_t *) data + stored, length - stored);
- if (len == -1 && errno == EINTR)
+ if (len == -1 && errno == EINTR) {
continue;
- else if (len > 0)
+ } else if (len > 0) {
stored += len;
- else { /* eof or error */
+ } else { /* eof or error */
/* don't log EOF error if it's just after connect (OSX 10.3 probe) */
if (len || stored || dsi->read_count) {
- LOG(log_error, logtype_dsi, "dsi_stream_read(%d): %s", len, (len < 0)?strerror(errno):"unexpected EOF");
+ if (! (dsi->flags & DSI_DISCONNECTED))
+ LOG(log_error, logtype_dsi, "dsi_stream_read(fd: %i): len:%d, %s",
+ dsi->socket, len, (len < 0) ? strerror(errno) : "unexpected EOF");
}
break;
}
return len;
}
-/* ---------------------------------------
-*/
-void dsi_sleep(DSI *dsi, const int state)
-{
- dsi->asleep = state;
-}
-
/* ---------------------------------------
*/
static void block_sig(DSI *dsi)
u_int8_t block[DSI_BLOCKSIZ];
size_t stored;
+ /* Immediateyl mark globally that we're a child now */
+ parent_or_child = 1;
+
/* reset signals */
server_reset_signal();
#define IFF_SLAVE 0
#endif
-static void guess_interface(DSI *dsi, const char *hostname)
+static void guess_interface(DSI *dsi, const char *hostname, const char *port)
{
int fd;
char **start, **list;
memset(&dsi->server, 0, sizeof(struct sockaddr_storage));
sa->sin_family = AF_INET;
- sa->sin_port = htons(548);
+ sa->sin_port = htons(atoi(port));
sa->sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
- LOG(log_info, logtype_dsi, "dsi_tcp: '%s' on interface '%s' will be used instead.",
- getip_string((struct sockaddr *)&dsi->server), ifr.ifr_name);
+ LOG(log_info, logtype_dsi, "dsi_tcp: '%s:%s' on interface '%s' will be used instead.",
+ getip_string((struct sockaddr *)&dsi->server), port, ifr.ifr_name);
goto iflist_done;
}
LOG(log_info, logtype_dsi, "dsi_tcp (Chooser will not select afp/tcp) "
freeaddrinfo(servinfo);
interfaces:
- guess_interface(dsi, hostname);
+ guess_interface(dsi, hostname, port ? port : "548");
return 1;
}
{
char block[DSI_BLOCKSIZ];
u_int16_t id;
- int ret;
- if (dsi->asleep || dsi->in_write)
+ if ((dsi->flags & DSI_SLEEPING) || dsi->in_write)
return 1;
id = htons(dsi_serverID(dsi));
memcpy(block + 2, &id, sizeof(id));
/* code = len = reserved = 0 */
- ret = dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT);
- /* we don't really care if we can't send a tickle, it will fail
- * elsewhere
- */
- ret = (ret == -1 || ret == DSI_BLOCKSIZ);
- return ret;
+ return dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT);
}
-/* This file is generated by contrib/misc/make-precompose.h.pl UnicodeData.txt */
/* DO NOT EDIT BY HAND!!! */
+/* This file is generated by */
+/* contrib/misc/make-precompose.h.pl UnicodeData.txt */
/* UnicodeData.txt is got from */
/* http://www.unicode.org/Public/UNIDATA/UnicodeData.txt */
+#define SBASE 0xAC00
+#define LBASE 0x1100
+#define VBASE 0x1161
+#define TBASE 0x11A7
+#define LCOUNT 19
+#define VCOUNT 21
+#define TCOUNT 28
+#define NCOUNT 588 /* (VCOUNT * TCOUNT) */
+#define SCOUNT 11172 /* (LCOUNT * NCOUNT) */
+
+#define PRECOMP_COUNT 955
+#define DECOMP_COUNT 955
+#define MAXCOMBLEN 3
+
+#define PRECOMP_SP_COUNT 16
+#define DECOMP_SP_COUNT 16
+#define MAXCOMBSPLEN 4
+
+#define COMBBUFLEN 4 /* max(MAXCOMBLEN,MAXCOMBSPLEN) */
+
static const struct {
unsigned int replacement;
unsigned int base;
{ 0x000030FE, 0x000030FD, 0x00003099 }, /* KATAKANA VOICED ITERATION MARK */
{ 0x0000FB2C, 0x0000FB49, 0x000005C1 }, /* HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT */
{ 0x0000FB2D, 0x0000FB49, 0x000005C2 }, /* HEBREW LETTER SHIN WITH DAGESH AND SIN DOT */
+/*{ 0x0001109A, 0x00011099, 0x000110BA },*/ /* KAITHI LETTER DDDHA */
+/*{ 0x0001109C, 0x0001109B, 0x000110BA },*/ /* KAITHI LETTER RHA */
+/*{ 0x000110AB, 0x000110A5, 0x000110BA },*/ /* KAITHI LETTER VA */
/*{ 0x0001D15E, 0x0001D157, 0x0001D165 },*/ /* MUSICAL SYMBOL HALF NOTE */
/*{ 0x0001D15F, 0x0001D158, 0x0001D165 },*/ /* MUSICAL SYMBOL QUARTER NOTE */
/*{ 0x0001D160, 0x0001D15F, 0x0001D16E },*/ /* MUSICAL SYMBOL EIGHTH NOTE */
{ 0x0000FB4C, 0x000005D1, 0x000005BF }, /* HEBREW LETTER BET WITH RAFE */
{ 0x0000FB4D, 0x000005DB, 0x000005BF }, /* HEBREW LETTER KAF WITH RAFE */
{ 0x0000FB4E, 0x000005E4, 0x000005BF }, /* HEBREW LETTER PE WITH RAFE */
+/*{ 0x0001109A, 0x00011099, 0x000110BA },*/ /* KAITHI LETTER DDDHA */
+/*{ 0x0001109C, 0x0001109B, 0x000110BA },*/ /* KAITHI LETTER RHA */
+/*{ 0x000110AB, 0x000110A5, 0x000110BA },*/ /* KAITHI LETTER VA */
/*{ 0x0001D15E, 0x0001D157, 0x0001D165 },*/ /* MUSICAL SYMBOL HALF NOTE */
/*{ 0x0001D15F, 0x0001D158, 0x0001D165 },*/ /* MUSICAL SYMBOL QUARTER NOTE */
/*{ 0x0001D160, 0x0001D15F, 0x0001D16E },*/ /* MUSICAL SYMBOL EIGHTH NOTE */
/*{ 0x0001D1C0, 0x0001D1BC, 0x0001D16F },*/ /* MUSICAL SYMBOL FUSA BLACK */
};
+static const struct {
+ unsigned int replacement_sp;
+ unsigned int base_sp;
+ unsigned int comb_sp;
+} precompositions_sp[] = {
+ { 0xD804DC9A, 0xD804DC99, 0xD804DCBA }, /* KAITHI LETTER DDDHA */
+ { 0xD804DC9C, 0xD804DC9B, 0xD804DCBA }, /* KAITHI LETTER RHA */
+ { 0xD804DCAB, 0xD804DCA5, 0xD804DCBA }, /* KAITHI LETTER VA */
+ { 0xD834DD5E, 0xD834DD57, 0xD834DD65 }, /* MUSICAL SYMBOL HALF NOTE */
+ { 0xD834DD5F, 0xD834DD58, 0xD834DD65 }, /* MUSICAL SYMBOL QUARTER NOTE */
+ { 0xD834DD60, 0xD834DD5F, 0xD834DD6E }, /* MUSICAL SYMBOL EIGHTH NOTE */
+ { 0xD834DD61, 0xD834DD5F, 0xD834DD6F }, /* MUSICAL SYMBOL SIXTEENTH NOTE */
+ { 0xD834DD62, 0xD834DD5F, 0xD834DD70 }, /* MUSICAL SYMBOL THIRTY-SECOND NOTE */
+ { 0xD834DD63, 0xD834DD5F, 0xD834DD71 }, /* MUSICAL SYMBOL SIXTY-FOURTH NOTE */
+ { 0xD834DD64, 0xD834DD5F, 0xD834DD72 }, /* MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE */
+ { 0xD834DDBB, 0xD834DDB9, 0xD834DD65 }, /* MUSICAL SYMBOL MINIMA */
+ { 0xD834DDBC, 0xD834DDBA, 0xD834DD65 }, /* MUSICAL SYMBOL MINIMA BLACK */
+ { 0xD834DDBD, 0xD834DDBB, 0xD834DD6E }, /* MUSICAL SYMBOL SEMIMINIMA WHITE */
+ { 0xD834DDBF, 0xD834DDBB, 0xD834DD6F }, /* MUSICAL SYMBOL FUSA WHITE */
+ { 0xD834DDBE, 0xD834DDBC, 0xD834DD6E }, /* MUSICAL SYMBOL SEMIMINIMA BLACK */
+ { 0xD834DDC0, 0xD834DDBC, 0xD834DD6F }, /* MUSICAL SYMBOL FUSA BLACK */
+};
+
+static const struct {
+ unsigned int replacement_sp;
+ unsigned int base_sp;
+ unsigned int comb_sp;
+} decompositions_sp[] = {
+ { 0xD804DC9A, 0xD804DC99, 0xD804DCBA }, /* KAITHI LETTER DDDHA */
+ { 0xD804DC9C, 0xD804DC9B, 0xD804DCBA }, /* KAITHI LETTER RHA */
+ { 0xD804DCAB, 0xD804DCA5, 0xD804DCBA }, /* KAITHI LETTER VA */
+ { 0xD834DD5E, 0xD834DD57, 0xD834DD65 }, /* MUSICAL SYMBOL HALF NOTE */
+ { 0xD834DD5F, 0xD834DD58, 0xD834DD65 }, /* MUSICAL SYMBOL QUARTER NOTE */
+ { 0xD834DD60, 0xD834DD5F, 0xD834DD6E }, /* MUSICAL SYMBOL EIGHTH NOTE */
+ { 0xD834DD61, 0xD834DD5F, 0xD834DD6F }, /* MUSICAL SYMBOL SIXTEENTH NOTE */
+ { 0xD834DD62, 0xD834DD5F, 0xD834DD70 }, /* MUSICAL SYMBOL THIRTY-SECOND NOTE */
+ { 0xD834DD63, 0xD834DD5F, 0xD834DD71 }, /* MUSICAL SYMBOL SIXTY-FOURTH NOTE */
+ { 0xD834DD64, 0xD834DD5F, 0xD834DD72 }, /* MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE */
+ { 0xD834DDBB, 0xD834DDB9, 0xD834DD65 }, /* MUSICAL SYMBOL MINIMA */
+ { 0xD834DDBC, 0xD834DDBA, 0xD834DD65 }, /* MUSICAL SYMBOL MINIMA BLACK */
+ { 0xD834DDBD, 0xD834DDBB, 0xD834DD6E }, /* MUSICAL SYMBOL SEMIMINIMA WHITE */
+ { 0xD834DDBE, 0xD834DDBC, 0xD834DD6E }, /* MUSICAL SYMBOL SEMIMINIMA BLACK */
+ { 0xD834DDBF, 0xD834DDBB, 0xD834DD6F }, /* MUSICAL SYMBOL FUSA WHITE */
+ { 0xD834DDC0, 0xD834DDBC, 0xD834DD6F }, /* MUSICAL SYMBOL FUSA BLACK */
+};
+
/* EOF */
NULL, NULL
};
-/* ------------------- Convert from UTF-8 to UCS-2 -------------------*/
+/* ------------------- Convert from UTF-8 to UTF-16 -------------------*/
static size_t utf8_pull(void *cd _U_, char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft)
{
ucs2_t uc = 0;
- ucs2_t hi, low; /* surrogate pair */
- unsigned int codepoint, surrogate;
+ unsigned int codepoint;
int len;
while (*inbytesleft >= 1 && *outbytesleft >= 2) {
}
codepoint = ((c[0] & 0x07) << 18) | GETUCVAL(c[1],12) |
GETUCVAL(c[2],6) | GETUCVAL(c[3],0);
- hi = (ucs2_t)( ((codepoint - 0x10000) >> 10) + 0xD800);
- low = (ucs2_t)(0xDC00 + (codepoint & 0x03FF));
- surrogate = (hi << 16) | low;
- SIVAL(*outbuf,0,surrogate);
+ SSVAL(*outbuf,0,(((codepoint - 0x10000) >> 10) + 0xD800)); /* hi */
+ SSVAL(*outbuf,2,(0xDC00 + (codepoint & 0x03FF))); /* low */
len = 4;
(*inbuf) += 4;
(*inbytesleft) -= 4;
return -1;
}
-/* --------------------- Convert from UCS-2 to UTF-8 -----------*/
+/* --------------------- Convert from UTF-16 to UTF-8 -----------*/
static size_t utf8_push(void *cd _U_, char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft)
{
ucs2_t uc=0;
ucs2_t hi, low;
- unsigned int surrogatepair, codepoint;
+ unsigned int codepoint;
int olen, ilen;
while (*inbytesleft >= 2 && *outbytesleft >= 1) {
errno = EINVAL;
return -1;
}
- surrogatepair = IVAL((*inbuf),0);
- low = (ucs2_t)surrogatepair;
- hi = (ucs2_t)(surrogatepair >> 16);
+ hi = SVAL((*inbuf),0);
+ low = SVAL((*inbuf),2);
if ( 0xd800 <= hi && hi <= 0xdbff && 0xdc00 <= low && low <= 0xdfff) {
codepoint = ((hi - 0xd800) << 10) + (low - 0xdc00) + 0x10000;
c[3] = GETUTF8TRAILBYTE(codepoint, 0);
#include "precompose.h"
#include "byteorder.h"
-#define HANGUL_SBASE 0xAC00
-#define HANGUL_LBASE 0x1100
-#define HANGUL_VBASE 0x1161
-#define HANGUL_TBASE 0x11A7
-#define HANGUL_LCOUNT 19
-#define HANGUL_VCOUNT 21
-#define HANGUL_TCOUNT 28
-#define HANGUL_NCOUNT (HANGUL_VCOUNT * HANGUL_TCOUNT) /* 588 */
-#define HANGUL_SCOUNT (HANGUL_LCOUNT * HANGUL_NCOUNT) /* 11172 */
-
-#define MAXCOMBLEN 3
-
+/*******************************************************************
+ Convert a wide character to upper/lower case.
+********************************************************************/
ucs2_t toupper_w(ucs2_t val)
{
if ( val >= 0x0040 && val <= 0x007F)
********************************************************************/
int strlower_w(ucs2_t *s)
{
- int ret = 0;
- while (*s) {
- ucs2_t v = tolower_w(*s);
- if (v != *s) {
- *s = v;
- ret = 1;
- }
- s++;
- }
- return ret;
+ int ret = 0;
+ while (*s) {
+ ucs2_t v = tolower_w(*s);
+ if (v != *s) {
+ *s = v;
+ ret = 1;
+ }
+ s++;
+ }
+ return ret;
}
/*******************************************************************
********************************************************************/
int strupper_w(ucs2_t *s)
{
- int ret = 0;
- while (*s) {
- ucs2_t v = toupper_w(*s);
- if (v != *s) {
- *s = v;
- ret = 1;
- }
- s++;
- }
- return ret;
+ int ret = 0;
+ while (*s) {
+ ucs2_t v = toupper_w(*s);
+ if (v != *s) {
+ *s = v;
+ ret = 1;
+ }
+ s++;
+ }
+ return ret;
}
int strcmp_w(const ucs2_t *a, const ucs2_t *b)
{
- while (*b && *a == *b) { a++; b++; }
- return (*a - *b);
- /* warning: if *a != *b and both are not 0 we retrun a random
- greater or lesser than 0 number not realted to which
- string is longer */
+ while (*b && *a == *b) { a++; b++; }
+ return (*a - *b);
+ /* warning: if *a != *b and both are not 0 we retrun a random
+ greater or lesser than 0 number not realted to which
+ string is longer */
}
int strncmp_w(const ucs2_t *a, const ucs2_t *b, size_t len)
{
- size_t n = 0;
- while ((n < len) && *b && *a == *b) { a++; b++; n++;}
- return (len - n)?(*a - *b):0;
+ size_t n = 0;
+ while ((n < len) && *b && *a == *b) { a++; b++; n++;}
+ return (len - n)?(*a - *b):0;
}
/*******************************************************************
********************************************************************/
int strcasecmp_w(const ucs2_t *a, const ucs2_t *b)
{
- while (*b && toupper_w(*a) == toupper_w(*b)) { a++; b++; }
- return (tolower_w(*a) - tolower_w(*b));
+ while (*b && toupper_w(*a) == toupper_w(*b)) { a++; b++; }
+ return (tolower_w(*a) - tolower_w(*b));
}
/*******************************************************************
********************************************************************/
int strncasecmp_w(const ucs2_t *a, const ucs2_t *b, size_t len)
{
- size_t n = 0;
- while ((n < len) && *b && (toupper_w(*a) == toupper_w(*b))) { a++; b++; n++; }
- return (len - n)?(tolower_w(*a) - tolower_w(*b)):0;
+ size_t n = 0;
+ while ((n < len) && *b && (toupper_w(*a) == toupper_w(*b))) { a++; b++; n++; }
+ return (len - n)?(tolower_w(*a) - tolower_w(*b)):0;
}
/*******************************************************************
/* if len == 0 then duplicate the whole string */
ucs2_t *strndup_w(const ucs2_t *src, size_t len)
{
- ucs2_t *dest;
+ ucs2_t *dest;
- if (!len) len = strlen_w(src);
- dest = (ucs2_t *)malloc((len + 1) * sizeof(ucs2_t));
- if (!dest) {
- LOG (log_error, logtype_default, "strdup_w: out of memory!");
- return NULL;
- }
+ if (!len) len = strlen_w(src);
+ dest = (ucs2_t *)malloc((len + 1) * sizeof(ucs2_t));
+ if (!dest) {
+ LOG (log_error, logtype_default, "strdup_w: out of memory!");
+ return NULL;
+ }
- memcpy(dest, src, len * sizeof(ucs2_t));
- dest[len] = 0;
+ memcpy(dest, src, len * sizeof(ucs2_t));
+ dest[len] = 0;
- return dest;
+ return dest;
}
ucs2_t *strdup_w(const ucs2_t *src)
{
- return strndup_w(src, 0);
+ return strndup_w(src, 0);
}
/*******************************************************************
ucs2_t *strncpy_w(ucs2_t *dest, const ucs2_t *src, const size_t max)
{
- size_t len;
+ size_t len;
- if (!dest || !src) return NULL;
+ if (!dest || !src) return NULL;
- for (len = 0; (src[len] != 0) && (len < max); len++)
- dest[len] = src[len];
- while (len < max)
- dest[len++] = 0;
+ for (len = 0; (src[len] != 0) && (len < max); len++)
+ dest[len] = src[len];
+ while (len < max)
+ dest[len++] = 0;
- return dest;
+ return dest;
}
ucs2_t *strncat_w(ucs2_t *dest, const ucs2_t *src, const size_t max)
{
- size_t start;
- size_t len;
+ size_t start;
+ size_t len;
- if (!dest || !src) return NULL;
+ if (!dest || !src) return NULL;
- start = strlen_w(dest);
- len = strnlen_w(src, max);
+ start = strlen_w(dest);
+ len = strnlen_w(src, max);
- memcpy(&dest[start], src, len*sizeof(ucs2_t));
- dest[start+len] = 0;
+ memcpy(&dest[start], src, len*sizeof(ucs2_t));
+ dest[start+len] = 0;
- return dest;
+ return dest;
}
ucs2_t *strcat_w(ucs2_t *dest, const ucs2_t *src)
{
- size_t start;
- size_t len;
+ size_t start;
+ size_t len;
- if (!dest || !src) return NULL;
+ if (!dest || !src) return NULL;
- start = strlen_w(dest);
- len = strlen_w(src);
+ start = strlen_w(dest);
+ len = strlen_w(src);
- memcpy(&dest[start], src, len*sizeof(ucs2_t));
- dest[start+len] = 0;
+ memcpy(&dest[start], src, len*sizeof(ucs2_t));
+ dest[start+len] = 0;
- return dest;
+ return dest;
}
-/* ------------------------ */
+/*******************************************************************
+binary search for pre|decomposition
+********************************************************************/
+
static ucs2_t do_precomposition(unsigned int base, unsigned int comb)
{
- int min = 0;
- int max = sizeof(precompositions) / sizeof(precompositions[0]) - 1;
- int mid;
- u_int32_t sought = (base << 16) | comb, that;
-
- /* binary search */
- while (max >= min) {
- mid = (min + max) / 2;
- that = (precompositions[mid].base << 16) | (precompositions[mid].comb);
- if (that < sought) {
- min = mid + 1;
- } else if (that > sought) {
- max = mid - 1;
- } else {
- return precompositions[mid].replacement;
- }
- }
- /* no match */
- return 0;
+ int min = 0;
+ int max = PRECOMP_COUNT - 1;
+ int mid;
+ u_int32_t sought = (base << 16) | comb, that;
+
+ /* binary search */
+ while (max >= min) {
+ mid = (min + max) / 2;
+ that = (precompositions[mid].base << 16) | (precompositions[mid].comb);
+ if (that < sought) {
+ min = mid + 1;
+ } else if (that > sought) {
+ max = mid - 1;
+ } else {
+ return precompositions[mid].replacement;
+ }
+ }
+ /* no match */
+ return 0;
+}
+
+/* ------------------------ */
+static u_int32_t do_precomposition_sp(unsigned int base_sp, unsigned int comb_sp)
+{
+ int min = 0;
+ int max = PRECOMP_SP_COUNT - 1;
+ int mid;
+ u_int64_t sought_sp = ((u_int64_t)base_sp << 32) | (u_int64_t)comb_sp, that_sp;
+
+ /* binary search */
+ while (max >= min) {
+ mid = (min + max) / 2;
+ that_sp = ((u_int64_t)precompositions_sp[mid].base_sp << 32) | ((u_int64_t)precompositions_sp[mid].comb_sp);
+ if (that_sp < sought_sp) {
+ min = mid + 1;
+ } else if (that_sp > sought_sp) {
+ max = mid - 1;
+ } else {
+ return precompositions_sp[mid].replacement_sp;
+ }
+ }
+ /* no match */
+ return 0;
}
/* -------------------------- */
static u_int32_t do_decomposition(ucs2_t base)
{
- int min = 0;
- int max = sizeof(decompositions) / sizeof(decompositions[0]) - 1;
- int mid;
- u_int32_t sought = base;
- u_int32_t result, that;
-
- /* binary search */
- while (max >= min) {
- mid = (min + max) / 2;
- that = decompositions[mid].replacement;
- if (that < sought) {
- min = mid + 1;
- } else if (that > sought) {
- max = mid - 1;
- } else {
- result = (decompositions[mid].base << 16) | (decompositions[mid].comb);
- return result;
- }
- }
- /* no match */
- return 0;
+ int min = 0;
+ int max = DECOMP_COUNT - 1;
+ int mid;
+ u_int32_t sought = base;
+ u_int32_t result, that;
+
+ /* binary search */
+ while (max >= min) {
+ mid = (min + max) / 2;
+ that = decompositions[mid].replacement;
+ if (that < sought) {
+ min = mid + 1;
+ } else if (that > sought) {
+ max = mid - 1;
+ } else {
+ result = (decompositions[mid].base << 16) | (decompositions[mid].comb);
+ return result;
+ }
+ }
+ /* no match */
+ return 0;
}
-/* we can't use static, this stuff needs to be reentrant */
-/* static char comp[MAXPATHLEN +1]; */
+/* -------------------------- */
+static u_int64_t do_decomposition_sp(unsigned int base_sp)
+{
+ int min = 0;
+ int max = DECOMP_SP_COUNT - 1;
+ int mid;
+ u_int32_t sought_sp = base_sp;
+ u_int32_t that_sp;
+ u_int64_t result_sp;
+
+ /* binary search */
+ while (max >= min) {
+ mid = (min + max) / 2;
+ that_sp = decompositions_sp[mid].replacement_sp;
+ if (that_sp < sought_sp) {
+ min = mid + 1;
+ } else if (that_sp > sought_sp) {
+ max = mid - 1;
+ } else {
+ result_sp = ((u_int64_t)decompositions_sp[mid].base_sp << 32) | ((u_int64_t)decompositions_sp[mid].comb_sp);
+ return result_sp;
+ }
+ }
+ /* no match */
+ return 0;
+}
+
+/*******************************************************************
+pre|decomposition
+
+ we can't use static, this stuff needs to be reentrant
+ static char comp[MAXPATHLEN +1];
+
+ We don't implement Singleton and Canonical Ordering.
+ We ignore CompositionExclusions.txt.
+ because they cause the problem of the roundtrip
+ such as Dancing Icon.
+
+ exclude U2000-U2FFF, UFE30-UFE4F and U2F800-U2FA1F ranges
+ in precompose.h from composition according to AFP 3.x spec
+********************************************************************/
size_t precompose_w (ucs2_t *name, size_t inplen, ucs2_t *comp, size_t *outlen)
{
size_t i;
ucs2_t base, comb;
+ u_int32_t base_sp, comb_sp;
ucs2_t *in, *out;
- ucs2_t hangul_lindex, hangul_vindex;
+ ucs2_t lindex, vindex;
ucs2_t result;
+ u_int32_t result_sp;
size_t o_len = *outlen;
-
- if (!inplen || (inplen & 1) || inplen > o_len)
- return (size_t)-1;
- /* Actually, */
- /* Decomposition and Canonical Ordering are necessary here. */
- /* */
- /* Ex. in = CanonicalOrdering(decompose_w(name)) */
- /* */
- /* A new mapping table is needed for CanonicalOrdering. */
+ if (!inplen || (inplen & 1) || inplen > o_len)
+ return (size_t)-1;
- i = 0;
- in = name;
+ i = 0;
+ in = name;
out = comp;
-
- base = *in;
- while (*outlen > 2) {
- i += 2;
- in++;
- if (i == inplen) {
- *out = base;
+
+ base = *in;
+ while (*outlen > 2) {
+ i += 2;
+ in++;
+
+ if (i == inplen) {
+ *out = base;
out++;
*out = 0;
- *outlen -= 2;
- return o_len - *outlen;
- }
- comb = *in;
+ *outlen -= 2;
+ return o_len - *outlen;
+ }
+
+ comb = *in;
result = 0;
-
+
/* Non-Combination Character */
if (comb < 0x300) ;
/* Unicode Standard Annex #15 A10.3 Hangul Composition */
/* Step 1 <L,V> */
- else if ((HANGUL_VBASE <= comb) && (comb <= HANGUL_VBASE + HANGUL_VCOUNT)) {
- if ((HANGUL_LBASE <= base) && (base < HANGUL_LBASE + HANGUL_LCOUNT)) {
+ else if ((VBASE <= comb) && (comb <= VBASE + VCOUNT)) {
+ if ((LBASE <= base) && (base < LBASE + LCOUNT)) {
result = 1;
- hangul_lindex = base - HANGUL_LBASE;
- hangul_vindex = comb - HANGUL_VBASE;
- base = HANGUL_SBASE + (hangul_lindex * HANGUL_VCOUNT + hangul_vindex) * HANGUL_TCOUNT;
+ lindex = base - LBASE;
+ vindex = comb - VBASE;
+ base = SBASE + (lindex * VCOUNT + vindex) * TCOUNT;
}
- }
+ }
/* Step 2 <LV,T> */
- else if ((HANGUL_TBASE < comb) && (comb < HANGUL_TBASE + HANGUL_TCOUNT)) {
- if ((HANGUL_SBASE <= base) && (base < HANGUL_SBASE +HANGUL_SCOUNT) && (((base - HANGUL_SBASE) % HANGUL_TCOUNT) == 0)) {
+ else if ((TBASE < comb) && (comb < TBASE + TCOUNT)) {
+ if ((SBASE <= base) && (base < SBASE + SCOUNT) && (((base - SBASE) % TCOUNT) == 0)) {
result = 1;
- base += comb - HANGUL_TBASE;
+ base += comb - TBASE;
}
}
- /* Combining Sequence */
- else if ((result = do_precomposition(base, comb))) {
+ /* Binary Search for Surrogate Pair */
+ else if ((0xD800 <= base) && (base < 0xDC00)) {
+ if ((0xDC00 <= comb) && (comb < 0xE000) && (i + 4 <= inplen)) {
+ base_sp = ((u_int32_t)base << 16) | (u_int32_t)comb;
+ do {
+ comb_sp = ((u_int32_t)in[1] << 16) | (u_int32_t)in[2];
+ if (result_sp = do_precomposition_sp(base_sp, comb_sp)) {
+ base_sp = result_sp;
+ i += 4;
+ in +=2;
+ }
+ } while ((i + 4 <= inplen) && result_sp) ;
+
+ *out = base_sp >> 16;
+ out++;
+ *outlen -= 2;
+
+ if (*outlen <= 2) {
+ errno = E2BIG;
+ return (size_t)-1;
+ }
+
+ *out = base_sp & 0xFFFF;
+ out++;
+ *outlen -= 2;
+
+ i += 2;
+ in++;
+ base = *in;
+
+ result = 1;
+ }
+ }
+
+ /* Binary Search for BMP */
+ else if (result = do_precomposition(base, comb)) {
base = result;
}
if (!result) {
- *out = base;
- out++;
- *outlen -= 2;
- base = comb;
- }
- }
-
+ *out = base;
+ out++;
+ *outlen -= 2;
+ base = comb;
+ }
+ }
+
errno = E2BIG;
return (size_t)-1;
}
/* --------------- */
-
-/* Singleton Decomposition is unsupported. */
-/* A new mapping table is needed for implementation. */
-
size_t decompose_w (ucs2_t *name, size_t inplen, ucs2_t *comp, size_t *outlen)
{
size_t i;
size_t comblen;
- ucs2_t base;
- ucs2_t comb[MAXCOMBLEN];
- ucs2_t hangul_sindex, tjamo;
+ ucs2_t base, comb[COMBBUFLEN];
+ u_int32_t base_sp;
+ ucs2_t sindex, tjamo;
ucs2_t *in, *out;
unsigned int result;
+ u_int64_t result_sp;
size_t o_len = *outlen;
- if (!inplen || (inplen & 1))
- return (size_t)-1;
+ if (!inplen || (inplen & 1))
+ return (size_t)-1;
i = 0;
in = name;
out = comp;
-
- while (i < inplen) {
- base = *in;
+
+ while (i < inplen) {
+ base = *in;
comblen = 0;
/* check ASCII first. this is frequent. */
if (base <= 0x007f) ;
/* Unicode Standard Annex #15 A10.2 Hangul Decomposition */
- else if ((HANGUL_SBASE <= base) && (base < HANGUL_SBASE + HANGUL_SCOUNT)) {
- hangul_sindex = base - HANGUL_SBASE;
- base = HANGUL_LBASE + hangul_sindex / HANGUL_NCOUNT;
- comb[MAXCOMBLEN-2] = HANGUL_VBASE + (hangul_sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT;
+ else if ((SBASE <= base) && (base < SBASE + SCOUNT)) {
+ sindex = base - SBASE;
+ base = LBASE + sindex / NCOUNT;
+ comb[COMBBUFLEN-2] = VBASE + (sindex % NCOUNT) / TCOUNT;
/* <L,V> */
- if ((tjamo = HANGUL_TBASE + hangul_sindex % HANGUL_TCOUNT) == HANGUL_TBASE) {
- comb[MAXCOMBLEN-1] = comb[MAXCOMBLEN-2];
+ if ((tjamo = TBASE + sindex % TCOUNT) == TBASE) {
+ comb[COMBBUFLEN-1] = comb[COMBBUFLEN-2];
comblen = 1;
}
/* <L,V,T> */
else {
- comb[MAXCOMBLEN-1] = tjamo;
+ comb[COMBBUFLEN-1] = tjamo;
comblen = 2;
}
}
- /* Combining Sequence */
- /* exclude U2000-U2FFF and UFE30-UFE4F ranges in decompositions[] */
- /* from decomposition according to AFP 3.1 spec */
+ /* Binary Search for Surrogate Pair */
+ else if ((0xD800 <= base) && (base < 0xDC00)) {
+ if (i + 2 < inplen) {
+ base_sp = ((u_int32_t)base << 16) | (u_int32_t)in[1];
+ do {
+ if ( !(result_sp = do_decomposition_sp(base_sp))) break;
+ comblen += 2;
+ base_sp = result_sp >> 32;
+ comb[COMBBUFLEN-comblen] = (result_sp >> 16) & 0xFFFF; /* hi */
+ comb[COMBBUFLEN-comblen+1] = result_sp & 0xFFFF; /* lo */
+ } while (comblen < MAXCOMBSPLEN);
+
+ if (*outlen < (comblen + 1) << 1) {
+ errno = E2BIG;
+ return (size_t)-1;
+ }
+
+ *out = base_sp >> 16; /* hi */
+ out++;
+ *outlen -= 2;
+
+ base = base_sp & 0xFFFF; /* lo */
+
+ i += 2;
+ in++;
+ }
+ }
+
+ /* Binary Search for BMP */
else {
do {
- if ((comblen >= MAXCOMBLEN) || !(result = do_decomposition(base))) break;
+ if ( !(result = do_decomposition(base))) break;
comblen++;
base = result >> 16;
- comb[MAXCOMBLEN-comblen] = result & 0xffff;
- } while (0x007f < base) ;
+ comb[COMBBUFLEN-comblen] = result & 0xFFFF;
+ } while ((0x007f < base) && (comblen < MAXCOMBLEN));
}
if (*outlen < (comblen + 1) << 1) {
- errno = E2BIG;
- return (size_t)-1;
- }
+ errno = E2BIG;
+ return (size_t)-1;
+ }
*out = base;
- out++;
- *outlen -= 2;
+ out++;
+ *outlen -= 2;
while ( comblen > 0 ) {
- *out = comb[MAXCOMBLEN-comblen];
- out++;
- *outlen -= 2;
+ *out = comb[COMBBUFLEN-comblen];
+ out++;
+ *outlen -= 2;
comblen--;
- }
+ }
- i += 2;
- in++;
- }
-
- /* Is Canonical Ordering necessary here? */
+ i += 2;
+ in++;
+ }
*out = 0;
return o_len-*outlen;
}
+/*******************************************************************
+length of UTF-8 character and string
+********************************************************************/
+
size_t utf8_charlen ( char* utf8 )
{
- unsigned char *p;
+ unsigned char *p;
- p = (unsigned char*) utf8;
+ p = (unsigned char*) utf8;
if ( *p < 0x80 )
- return (1);
+ return (1);
else if ( *p > 0xC1 && *p < 0xe0 && *(p+1) > 0x7f && *(p+1) < 0xC0)
return (2);
else if ( *p == 0xe0 && *(p+1) > 0x9f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
size_t utf8_strlen_validate ( char * utf8 )
{
- size_t len;
- unsigned char *p;
+ size_t len;
+ unsigned char *p;
- p = (unsigned char*) utf8;
- len = 0;
+ p = (unsigned char*) utf8;
+ len = 0;
- /* see http://www.unicode.org/unicode/reports/tr27/ for an explanation */
+ /* see http://www.unicode.org/unicode/reports/tr27/ for an explanation */
- while ( *p != '\0')
- {
- if ( *p < 0x80 )
- p++;
+ while ( *p != '\0')
+ {
+ if ( *p < 0x80 )
+ p++;
- else if ( *p > 0xC1 && *p < 0xe0 && *(p+1) > 0x7f && *(p+1) < 0xC0)
- p += 2;
+ else if ( *p > 0xC1 && *p < 0xe0 && *(p+1) > 0x7f && *(p+1) < 0xC0)
+ p += 2;
- else if ( *p == 0xe0 && *(p+1) > 0x9f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
- p += 3;
+ else if ( *p == 0xe0 && *(p+1) > 0x9f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
+ p += 3;
- else if ( *p > 0xe0 && *p < 0xf0 && *(p+1) > 0x7f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
- p += 3;
+ else if ( *p > 0xe0 && *p < 0xf0 && *(p+1) > 0x7f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
+ p += 3;
- else if ( *p == 0xf0 && *(p+1) > 0x8f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
- p += 4;
+ else if ( *p == 0xf0 && *(p+1) > 0x8f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
+ p += 4;
- else if ( *p > 0xf0 && *p < 0xf4 && *(p+1) > 0x7f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
- p += 4;
+ else if ( *p > 0xf0 && *p < 0xf4 && *(p+1) > 0x7f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
+ p += 4;
- else if ( *p == 0xf4 && *(p+1) > 0x7f && *(p+1) < 0x90 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
- p += 4;
+ else if ( *p == 0xf4 && *(p+1) > 0x7f && *(p+1) < 0x90 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
+ p += 4;
- else
- return ((size_t) -1);
+ else
+ return ((size_t) -1);
- len++;
- }
+ len++;
+ }
- return (len);
+ return (len);
}
-
# Makefile.am for libatalk/util/
-SUBDIRS = . test
-
noinst_LTLIBRARIES = libutil.la
AM_CFLAGS = -I$(top_srcdir)/sys
atalk_addr.c \
bprint.c \
fault.c \
+ ftw.c \
getiface.c \
locking.c \
logger.c \
module.c \
+ queue.c \
server_child.c \
server_ipc.c \
server_lock.c \
#include "config.h"
#endif
-#ifdef DEBUG1
-
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
Something really nasty happened - panic !
********************************************************************/
-static void smb_panic(const char *why)
+void netatalk_panic(const char *why)
{
-#if 0
- char *cmd;
- int result;
-#endif
#ifdef HAVE_BACKTRACE_SYMBOLS
void *backtrace_stack[BACKTRACE_STACK_SIZE];
size_t backtrace_size;
char **backtrace_strings;
-#endif
-
-#ifdef DEVELOPER
- {
- extern char *global_clobber_region_function;
- extern unsigned int global_clobber_region_line;
-
- if (global_clobber_region_function) {
- DEBUG(0,("smb_panic: clobber_region() last called from [%s(%u)]",
- global_clobber_region_function,
- global_clobber_region_line));
- }
- }
-#endif
-#if 0
- cmd = lp_panic_action();
- if (cmd && *cmd) {
- DEBUG(0, ("smb_panic(): calling panic action [%s]\n", cmd));
- result = system(cmd);
-
- if (result == -1)
- DEBUG(0, ("smb_panic(): fork failed in panic action: %s\n",
- strerror(errno)));
- else
- DEBUG(0, ("smb_panic(): action returned status %d\n",
- WEXITSTATUS(result)));
- }
- DEBUG(0,("PANIC: %s\n", why));
-#endif
-
-#ifdef HAVE_BACKTRACE_SYMBOLS
/* get the backtrace (stack frames) */
backtrace_size = backtrace(backtrace_stack,BACKTRACE_STACK_SIZE);
backtrace_strings = backtrace_symbols(backtrace_stack, backtrace_size);
- LOG(log_error, logtype_default, "BACKTRACE: %d stack frames:\n", backtrace_size);
+ LOG(log_severe, logtype_default, "BACKTRACE: %d stack frames:", backtrace_size);
if (backtrace_strings) {
size_t i;
for (i = 0; i < backtrace_size; i++)
- LOG(log_error, logtype_default, " #%u %s", i, backtrace_strings[i]);
+ LOG(log_severe, logtype_default, " #%u %s", i, backtrace_strings[i]);
SAFE_FREE(backtrace_strings);
}
-
#endif
-
}
{
static int counter;
- if (counter) _exit(1);
+ if (counter)
+ abort();
counter++;
- LOG(log_error, logtype_default, "===============================================================");
- LOG(log_error, logtype_default, "INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),VERSION);
- LOG(log_error, logtype_default, "===============================================================");
+ LOG(log_severe, logtype_default, "===============================================================");
+ LOG(log_severe, logtype_default, "INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),VERSION);
+ LOG(log_severe, logtype_default, "===============================================================");
- smb_panic("internal error");
+ netatalk_panic("internal error");
if (cont_fn) {
cont_fn(NULL);
#endif
return; /* this should cause a core dump */
}
- exit(1);
+ abort();
}
/****************************************************************************
#endif
}
-#endif
--- /dev/null
+/* File tree walker functions.
+ Copyright (C) 1996-2004, 2006-2008, 2010 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if __GNUC__
+# define alloca __builtin_alloca
+#else
+# include <alloca.h>
+#endif
+
+#include <dirent.h>
+#define NAMLEN(dirent) strlen ((dirent)->d_name)
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <search.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#include <atalk/ftw.h>
+
+#define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
+
+#define NDEBUG 1
+#include <assert.h>
+
+#ifndef _LIBC
+# undef __chdir
+# define __chdir chdir
+# undef __closedir
+# define __closedir closedir
+# undef __fchdir
+# define __fchdir fchdir
+# undef __getcwd
+# define __getcwd(P, N) xgetcwd ()
+# undef __mempcpy
+# define __mempcpy mempcpy
+# undef __opendir
+# define __opendir opendir
+# undef __readdir64
+# define __readdir64 readdir
+# undef __tdestroy
+# define __tdestroy tdestroy
+# undef __tfind
+# define __tfind tfind
+# undef __tsearch
+# define __tsearch tsearch
+# undef internal_function
+# define internal_function /* empty */
+# undef dirent64
+# define dirent64 dirent
+# undef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef __set_errno
+# define __set_errno(Val) errno = (Val)
+#endif
+
+/* Support for the LFS API version. */
+#ifndef FTW_NAME
+# define FTW_NAME ftw
+# define NFTW_NAME nftw
+# define NFTW_OLD_NAME __old_nftw
+# define NFTW_NEW_NAME __new_nftw
+# define INO_T ino_t
+# define STAT stat
+# define LXSTAT(V,f,sb) lstat (f,sb)
+# define XSTAT(V,f,sb) stat (f,sb)
+# define FXSTATAT(V,d,f,sb,m) fstatat (d, f, sb, m)
+
+#endif
+
+/* We define PATH_MAX if the system does not provide a definition.
+ This does not artificially limit any operation. PATH_MAX is simply
+ used as a guesstimate for the expected maximal path length.
+ Buffers will be enlarged if necessary. */
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+struct dir_data
+{
+ DIR *stream;
+ int streamfd;
+ char *content;
+};
+
+struct known_object
+{
+ dev_t dev;
+ INO_T ino;
+};
+
+struct ftw_data
+{
+ /* Array with pointers to open directory streams. */
+ struct dir_data **dirstreams;
+ size_t actdir;
+ size_t maxdir;
+
+ /* Buffer containing name of currently processed object. */
+ char *dirbuf;
+ size_t dirbufsize;
+
+ /* Passed as fourth argument to `nftw' callback. The `base' member
+ tracks the content of the `dirbuf'. */
+ struct FTW ftw;
+
+ /* Flags passed to `nftw' function. 0 for `ftw'. */
+ int flags;
+
+ /* Conversion array for flag values. It is the identity mapping for
+ `nftw' calls, otherwise it maps the values to those known by
+ `ftw'. */
+ const int *cvt_arr;
+
+ /* Callback function. We always use the `nftw' form. */
+ NFTW_FUNC_T func;
+
+ /* Device of starting point. Needed for FTW_MOUNT. */
+ dev_t dev;
+
+ /* Data structure for keeping fingerprints of already processed
+ object. This is needed when not using FTW_PHYS. */
+ void *known_objects;
+};
+
+
+/* Internally we use the FTW_* constants used for `nftw'. When invoked
+ as `ftw', map each flag to the subset of values used by `ftw'. */
+static const int nftw_arr[] =
+{
+ FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
+};
+
+static const int ftw_arr[] =
+{
+ FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
+};
+
+
+static dir_notification_func_t upfunc;
+
+/* Forward declarations of local functions. */
+static int ftw_dir (struct ftw_data *data, struct STAT *st,
+ struct dir_data *old_dir) internal_function;
+
+typedef void (*__free_fn_t) (void *__nodep);
+typedef struct node_t {
+ const void *key;
+ struct node_t *left;
+ struct node_t *right;
+ unsigned int red:1;
+} *node;
+
+static void tdestroy_recurse (node root, __free_fn_t freefct)
+{
+ if (root->left != NULL)
+ tdestroy_recurse (root->left, freefct);
+ if (root->right != NULL)
+ tdestroy_recurse (root->right, freefct);
+ (*freefct) ((void *) root->key);
+ /* Free the node itself. */
+ free (root);
+}
+
+static void mytdestroy (void *vroot, __free_fn_t freefct)
+{
+ node root = (node) vroot;
+
+ if (root != NULL)
+ tdestroy_recurse (root, freefct);
+}
+
+static char *mystpcpy(char *a, const char *b)
+{
+ strcpy(a, b);
+ return (a + strlen(a));
+}
+
+static char *xgetcwd(void)
+{
+ char *cwd;
+ char *ret;
+ unsigned path_max;
+
+ errno = 0;
+ path_max = (unsigned) PATH_MAX;
+ path_max += 2; /* The getcwd docs say to do this. */
+
+ cwd = malloc (path_max);
+ errno = 0;
+ while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE) {
+ path_max += 512;
+ cwd = realloc (cwd, path_max);
+ errno = 0;
+ }
+
+ if (ret == NULL) {
+ int save_errno = errno;
+ free (cwd);
+ errno = save_errno;
+ return NULL;
+ }
+ return cwd;
+}
+
+static int
+object_compare (const void *p1, const void *p2)
+{
+ /* We don't need a sophisticated and useful comparison. We are only
+ interested in equality. However, we must be careful not to
+ accidentally compare `holes' in the structure. */
+ const struct known_object *kp1 = p1, *kp2 = p2;
+ int cmp1;
+ cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
+ if (cmp1 != 0)
+ return cmp1;
+ return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
+}
+
+
+static int
+add_object (struct ftw_data *data, struct STAT *st)
+{
+ struct known_object *newp = malloc (sizeof (struct known_object));
+ if (newp == NULL)
+ return -1;
+ newp->dev = st->st_dev;
+ newp->ino = st->st_ino;
+ return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
+}
+
+
+static inline int
+find_object (struct ftw_data *data, struct STAT *st)
+{
+ struct known_object obj;
+ obj.dev = st->st_dev;
+ obj.ino = st->st_ino;
+ return __tfind (&obj, &data->known_objects, object_compare) != NULL;
+}
+
+
+static inline int
+open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp)
+{
+ int result = 0;
+
+ if (data->dirstreams[data->actdir] != NULL)
+ {
+ /* Oh, oh. We must close this stream. Get all remaining
+ entries and store them as a list in the `content' member of
+ the `struct dir_data' variable. */
+ size_t bufsize = 1024;
+ char *buf = malloc (bufsize);
+
+ if (buf == NULL)
+ result = -1;
+ else
+ {
+ DIR *st = data->dirstreams[data->actdir]->stream;
+ struct dirent64 *d;
+ size_t actsize = 0;
+
+ while ((d = __readdir64 (st)) != NULL)
+ {
+ size_t this_len = NAMLEN (d);
+ if (actsize + this_len + 2 >= bufsize)
+ {
+ char *newp;
+ bufsize += MAX (1024, 2 * this_len);
+ newp = (char *) realloc (buf, bufsize);
+ if (newp == NULL)
+ {
+ /* No more memory. */
+ int save_err = errno;
+ free (buf);
+ __set_errno (save_err);
+ return -1;
+ }
+ buf = newp;
+ }
+
+ *((char *) __mempcpy (buf + actsize, d->d_name, this_len))
+ = '\0';
+ actsize += this_len + 1;
+ }
+
+ /* Terminate the list with an additional NUL byte. */
+ buf[actsize++] = '\0';
+
+ /* Shrink the buffer to what we actually need. */
+ data->dirstreams[data->actdir]->content = realloc (buf, actsize);
+ if (data->dirstreams[data->actdir]->content == NULL)
+ {
+ int save_err = errno;
+ free (buf);
+ __set_errno (save_err);
+ result = -1;
+ }
+ else
+ {
+ __closedir (st);
+ data->dirstreams[data->actdir]->stream = NULL;
+ data->dirstreams[data->actdir]->streamfd = -1;
+ data->dirstreams[data->actdir] = NULL;
+ }
+ }
+ }
+
+ /* Open the new stream. */
+ if (result == 0)
+ {
+ assert (data->dirstreams[data->actdir] == NULL);
+
+ if (dfdp != NULL && *dfdp != -1)
+ {
+ int fd = openat(*dfdp, data->dirbuf + data->ftw.base, O_RDONLY);
+ dirp->stream = NULL;
+ if (fd != -1 && (dirp->stream = fdopendir (fd)) == NULL)
+ close(fd);
+ }
+ else
+ {
+ const char *name;
+
+ if (data->flags & FTW_CHDIR)
+ {
+ name = data->dirbuf + data->ftw.base;
+ if (name[0] == '\0')
+ name = ".";
+ }
+ else
+ name = data->dirbuf;
+
+ dirp->stream = __opendir (name);
+ }
+
+ if (dirp->stream == NULL)
+ result = -1;
+ else
+ {
+ dirp->streamfd = dirfd (dirp->stream);
+ dirp->content = NULL;
+ data->dirstreams[data->actdir] = dirp;
+
+ if (++data->actdir == data->maxdir)
+ data->actdir = 0;
+ }
+ }
+
+ return result;
+}
+
+
+static int
+process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, size_t namlen)
+{
+ struct STAT st;
+ int result = 0;
+ int flag = 0;
+ size_t new_buflen;
+
+ if (name[0] == '.' && (name[1] == '\0'
+ || (name[1] == '.' && name[2] == '\0')))
+ /* Don't process the "." and ".." entries. */
+ return 0;
+
+ new_buflen = data->ftw.base + namlen + 2;
+ if (data->dirbufsize < new_buflen)
+ {
+ /* Enlarge the buffer. */
+ char *newp;
+
+ data->dirbufsize = 2 * new_buflen;
+ newp = (char *) realloc (data->dirbuf, data->dirbufsize);
+ if (newp == NULL)
+ return -1;
+ data->dirbuf = newp;
+ }
+
+ *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
+
+ int statres;
+ if (dir->streamfd != -1)
+ statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
+ (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0);
+ else
+ {
+ if ((data->flags & FTW_CHDIR) == 0)
+ name = data->dirbuf;
+
+ statres = ((data->flags & FTW_PHYS)
+ ? LXSTAT (_STAT_VER, name, &st)
+ : XSTAT (_STAT_VER, name, &st));
+ }
+
+ if (statres < 0)
+ {
+ if (errno != EACCES && errno != ENOENT)
+ result = -1;
+ else if (data->flags & FTW_PHYS)
+ flag = FTW_NS;
+ else
+ {
+ if (dir->streamfd != -1)
+ statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
+ AT_SYMLINK_NOFOLLOW);
+ else
+ statres = LXSTAT (_STAT_VER, name, &st);
+ if (statres == 0 && S_ISLNK (st.st_mode))
+ flag = FTW_SLN;
+ else
+ flag = FTW_NS;
+ }
+ }
+ else
+ {
+ if (S_ISDIR (st.st_mode))
+ flag = FTW_D;
+ else if (S_ISLNK (st.st_mode))
+ flag = FTW_SL;
+ else
+ flag = FTW_F;
+ }
+
+ if (result == 0
+ && (flag == FTW_NS
+ || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
+ {
+ if (flag == FTW_D)
+ {
+ if ((data->flags & FTW_PHYS)
+ || (!find_object (data, &st)
+ /* Remember the object. */
+ && (result = add_object (data, &st)) == 0))
+ result = ftw_dir (data, &st, dir);
+ }
+ else
+ result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
+ &data->ftw);
+ }
+
+ if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
+ result = 0;
+
+ return result;
+}
+
+
+static int
+ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir)
+{
+ struct dir_data dir;
+ struct dirent64 *d;
+ int previous_base = data->ftw.base;
+ int result;
+ char *startp;
+
+ /* Open the stream for this directory. This might require that
+ another stream has to be closed. */
+ result = open_dir_stream (old_dir == NULL ? NULL : &old_dir->streamfd,
+ data, &dir);
+ if (result != 0)
+ {
+ if (errno == EACCES)
+ /* We cannot read the directory. Signal this with a special flag. */
+ result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw);
+
+ return result;
+ }
+
+ /* First, report the directory (if not depth-first). */
+ if (!(data->flags & FTW_DEPTH))
+ {
+ result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw);
+ if (result != 0)
+ {
+ int save_err;
+ fail:
+ save_err = errno;
+ __closedir (dir.stream);
+ dir.streamfd = -1;
+ __set_errno (save_err);
+
+ if (data->actdir-- == 0)
+ data->actdir = data->maxdir - 1;
+ data->dirstreams[data->actdir] = NULL;
+ return result;
+ }
+ }
+
+ /* If necessary, change to this directory. */
+ if (data->flags & FTW_CHDIR)
+ {
+ if (__fchdir (dirfd (dir.stream)) < 0)
+ {
+ result = -1;
+ goto fail;
+ }
+ }
+
+ /* Next, update the `struct FTW' information. */
+ ++data->ftw.level;
+ startp = data->dirbuf + strlen(data->dirbuf);
+ /* There always must be a directory name. */
+ assert (startp != data->dirbuf);
+ if (startp[-1] != '/')
+ *startp++ = '/';
+ data->ftw.base = startp - data->dirbuf;
+
+ while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL)
+ {
+ result = process_entry (data, &dir, d->d_name, NAMLEN (d));
+ if (result != 0)
+ break;
+ }
+
+ if (dir.stream != NULL)
+ {
+ /* The stream is still open. I.e., we did not need more
+ descriptors. Simply close the stream now. */
+ int save_err = errno;
+
+ assert (dir.content == NULL);
+
+ __closedir (dir.stream);
+ dir.streamfd = -1;
+ __set_errno (save_err);
+
+ if (data->actdir-- == 0)
+ data->actdir = data->maxdir - 1;
+ data->dirstreams[data->actdir] = NULL;
+ }
+ else
+ {
+ int save_err;
+ char *runp = dir.content;
+
+ while (result == 0 && *runp != '\0')
+ {
+ char *endp = strchr (runp, '\0');
+
+ // XXX Should store the d_type values as well?!
+ result = process_entry (data, &dir, runp, endp - runp);
+
+ runp = endp + 1;
+ }
+
+ save_err = errno;
+ free (dir.content);
+ __set_errno (save_err);
+ }
+
+ if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
+ result = 0;
+
+ /* Prepare the return, revert the `struct FTW' information. */
+ data->dirbuf[data->ftw.base - 1] = '\0';
+ --data->ftw.level;
+ if (upfunc)
+ (*upfunc)();
+ data->ftw.base = previous_base;
+
+ /* Finally, if we process depth-first report the directory. */
+ if (result == 0 && (data->flags & FTW_DEPTH))
+ result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw);
+
+ if (old_dir
+ && (data->flags & FTW_CHDIR)
+ && (result == 0
+ || ((data->flags & FTW_ACTIONRETVAL)
+ && (result != -1 && result != FTW_STOP))))
+ {
+ /* Change back to the parent directory. */
+ int done = 0;
+ if (old_dir->stream != NULL)
+ if (__fchdir (dirfd (old_dir->stream)) == 0)
+ done = 1;
+
+ if (!done)
+ {
+ if (data->ftw.base == 1)
+ {
+ if (__chdir ("/") < 0)
+ result = -1;
+ }
+ else
+ if (__chdir ("..") < 0)
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+
+static int ftw_startup (const char *dir,
+ int is_nftw,
+ void *func,
+ dir_notification_func_t up,
+ int descriptors,
+ int flags)
+{
+ struct ftw_data data;
+ struct STAT st;
+ int result = 0;
+ int save_err;
+ int cwdfd = -1;
+ char *cwd = NULL;
+ char *cp;
+
+ upfunc = up;
+
+ /* First make sure the parameters are reasonable. */
+ if (dir[0] == '\0')
+ {
+ __set_errno (ENOENT);
+ return -1;
+ }
+
+ data.maxdir = descriptors < 1 ? 1 : descriptors;
+ data.actdir = 0;
+ data.dirstreams = (struct dir_data **) alloca (data.maxdir
+ * sizeof (struct dir_data *));
+ memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *));
+
+ /* PATH_MAX is always defined when we get here. */
+ data.dirbufsize = MAX (2 * strlen (dir), PATH_MAX);
+ data.dirbuf = (char *) malloc (data.dirbufsize);
+ if (data.dirbuf == NULL)
+ return -1;
+ cp = mystpcpy (data.dirbuf, dir);
+ /* Strip trailing slashes. */
+ while (cp > data.dirbuf + 1 && cp[-1] == '/')
+ --cp;
+ *cp = '\0';
+
+ data.ftw.level = 0;
+
+ /* Find basename. */
+ while (cp > data.dirbuf && cp[-1] != '/')
+ --cp;
+ data.ftw.base = cp - data.dirbuf;
+
+ data.flags = flags;
+
+ /* This assignment might seem to be strange but it is what we want.
+ The trick is that the first three arguments to the `ftw' and
+ `nftw' callback functions are equal. Therefore we can call in
+ every case the callback using the format of the `nftw' version
+ and get the correct result since the stack layout for a function
+ call in C allows this. */
+ data.func = (NFTW_FUNC_T) func;
+
+ /* Since we internally use the complete set of FTW_* values we need
+ to reduce the value range before calling a `ftw' callback. */
+ data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;
+
+ /* No object known so far. */
+ data.known_objects = NULL;
+
+ /* Now go to the directory containing the initial file/directory. */
+ if (flags & FTW_CHDIR)
+ {
+ /* We have to be able to go back to the current working
+ directory. The best way to do this is to use a file
+ descriptor. */
+ cwdfd = open (".", O_RDONLY);
+ if (cwdfd == -1)
+ {
+ /* Try getting the directory name. This can be needed if
+ the current directory is executable but not readable. */
+ if (errno == EACCES)
+ /* GNU extension ahead. */
+ cwd = __getcwd(NULL, 0);
+
+ if (cwd == NULL)
+ goto out_fail;
+ }
+ else if (data.maxdir > 1)
+ /* Account for the file descriptor we use here. */
+ --data.maxdir;
+
+ if (data.ftw.base > 0)
+ {
+ /* Change to the directory the file is in. In data.dirbuf
+ we have a writable copy of the file name. Just NUL
+ terminate it for now and change the directory. */
+ if (data.ftw.base == 1)
+ /* I.e., the file is in the root directory. */
+ result = __chdir ("/");
+ else
+ {
+ char ch = data.dirbuf[data.ftw.base - 1];
+ data.dirbuf[data.ftw.base - 1] = '\0';
+ result = __chdir (data.dirbuf);
+ data.dirbuf[data.ftw.base - 1] = ch;
+ }
+ }
+ }
+
+ /* Get stat info for start directory. */
+ if (result == 0)
+ {
+ const char *name;
+
+ if (data.flags & FTW_CHDIR)
+ {
+ name = data.dirbuf + data.ftw.base;
+ if (name[0] == '\0')
+ name = ".";
+ }
+ else
+ name = data.dirbuf;
+
+ if (((flags & FTW_PHYS)
+ ? LXSTAT (_STAT_VER, name, &st)
+ : XSTAT (_STAT_VER, name, &st)) < 0)
+ {
+ if (!(flags & FTW_PHYS)
+ && errno == ENOENT
+ && LXSTAT (_STAT_VER, name, &st) == 0
+ && S_ISLNK (st.st_mode))
+ result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN],
+ &data.ftw);
+ else
+ /* No need to call the callback since we cannot say anything
+ about the object. */
+ result = -1;
+ }
+ else
+ {
+ if (S_ISDIR (st.st_mode))
+ {
+ /* Remember the device of the initial directory in case
+ FTW_MOUNT is given. */
+ data.dev = st.st_dev;
+
+ /* We know this directory now. */
+ if (!(flags & FTW_PHYS))
+ result = add_object (&data, &st);
+
+ if (result == 0)
+ result = ftw_dir (&data, &st, NULL);
+ }
+ else
+ {
+ int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F;
+
+ result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag],
+ &data.ftw);
+ }
+ }
+
+ if ((flags & FTW_ACTIONRETVAL)
+ && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
+ result = 0;
+ }
+
+ /* Return to the start directory (if necessary). */
+ if (cwdfd != -1)
+ {
+ int save_err = errno;
+ __fchdir (cwdfd);
+ close(cwdfd);
+ __set_errno (save_err);
+ }
+ else if (cwd != NULL)
+ {
+ int save_err = errno;
+ __chdir (cwd);
+ free (cwd);
+ __set_errno (save_err);
+ }
+
+ /* Free all memory. */
+out_fail:
+ save_err = errno;
+ mytdestroy (data.known_objects, free);
+ free (data.dirbuf);
+ __set_errno (save_err);
+
+ return result;
+}
+
+
+
+/* Entry points. */
+int NFTW_NAME(const char *path,
+ NFTW_FUNC_T func,
+ dir_notification_func_t up,
+ int descriptors,
+ int flags)
+{
+ return ftw_startup (path, 1, func, up, descriptors, flags);
+}
+
--- /dev/null
+/*
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*!
+ * @file
+ * Netatalk utility functions: queue
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdlib.h>
+
+#include <atalk/queue.h>
+
+static qnode_t *alloc_init_node(void *data)
+{
+ qnode_t *node;
+ if ((node = malloc(sizeof(qnode_t))) == NULL)
+ return NULL;
+ node->data = data;
+
+ return node;
+}
+
+/********************************************************************************
+ * Interface
+ *******************************************************************************/
+
+q_t *queue_init(void)
+{
+ q_t *queue;
+
+ if ((queue = alloc_init_node(NULL)) == NULL)
+ return NULL;
+
+ queue->prev = queue->next = queue;
+ return queue;
+}
+
+/* Insert at tail */
+qnode_t *enqueue(q_t *q, void *data)
+{
+ qnode_t *node;
+
+ if ((node = alloc_init_node(data)) == NULL)
+ return NULL;
+
+ /* insert at tail */
+ node->next = q;
+ node->prev = q->prev;
+ q->prev->next = node;
+ q->prev = node;
+
+ return node;
+}
+
+/* Insert at head */
+qnode_t *prequeue(q_t *q, void *data)
+{
+ qnode_t *node;
+
+ if ((node = alloc_init_node(data)) == NULL)
+ return NULL;
+
+ /* insert at head */
+ q->next->prev = node;
+ node->next = q->next;
+ node->prev = q;
+ q->next = node;
+
+ return node;
+}
+
+/* Take from head */
+void *dequeue(q_t *q)
+{
+ qnode_t *node;
+ void *data;
+
+ if (q == NULL || q->next == q)
+ return NULL;
+
+ /* take first node from head */
+ node = q->next;
+ data = node->data;
+ q->next = node->next;
+ node->next->prev = node->prev;
+ free(node);
+
+ return data;
+}
+
+void queue_destroy(q_t *q, void (*callback)(void *))
+{
+ void *p;
+
+ while ((p = dequeue(q)) != NULL)
+ callback(p);
+
+ free(q);
+ q = NULL;
+}
+
/*
- * $Id: server_child.c,v 1.12 2010-01-21 14:14:49 didg Exp $
- *
* Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
* All rights reserved. See COPYRIGHT.
*
*
- * handle inserting, removing, and freeing of children.
+ * handle inserting, removing, and freeing of children.
* this does it via a hash table. it incurs some overhead over
* a linear append/remove in total removal and kills, but it makes
* single-entry removals a fast operation. as total removals occur during
- * child initialization and kills during server shutdown, this is
+ * child initialization and kills during server shutdown, this is
* probably a win for a lot of connections and unimportant for a small
* number of connections.
*/
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <signal.h>
-#include <atalk/logger.h>
+#include <errno.h>
/* POSIX.1 sys/wait.h check */
#include <sys/types.h>
#endif /* HAVE_SYS_WAIT_H */
#include <sys/time.h>
+#include <atalk/logger.h>
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/server_child.h>
+
#ifndef WEXITSTATUS
#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif /* ! WEXITSTATUS */
#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
#endif
#ifndef WIFSIGNALED
-#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
+#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
#endif
#ifndef WTERMSIG
#define WTERMSIG(status) ((status) & 0x7f)
#endif
-#include <atalk/server_child.h>
-
/* hash/child functions: hash OR's pid */
#define CHILD_HASHSIZE 32
#define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
-struct server_child_data {
- 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 */
- u_int32_t time; /* client boot time (from the mac client) */
- int killed; /* 1 if we already tried to kill the client */
-
- u_int32_t idlen; /* clientid len (from the Mac client) */
- char *clientid; /* clientid (from the Mac client) */
- struct server_child_data **prevp, *next;
-};
-
typedef struct server_child_fork {
- struct server_child_data *table[CHILD_HASHSIZE];
- void (*cleanup)(const pid_t);
+ struct server_child_data *table[CHILD_HASHSIZE];
+ void (*cleanup)(const pid_t);
} server_child_fork;
-
+int parent_or_child; /* 0: parent, 1: child */
+
static inline void hash_child(struct server_child_data **htable,
- struct server_child_data *child)
+ struct server_child_data *child)
{
- struct server_child_data **table;
+ struct server_child_data **table;
- table = &htable[HASH(child->pid)];
- if ((child->next = *table) != NULL)
- (*table)->prevp = &child->next;
- *table = child;
- child->prevp = table;
+ table = &htable[HASH(child->pid)];
+ if ((child->next = *table) != NULL)
+ (*table)->prevp = &child->next;
+ *table = child;
+ child->prevp = table;
}
static inline void unhash_child(struct server_child_data *child)
{
- if (child->prevp) {
- if (child->next)
- child->next->prevp = child->prevp;
- *(child->prevp) = child->next;
- }
+ if (child->prevp) {
+ if (child->next)
+ child->next->prevp = child->prevp;
+ *(child->prevp) = child->next;
+ }
}
-static inline struct server_child_data
-*resolve_child(struct server_child_data **table, const pid_t pid)
+static struct server_child_data *resolve_child(struct server_child_data **table, pid_t pid)
{
- struct server_child_data *child;
+ struct server_child_data *child;
- for (child = table[HASH(pid)]; child; child = child->next) {
- if (child->pid == pid)
- break;
- }
+ for (child = table[HASH(pid)]; child; child = child->next) {
+ if (child->pid == pid)
+ break;
+ }
- return child;
+ return child;
}
/* initialize server_child structure */
server_child *server_child_alloc(const int connections, const int nforks)
{
- server_child *children;
+ server_child *children;
- children = (server_child *) calloc(1, sizeof(server_child));
- if (!children)
- return NULL;
+ children = (server_child *) calloc(1, sizeof(server_child));
+ if (!children)
+ return NULL;
- children->nsessions = connections;
- children->nforks = nforks;
- children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
-
- if (!children->fork) {
- free(children);
- return NULL;
- }
+ children->nsessions = connections;
+ children->nforks = nforks;
+ children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
- return children;
+ if (!children->fork) {
+ free(children);
+ return NULL;
+ }
+
+ return children;
}
-/* add a child in. return 0 on success, -1 on serious error, and
- * > 0 for a non-serious error. */
-int server_child_add(server_child *children, const int forkid,
- const pid_t pid)
+/*!
+ * add a child
+ * @return pointer to struct server_child_data on success, NULL on error
+ */
+afp_child_t *server_child_add(server_child *children, int forkid, pid_t pid, uint ipc_fds[2])
{
- server_child_fork *fork;
- struct server_child_data *child;
- sigset_t sig, oldsig;
-
- /* we need to prevent deletions from occuring before we get a
- * chance to add the child in. */
- sigemptyset(&sig);
- sigaddset(&sig, SIGCHLD);
- sigprocmask(SIG_BLOCK, &sig, &oldsig);
-
- /* it's possible that the child could have already died before the
- * sigprocmask. we need to check for this. */
- if (kill(pid, 0) < 0) {
- sigprocmask(SIG_SETMASK, &oldsig, NULL);
- return 1;
- }
-
- fork = (server_child_fork *) children->fork + forkid;
-
- /* if we already have an entry. just return. */
- if (resolve_child(fork->table, pid)) {
- sigprocmask(SIG_SETMASK, &oldsig, NULL);
- return 0;
- }
-
- if ((child = (struct server_child_data *)
- calloc(1, sizeof(struct server_child_data))) == NULL) {
- sigprocmask(SIG_SETMASK, &oldsig, NULL);
- return -1;
- }
-
- child->pid = pid;
- child->valid = 0;
- child->killed = 0;
- hash_child(fork->table, child);
- children->count++;
- sigprocmask(SIG_SETMASK, &oldsig, NULL);
-
- return 0;
+ server_child_fork *fork;
+ afp_child_t *child = NULL;
+ sigset_t sig, oldsig;
+
+ /* we need to prevent deletions from occuring before we get a
+ * chance to add the child in. */
+ sigemptyset(&sig);
+ sigaddset(&sig, SIGCHLD);
+ pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
+
+ /* it's possible that the child could have already died before the
+ * pthread_sigmask. we need to check for this. */
+ if (kill(pid, 0) < 0)
+ goto exit;
+
+ fork = (server_child_fork *) children->fork + forkid;
+
+ /* if we already have an entry. just return. */
+ if (child = resolve_child(fork->table, pid))
+ goto exit;
+
+ if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
+ goto exit;
+
+ child->pid = pid;
+ child->valid = 0;
+ child->killed = 0;
+ child->ipc_fds[0] = ipc_fds[0];
+ child->ipc_fds[1] = ipc_fds[1];
+
+ hash_child(fork->table, child);
+ children->count++;
+
+exit:
+ pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+ return child;
}
/* remove a child and free it */
-int server_child_remove(server_child *children, const int forkid,
- const pid_t pid)
+int server_child_remove(server_child *children, const int forkid, pid_t pid)
{
- server_child_fork *fork;
- struct server_child_data *child;
-
- fork = (server_child_fork *) children->fork + forkid;
- if (!(child = resolve_child(fork->table, pid)))
- return 0;
-
- unhash_child(child);
- if (child->clientid) {
- free(child->clientid);
- }
- free(child);
- children->count--;
- return 1;
+ int fd;
+ server_child_fork *fork;
+ struct server_child_data *child;
+
+ fork = (server_child_fork *) children->fork + forkid;
+ if (!(child = resolve_child(fork->table, pid)))
+ return -1;
+
+ unhash_child(child);
+ if (child->clientid) {
+ free(child->clientid);
+ child->clientid = NULL;
+ }
+
+ /* In main:child_handler() we need the fd in order to remove it from the pollfd set */
+ fd = child->ipc_fds[0];
+ if (child->ipc_fds[0] != -1) {
+ close(child->ipc_fds[0]);
+ child->ipc_fds[0] = -1;
+ }
+ if (child->ipc_fds[1] != -1) {
+ close(child->ipc_fds[1]);
+ child->ipc_fds[1] = -1;
+ }
+
+ free(child);
+ children->count--;
+
+ if (fork->cleanup)
+ fork->cleanup(pid);
+
+ return fd;
}
/* free everything: by using a hash table, this increases the cost of
* this part over a linked list by the size of the hash table */
void server_child_free(server_child *children)
{
- server_child_fork *fork;
- struct server_child_data *child, *tmp;
- int i, j;
-
- for (i = 0; i < children->nforks; i++) {
- fork = (server_child_fork *) children->fork + i;
- for (j = 0; j < CHILD_HASHSIZE; j++) {
- child = fork->table[j]; /* start at the beginning */
- while (child) {
- tmp = child->next;
- if (child->clientid) {
- free(child->clientid);
+ server_child_fork *fork;
+ struct server_child_data *child, *tmp;
+ int i, j;
+
+ for (i = 0; i < children->nforks; i++) {
+ fork = (server_child_fork *) children->fork + i;
+ for (j = 0; j < CHILD_HASHSIZE; j++) {
+ child = fork->table[j]; /* start at the beginning */
+ while (child) {
+ tmp = child->next;
+ if (child->clientid) {
+ free(child->clientid);
+ }
+ free(child);
+ child = tmp;
+ }
}
- free(child);
- child = tmp;
- }
}
- }
- free(children->fork);
- free(children);
+ free(children->fork);
+ free(children);
}
-/* send kill to child processes: this also has an increased cost over
- * a plain-old linked list */
-void server_child_kill(server_child *children, const int forkid,
- const int sig)
+/* send signal to all child processes */
+void server_child_kill(server_child *children, int forkid, int sig)
{
- server_child_fork *fork;
- struct server_child_data *child, *tmp;
- int i;
-
- fork = (server_child_fork *) children->fork + forkid;
- for (i = 0; i < CHILD_HASHSIZE; i++) {
- child = fork->table[i];
- while (child) {
- tmp = child->next;
- kill(child->pid, sig);
- child = tmp;
+ server_child_fork *fork;
+ struct server_child_data *child, *tmp;
+ int i;
+
+ fork = (server_child_fork *) children->fork + forkid;
+ for (i = 0; i < CHILD_HASHSIZE; i++) {
+ child = fork->table[i];
+ while (child) {
+ tmp = child->next;
+ kill(child->pid, sig);
+ child = tmp;
+ }
}
- }
}
/* send kill to a child processes.
- * a plain-old linked list
+ * a plain-old linked list
* FIXME use resolve_child ?
*/
static int kill_child(struct server_child_data *child)
{
- if (!child->killed) {
- kill(child->pid, SIGTERM);
- /* we don't wait because there's no guarantee that we can really kill it */
- child->killed = 1;
- return 1;
- }
- else {
- LOG(log_info, logtype_default, "Already tried to kill (%d) before! Still there?", child->pid);
- }
- return 0;
+ if (!child->killed) {
+ kill(child->pid, SIGTERM);
+ /* we don't wait because there's no guarantee that we can really kill it */
+ child->killed = 1;
+ return 1;
+ } else {
+ LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->pid);
+ kill(child->pid, SIGKILL);
+ }
+ return 1;
}
-/* -------------------- */
-void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid)
+/*!
+ * Try to find an old session and pass socket
+ * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
+ */
+int server_child_transfer_session(server_child *children,
+ int forkid,
+ pid_t pid,
+ uid_t uid,
+ int afp_socket,
+ uint16_t DSI_requestID)
{
- server_child_fork *fork;
- struct server_child_data *child, *tmp;
- int i;
-
- fork = (server_child_fork *) children->fork + forkid;
- for (i = 0; i < CHILD_HASHSIZE; i++) {
- child = fork->table[i];
- while (child) {
- tmp = child->next;
- if (child->pid == pid) {
- if (!child->valid) {
- /* hmm, client 'guess' the pid, rogue? */
- LOG(log_info, logtype_default, "Disconnecting old session (%d) no token, bailout!", child->pid);
- }
- else if (child->uid != uid) {
- LOG(log_info, logtype_default, "Disconnecting old session (%d) not the same user, bailout!", child->pid);
- }
- else {
- kill_child(child);
- }
- }
- child = tmp;
+ EC_INIT;
+ server_child_fork *fork;
+ struct server_child_data *child;
+ int i;
+
+ 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);
+ return 0;
+ }
+
+ if (!child->valid) {
+ /* hmm, client 'guess' the pid, rogue? */
+ LOG(log_error, logtype_default, "Reconnect: invalidated child[%u]", pid);
+ return 0;
+ } else if (child->uid != uid) {
+ LOG(log_error, logtype_default, "Reconnect: child[%u] not the same user", pid);
+ return 0;
}
- }
+
+ LOG(log_note, logtype_default, "Reconnect: transfering session to child[%u]", pid);
+
+ if (writet(child->ipc_fds[0], &DSI_requestID, 2, 0, 2) != 2) {
+ LOG(log_error, logtype_default, "Reconnect: error sending DSI id to child[%u]", pid);
+ EC_STATUS(-1);
+ goto EC_CLEANUP;
+ }
+ EC_ZERO_LOG(send_fd(child->ipc_fds[0], afp_socket));
+ EC_ZERO_LOG(kill(pid, SIGURG));
+
+ EC_STATUS(1);
+
+EC_CLEANUP:
+ EC_EXIT;
}
/* see if there is a process for the same mac */
/* if the times don't match mac has been rebooted */
-void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid,
- const uid_t uid,
- const u_int32_t idlen, char *id, u_int32_t boottime)
+void server_child_kill_one_by_id(server_child *children, int forkid, pid_t pid,
+ uid_t uid, uint32_t idlen, char *id, uint32_t boottime)
{
- server_child_fork *fork;
- struct server_child_data *child, *tmp;
- int i;
-
- fork = (server_child_fork *) children->fork + forkid;
- for (i = 0; i < CHILD_HASHSIZE; i++) {
- child = fork->table[i];
- while (child) {
- tmp = child->next;
- if ( child->pid != pid) {
- if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) {
- if ( child->time != boottime ) {
- if (uid == child->uid) {
- if (kill_child(child)) {
- LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.", child->pid);
- }
- }
- else {
- LOG(log_info, logtype_default, "Disconnecting old session not the same uid, bailout!");
- }
- }
- else if (child->killed) {
- /* there's case where a Mac close a connection and restart a new one before
- * the first is 'waited' by the master afpd process
- */
- LOG(log_info, logtype_default,
- "WARNING: connection (%d) killed but still there.", child->pid);
- }
- else {
- LOG(log_info, logtype_default,
- "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.",
- child->pid, pid);
- }
-
- }
- }
- else
- {
- child->time = boottime;
- /* free old token if any */
- if (child->clientid) {
- free(child->clientid);
- }
- child->uid = uid;
- child->valid = 1;
- child->idlen = idlen;
- child->clientid = id;
- LOG(log_debug, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
- }
- child = tmp;
+ server_child_fork *fork;
+ struct server_child_data *child, *tmp;
+ int i;
+
+ fork = (server_child_fork *)children->fork + forkid;
+
+ for (i = 0; i < CHILD_HASHSIZE; i++) {
+ child = fork->table[i];
+ while (child) {
+ tmp = child->next;
+ if ( child->pid != pid) {
+ if (child->idlen == idlen && memcmp(child->clientid, id, idlen) == 0) {
+ if ( child->time != boottime ) {
+ /* Client rebooted */
+ if (uid == child->uid) {
+ kill_child(child);
+ LOG(log_warning, logtype_default,
+ "Terminated disconnected child[%u], client rebooted.",
+ child->pid);
+ } else {
+ LOG(log_warning, logtype_default,
+ "Session with different pid[%u]", child->pid);
+ }
+ } else {
+ kill_child(child);
+ LOG(log_note, logtype_default,
+ "Terminated disconnected session[%u]", child->pid);
+ }
+ }
+ } else {
+ /* update childs own slot */
+ child->time = boottime;
+ if (child->clientid)
+ free(child->clientid);
+ LOG(log_debug, logtype_default, "Setting client ID for %d", child->pid);
+ child->uid = uid;
+ child->valid = 1;
+ child->idlen = idlen;
+ child->clientid = id;
+ }
+ child = tmp;
+ }
}
- }
}
/* for extra cleanup if necessary */
void server_child_setup(server_child *children, const int forkid,
- void (*fcn)(const pid_t))
+ void (*fcn)(const pid_t))
{
- server_child_fork *fork;
+ server_child_fork *fork;
- fork = (server_child_fork *) children->fork + forkid;
- fork->cleanup = fcn;
+ fork = (server_child_fork *) children->fork + forkid;
+ fork->cleanup = fcn;
}
-/* keep track of children. */
-void server_child_handler(server_child *children)
-{
- int status, i;
- pid_t pid;
-
-#ifndef WAIT_ANY
-#define WAIT_ANY (-1)
-#endif /* ! WAIT_ANY */
-
- while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
- for (i = 0; i < children->nforks; i++) {
- if (server_child_remove(children, i, pid)) {
- server_child_fork *fork;
-
- fork = (server_child_fork *) children->fork + i;
- if (fork->cleanup)
- fork->cleanup(pid);
- break;
- }
- }
-
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status)) {
- LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid,
- WEXITSTATUS(status));
- } else {
- LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
- }
- } else {
- if (WIFSIGNALED(status))
- {
- LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,
- WTERMSIG (status));
- }
- else
- {
- LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);
- }
- }
- }
-}
-
-/* ---------------------------
+/* ---------------------------
* reset children signals
-*/
+ */
void server_reset_signal(void)
{
struct sigaction sv;
memset(&sv, 0, sizeof(sv));
sv.sa_handler = SIG_DFL;
sigemptyset( &sv.sa_mask );
-
+
sigaction(SIGALRM, &sv, NULL );
sigaction(SIGHUP, &sv, NULL );
sigaction(SIGTERM, &sv, NULL );
sigaction(SIGUSR1, &sv, NULL );
sigaction(SIGCHLD, &sv, NULL );
-
+
sigemptyset(&sigs);
sigaddset(&sigs, SIGALRM);
sigaddset(&sigs, SIGHUP);
sigaddset(&sigs, SIGUSR1);
sigaddset(&sigs, SIGCHLD);
- sigprocmask(SIG_UNBLOCK, &sigs, NULL);
-
+ pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
+
}
/*
- * $Id: server_ipc.c,v 1.4 2010-01-21 14:14:49 didg Exp $
- *
* All rights reserved. See COPYRIGHT.
*
- *
- * ipc between parent and children.
+ * IPC over socketpair between parent and children.
*/
#ifdef HAVE_CONFIG_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <signal.h>
#include <atalk/server_child.h>
#include <atalk/server_ipc.h>
#include <atalk/logger.h>
+#include <atalk/util.h>
typedef struct ipc_header {
- u_int16_t command;
- pid_t child_pid;
- uid_t uid;
- u_int32_t len;
- char *msg;
-} ipc_header;
-
-static int pipe_fd[2];
-
-void *server_ipc_create(void)
-{
- if (pipe(pipe_fd)) {
- return NULL;
- }
- return &pipe_fd;
-}
-
-/* ----------------- */
-int server_ipc_child(void *obj _U_)
-{
- /* close input */
- close(pipe_fd[0]);
- return pipe_fd[1];
-}
+ uint16_t command;
+ pid_t child_pid;
+ uid_t uid;
+ uint32_t len;
+ char *msg;
+ int afp_socket;
+ uint16_t DSI_requestID;
+} ipc_header_t;
+
+static char *ipc_cmd_str[] = { "IPC_DISCOLDSESSION",
+ "IPC_GETSESSION"};
-/* ----------------- */
-int server_ipc_parent(void *obj _U_)
-{
- return pipe_fd[0];
-}
-
-/* ----------------- */
-static int ipc_kill_token (struct ipc_header *ipc, server_child *children)
+/*
+ * Pass afp_socket to old disconnected session if one has a matching token (token = pid)
+ * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
+ */
+static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
{
pid_t pid;
/* assume signals SA_RESTART set */
memcpy (&pid, ipc->msg, sizeof(pid_t));
- LOG(log_info, logtype_default, "child %d user %d disconnected", pid, ipc->uid);
- server_child_kill_one(children, CHILD_DSIFORK, pid, ipc->uid);
- return 0;
+ return server_child_transfer_session(children,
+ CHILD_DSIFORK,
+ pid,
+ ipc->uid,
+ ipc->afp_socket,
+ ipc->DSI_requestID);
}
/* ----------------- */
-static int ipc_get_session (struct ipc_header *ipc, server_child *children)
+static int ipc_get_session(struct ipc_header *ipc, server_child *children)
{
u_int32_t boottime;
u_int32_t idlen;
char *clientid, *p;
+
+ if (ipc->len < (sizeof(idlen) + sizeof(boottime)) )
+ return -1;
- if (ipc->len < (sizeof(idlen) + sizeof(boottime)) ) {
- return -1;
- }
p = ipc->msg;
memcpy (&idlen, p, sizeof(idlen));
idlen = ntohl (idlen);
memcpy (&boottime, p, sizeof(boottime));
p += sizeof(boottime);
- if (ipc->len < idlen + sizeof(idlen) + sizeof(boottime)) {
- return -1;
- }
- if (NULL == (clientid = (char*) malloc(idlen)) ) {
- return -1;
- }
+ if (ipc->len < idlen + sizeof(idlen) + sizeof(boottime))
+ return -1;
+
+ if (NULL == (clientid = (char*) malloc(idlen)) )
+ return -1;
memcpy (clientid, p, idlen);
- server_child_kill_one_by_id (children, CHILD_DSIFORK, ipc->child_pid, ipc->uid, idlen, clientid, boottime);
- /* FIXME byte to ascii if we want to log clientid */
- LOG (log_debug, logtype_afpd, "ipc_get_session: len: %u, idlen %d, time %x", ipc->len, idlen, boottime);
+ LOG(log_debug, logtype_afpd, "ipc_get_session(pid: %u, uid: %u, time: 0x%08x)",
+ ipc->child_pid, ipc->uid, boottime);
+
+ server_child_kill_one_by_id(children,
+ CHILD_DSIFORK,
+ ipc->child_pid,
+ ipc->uid,
+ idlen,
+ clientid,
+ boottime);
+
return 0;
}
* uid
*
*/
-int server_ipc_read(server_child *children)
+
+/*!
+ * Read a IPC message from a child
+ *
+ * @args children (rw) pointer to our structure with all childs
+ * @args fd (r) IPC socket with child
+ *
+ * @returns number of bytes transfered, -1 on error, 0 on EOF
+ */
+int ipc_server_read(server_child *children, int fd)
{
int ret = 0;
struct ipc_header ipc;
char buf[IPC_MAXMSGSIZE], *p;
- if ((ret = read(pipe_fd[0], buf, IPC_HEADERLEN)) != IPC_HEADERLEN) {
- LOG (log_info, logtype_afpd, "Reading IPC header failed (%u of %u bytes read)", ret, IPC_HEADERLEN);
- return -1;
- }
+ if ((ret = read(fd, buf, IPC_HEADERLEN)) != IPC_HEADERLEN) {
+ LOG(log_error, logtype_afpd, "Reading IPC header failed (%i of %u bytes read): %s",
+ ret, IPC_HEADERLEN, strerror(errno));
+ return ret;
+ }
p = buf;
memcpy(&ipc.len, p, sizeof(ipc.len));
/* This should never happen */
- if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN))
- {
- LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len);
- return -1;
+ if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN)) {
+ LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len);
+ return -1;
}
memset (buf, 0, IPC_MAXMSGSIZE);
if ( ipc.len != 0) {
- if ((ret = read(pipe_fd[0], buf, ipc.len)) != (int) ipc.len) {
- LOG (log_info, logtype_afpd, "Reading IPC message failed (%u of %u bytes read)", ret, ipc.len);
- return -1;
+ if ((ret = read(fd, buf, ipc.len)) != (int) ipc.len) {
+ LOG(log_info, logtype_afpd, "Reading IPC message failed (%u of %u bytes read): %s",
+ ret, ipc.len, strerror(errno));
+ return ret;
}
}
ipc.msg = buf;
-
- LOG (log_debug, logtype_afpd, "ipc_read: command: %u, pid: %u, len: %u", ipc.command, ipc.child_pid, ipc.len);
- switch (ipc.command)
- {
- case IPC_KILLTOKEN:
- return (ipc_kill_token(&ipc, children));
- break;
+ LOG(log_debug, logtype_afpd, "ipc_server_read(%s): pid: %u",
+ ipc_cmd_str[ipc.command], ipc.child_pid);
+
+ int afp_socket;
+
+ switch (ipc.command) {
+
+ case IPC_DISCOLDSESSION:
+ if (readt(fd, &ipc.DSI_requestID, 2, 0, 2) != 2) {
+ LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): couldnt read DSI id: %s",
+ ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno));
+ }
+ if ((ipc.afp_socket = recv_fd(fd, 1)) == -1) {
+ LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): recv_fd: %s",
+ ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno));
+ return -1;
+ }
+ if (ipc_kill_token(&ipc, children) == 1) {
+ /* Transfered session (ie afp_socket) to old disconnected child, now kill the new one */
+ LOG(log_note, logtype_afpd, "Reconnect: killing new session child[%u] after transfer",
+ ipc.child_pid);
+ kill(ipc.child_pid, SIGTERM);
+ }
+ close(ipc.afp_socket);
+ break;
+
case IPC_GETSESSION:
- return (ipc_get_session(&ipc, children));
- break;
+ if (ipc_get_session(&ipc, children) != 0)
+ return -1;
+ break;
+
default:
LOG (log_info, logtype_afpd, "ipc_read: unknown command: %d", ipc.command);
return -1;
}
+ return ret;
}
/* ----------------- */
-int server_ipc_write( u_int16_t command, int len, void *msg)
+int ipc_child_write(int fd, uint16_t command, int len, void *msg)
{
char block[IPC_MAXMSGSIZE], *p;
pid_t pid;
memcpy(p, msg, len);
- LOG (log_debug, logtype_afpd, "ipc_write: command: %u, pid: %u, msglen: %u", command, pid, len);
- return write(pipe_fd[1], block, len+IPC_HEADERLEN );
+ LOG(log_debug, logtype_afpd, "ipc_child_write(%s)", ipc_cmd_str[command]);
+
+ return write(fd, block, len+IPC_HEADERLEN );
}
/*
- $Id: socket.c,v 1.6 2010-01-05 19:05:52 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include "config.h"
#endif /* HAVE_CONFIG_H */
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__
+#endif
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/time.h>
#include <time.h>
+#include <sys/ioctl.h>
#include <atalk/logger.h>
+#include <atalk/util.h>
static char ipv4mapprefix[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
return stored;
}
+/*!
+ * non-blocking drop-in replacement for read with timeout using select
+ *
+ * @param socket (r) socket, if in blocking mode, pass "setnonblocking" arg as 1
+ * @param data (rw) buffer for the read data
+ * @param lenght (r) how many bytes to read
+ * @param setnonblocking (r) when non-zero this func will enable and disable non blocking
+ * io mode for the socket
+ * @param timeout (r) number of seconds to try reading
+ *
+ * @returns number of bytes actually read or -1 on fatal error
+ */
+ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout)
+{
+ size_t stored = 0;
+ ssize_t len = 0;
+ struct timeval now, end, tv;
+ fd_set rfds;
+ int ret;
+
+ if (setnonblocking) {
+ if (setnonblock(socket, 1) != 0)
+ return -1;
+ }
+
+ /* Calculate end time */
+ (void)gettimeofday(&now, NULL);
+ end = now;
+ end.tv_sec += timeout;
+
+ while (stored < length) {
+ len = write(socket, (char *) data + stored, length - stored);
+ if (len == -1) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ case EAGAIN:
+ FD_ZERO(&rfds);
+ FD_SET(socket, &rfds);
+ tv.tv_usec = 0;
+ tv.tv_sec = timeout;
+
+ while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
+ switch (ret) {
+ case 0:
+ LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
+ goto exit;
+
+ default: /* -1 */
+ if (errno == 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);
+ goto exit;
+ }
+ if (now.tv_usec > end.tv_usec) {
+ tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
+ tv.tv_sec = end.tv_sec - now.tv_sec - 1;
+ } else {
+ tv.tv_usec = end.tv_usec - now.tv_usec;
+ tv.tv_sec = end.tv_sec - now.tv_sec;
+ }
+ FD_ZERO(&rfds);
+ FD_SET(socket, &rfds);
+ continue;
+ }
+ LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
+ stored = -1;
+ goto exit;
+ }
+ } /* while (select) */
+ continue;
+ } /* switch (errno) */
+ LOG(log_error, logtype_afpd, "read: %s", strerror(errno));
+ stored = -1;
+ goto exit;
+ } /* (len == -1) */
+ else if (len > 0)
+ stored += len;
+ else
+ break;
+ } /* while (stored < length) */
+
+exit:
+ if (setnonblocking) {
+ if (setnonblock(socket, 0) != 0)
+ return -1;
+ }
+
+ if (len == -1 && stored == 0)
+ /* last read or select got an error and we haven't got yet anything => return -1*/
+ return -1;
+ return stored;
+}
+
/*!
* @brief convert an IPv4 or IPv6 address to a static string using inet_ntop
*
* @param ai (rw) pointer to an struct sockaddr
* @parma mask (r) number of maskbits
*/
-void apply_ip_mask(struct sockaddr *sa, uint32_t mask)
+void apply_ip_mask(struct sockaddr *sa, int mask)
{
switch (sa->sa_family) {
return ret;
}
+
+#define POLL_FD_SET_STARTSIZE 512
+#define POLL_FD_SET_INCREASE 128
+/*!
+ * Add a fd to a dynamic pollfd array that is allocated and grown as needed
+ *
+ * This uses an additional array of struct polldata which stores type information
+ * (enum fdtype) and a pointer to anciliary user data.
+ *
+ * 1. Allocate the arrays with an intial size of [POLL_FD_SET_STARTSIZE] if
+ * *fdsetp is NULL.
+ * 2. Grow array as needed
+ * 3. Fill in both array elements and increase count of used elements
+ *
+ * @param fdsetp (rw) pointer to callers pointer to the pollfd array
+ * @param polldatap (rw) pointer to callers pointer to the polldata array
+ * @param fdset_usedp (rw) pointer to an int with the number of used elements
+ * @param fdset_sizep (rw) pointer to an int which stores the array sizes
+ * @param fd (r) file descriptor to add to the arrays
+ * @param fdtype (r) type of fd, currently IPC_FD or LISTEN_FD
+ * @param data (rw) pointer to data the caller want to associate with an fd
+ */
+void fdset_add_fd(struct pollfd **fdsetp,
+ struct polldata **polldatap,
+ int *fdset_usedp,
+ int *fdset_sizep,
+ int fd,
+ enum fdtype fdtype,
+ void *data)
+{
+ struct pollfd *fdset = *fdsetp;
+ struct polldata *polldata = *polldatap;
+ int fdset_size = *fdset_sizep;
+
+ LOG(log_debug, logtype_default, "fdset_add_fd: adding fd %i in slot %i", fd, *fdset_usedp);
+
+ if (fdset == NULL) { /* 1 */
+ /* Initialize with space for 512 fds */
+ fdset = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct pollfd));
+ if (! fdset)
+ exit(EXITERR_SYS);
+
+ polldata = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct polldata));
+ if (! polldata)
+ exit(EXITERR_SYS);
+
+ fdset_size = 512;
+ *fdset_sizep = fdset_size;
+ *fdsetp = fdset;
+ *polldatap = polldata;
+ }
+
+ if (*fdset_usedp >= fdset_size) { /* 2 */
+ fdset = realloc(fdset, sizeof(struct pollfd) * (fdset_size + POLL_FD_SET_INCREASE));
+ if (fdset == NULL)
+ exit(EXITERR_SYS);
+
+ polldata = realloc(polldata, sizeof(struct polldata) * (fdset_size + POLL_FD_SET_INCREASE));
+ if (polldata == NULL)
+ exit(EXITERR_SYS);
+
+ fdset_size += POLL_FD_SET_INCREASE;
+ *fdset_sizep = fdset_size;
+ *fdsetp = fdset;
+ *polldatap = polldata;
+ }
+
+ /* 3 */
+ fdset[*fdset_usedp].fd = fd;
+ fdset[*fdset_usedp].events = POLLIN;
+ polldata[*fdset_usedp].fdtype = fdtype;
+ polldata[*fdset_usedp].data = data;
+ (*fdset_usedp)++;
+}
+
+/*!
+ * Remove a fd from our pollfd array
+ *
+ * 1. Search fd
+ * 2. 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
+ *
+ * This currently doesn't shrink the allocated storage of the array.
+ *
+ * @param fdsetp (rw) pointer to callers pointer to the pollfd array
+ * @param polldatap (rw) pointer to callers pointer to the polldata array
+ * @param fdset_usedp (rw) pointer to an int with the number of used elements
+ * @param fdset_sizep (rw) pointer to an int which stores the array sizes
+ * @param fd (r) file descriptor to remove from the arrays
+ */
+void fdset_del_fd(struct pollfd **fdsetp,
+ struct polldata **polldatap,
+ int *fdset_usedp,
+ int *fdset_sizep _U_,
+ int fd)
+{
+ struct pollfd *fdset = *fdsetp;
+ struct polldata *polldata = *polldatap;
+
+ for (int i = 0; i < *fdset_usedp; i++) {
+ if (fdset[i].fd == fd) { /* 1 */
+ if (i < (*fdset_usedp - 1)) { /* 2 */
+ 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 */
+ }
+ (*fdset_usedp)--;
+ break;
+ }
+ }
+}
+
+/* Length of the space taken up by a padded control message of length len */
+#ifndef CMSG_SPACE
+#define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len))
+#endif
+
+/*
+ * Receive a fd on a suitable socket
+ * @args fd (r) PF_UNIX socket to receive on
+ * @args nonblocking (r) 0: fd is in blocking mode - 1: fd is nonblocking, poll for 1 sec
+ * @returns fd on success, -1 on error
+ */
+int recv_fd(int fd, int nonblocking)
+{
+ int ret;
+ struct msghdr msgh;
+ struct iovec iov[1];
+ struct cmsghdr *cmsgp = NULL;
+ char buf[CMSG_SPACE(sizeof(int))];
+ char dbuf[80];
+ struct pollfd pollfds[1];
+
+ pollfds[0].fd = fd;
+ pollfds[0].events = POLLIN;
+
+ memset(&msgh,0,sizeof(msgh));
+ memset(buf,0,sizeof(buf));
+
+ msgh.msg_name = NULL;
+ msgh.msg_namelen = 0;
+
+ msgh.msg_iov = iov;
+ msgh.msg_iovlen = 1;
+
+ iov[0].iov_base = dbuf;
+ iov[0].iov_len = sizeof(dbuf);
+
+ msgh.msg_control = buf;
+ msgh.msg_controllen = sizeof(buf);
+
+ if (nonblocking) {
+ do {
+ ret = poll(pollfds, 1, 2000); /* poll 2 seconds, evtl. multipe times (EINTR) */
+ } while ( ret == -1 && errno == EINTR );
+ if (ret != 1)
+ return -1;
+ ret = recvmsg(fd, &msgh, 0);
+ } else {
+ do {
+ ret = recvmsg(fd, &msgh, 0);
+ } while ( ret == -1 && errno == EINTR );
+ }
+
+ if ( ret == -1 ) {
+ return -1;
+ }
+
+ for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
+ if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
+ return *(int *) CMSG_DATA(cmsgp);
+ }
+ }
+
+ if ( ret == sizeof (int) )
+ errno = *(int *)dbuf; /* Rcvd errno */
+ else
+ errno = ENOENT; /* Default errno */
+
+ return -1;
+}
+
+/*
+ * Send a fd across a suitable socket
+ */
+int send_fd(int socket, int fd)
+{
+ int ret;
+ struct msghdr msgh;
+ struct iovec iov[1];
+ struct cmsghdr *cmsgp = NULL;
+ char *buf;
+ size_t size;
+ int er=0;
+
+ size = CMSG_SPACE(sizeof fd);
+ buf = malloc(size);
+ if (!buf) {
+ LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
+ return -1;
+ }
+
+ memset(&msgh,0,sizeof (msgh));
+ memset(buf,0, size);
+
+ msgh.msg_name = NULL;
+ msgh.msg_namelen = 0;
+
+ msgh.msg_iov = iov;
+ msgh.msg_iovlen = 1;
+
+ iov[0].iov_base = &er;
+ iov[0].iov_len = sizeof(er);
+
+ msgh.msg_control = buf;
+ msgh.msg_controllen = size;
+
+ cmsgp = CMSG_FIRSTHDR(&msgh);
+ cmsgp->cmsg_level = SOL_SOCKET;
+ cmsgp->cmsg_type = SCM_RIGHTS;
+ cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
+
+ *((int *)CMSG_DATA(cmsgp)) = fd;
+ msgh.msg_controllen = cmsgp->cmsg_len;
+
+ do {
+ ret = sendmsg(socket,&msgh, 0);
+ } while ( ret == -1 && errno == EINTR );
+ if (ret == -1) {
+ LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
+ free(buf);
+ return -1;
+ }
+ free(buf);
+ return 0;
+}
+++ /dev/null
-Makefile
-Makefile.in
-logger_test
-test.log
-.deps
-.libs
-
-.gitignore
-logger_test.o
+++ /dev/null
-bin_PROGRAMS = logger_test
-
-logger_test_SOURCES = logger_test.c
-logger_test_LDADD = $(top_builddir)/libatalk/util/libutil.la
+++ /dev/null
-#include <stdio.h>
-
-#include <atalk/boolean.h>
-#include <atalk/logger.h>
-
-int main(int argc, char *argv[])
-{
- set_processname("logger_Test");
-#if 0
- LOG(log_severe, logtype_logger, "Logging Test starting: this should only log to syslog");
-
- /* syslog testing */
- LOG(log_severe, logtype_logger, "Disabling syslog logging.");
- unsetuplog("Default");
- LOG(log_error, logtype_default, "This shouldn't log to syslog: LOG(log_error, logtype_default).");
- LOG(log_error, logtype_logger, "This shouldn't log to syslog: LOG(log_error, logtype_logger).");
- setuplog("Default LOG_INFO");
- LOG(log_info, logtype_logger, "Set syslog logging to 'log_info', so this should log again. LOG(log_info, logtype_logger).");
- LOG(log_error, logtype_logger, "This should log to syslog: LOG(log_error, logtype_logger).");
- LOG(log_error, logtype_default, "This should log to syslog. LOG(log_error, logtype_default).");
- LOG(log_debug, logtype_logger, "This shouldn't log to syslog. LOG(log_debug, logtype_logger).");
- LOG(log_debug, logtype_default, "This shouldn't log to syslog. LOG(log_debug, logtype_default).");
- LOG(log_severe, logtype_logger, "Disabling syslog logging.");
- unsetuplog("Default");
-#endif
- /* filelog testing */
-
- setuplog("DSI log_maxdebug test.log");
- LOG(log_info, logtype_dsi, "This should log.");
- LOG(log_error, logtype_default, "This should not log.");
-
- setuplog("Default log_debug test.log");
- LOG(log_debug, logtype_default, "This should log.");
- LOG(log_maxdebug, logtype_default, "This should not log.");
-
- LOG(log_maxdebug, logtype_dsi, "This should still log.");
-
- /* flooding prevention check */
- LOG(log_debug, logtype_default, "Flooding 3x");
- for (int i = 0; i < 3; i++) {
- LOG(log_debug, logtype_default, "Flooding...");
- }
- /* wipe the array */
- LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");
-
- LOG(log_debug, logtype_default, "-============");
- LOG(log_debug, logtype_default, "Flooding 5x");
- for (int i = 0; i < 5; i++) {
- LOG(log_debug, logtype_default, "Flooding...");
- }
- LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");
-
- LOG(log_debug, logtype_default, "o============");
- LOG(log_debug, logtype_default, "Flooding 2005x");
- for (int i = 0; i < 2005; i++) {
- LOG(log_debug, logtype_default, "Flooding...");
- }
- LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");
-
- LOG(log_debug, logtype_default, "0============");
- LOG(log_debug, logtype_default, "Flooding 11x1");
- for (int i = 0; i < 11; i++) {
- LOG(log_error, logtype_default, "flooding 11x1 1");
- }
-
- LOG(log_debug, logtype_default, "1============");
- LOG(log_debug, logtype_default, "Flooding 11x2");
- for (int i = 0; i < 11; i++) {
- LOG(log_error, logtype_default, "flooding 11x2 1");
- LOG(log_error, logtype_default, "flooding 11x2 2");
- }
-
- LOG(log_debug, logtype_default, "2============");
- LOG(log_debug, logtype_default, "Flooding 11x3");
- for (int i = 0; i < 11; i++) {
- LOG(log_error, logtype_default, "flooding 11x3 1");
- LOG(log_error, logtype_default, "flooding 11x3 2");
- LOG(log_error, logtype_default, "flooding 11x3 3");
- }
-
- LOG(log_debug, logtype_default, "3============");
- LOG(log_debug, logtype_default, "Flooding 11x4");
- for (int i = 0; i < 11; i++) {
- LOG(log_error, logtype_default, "flooding 11x4 1");
- LOG(log_error, logtype_default, "flooding 11x4 2");
- LOG(log_error, logtype_default, "flooding 11x4 3");
- LOG(log_error, logtype_default, "flooding 11x4 4");
- }
-
-
- return 0;
-}
-
-
/*
- $Id: unix.c,v 1.6 2010-02-28 22:29:16 didg Exp $
Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
#include <atalk/adouble.h>
#include <atalk/ea.h>
return strerror(errno);
}
+/*!
+ * Takes a buffer with a path, strips slashs, returns basename
+ *
+ * @param p (rw) path
+ * path may be
+ * "[/][dir/[...]]file"
+ * or
+ * "[/][dir/[...]]dir/[/]"
+ * Result is "file" or "dir"
+ *
+ * @returns pointer to basename in path buffer, buffer is possibly modified
+ */
+char *stripped_slashes_basename(char *p)
+{
+ int i = strlen(p) - 1;
+ while (i > 0 && p[i] == '/')
+ p[i--] = 0;
+ return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
+}
+
/*!
* @brief symlink safe chdir replacement
*
return 0;
}
+
+/*!
+ * Store n random bytes an buf
+ */
+void randombytes(void *buf, int n)
+{
+ char *p = (char *)buf;
+ int fd, i;
+ struct timeval tv;
+
+ if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
+ /* generate from /dev/urandom */
+ if (read(fd, buf, n) != n) {
+ close(fd);
+ fd = -1;
+ } else {
+ close(fd);
+ /* fd now != -1, so srandom wont be called below */
+ }
+ }
+
+ if (fd == -1) {
+ gettimeofday(&tv, NULL);
+ srandom((unsigned int)tv.tv_usec);
+ for (i=0 ; i < n ; i++)
+ p[i] = random() & 0xFF;
+ }
+
+ return;
+}
char abspath[MAXPATHLEN];
char *p;
- if (stat(path, &st) != 0) {
- return NULL;
- }
+ strlcpy(abspath, path, sizeof(abspath));
+
+ /* we might be called from `ad cp ...` with non existing target */
+ if (stat(abspath, &st) != 0) {
+ if (errno != ENOENT)
+ return NULL;
+
+ if (NULL == (p = strrchr(abspath, '/')) )
+ /* single component `ad cp SOURCEFILE TARGETFILE`, use "." instead */
+ strcpy(abspath, ".");
+ else
+ /* try without the last path element */
+ *p = '\0';
- strlcpy (abspath, path, sizeof(abspath));
+ if (stat(abspath, &st) != 0) {
+ return NULL;
+ }
+ }
- if (!S_ISDIR(st.st_mode)) {
- if (NULL == (p=strrchr(abspath, '/')) )
+ if (S_ISREG(st.st_mode)) {
+ /* single file copy SOURCE */
+ if (NULL == (p = strrchr(abspath, '/')) )
+ /* no path, use "." instead */
strcpy(abspath, ".");
else
+ /* try without the last path element */
*p = '\0';
}
}
break;
case CNIDDBDPORT:
- vol->v_dbd_port = atoi(value);
- break;
- case CNID_DBPATH:
- if ((vol->v_dbpath = strdup(value)) == NULL) {
+ if ((vol->v_dbd_port = strdup(value)) == NULL) {
fprintf (stderr, "strdup: %s", strerror(errno));
- return -1;
+ return -1;
}
break;
+ case CNID_DBPATH:
+ if ((vol->v_dbpath = malloc(MAXPATHLEN+1)) == NULL)
+ return -1;
+ strcpy(vol->v_dbpath, value);
+ break;
case ADOUBLE_VER:
if (strcasecmp(value, "v1") == 0) {
vol->v_adouble = AD_VERSION1;
char volinfofile[MAXPATHLEN];
char buf[MAXPATHLEN];
struct flock lock;
- int fd;
+ int fd, len;
FILE *fp;
if ( !path || !vol)
return -1;
if ((vol->v_path = strdup(volinfofile)) == NULL ) {
- fprintf (stderr, "strdup: %s", strerror(errno));
+ fprintf (stderr, "strdup: %s", strerror(errno));
return (-1);
}
+ /* Remove trailing slashes */
+ len = strlen(vol->v_path);
+ while (len && (vol->v_path[len-1] == '/')) {
+ vol->v_path[len-1] = 0;
+ len--;
+ }
+
strlcat(volinfofile, ".AppleDesktop/", sizeof(volinfofile));
strlcat(volinfofile, VOLINFOFILE, sizeof(volinfofile));
if ((vol->v_flags & AFPVOL_INV_DOTS))
vol->v_ad_options |= ADVOL_INVDOTS;
+ vol->retaincount = 1;
+
fclose(fp);
return 0;
}
+/*!
+ * Allocate a struct volinfo object for refcounting usage with retain and close, and
+ * call loadvolinfo with it
+ */
+struct volinfo *allocvolinfo(char *path)
+{
+ struct volinfo *p = malloc(sizeof(struct volinfo));
+ if (p == NULL)
+ return NULL;
+
+ if (loadvolinfo(path, p) == -1)
+ return NULL;
+
+ p->malloced = 1;
+
+ return p;
+}
+
+void retainvolinfo(struct volinfo *vol)
+{
+ vol->retaincount++;
+}
+
+/*!
+ * Decrement retain count, free resources when retaincount reaches 0
+ */
+int closevolinfo(struct volinfo *volinfo)
+{
+ if (volinfo->retaincount <= 0)
+ abort();
+
+ volinfo->retaincount--;
+
+ if (volinfo->retaincount == 0) {
+ free(volinfo->v_name); volinfo->v_name = NULL;
+ free(volinfo->v_path); volinfo->v_path = NULL;
+ free(volinfo->v_cnidscheme); volinfo->v_cnidscheme = NULL;
+ free(volinfo->v_dbpath); volinfo->v_dbpath = NULL;
+ free(volinfo->v_volcodepage); volinfo->v_volcodepage = NULL;
+ free(volinfo->v_maccodepage); volinfo->v_maccodepage = NULL;
+ free(volinfo->v_dbd_host); volinfo->v_dbd_host = NULL;
+ free(volinfo->v_dbd_port); volinfo->v_dbd_port = NULL;
+ if (volinfo->malloced) {
+ volinfo->malloced = 0;
+ free(volinfo);
+ }
+ }
+
+ return 0;
+}
+
/*
* Save the volume options to a file, used by shell utilities. Writing the file
* everytime a volume is opened is unnecessary, but it shouldn't hurt much.
-
-# Makefile.am for libatalk/adouble/
+# Makefile.am for libatalk/vfs/
noinst_LTLIBRARIES = libvfs.la
libvfs_la_SOURCES = vfs.c unix.c ea.c sys_ea.c ea_sys.c
-if USE_NFSv4_ACLS
+if HAVE_ACLS
libvfs_la_SOURCES += acl.c
endif
/*
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-#include <sys/acl.h>
#include <atalk/afp.h>
#include <atalk/util.h>
#include <atalk/logger.h>
+#include <atalk/errchk.h>
+#include <atalk/acl.h>
+
+#ifdef HAVE_SOLARIS_ACLS
/* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
-int remove_acl(const char *name)
+int remove_acl_vfs(const char *name)
{
int ret,i, ace_count, trivial_aces, new_aces_count;
ace_t *old_aces = NULL;
LOG(log_debug9, logtype_afpd, "remove_acl: END");
return ret;
}
+
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+/*!
+ * Remove any ACL_USER, ACL_GROUP or ACL_TYPE_DEFAULT ACEs from an object
+ *
+ * @param name (r) filesystem object name
+ *
+ * @returns AFP error code, AFP_OK (= 0) on success, AFPERR_MISC on error
+ */
+int remove_acl_vfs(const char *name)
+{
+ EC_INIT;
+
+ struct stat st;
+ acl_t acl = NULL;
+ acl_entry_t e;
+ acl_tag_t tag;
+ int entry_id = ACL_FIRST_ENTRY;
+
+
+ /* Remove default ACL if it's a dir */
+ EC_ZERO_LOG_ERR(stat(name, &st), AFPERR_MISC);
+ if (S_ISDIR(st.st_mode)) {
+ EC_NULL_LOG_ERR(acl = acl_init(0), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, acl), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(acl_free(acl), AFPERR_MISC);
+ acl = NULL;
+ }
+
+ /* Now get ACL and remove ACL_USER or ACL_GROUP entries, then re-set the ACL again */
+ EC_NULL_LOG_ERR(acl = acl_get_file(name, ACL_TYPE_ACCESS), AFPERR_MISC);
+ for ( ; acl_get_entry(acl, entry_id, &e) == 1; entry_id = ACL_NEXT_ENTRY) {
+ EC_ZERO_LOG_ERR(acl_get_tag_type(e, &tag), AFPERR_MISC);
+ if (tag == ACL_USER || tag == ACL_GROUP)
+ EC_ZERO_LOG_ERR(acl_delete_entry(acl, e), AFPERR_MISC);
+ }
+ EC_ZERO_LOG_ERR(acl_calc_mask(&acl), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(acl_valid(acl), AFPERR_MISC);
+ EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, acl), AFPERR_MISC);
+
+EC_CLEANUP:
+ if (acl) acl_free(acl);
+
+ EC_EXIT;
+}
+#endif /* HAVE_POSIX_ACLS */
/*
- * $Id: unix.c,v 1.11 2010-04-18 16:14:51 hat001 Exp $
- *
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
- *
*/
#ifdef HAVE_CONFIG_H
return -1;
}
- if ((dfd = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
+ if ((dfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) {
LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s",
src, dst, dst, strerror(errno));
ret = -1;
#include "config.h"
#endif /* HAVE_CONFIG_H */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
+#include <libgen.h>
#include <atalk/afp.h>
#include <atalk/adouble.h>
#include <atalk/vfs.h>
#include <atalk/directory.h>
#include <atalk/unix.h>
+#include <atalk/errchk.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
struct perm {
uid_t uid;
return 0;
}
-#ifdef HAVE_NFSv4_ACLS
+static int RF_copyfile_adouble(VFS_FUNC_ARGS_COPYFILE)
+/* const struct vol *vol, int sfd, const char *src, const char *dst */
+{
+ EC_INIT;
+ bstring s = NULL, d = NULL;
+ char *dup1 = NULL;
+ char *dup2 = NULL;
+ char *dup3 = NULL;
+ char *dup4 = NULL;
+ const char *name = NULL;
+ const char *dir = NULL;
+
+ struct stat st;
+ EC_ZERO(stat(dst, &st));
+
+ if (S_ISDIR(st.st_mode)) {
+ /* build src path to AppleDouble file*/
+ EC_NULL(s = bfromcstr(src));
+ EC_ZERO(bcatcstr(s, "/.AppleDouble/.Parent"));
+
+ /* build dst path to AppleDouble file*/
+ EC_NULL(d = bfromcstr(dst));
+ EC_ZERO(bcatcstr(d, "/.AppleDouble/.Parent"));
+ } else {
+ /* get basename */
+
+ /* build src path to AppleDouble file*/
+ EC_NULL(dup1 = strdup(src));
+ EC_NULL(name = basename(strdup(dup1)));
+
+ EC_NULL(dup2 = strdup(src));
+ EC_NULL(dir = dirname(dup2));
+ EC_NULL(s = bfromcstr(dir));
+ EC_ZERO(bcatcstr(s, "/.AppleDouble/"));
+ EC_ZERO(bcatcstr(s, name));
+
+ /* build dst path to AppleDouble file*/
+ EC_NULL(dup4 = strdup(dst));
+ EC_NULL(name = basename(strdup(dup4)));
+
+ EC_NULL(dup3 = strdup(dst));
+ EC_NULL(dir = dirname(dup3));
+ EC_NULL(d = bfromcstr(dir));
+ EC_ZERO(bcatcstr(d, "/.AppleDouble/"));
+ EC_ZERO(bcatcstr(d, name));
+ }
+
+ EC_ZERO(copy_file(sfd, cfrombstr(s), cfrombstr(d), 0666));
+
+EC_CLEANUP:
+ bdestroy(s);
+ bdestroy(d);
+ if (dup1) free(dup1);
+ if (dup2) free(dup2);
+ if (dup3) free(dup3);
+ if (dup4) free(dup4);
+
+ EC_EXIT;
+}
+
+#ifdef HAVE_SOLARIS_ACLS
static int RF_solaris_acl(VFS_FUNC_ARGS_ACL)
{
static char buf[ MAXPATHLEN + 1];
if (len < 0 || len >= MAXPATHLEN)
return AFPERR_MISC;
/* remove ACL from .AppleDouble/.Parent first */
- if ((ret = remove_acl(vol->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
+ if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
return ret;
/* now remove from .AppleDouble dir */
- if ((ret = remove_acl(buf)) != AFP_OK)
+ if ((ret = remove_acl_vfs(buf)) != AFP_OK)
return ret;
} else
/* remove ACL from ressource fork */
- if ((ret = remove_acl(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK)
+ if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK)
return ret;
return AFP_OK;
}
#endif
+#ifdef HAVE_POSIX_ACLS
+static int RF_posix_acl(VFS_FUNC_ARGS_ACL)
+{
+ EC_INIT;
+ static char buf[ MAXPATHLEN + 1];
+ struct stat st;
+ int len;
+
+ if (S_ISDIR(st.st_mode)) {
+ len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
+ if (len < 0 || len >= MAXPATHLEN)
+ EC_FAIL;
+ /* set acl on .AppleDouble dir first */
+ EC_ZERO_LOG(acl_set_file(buf, type, acl));
+
+ if (type == ACL_TYPE_ACCESS)
+ /* set ACL on ressource fork (".Parent") too */
+ EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_DIR), type, acl));
+ } else {
+ /* set ACL on ressource fork */
+ EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_HF), type, acl));
+ }
+
+EC_CLEANUP:
+ if (ret != 0)
+ return AFPERR_MISC;
+ return AFP_OK;
+}
+
+static int RF_posix_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
+{
+ EC_INIT;
+ static char buf[ MAXPATHLEN + 1];
+ int len;
+
+ if (dir) {
+ len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
+ if (len < 0 || len >= MAXPATHLEN)
+ return AFPERR_MISC;
+ /* remove ACL from .AppleDouble/.Parent first */
+ EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR)), AFPERR_MISC);
+
+ /* now remove from .AppleDouble dir */
+ EC_ZERO_LOG_ERR(remove_acl_vfs(buf), AFPERR_MISC);
+ } else {
+ /* remove ACL from ressource fork */
+ EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF)), AFPERR_MISC);
+ }
+
+EC_CLEANUP:
+ EC_EXIT;
+}
+#endif
+
/*********************************************************************************
* sfm adouble format
*********************************************************************************/
VFS_MFUNC(deletefile, VFS_FUNC_ARGS_DELETEFILE, VFS_FUNC_VARS_DELETEFILE)
VFS_MFUNC(renamefile, VFS_FUNC_ARGS_RENAMEFILE, VFS_FUNC_VARS_RENAMEFILE)
VFS_MFUNC(copyfile, VFS_FUNC_ARGS_COPYFILE, VFS_FUNC_VARS_COPYFILE)
+#ifdef HAVE_ACLS
VFS_MFUNC(acl, VFS_FUNC_ARGS_ACL, VFS_FUNC_VARS_ACL)
VFS_MFUNC(remove_acl, VFS_FUNC_ARGS_REMOVE_ACL, VFS_FUNC_VARS_REMOVE_ACL)
+#endif
VFS_MFUNC(ea_getsize, VFS_FUNC_ARGS_EA_GETSIZE, VFS_FUNC_VARS_EA_GETSIZE)
VFS_MFUNC(ea_getcontent, VFS_FUNC_ARGS_EA_GETCONTENT, VFS_FUNC_VARS_EA_GETCONTENT)
VFS_MFUNC(ea_list, VFS_FUNC_ARGS_EA_LIST, VFS_FUNC_VARS_EA_LIST)
vfs_deletefile,
vfs_renamefile,
vfs_copyfile,
+#ifdef HAVE_ACLS
vfs_acl,
vfs_remove_acl,
+#endif
vfs_ea_getsize,
vfs_ea_getcontent,
vfs_ea_list,
/* vfs_setdirowner: */ RF_setdirowner_adouble,
/* vfs_deletefile: */ RF_deletefile_adouble,
/* vfs_renamefile: */ RF_renamefile_adouble,
- /* vfs_copyfile: */ NULL,
+ /* vfs_copyfile: */ RF_copyfile_adouble,
NULL
};
/* vfs_deletefile: */ ea_deletefile,
/* vfs_renamefile: */ ea_renamefile,
/* vfs_copyfile */ ea_copyfile,
+#ifdef HAVE_ACLS
/* vfs_acl: */ NULL,
/* vfs_remove_acl */ NULL,
+#endif
/* vfs_getsize */ get_easize,
/* vfs_getcontent */ get_eacontent,
/* vfs_list */ list_eas,
/* rf_deletefile: */ NULL,
/* rf_renamefile: */ NULL,
/* vfs_copyfile: */ sys_ea_copyfile,
+#ifdef HAVE_ACLS
/* rf_acl: */ NULL,
/* rf_remove_acl */ NULL,
+#endif
/* ea_getsize */ sys_get_easize,
/* ea_getcontent */ sys_get_eacontent,
/* ea_list */ sys_list_eas,
* Tertiary VFS modules for ACLs
*/
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
static struct vfs_ops netatalk_solaris_acl_adouble = {
/* validupath: */ NULL,
/* rf_chown: */ NULL,
};
#endif
+#ifdef HAVE_POSIX_ACLS
+static struct vfs_ops netatalk_posix_acl_adouble = {
+ /* validupath: */ NULL,
+ /* rf_chown: */ NULL,
+ /* rf_renamedir: */ NULL,
+ /* rf_deletecurdir: */ NULL,
+ /* rf_setfilmode: */ NULL,
+ /* rf_setdirmode: */ NULL,
+ /* rf_setdirunixmode: */ NULL,
+ /* rf_setdirowner: */ NULL,
+ /* rf_deletefile: */ NULL,
+ /* rf_renamefile: */ NULL,
+ /* vfs_copyfile */ NULL,
+ /* rf_acl: */ RF_posix_acl,
+ /* rf_remove_acl */ RF_posix_remove_acl,
+ NULL
+};
+#endif
+
/* ---------------- */
void initvol_vfs(struct vol *vol)
{
}
/* ACLs */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
vol->vfs_modules[2] = &netatalk_solaris_acl_adouble;
#endif
+#ifdef HAVE_POSIX_ACLS
+ vol->vfs_modules[2] = &netatalk_posix_acl_adouble;
+#endif
+
}
break;
fi
- dnl -- Search for 64bit lib in "lib" too
- if test x"$atalk_libname" = x"lib64" ; then
+ dnl -- Search lib in "lib" too, as $atalk_libname might be set
+ dnl -- to "lib64" or "lib/64" which would not be found above
+ dnl -- if 64bit lib were installed in a dir named "lib"
+ if test x"$atalk_libname" != x"lib" ; then
bdblibdir="${bdbdir}/lib"
bdbbindir="${bdbdir}/bin"
AC_MSG_RESULT([ Large file support (>2GB) for AFP3: $wx_largefile])
fi
AC_MSG_RESULT([ Extended Attributes: $neta_cv_eas])
- AC_MSG_RESULT([ DDP enabled: $netatalk_cv_ddp_enabled])
AC_MSG_RESULT([ CNID:])
AC_MSG_RESULT([ backends: $compiled_backends])
AC_MSG_RESULT([ UAMS:])
AC_MSG_RESULT([ passwd ($uams_using_options)])
AC_MSG_RESULT([ guest])
AC_MSG_RESULT([ Options:])
- AC_MSG_RESULT([ CUPS support: $netatalk_cv_use_cups])
- AC_MSG_RESULT([ SLP support: $netatalk_cv_srvloc])
- AC_MSG_RESULT([ tcp wrapper support: $netatalk_cv_tcpwrap])
+ AC_MSG_RESULT([ DDP (AppleTalk) support: $netatalk_cv_ddp_enabled])
+ AC_MSG_RESULT([ CUPS support: $netatalk_cv_use_cups])
+ AC_MSG_RESULT([ SLP support: $netatalk_cv_srvloc])
+ AC_MSG_RESULT([ Zeroconf support: $netatalk_cv_zeroconf])
+ AC_MSG_RESULT([ tcp wrapper support: $netatalk_cv_tcpwrap])
dnl if test x"$netatalk_cv_linux_sendfile" != x; then
-dnl AC_MSG_RESULT([ Linux sendfile support: $netatalk_cv_linux_sendfile])
+dnl AC_MSG_RESULT([ Linux sendfile support: $netatalk_cv_linux_sendfile])
dnl fi
- AC_MSG_RESULT([ quota support: $netatalk_cv_quotasupport])
- AC_MSG_RESULT([ admin group support: $netatalk_cv_admin_group])
- AC_MSG_RESULT([ valid shell check: $netatalk_cv_use_shellcheck])
- AC_MSG_RESULT([ cracklib support: $netatalk_cv_with_cracklib])
- AC_MSG_RESULT([ dropbox kludge: $netatalk_cv_dropkludge])
- AC_MSG_RESULT([ force volume uid/gid: $netatalk_cv_force_uidgid])
- AC_MSG_RESULT([ Apple 2 boot support: $compile_a2boot])
- AC_MSG_RESULT([ ACL support: $neta_cv_nfsv4acl])
+ AC_MSG_RESULT([ quota support: $netatalk_cv_quotasupport])
+ AC_MSG_RESULT([ admin group support: $netatalk_cv_admin_group])
+ AC_MSG_RESULT([ valid shell check: $netatalk_cv_use_shellcheck])
+ AC_MSG_RESULT([ cracklib support: $netatalk_cv_with_cracklib])
+ AC_MSG_RESULT([ dropbox kludge: $netatalk_cv_dropkludge])
+ AC_MSG_RESULT([ force volume uid/gid: $netatalk_cv_force_uidgid])
+ AC_MSG_RESULT([ Apple 2 boot support: $compile_a2boot])
+ AC_MSG_RESULT([ ACL support: $with_acl_support])
if test x"$use_pam_so" = x"yes" -a x"$netatalk_cv_install_pam" = x"no"; then
AC_MSG_RESULT([])
AC_MSG_WARN([ PAM support was configured for your system, but the netatalk PAM configuration file])
--- /dev/null
+dnl Check for optional Zeroconf support
+
+AC_DEFUN([NETATALK_ZEROCONF], [
+ ZEROCONF_LIBS=""
+ ZEROCONF_CFLAGS=""
+ found_zeroconf=no
+ zeroconf_dir=""
+
+ AC_ARG_ENABLE(zeroconf,
+ [ --enable-zeroconf[[=DIR]] enable Zeroconf support [[auto]]],
+ [zeroconf=$enableval],
+ [zeroconf=try]
+ )
+
+ dnl make sure atalk_libname is defined beforehand
+ [[ -n "$atalk_libname" ]] || AC_MSG_ERROR([internal error, atalk_libname undefined])
+
+ if test "x$zeroconf" != "xno"; then
+ savedcppflags="$CPPFLAGS"
+ savedldflags="$LDFLAGS"
+
+ if test "x$zeroconf" = "xyes" -o "x$zeroconf" = "xtry"; then
+ zeroconf_dir="/usr"
+ else
+ zeroconf_dir="$zeroconf"
+ fi
+
+ # mDNS support using Avahi
+ AC_CHECK_HEADER(
+ avahi-client/client.h,
+ AC_CHECK_LIB(
+ avahi-client,
+ avahi_client_new,
+ AC_DEFINE(USE_ZEROCONF, 1, [Use DNS-SD registration]))
+ )
+
+ case "$ac_cv_lib_avahi_client_avahi_client_new" in
+ yes)
+ PKG_CHECK_MODULES(AVAHI, [ avahi-client >= 0.6 ])
+ PKG_CHECK_MODULES(AVAHI_TPOLL, [ avahi-client >= 0.6.4 ],
+ [AC_DEFINE(HAVE_AVAHI_THREADED_POLL, 1, [Uses Avahis threaded poll implementation])],
+ [AC_MSG_WARN(This Avahi implementation is not supporting threaded poll objects. Maybe this is not what you want.)])
+ ZEROCONF_LIBS="$AVAHI_LIBS"
+ ZEROCONF_CFLAGS="$AVAHI_CFLAGS"
+ AC_DEFINE(HAVE_AVAHI, 1, [Use Avahi/DNS-SD registration])
+ found_zeroconf=yes
+ ;;
+ esac
+
+ CPPFLAGS="$savedcppflags"
+ LDFLAGS="$savedldflags"
+ fi
+
+ netatalk_cv_zeroconf=no
+ AC_MSG_CHECKING([whether to enable Zerconf support])
+ if test "x$found_zeroconf" = "xyes"; then
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(USE_ZEROCONF, 1, [Define to enable Zeroconf support])
+ netatalk_cv_zeroconf=yes
+ else
+ AC_MSG_RESULT([no])
+ if test "x$zeroconf" != "xno" -a "x$zeroconf" != "xtry"; then
+ AC_MSG_ERROR([Zeroconf installation not found])
+ fi
+ fi
+
+ LIB_REMOVE_USR_LIB(ZEROCONF_LIBS)
+ CFLAGS_REMOVE_USR_INCLUDE(ZEROCONF_CFLAGS)
+ AC_SUBST(ZEROCONF_LIBS)
+ AC_SUBST(ZEROCONF_CFLAGS)
+])
-e s@:DEFAULT_CNID_SCHEME:@${DEFAULT_CNID_SCHEME}@ \
<$< >$@
-GENERATED_MANS = apple_cp.1 apple_mv.1 apple_rm.1 uniconv.1 asip-status.pl.1
-TEMPLATE_FILES = apple_cp.1.tmpl apple_mv.1.tmpl apple_rm.1.tmpl uniconv.1.tmpl asip-status.pl.1.tmpl
-NONGENERATED_MANS = achfile.1 \
- ad.1 \
- aecho.1 \
- afile.1 \
+GENERATED_MANS = uniconv.1 asip-status.pl.1
+TEMPLATE_FILES = uniconv.1.tmpl asip-status.pl.1.tmpl
+NONGENERATED_MANS = ad.1 \
afppasswd.1 \
apple_dump.1 \
dbd.1 \
- getzones.1 \
hqx2bin.1 \
macbinary.1 \
megatron.1 \
+ netatalk-config.1 \
+ single2bin.1 \
+ unbin.1 \
+ unhex.1 \
+ unsingle.1
+ATALK_MANS = aecho.1 \
+ getzones.1 \
nbp.1 \
nbplkup.1 \
nbprgstr.1 \
nbpunrgstr.1 \
- netatalk-config.1 \
pap.1 \
papstatus.1 \
- psorder.1 \
- single2bin.1 \
- unbin.1 \
- unhex.1 \
- unsingle.1
+ psorder.1
-man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
+if USE_APPLETALK
+NONGENERATED_MANS += $(ATALK_MANS)
+endif
+man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
CLEANFILES = $(GENERATED_MANS)
-
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
-
+EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS) $(ATALK_MANS)
+++ /dev/null
-'\" t
-.\" Title: achfile
-.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 26 Feb 1998
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
-.\" Language: English
-.\"
-.TH "ACHFILE" "1" "26 Feb 1998" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-achfile \- change type and/or creator of Apple Macintosh files (netatalk format)
-.SH "SYNOPSIS"
-.HP \w'\fBachfile\fR\fB\fR\ 'u
-\fBachfile\fR\fB\fR [\-t\ \fItype\fR] [\-c\ \fIcreator\fR] \fIfile\fR...
-.SH "DESCRIPTION"
-.PP
-\fBachfile\fR
-changes the Macintosh type and/or creator of the
-\fIfile\fR
-arguments which have a corresponding \&.AppleDouble file\&.
-.SH "OPTIONS"
-.PP
-\fB\-t\fR
-\fItype\fR
-change the type\&.
-.PP
-\fB\-c\fR
-\fIcreator\fR
-change the creator\&.
-.SH "DIAGNOSTICS"
-.PP
-returns exit status 0 if all files changed successfully
-.SH "SEE ALSO"
-.PP
-\fBafile\fR(1),
-\fBafpd\fR(8)
.\" Title: ad
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 01 Sep 2009
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
+.\" Date: 12 Oct 2010
+.\" Manual: Netatalk 2.2
+.\" Source: Netatalk 2.2
.\" Language: English
.\"
-.TH "AD" "1" "01 Sep 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "AD" "1" "12 Oct 2010" "Netatalk 2.2" "Netatalk 2.2"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
ad \- Netatalk compatible UNIX file utility suite\&.
.SH "SYNOPSIS"
.HP \w'\fBad\fR\ 'u
-\fBad\fR {ls} [\&.\&.\&.]
+\fBad\fR {ls\ |\ cp\ |\ mv\ |\ rm} [\&.\&.\&.]
.SH "DESCRIPTION"
.PP
\fBad\fR
\&.AppleDouble
directories and the CNID databases are updated as appropiate\&.
.SH "AVAILABLE COMMANDS"
+.HP \w'\fBad\ ls\fR\ 'u
+\fBad ls\fR [\-dRl\ [u]] {file|dir\ [\&.\&.\&.]}
.PP
-.HP \w'\fBad\ ls\fR\ 'u \fBad ls\fR [\-dRl\ [u]] {file|dir\ [\&.\&.\&.]}
+List files and directories\&.
+.HP \w'\fBad\ cp\fR\ 'u
+\fBad cp\fR [\-aipvf] {src_file} {dst_file}
+.HP \w'\fBad\ cp\ \-R\fR\ 'u
+\fBad cp \-R\fR [\-aipvf] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
+.PP
+Copy files and directories\&.
+.HP \w'\fBad\ mv\fR\ 'u
+\fBad mv\fR [\-finv] {src_file} {dst_file}
+.HP \w'\fBad\ mv\fR\ 'u
+\fBad mv\fR [\-finv] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
+.PP
+Move files and directories\&.
+.HP \w'\fBad\ rm\fR\ 'u
+\fBad rm\fR [\-Rv] {file|directory}
+.PP
+Remove files and directories\&.
+.SH "AD LS"
+.PP
+List files and directories\&. Options:
+.PP
+\-d
+.RS 4
+Directories are listed as plain files
+.RE
+.PP
+\-R
+.RS 4
+list subdirectories recursively
+.RE
+.PP
+\-l
+.RS 4
+Long output, list AFP info
+.RE
+.PP
+\-u
+.RS 4
+List UNIX info
+.RE
+.PP
+\fILong output description\fR
.sp
.if n \{\
.RS 4
.\}
.nf
-\-l Long Output [\-u: unix info]:
+<unixinfo> <FinderFlags> <AFP Attributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>
+
+FinderFlags (valid for (f)iles and/or (d)irectories):
- <unixinfo \&.\&.\&.> <FinderFlags> <AFPAttributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>
+ d = On Desktop (f/d)
+ e = Hidden extension (f/d)
+ m = Shared (can run multiple times) (f)
+ n = No INIT resources (f)
+ i = Inited (f/d)
+ c = Custom icon (f/d)
+ t = Stationery (f)
+ s = Name locked (f/d)
+ b = Bundle (f/d)
+ v = Invisible (f/d)
+ a = Alias file (f/d)
- FinderFlags (valid for (f)ile and/or (d)irectory):
- d = On Desktop (f/d)
- e = Hidden extension (f/d)
- m = Shared (can run multiple times) (f)
- n = No INIT resources (f)
- i = Inited (f/d)
- c = Custom icon (f/d)
- t = Stationery (f)
- s = Name locked (f/d)
- b = Bundle (f/d)
- v = Invisible (f/d)
- a = Alias file (f/d)
+AFP Attributes:
- AFPAttributes:
- y = System (f/d)
- w = No write (f)
- p = Needs backup (f/d)
- r = No rename (f/d)
- l = No delete (f/d)
- o = No copy (f)
+ y = System (f/d)
+ w = No write (f)
+ p = Needs backup (f/d)
+ r = No rename (f/d)
+ l = No delete (f/d)
+ o = No copy (f)
- Note: any letter appearing in uppercase means the flag is set but it\'s a directory for which the flag is not allowed\&.
+Note: any letter appearing in uppercase means the flag is set but it\'s a directory for which the flag is not allowed\&.
.fi
.if n \{\
.RE
.\}
+.SH "AD CP"
+.PP
+Copy files and directories\&.
+.PP
+In the first synopsis form, the cp utility copies the contents of the source_file to the target_file\&. In the second synopsis form, the contents of each named source_file is copied to the destination target_directory\&. The names of the files themselves are not changed\&. If cp detects an attempt to copy a file to itself, the copy will fail\&.
+.PP
+Netatalk AFP volumes are detected by means of their "\&.AppleDesktop" directory which is located in their volume root\&. When a copy targetting an AFP volume is detected, its CNID database daemon is connected and all copies will also go through the CNID database\&. AppleDouble files are also copied and created as needed when the target is an AFP volume\&.
+.PP
+Options:
+.PP
+\-a
+.RS 4
+Archive mode\&. Same as \-Rp\&.
+.RE
+.PP
+\-f
+.RS 4
+For each existing destination pathname, remove it and create a new file, without prompting for confirmation regardless of its permis\- sions\&. (The \-f option overrides any previous \-i or \-n options\&.)
+.RE
+.PP
+\-i
+.RS 4
+Cause cp to write a prompt to the standard error output before copying a file that would overwrite an existing file\&. If the response from the standard input begins with the character \'y\' or \'Y\', the file copy is attempted\&. (The \-i option overrides any pre\- vious \-f or \-n options\&.)
+.RE
+.PP
+\-n
+.RS 4
+Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
+.RE
+.PP
+\-p
+.RS 4
+Cause cp to preserve the following attributes of each source file in the copy: modification time, access time, file flags, file mode, user ID, and group ID, as allowed by permissions\&. If the user ID and group ID cannot be preserved, no error message is displayed and the exit value is not altered\&.
+.RE
+.PP
+\-R
+.RS 4
+If source_file designates a directory, cp copies the directory and the entire subtree connected at that point\&.If the source_file ends in a /, the contents of the directory are copied rather than the directory itself\&.
+.RE
+.PP
+\-v
+.RS 4
+Cause cp to be verbose, showing files as they are copied\&.
+.RE
+.PP
+\-x
+.RS 4
+File system mount points are not traversed\&.
+.RE
+.SH "AD MV"
+.PP
+Move files and directories\&.
+.PP
+Move files around within an AFP volume, updating the CNID database as needed\&. If either:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+source or destination is not an AFP volume
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+source AFP volume != destination AFP volume
+.RE
+.sp
+.RE
+the files are copied and removed from the source\&.
+.PP
+Options:
+.PP
+\-f
+.RS 4
+Do not prompt for confirmation before overwriting the destination path\&. (The \-f option overrides any previous \-i or \-n options\&.)
+.RE
+.PP
+\-i
+.RS 4
+Cause mv to write a prompt to standard error before moving a file that would overwrite an existing file\&. If the response from the standard input begins with the character `y\' or `Y\', the move is attempted\&. (The \-i option overrides any previous \-f or \-n options\&.)
+.RE
+.PP
+\-n
+.RS 4
+Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
+.RE
+.PP
+\-v
+.RS 4
+Cause mv to be verbose, showing files after they are moved\&.
+.RE
+.SH "AD RM"
+.PP
+Remove files and directories\&.
+.PP
+The rm utility attempts to remove the non\-directory type files specified on the command line\&. If the files and directories reside on an AFP volume, the corresponding CNIDs are deleted from the volumes database\&.
+.PP
+The options are as follows:
+.PP
+\-R
+.RS 4
+Attempt to remove the file hierarchy rooted in each file argument\&.
+.RE
+.PP
+\-v
+.RS 4
+Be verbose when deleting files, showing them as they are removed\&.
+.RE
.SH "REPORTING BUGS"
.PP
Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
+++ /dev/null
-'\" t
-.\" Title: apple_cp
-.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 22 Aug 2004
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
-.\" Language: English
-.\"
-.TH "APPLE_CP" "1" "22 Aug 2004" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-apple_cp \- Do an apple copy, copying file metadata and the resource fork as well
-.SH "SYNOPSIS"
-.PP
-\fB:BINDIR:/apple_cp\fR\fB\fR
-\fISOURCE DEST\fR
-\fB:BINDIR:/apple_cp\fR
-\fISOURCE\fR\&.\&.\&.
-\fIDIRECTORY\fR
-.SH "DESCRIPTION"
-.PP
-\fBapple_cp\fR
-is a perl script to copy SOURCE to DEST or multiple SOURCE(s) to DIRECTORY\&. It also copies the file specific metadata (including resource forks) to the \&.AppleDouble directory for DEST or DIRECTORY\&. If the \&.AppleDouble directory doesn\'t exist for DEST or DIRECTORY it will create it\&.
-.SH "EXAMPLES"
-.PP
-\fB:BINDIR:/apple_cp\fR
-test\&.text /target/directory/
-.PP
-\fB:BINDIR:/apple_cp\fR
-test\&.text /target/directory/test2\&.text
-.PP
-\fB:BINDIR:/apple_cp\fR
-test\&.text testing\&.text /target/directory/
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
-.SH "SEE ALSO"
-.PP
-\fBapple_mv\fR(1),
-\fBapple_rm\fR(1)\&.
+++ /dev/null
-'\" t
-.\" Title: apple_mv
-.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 22 Aug 2004
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
-.\" Language: English
-.\"
-.TH "APPLE_MV" "1" "22 Aug 2004" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-apple_mv \- Do an apple move, moving metadata and the resource fork as well
-.SH "SYNOPSIS"
-.PP
-\fB:BINDIR:/apple_mv\fR\fB\fR
-\fISOURCE DEST\fR
-\fB:BINDIR:/apple_mv\fR
-\fISOURCE\fR\&.\&.\&.
-\fIDIRECTORY\fR
-.SH "DESCRIPTION"
-.PP
-\fBapple_mv\fR
-is a perl script to move SOURCE to DEST or multiple SOURCE(s) to DIRECTORY\&. It also moves the file specific metadata (including resource forks) to the \&.AppleDouble directory for DEST or DIRECTORY\&. If the \&.AppleDouble directory doesn\'t exist for DEST or DIRECTORY it will create it\&.
-.SH "EXAMPLES"
-.PP
-\fB:BINDIR:/apple_mv\fR
-test\&.text /target/directory/
-.PP
-\fB:BINDIR:/apple_mv\fR
-test\&.text /target/directory/test2\&.text
-.PP
-\fB:BINDIR:/apple_mv\fR
-test\&.text testing\&.text /target/directory/
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
-.SH "SEE ALSO"
-.PP
-\fBapple_cp\fR(1),
-\fBapple_rm\fR(1)\&.
+++ /dev/null
-'\" t
-.\" Title: apple_rm
-.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 22 Aug 2004
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
-.\" Language: English
-.\"
-.TH "APPLE_RM" "1" "22 Aug 2004" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-apple_rm \- Do an apple remove, remove metadata and resource fork as well
-.SH "SYNOPSIS"
-.PP
-\fB:BINDIR:/apple_rm\fR\fB\fR
-\fIFILE\fR\&.\&.\&.
-.SH "DESCRIPTION"
-.PP
-\fBapple_rm\fR
-is a perl script that removes FILE(s) as well as the \&.AppleDouble metadata file(s) that corresponds to FILE(s)\&. These AppleDouble header files eventually also contain the resource fork if the files had one\&.
-\fBapple_rm\fR
-does not delete directories\&.
-.SH "EXAMPLES"
-.PP
-\fB:BINDIR:/apple_rm\fR
-test\&.text
-.PP
-\fB:BINDIR:/apple_rm\fR
-test\&.text testing\&.text
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
-.SH "SEE ALSO"
-.PP
-\fBapple_cp\fR(1),
-\fBapple_mv\fR(1)\&.
.\" Title: dbd
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 23 Dec 2009
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
+.\" Date: 12 Oct 2010
+.\" Manual: Netatalk 2.2
+.\" Source: Netatalk 2.2
.\" Language: English
.\"
-.TH "DBD" "1" "23 Dec 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "DBD" "1" "12 Oct 2010" "Netatalk 2.2" "Netatalk 2.2"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.RS 4
\fB\-c\fR
Don\'t check \&.AppleDouble stuff, only check orphaned\&.
-
\fB\-n\fR
Don\'t open CNID database, skip CNID checks, only traverse filesystem
.RE
# Makefile.am for man/man3
-man_MANS = atalk_aton.3 nbp_name.3
+ATALK_MANS = atalk_aton.3 nbp_name.3
-EXTRA_DIST = $(man_MANS)
+if USE_APPLETALK
+man_MANS = $(ATALK_MANS)
+endif
+
+EXTRA_DIST = $(ATALK_MANS)
# Makefile.am for man/man4/
-man_MANS = atalk.4
+ATALK_MANS = atalk.4
-EXTRA_DIST = $(man_MANS)
+if USE_APPLETALK
+man_MANS = $(ATALK_MANS)
+endif
+
+EXTRA_DIST = $(ATALK_MANS)
.\" Title: AppleVolumes.default
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 22 Apr 2010
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
+.\" Date: 07 Dec 2010
+.\" Manual: Netatalk 2.2
+.\" Source: Netatalk 2.2
.\" Language: English
.\"
-.TH "APPLEVOLUMES\&.DEFAU" "5" "22 Apr 2010" "Netatalk 2.1" "Netatalk 2.1"
+.TH "APPLEVOLUMES\&.DEFAU" "5" "07 Dec 2010" "Netatalk 2.2" "Netatalk 2.2"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.RS 4
This allows multiple options to be specified in a comma delimited format\&. The available options are:
.PP
-acls
+searchdb
.RS 4
-Enable ACLs on this volume\&. Requires a
-\fINFSv4 ACLs\fR
-compatible filesystem (e\&.g\&. ZFS) and an ACL API compatible to *Solaris\&. In other words: this requires Solaris, Opensolaris or a derived distribution\&.
+Use fast CNID database namesearch instead of slow recursive filesystem search\&. Relies on a consistent CNID database, ie Samba or local filesystem access lead to inaccurate or wrong results\&. Works only for "dbd" CNID db volumes\&.
.RE
.PP
tm
Allows certain users and groups to have read/write access to a share\&. This follows the allow option format\&.
.RE
.PP
-veto:\fI[vetoed names]\fR
+veto:\fI[vetoed name]\fR
.RS 4
-hide files and directories,where the path matches one of the \'/\' delimited vetoed names\&. The veto string must always be terminated with a \'/\', eg\&. "veto1/", "veto1/veto2/"\&.
+hide files and directories,where the path matches one of the \'/\' delimited vetoed names\&. Matches are partial, e\&.g\&. path is
+/abc/def/file
+and veto:/abc/ will hide the file\&.
.RE
.PP
volcharset:\fI[charset]\fR
-e "s@:COMPILED_BACKENDS:@${compiled_backends}@g" \
<$< >$@
-GENERATED_MANS = AppleVolumes.default.5 afpd.conf.5 \
- atalkd.conf.5 netatalk.conf.5 papd.conf.5 \
- afp_ldap.conf.5 afp_signature.conf.5
+GENERATED_MANS = AppleVolumes.default.5 \
+ afpd.conf.5 \
+ netatalk.conf.5 \
+ afp_ldap.conf.5 \
+ afp_signature.conf.5
-TEMPLATE_FILES = AppleVolumes.default.5.tmpl afpd.conf.5.tmpl \
- atalkd.conf.5.tmpl netatalk.conf.5.tmpl papd.conf.5.tmpl \
- afp_ldap.conf.5.tmpl afp_signature.conf.5.tmpl
+TEMPLATE_FILES = AppleVolumes.default.5.tmpl \
+ afpd.conf.5.tmpl \
+ netatalk.conf.5.tmpl \
+ afp_ldap.conf.5.tmpl \
+ afp_signature.conf.5.tmpl
+
+ATALK_MANS = atalkd.conf.5.tmpl papd.conf.5.tmpl
+
+if USE_APPLETALK
+GENERATED_MANS += atalkd.conf.5 papd.conf.5
+TEMPLATE_FILES += $(ATALK_MANS)
+endif
NONGENERATED_MANS = AppleVolumes.5 AppleVolumes.system.5
CLEANFILES = $(GENERATED_MANS)
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
+EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS) $(ATALK_MANS)
.\" Title: afpd.conf
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 23 December 2009
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
+.\" Date: 1 November 2010
+.\" Manual: Netatalk 2.2
+.\" Source: Netatalk 2.2
.\" Language: English
.\"
-.TH "AFPD\&.CONF" "5" "23 December 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "AFPD\&.CONF" "5" "1 November 2010" "Netatalk 2.2" "Netatalk 2.2"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.PP
\-cnidserver \fI[ipaddress:port]\fR
.RS 4
-Specifies the IP address and port of a cnid_metad server, required for CNID dbd backend\&. Defaults to localhost:4700\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
+Specifies the IP address and port of a cnid_metad server, required for CNID dbd backend\&. Defaults to localhost:4700\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.\-
+.RE
+.PP
+\-dircachesize\fI entries\fR
+.RS 4
+Maximum possible entries in the directory cache\&. The cache stores directories and files\&. It is used to cache the full path to directories and CNIDs which considerably speeds up directory enumeration\&.
+.sp
+Default size is 8192, maximum size is 131072\&. Given value is rounded up to nearest power of 2\&. Each entry takes about 100 bytes, which is not much, but remember that every afpd child process for every connected user has its cache\&.
.RE
.PP
\-guestname \fI[name]\fR
.\" Title: netatalk.conf
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 9 Jun 2009
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
+.\" Date: 26 Sep 2010
+.\" Manual: Netatalk 2.2
+.\" Source: Netatalk 2.2
.\" Language: English
.\"
-.TH "NETATALK\&.CONF" "5" "9 Jun 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "NETATALK\&.CONF" "5" "26 Sep 2010" "Netatalk 2.2" "Netatalk 2.2"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" -----------------------------------------------------------------
.SH "NAME"
netatalk.conf \- Configuration file used by netatalk(8) to determine its general configuration
+.SH "SYNOPSIS"
+.HP \w'\fB:ETCDIR:/netatalk\&.conf\fR\fB\fR\fB:DESTDIR:/etc/default/netatalk\fR\fB\fR\ 'u
+\fB:ETCDIR:/netatalk\&.conf\fR\fB\fR
+.br
+\fB:DESTDIR:/etc/default/netatalk\fR\fB\fR
.SH "DESCRIPTION"
.PP
\fB:ETCDIR:/netatalk\&.conf\fR
is the configuration file used by afpd to determine what portions of the file system will be shared via AFP, as well as their behaviors\&.
.PP
+If netatalk has been configured with \-\-enable\-debian, it is not
+\fB:ETCDIR:/netatalk\&.conf\fR
+but
+\fB:DESTDIR:/etc/default/netatalk\fR\&.
+.PP
Any line not prefixed with
\fB#\fR
is interpreted\&. The configuration lines are composed like:
<$< >$@
NONGENERATED_MANS = timelord.8
-GENERATED_MANS = afp_acls.8 afpd.8 atalkd.8 cnid_dbd.8 cnid_metad.8 papd.8 papstatus.8 psf.8
-TEMPLATE_FILES = afp_acls.8.tmpl afpd.8.tmpl atalkd.8.tmpl cnid_dbd.8.tmpl \
- cnid_metad.8.tmpl papd.8.tmpl papstatus.8.tmpl psf.8.tmpl
+GENERATED_MANS = afpd.8 cnid_dbd.8 cnid_metad.8
+TEMPLATE_FILES = afpd.8.tmpl cnid_dbd.8.tmpl cnid_metad.8.tmpl
+ATALK_MANS = atalkd.8.tmpl papd.8.tmpl papstatus.8.tmpl psf.8.tmpl
+
+if USE_APPLETALK
+GENERATED_MANS += atalkd.8 papd.8 papstatus.8 psf.8
+TEMPLATE_FILES += $(ATALK_MANS)
+endif
man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
CLEANFILES = $(GENERATED_MANS)
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
+EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS) $(ATALK_MANS)
+++ /dev/null
-'\" t
-.\" Title: afp_acls
-.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 20 Oct 2010
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
-.\" Language: English
-.\"
-.TH "AFP_ACLS" "8" "20 Oct 2010" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afp_acls \- Setup and Usage Howto for ACLs with Netatalk
-.SH "DESCRIPTION"
-.PP
-ACL support for AFP is implemented with NFSv4 ACLs\&. Few filesystems and fewer OSes support these\&. At the time of implementation its only provided with ZFS on Solaris, Opensolaris and derived distributions\&.
-.SH "CONFIGURATION"
-.PP
-In order to be able to support ACLs, the following things have to be configured:
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 1.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP " 1." 4.2
-.\}
-ZFS Volumes
-.sp
-You MUST configure one ACL parameter for any volume you want to use with Netatalk:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-aclinherit = passthrough
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-For an explanation of what this parameter means and how to apply it see, your hosts ZFS documentation (e\&.g\&. man zfs)\&.
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 2.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP " 2." 4.2
-.\}
-Authentication Domain
-.sp
-Your server and the clients must be part of a security association where identity data is coming from a common source\&. ACLs in Darwin are based on UUIDs and so is the ACL specification in AFP 3\&.2\&. Therefor your source of identity data has to provide an attribute for every user and group where a UUID is stored as a ASCII string\&.
-.sp
-In other words:
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-you need an Open Directory Server or an LDAP server where you store UUIDs in some attribute
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-your clients must be configured to use this server
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-your server should be configured to use this server via nsswitch and PAM\&.
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-configure Netatalk via afp_ldap\&.conf so that Netatalk is able to retrieve the UUID for users and groups via LDAP search queries
-.RE
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 3.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP " 3." 4.2
-.\}
-Netatalk Volumes
-.sp
-Finally you can add
-\fBoptions:acls\fR
-to your volume defintion to add ACL support\&. In case your volume basedir doesn\'t grant read permissions via mode (like:
-\fB0700 root:adm\fR) but only via ACLs, you MUST add the
-\fBnostat\fR
-option to the volume defintion\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafp_ldap.conf\fR(5),
-\fBAppleVolumes.default\fR(5)
.\" Title: cnid_dbd
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: 21 Mar 2009
-.\" Manual: Netatalk 2.1
-.\" Source: Netatalk 2.1
+.\" Date: 10 Dec 2010
+.\" Manual: Netatalk 2.2
+.\" Source: Netatalk 2.2
.\" Language: English
.\"
-.TH "CNID_DBD" "8" "21 Mar 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "CNID_DBD" "8" "10 Dec 2010" "Netatalk 2.2" "Netatalk 2.2"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
cnid_dbd \- implement access to CNID databases through a dedicated daemon process
.SH "SYNOPSIS"
.HP \w'\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR\ 'u
-\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR \fIdbdir\fR \fIctrlfd\fR \fIclntfd\fR \fIlogconfig_string\fR
+\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR \fIvolpath\fR \fIctrlfd\fR \fIclntfd\fR \fIlogconfig_string\fR
.SH "DESCRIPTION"
.PP
\fBcnid_dbd\fR
\fBcnid_dbd\fR
is never started via the command line or system startup scripts but only by the
\fBcnid_metad\fR
-daemon\&. There is at most one instance of
+daemon\&. There is one instance of
\fBcnid_dbd\fR
per netatalk volume\&.
.PP
backend with transactions will avoid corruption of the CNID database even if the system crashes unexpectedly\&.
.PP
\fBcnid_dbd\fR
-uses the same on\-disk database format as the
-\fBcdb\fR
-backend\&. It is therefore possible to switch between the two backends as necessary\&.
-.PP
-\fBcnid_dbd\fR
inherits the effective userid and groupid from
\fBcnid_metad\fR
on startup, which is normally caused by
\fBlogfile_autoremove\fR
option is specified in the
\fIdb_param\fR
-configuration file (see below)\&.
+configuration file (see below) with a value of 0 (default 1)\&.
.PP
Do not use
\fBcnid_dbd\fR
.RE
.SH "UPDATING"
.PP
-In order to update between Netatalk releases using different BerkeleyDB library versions, follow this steps:
+Note that the first version to appear
+\fIafter\fR
+Netatalk 2\&.1 ie Netatalk 2\&.1\&.1, will support BerkeleyDB updates on the fly without manual intervention\&. In other words Netatalk 2\&.1 does contain code to prepare the BerkeleyDB database for upgrades and to upgrade it in case it has been prepared before\&. That means it can\'t upgrade a 2\&.0\&.x version because that one didn\'t prepare the database\&.
+.PP
+In order to update between older Netatalk releases using different BerkeleyDB library versions, follow this steps:
.sp
.RS 4
.ie n \{\
.\}
Start the the new version of Netatalk
.RE
-.PP
-Note that the first version to appear
-\fIafter\fR
-Netatalk 2\&.1 ie Netatalk 2\&.1\&.1, will support BerkeleyDB updates on the fly without manual intervention\&. In other words Netatalk 2\&.1 does contain code to prepare the BerkeleyDB database for upgrades and to upgrade it in case it has been prepared before\&. That means it can\'t upgrade a 2\&.0\&.x version because that one didn\'t prepare the database\&.
.SH "SEE ALSO"
.PP
\fBcnid_metad\fR(8),
--- /dev/null
+Makefile
+Makefile.in
--- /dev/null
+SUBDIRS = afpd
--- /dev/null
+Makefile
+Makefile.in
+.deps
+.libs
+test
+test.conf
+test.default
\ No newline at end of file
--- /dev/null
+# Makefile.am for test/afpd/
+
+pkgconfdir = @PKGCONFDIR@
+
+TESTS = test.sh test
+
+check_PROGRAMS = test
+noinst_HEADERS = test.h subtests.h afpfunc_helpers.h
+EXTRA_DIST = test.sh
+CLEANFILES = test.default test.conf
+
+test_SOURCES = test.c subtests.c afpfunc_helpers.c \
+ $(top_builddir)/etc/afpd/afp_avahi.c \
+ $(top_builddir)/etc/afpd/afp_config.c \
+ $(top_builddir)/etc/afpd/afp_dsi.c \
+ $(top_builddir)/etc/afpd/afp_options.c \
+ $(top_builddir)/etc/afpd/afp_util.c \
+ $(top_builddir)/etc/afpd/afprun.c \
+ $(top_builddir)/etc/afpd/appl.c \
+ $(top_builddir)/etc/afpd/auth.c \
+ $(top_builddir)/etc/afpd/afp_zeroconf.c \
+ $(top_builddir)/etc/afpd/catsearch.c \
+ $(top_builddir)/etc/afpd/desktop.c \
+ $(top_builddir)/etc/afpd/dircache.c \
+ $(top_builddir)/etc/afpd/directory.c \
+ $(top_builddir)/etc/afpd/enumerate.c \
+ $(top_builddir)/etc/afpd/extattrs.c \
+ $(top_builddir)/etc/afpd/file.c \
+ $(top_builddir)/etc/afpd/filedir.c \
+ $(top_builddir)/etc/afpd/fork.c \
+ $(top_builddir)/etc/afpd/gettok.c \
+ $(top_builddir)/etc/afpd/hash.c \
+ $(top_builddir)/etc/afpd/mangle.c \
+ $(top_builddir)/etc/afpd/messages.c \
+ $(top_builddir)/etc/afpd/nfsquota.c \
+ $(top_builddir)/etc/afpd/ofork.c \
+ $(top_builddir)/etc/afpd/quota.c \
+ $(top_builddir)/etc/afpd/status.c \
+ $(top_builddir)/etc/afpd/switch.c \
+ $(top_builddir)/etc/afpd/uam.c \
+ $(top_builddir)/etc/afpd/unix.c \
+ $(top_builddir)/etc/afpd/volume.c
+
+if HAVE_ACLS
+test_SOURCES += $(top_builddir)/etc/afpd/acls.c
+endif
+
+test_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/etc/afpd \
+ @SLP_CFLAGS@ @ZEROCONF_CFLAGS@ \
+ -DAPPLCNAME \
+ -DSERVERTEXT=\"$(SERVERTEXT)/\" \
+ -D_PATH_AFPDDEFVOL=\"$(pkgconfdir)/AppleVolumes.default\" \
+ -D_PATH_AFPDSYSVOL=\"$(pkgconfdir)/AppleVolumes.system\" \
+ -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
+ -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
+ -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
+ -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\" \
+ -D_PATH_AFPDSIGCONF=\"$(pkgconfdir)/afp_signature.conf\" \
+ -D_PATH_AFPDUUIDCONF=\"$(pkgconfdir)/afp_voluuid.conf\"
+
+test_LDADD = $(top_builddir)/libatalk/cnid/libcnid.la \
+ $(top_builddir)/libatalk/libatalk.la \
+ @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@
+
+test_LDFLAGS = -export-dynamic
--- /dev/null
+/*
+ $Id: afpfunc_helpers.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "file.h"
+#include "filedir.h"
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+
+
+#define rbufsize 128000
+static char rbuf[rbufsize];
+static size_t rbuflen;
+
+#define ADD(a, b, c) (a) += (c); \
+ (b) += (c)
+
+#define PUSHBUF(p, val, size, len) \
+ memcpy((p), (val), (size)); \
+ (p) += (size); \
+ (len) += (size)
+
+#define PUSHVAL(p, type, val, len) \
+ { \
+ type type = val; \
+ memcpy(p, &type, sizeof(type)); \
+ (p) += sizeof(type); \
+ (len) += sizeof(type); \
+ }
+
+static int push_path(char **bufp, const char *name)
+{
+ int len = 0;
+ int slen = strlen(name);
+ char *p = *bufp;
+
+ PUSHVAL(p, uint8_t, 3, len); /* path type */
+ PUSHVAL(p, uint32_t, kTextEncodingUTF8, len); /* text encoding hint */
+ PUSHVAL(p, uint16_t, htons(slen), len);
+ if (slen) {
+ for (int i = 0; i < slen; i++) {
+ if (name[i] == '/')
+ p[i] = 0;
+ else
+ p[i] = name[i];
+ }
+ len += slen;
+ }
+
+ *bufp += len;
+ return len;
+}
+
+/***********************************************************************************
+ * Interface
+ ***********************************************************************************/
+
+char **cnamewrap(const char *name)
+{
+ static char buf[256];
+ static char *p = buf;
+ int len = 0;
+
+ PUSHVAL(p, uint8_t, 3, len); /* path type */
+ PUSHVAL(p, uint32_t, kTextEncodingUTF8, len); /* text encoding hint */
+ PUSHVAL(p, uint16_t, ntohs(strlen(name)), len);
+ strcpy(p, name);
+
+ p = buf;
+ return &p;
+}
+
+int getfiledirparms(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+ const int bufsize = 256;
+ char buf[bufsize];
+ char *p = buf;
+ int len = 0;
+
+ ADD(p, len , 2);
+
+ PUSHVAL(p, uint16_t, vid, len);
+ PUSHVAL(p, cnid_t, did, len);
+ PUSHVAL(p, uint16_t, htons(FILPBIT_FNUM | FILPBIT_PDINFO), len);
+ PUSHVAL(p, uint16_t, htons(DIRPBIT_DID | DIRPBIT_PDINFO), len);
+
+ len += push_path(&p, name);
+
+ return afp_getfildirparams(obj, buf, len, rbuf, &rbuflen);
+}
+
+int createdir(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+ const int bufsize = 256;
+ char buf[bufsize];
+ char *p = buf;
+ int len = 0;
+
+ ADD(p, len , 2);
+
+ PUSHVAL(p, uint16_t, vid, len);
+ PUSHVAL(p, cnid_t, did, len);
+ len += push_path(&p, name);
+
+ return afp_createdir(obj, buf, len, rbuf, &rbuflen);
+}
+
+int createfile(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+ const int bufsize = 256;
+ char buf[bufsize];
+ char *p = buf;
+ int len = 0;
+
+ PUSHVAL(p, uint16_t, htons(128), len); /* hard create */
+ PUSHVAL(p, uint16_t, vid, len);
+ PUSHVAL(p, cnid_t, did, len);
+ len += push_path(&p, name);
+
+ return afp_createfile(obj, buf, len, rbuf, &rbuflen);
+}
+
+int delete(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+ const int bufsize = 256;
+ char buf[bufsize];
+ char *p = buf;
+ int len = 0;
+
+ PUSHVAL(p, uint16_t, htons(128), len); /* hard create */
+ PUSHVAL(p, uint16_t, vid, len);
+ PUSHVAL(p, cnid_t, did, len);
+ len += push_path(&p, name);
+
+ return afp_delete(obj, buf, len, rbuf, &rbuflen);
+}
+
+int enumerate(AFPObj *obj, uint16_t vid, cnid_t did)
+{
+ const int bufsize = 256;
+ char buf[bufsize];
+ char *p = buf;
+ int len = 0;
+
+ ADD(p, len , 2);
+
+ PUSHVAL(p, uint16_t, vid, len);
+ PUSHVAL(p, cnid_t, did, len);
+ PUSHVAL(p, uint16_t, htons(FILPBIT_PDID | FILPBIT_FNUM | FILPBIT_PDINFO), len);
+ PUSHVAL(p, uint16_t, htons(DIRPBIT_PDID | DIRPBIT_DID | DIRPBIT_PDINFO), len);
+ PUSHVAL(p, uint16_t, htons(20), len); /* reqcount */
+ PUSHVAL(p, uint32_t, htonl(1), len); /* startindex */
+ PUSHVAL(p, uint32_t, htonl(rbufsize), len); /* max replysize */
+
+ len += push_path(&p, "");
+
+ return afp_enumerate_ext2(obj, buf, len, rbuf, &rbuflen);
+}
+
+uint16_t openvol(AFPObj *obj, const char *name)
+{
+ int ret;
+ uint16_t bitmap;
+ uint16_t vid;
+ const int bufsize = 32;
+ char buf[bufsize];
+ char *p = buf;
+ char len = strlen(name);
+
+ memset(p, 0, bufsize);
+ p += 2;
+
+ /* bitmap */
+ bitmap = htons(1<<VOLPBIT_VID);
+ memcpy(p, &bitmap, 2);
+ p += 2;
+
+ /* name */
+ *p = len;
+ p++;
+ memcpy(p, name, len);
+ p += len;
+
+ len += 2 + 2 + 1; /* (command+pad) + bitmap + len */
+ if (len & 1)
+ len++;
+
+ rbuflen = 0;
+ if ((ret = afp_openvol(obj, buf, len, rbuf, &rbuflen)) != AFP_OK)
+ return 0;
+
+ p = rbuf;
+ memcpy(&bitmap, p, 2);
+ p += 2;
+ bitmap = ntohs(bitmap);
+ if ( ! (bitmap & 1<<VOLPBIT_VID))
+ return 0;
+
+ memcpy(&vid, p, 2);
+ return vid;
+}
+
--- /dev/null
+/*
+ $Id: afpfunc_helpers.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef AFPFUNC_HELPERS
+#define AFPFUNC_HELPERS
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "file.h"
+#include "filedir.h"
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+
+extern char **cnamewrap(const char *name);
+
+extern int getfiledirparms(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int createdir(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int createfile(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int delete(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int enumerate(AFPObj *obj, uint16_t vid, cnid_t did);
+extern uint16_t openvol(AFPObj *obj, const char *name);
+
+#endif /* AFPFUNC_HELPERS */
--- /dev/null
+/*
+ $Id: subtests.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+
+static int reti; /* for the TEST_int macro */
+
+int test001_add_x_dirs(const struct vol *vol, cnid_t start, cnid_t end)
+{
+ struct dir *dir;
+ char dirname[20];
+ while (start++ < end) {
+ sprintf(dirname, "dir%04u", start);
+ dir = dir_new(dirname, dirname, vol, DIRDID_ROOT, htonl(start), bfromcstr(vol->v_path), 0);
+ if (dir == NULL)
+ return -1;
+ if (dircache_add(dir) != 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int test002_rem_x_dirs(const struct vol *vol, cnid_t start, cnid_t end)
+{
+ struct dir *dir;
+ while (start++ < end) {
+ if ((dir = dircache_search_by_did(vol, htonl(start))))
+ if (dir_remove(vol, dir) != 0)
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ $Id: subtests.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef SUBTESTS_H
+#define SUBTESTS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+extern int test001_add_x_dirs(const struct vol *vol, cnid_t start, cnid_t end);
+extern int test002_rem_x_dirs(const struct vol *vol, cnid_t start, cnid_t end);
+#endif /* SUBTESTS_H */
--- /dev/null
+/*
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "file.h"
+#include "filedir.h"
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+#include "afpfunc_helpers.h"
+
+/* Stuff from main.c which of cource can't be added as source to testbin */
+unsigned char nologin = 0;
+struct afp_options default_options;
+static AFPConfig *configs;
+
+/* Static variables */
+
+int main(int argc, char **argv)
+{
+ #define ARGNUM 7
+ char *args[ARGNUM] = {"test", "-F", "test.conf", "-f", "test.default", "-s" ,"test.system"};
+ int reti;
+ uint16_t vid;
+ struct vol *vol;
+ struct dir *retdir;
+ struct path *path;
+
+ /* initialize */
+ afp_version = 32;
+ printf("Initializing\n============\n");
+ TEST(setuplog("default log_note /dev/tty"));
+ TEST(afp_options_init(&default_options));
+ TEST_int(afp_options_parse( ARGNUM, args, &default_options), 1);
+ TEST_expr(configs = configinit(&default_options), configs != NULL);
+ TEST(cnid_init());
+ TEST(load_volumes(&configs->obj));
+ TEST_int(dircache_init(8192), 0);
+
+ printf("\n");
+
+ /* now run tests */
+ printf("Running tests\n=============\n");
+
+ TEST_expr(vid = openvol(&configs->obj, "test"), vid != 0);
+ TEST_expr(vol = getvolbyvid(vid), vol != NULL);
+
+ /* test directory.c stuff */
+ TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT_PARENT), retdir != NULL);
+ TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL);
+ TEST_expr(path = cname(vol, retdir, cnamewrap("Network Trash Folder")), path != NULL);
+
+ TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL);
+ TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT_PARENT, "test"), 0);
+ TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, ""), 0);
+
+ TEST_expr(reti = createdir(&configs->obj, vid, DIRDID_ROOT, "dir1"),
+ reti == 0 || reti == AFPERR_EXIST);
+
+ TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "dir1"), 0);
+/*
+ FIXME: this doesn't work although it should. "//" get translated to \000 \000 at means ".."
+ ie this should getfiledirparms for DIRDID_ROOT_PARENT -- at least afair!
+ TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "//"), 0);
+*/
+ TEST_int(createfile(&configs->obj, vid, DIRDID_ROOT, "dir1/file1"), 0);
+ TEST_int(delete(&configs->obj, vid, DIRDID_ROOT, "dir1/file1"), 0);
+ TEST_int(delete(&configs->obj, vid, DIRDID_ROOT, "dir1"), 0);
+
+ TEST_int(createfile(&configs->obj, vid, DIRDID_ROOT, "file1"), 0);
+ TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "file1"), 0);
+ TEST_int(delete(&configs->obj, vid, DIRDID_ROOT, "file1"), 0);
+
+
+ /* test enumerate.c stuff */
+ TEST_int(enumerate(&configs->obj, vid, DIRDID_ROOT), 0);
+}
--- /dev/null
+/*
+ $Id: test.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+ Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef TEST_H
+#define TEST_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+#include "subtests.h"
+
+static inline void alignok(int len)
+{
+ int i = 1;
+ if (len < 80)
+ i = 80 - len;
+ while (i--)
+ printf(" ");
+}
+
+#define TEST(a) \
+ printf("Testing: %s ... ", (#a) ); \
+ alignok(strlen(#a)); \
+ a; \
+ printf("[ok]\n");
+
+#define TEST_int(a, b) \
+ printf("Testing: %s ... ", (#a) ); \
+ alignok(strlen(#a)); \
+ if ((reti = (a)) != b) { \
+ printf("[error]\n"); \
+ exit(1); \
+ } else { printf("[ok]\n"); }
+
+#define TEST_expr(a, b) \
+ printf("Testing: %s ... ", (#a) ); \
+ alignok(strlen(#a)); \
+ a; \
+ if (b) { \
+ printf("[ok]\n"); \
+ } else { \
+ printf("[error]\n"); \
+ exit(1); \
+ }
+#endif /* TEST_H */
--- /dev/null
+#!/bin/sh
+if [ ! -d /tmp/AFPtestvolume ] ; then
+ mkdir -p /tmp/AFPtestvolume
+ if [ $? -ne 0 ] ; then
+ echo Error creating AFP test volume /tmp/AFPtestvolume
+ exit 1
+ fi
+fi
+
+if [ ! -f test.conf ] ; then
+ echo -n "Creating configuration template ... "
+ cat > test.conf <<EOF
+test -noddp -port 10548
+EOF
+ echo [ok]
+fi
+
+if [ ! -f test.default ] ; then
+ echo -n "Creating volume config template ... "
+ cat > test.default <<EOF
+/tmp/AFPtestvolume "test" ea:none
+EOF
+ echo [ok]
+fi