From: Frank Lahm Date: Wed, 12 Jan 2011 15:42:53 +0000 (+0100) Subject: Merge branch-2-1 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=commitdiff_plain;h=ff3b4646472add7902b0d36dd1a941ec1a54e999;hp=ec3fb8214e130311c2ac5481b9c61ba15fdc1ed8 Merge branch-2-1 --- diff --git a/Makefile.am b/Makefile.am index 44ac0453..12030231 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,9 +1,8 @@ # 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 diff --git a/NEWS b/NEWS index 2277b097..3a604367 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,63 @@ +Changes in 2.2beta1 +==================== + +* FIX: composition of Surrogate Pair + +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 ================ @@ -28,7 +88,7 @@ Changes in 2.1.4 * 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 @@ -80,6 +140,7 @@ Changes in 2.1-release 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". diff --git a/README b/README deleted file mode 100644 index 5d09f07d..00000000 --- a/README +++ /dev/null @@ -1,16 +0,0 @@ -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 diff --git a/TODO b/TODO deleted file mode 100644 index 2c9540c6..00000000 --- a/TODO +++ /dev/null @@ -1,84 +0,0 @@ -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 ): - 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 diff --git a/VERSION b/VERSION index b6545a27..ff8388c3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.6dev \ No newline at end of file +2.2beta1 \ No newline at end of file diff --git a/bin/Makefile.am b/bin/Makefile.am index d23a5e5e..423e7e84 100644 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -1,3 +1,7 @@ # 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 diff --git a/bin/ad/.gitignore b/bin/ad/.gitignore new file mode 100644 index 00000000..28432ec5 --- /dev/null +++ b/bin/ad/.gitignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +ad +.deps +.libs +*.o diff --git a/bin/ad/Makefile.am b/bin/ad/Makefile.am new file mode 100644 index 00000000..7e6fe922 --- /dev/null +++ b/bin/ad/Makefile.am @@ -0,0 +1,24 @@ +# 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 diff --git a/bin/ad/ad.c b/bin/ad/ad.c new file mode 100644 index 00000000..71a0f744 --- /dev/null +++ b/bin/ad/ad.c @@ -0,0 +1,66 @@ +/* + Copyright (c) 2009 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. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/bin/ad/ad.h b/bin/ad/ad.h new file mode 100644 index 00000000..1b484e67 --- /dev/null +++ b/bin/ad/ad.h @@ -0,0 +1,80 @@ +/* + Copyright (c) 2009 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. +*/ + +#ifndef AD_H +#define AD_H + +#define _XOPEN_SOURCE 600 + +#include +#include +#include + +#include +#include +#include + +#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 */ diff --git a/bin/ad/ad_cp.c b/bin/ad/ad_cp.c new file mode 100644 index 00000000..97b30610 --- /dev/null +++ b/bin/ad/ad_cp.c @@ -0,0 +1,1030 @@ +/* + * Copyright (c) 2010, Frank Lahm + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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] \n" + " ad cp [-R] [-aipvfx] \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); +} diff --git a/bin/ad/ad_find.c b/bin/ad/ad_find.c new file mode 100644 index 00000000..8501500c --- /dev/null +++ b/bin/ad/ad_find.c @@ -0,0 +1,177 @@ +/* + 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. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/bin/ad/ad_ls.c b/bin/ad/ad_ls.c new file mode 100644 index 00000000..63226a37 --- /dev/null +++ b/bin/ad/ad_ls.c @@ -0,0 +1,688 @@ +/* + Copyright (c) 2009 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. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#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" + " \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; +} diff --git a/bin/ad/ad_mv.c b/bin/ad/ad_mv.c new file mode 100644 index 00000000..342f2221 --- /dev/null +++ b/bin/ad/ad_mv.c @@ -0,0 +1,480 @@ +/* + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) +{ + ; +} diff --git a/bin/ad/ad_rm.c b/bin/ad/ad_rm.c new file mode 100644 index 00000000..b9ef2c3c --- /dev/null +++ b/bin/ad/ad_rm.c @@ -0,0 +1,322 @@ +/* + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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] [ ...]\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; +} diff --git a/bin/ad/ad_util.c b/bin/ad/ad_util.c new file mode 100644 index 00000000..5a9b0973 --- /dev/null +++ b/bin/ad/ad_util.c @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2009 Frank Lahm + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SOLARIS_ACLS +#include +#endif /* HAVE_SOLARIS_ACLS */ + +#ifdef HAVE_POSIX_ACLS +#include +#include +#endif /* HAVE_POSIX_ACLS */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} + diff --git a/bin/afile/Makefile.am b/bin/afile/Makefile.am deleted file mode 100644 index 376eab69..00000000 --- a/bin/afile/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -# 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 diff --git a/bin/afile/achfile.c b/bin/afile/achfile.c deleted file mode 100644 index 99133cc9..00000000 --- a/bin/afile/achfile.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * $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 -#include -#include - -#include -#include -#ifdef HAVE_FCNTL_H -#include -#endif /* HAVE_FCNTL_H */ -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ - -#include - -#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; -} diff --git a/bin/afile/afile.c b/bin/afile/afile.c deleted file mode 100644 index e70c8500..00000000 --- a/bin/afile/afile.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * $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 -#include -#include - -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ - -#include - -#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; -} diff --git a/bin/afile/common.c b/bin/afile/common.c deleted file mode 100644 index 6ec68a56..00000000 --- a/bin/afile/common.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * $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 -#include - -#include -#include -#ifdef HAVE_FCNTL_H -#include -#endif /* HAVE_FCNTL_H */ -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ - -#include -#include - -#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; -} diff --git a/bin/afile/common.h b/bin/afile/common.h deleted file mode 100644 index 83689097..00000000 --- a/bin/afile/common.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * $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 - - -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); diff --git a/bin/cnid/Makefile.am b/bin/cnid/Makefile.am index 3aa59e93..2ade6f5d 100644 --- a/bin/cnid/Makefile.am +++ b/bin/cnid/Makefile.am @@ -1,15 +1,7 @@ # 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 diff --git a/bin/cnid/ad.c b/bin/cnid/ad.c deleted file mode 100644 index 95faece7..00000000 --- a/bin/cnid/ad.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - $Id: ad.c,v 1.2 2009-10-13 22:55:36 didg Exp $ - - Copyright (c) 2009 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. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#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; -} diff --git a/bin/cnid/ad.h b/bin/cnid/ad.h deleted file mode 100644 index bb080856..00000000 --- a/bin/cnid/ad.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - $Id: ad.h,v 1.1 2009-09-01 14:28:07 franklahm Exp $ - - Copyright (c) 2009 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. -*/ - -#ifndef AD_H -#define AD_H - -#include - -#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 */ diff --git a/bin/cnid/ad_cp.c b/bin/cnid/ad_cp.c deleted file mode 100644 index 938ce1b4..00000000 --- a/bin/cnid/ad_cp.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - Copyright (c) 2009 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. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#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] \n" - "Usage: ad cp [-R [-L | -P]] [-pv] \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; - -} diff --git a/bin/cnid/ad_ls.c b/bin/cnid/ad_ls.c deleted file mode 100644 index cd6545e1..00000000 --- a/bin/cnid/ad_ls.c +++ /dev/null @@ -1,637 +0,0 @@ -/* - $Id: ad_ls.c,v 1.4 2009-10-14 01:38:28 didg Exp $ - - Copyright (c) 2009 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. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#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" - " \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; -} diff --git a/bin/cnid/ad_util.c b/bin/cnid/ad_util.c deleted file mode 100644 index 339a72b4..00000000 --- a/bin/cnid/ad_util.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - $Id: ad_util.c,v 1.2 2009-10-14 02:30:42 didg Exp $ - - Copyright (c) 2009 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. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#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 -} diff --git a/bin/misc/.gitignore b/bin/misc/.gitignore index 15f4cd1a..2bff10ed 100644 --- a/bin/misc/.gitignore +++ b/bin/misc/.gitignore @@ -1,9 +1,10 @@ Makefile Makefile.in netacnv +afpldaptest +logger_test .deps .libs *.o -afpldaptest -.gitignore -netacnv.o + + diff --git a/bin/misc/Makefile.am b/bin/misc/Makefile.am index 9b55ee36..04a53e23 100644 --- a/bin/misc/Makefile.am +++ b/bin/misc/Makefile.am @@ -1,16 +1,18 @@ # 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 diff --git a/bin/misc/logger_test.c b/bin/misc/logger_test.c new file mode 100644 index 00000000..a968cce3 --- /dev/null +++ b/bin/misc/logger_test.c @@ -0,0 +1,94 @@ +#include + +#include +#include + +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; +} + + diff --git a/bin/misc/uuidtest.c b/bin/misc/uuidtest.c index 77a7fa79..b12585ee 100644 --- a/bin/misc/uuidtest.c +++ b/bin/misc/uuidtest.c @@ -1,5 +1,4 @@ /* - $Id: uuidtest.c,v 1.3 2009-11-28 12:27:24 franklahm Exp $ Copyright (c) 2008,2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -17,14 +16,16 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#ifdef HAVE_NFSv4_ACLS - #include #include #include #include #include + +#ifdef HAVE_LDAP +#define LDAP_DEPRECATED 1 #include +#endif #include #include @@ -42,6 +43,7 @@ static void parse_ldapconf() 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); @@ -57,21 +59,22 @@ static void parse_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) { @@ -92,9 +95,7 @@ int main( int argc, char **argv) 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); } @@ -107,9 +108,7 @@ int main( int argc, char **argv) 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); } @@ -123,10 +122,17 @@ int main( int argc, char **argv) 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); @@ -144,4 +150,3 @@ int main( int argc, char **argv) return 0; } -#endif /* HAVE_NFSv4_ACLS */ diff --git a/config/AppleVolumes.default.tmpl b/config/AppleVolumes.default.tmpl index d4453a70..945672cc 100644 --- a/config/AppleVolumes.default.tmpl +++ b/config/AppleVolumes.default.tmpl @@ -160,10 +160,6 @@ # 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`! diff --git a/config/Makefile.am b/config/Makefile.am index 1c3699a5..4817238a 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -3,22 +3,33 @@ 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@ @@ -64,6 +75,15 @@ install-config-files: $(CONFFILES) $(GENFILES) echo "not overwriting $$f"; \ fi; \ done +if USE_DEBIAN + $(mkinstalldirs) $(DESTDIR)/default + if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f /etc/default/netatalk; then \ + echo "$(INSTALL_DATA) netatalk.conf $(DESTDIR)/etc/default/netatalk"; \ + $(INSTALL_DATA) netatalk.conf $(DESTDIR)/etc/default/netatalk; \ + else \ + echo "not overwriting /etc/default/netatalk"; \ + fi; +endif if USE_DEBIAN $(mkinstalldirs) $(DESTDIR)/default diff --git a/config/afpd.conf.tmpl b/config/afpd.conf.tmpl index 4c9ea98d..37759212 100644 --- a/config/afpd.conf.tmpl +++ b/config/afpd.conf.tmpl @@ -69,6 +69,8 @@ # 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 diff --git a/config/netatalk.conf b/config/netatalk.conf index ea893433..b8c1e67b 100644 --- a/config/netatalk.conf +++ b/config/netatalk.conf @@ -1,43 +1,60 @@ # 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 diff --git a/configure.in b/configure.in index 9b6e775a..98d045d7 100644 --- a/configure.in +++ b/configure.in @@ -1,4 +1,3 @@ -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) @@ -25,71 +24,11 @@ AC_PROG_PS 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" @@ -100,8 +39,6 @@ AC_CHECK_HEADERS([sys/mount.h], , , #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 @@ -124,7 +61,6 @@ if test x"$libltdl_cv_need_uscore" = xyes; then AC_DEFINE(DLSYM_PREPEND_UNDERSCORE, 1, [BSD compatibility macro]) fi - dnl Checks for library functions. AC_TYPE_GETGROUPS AC_PROG_GCC_TRADITIONAL @@ -135,10 +71,20 @@ AC_TYPE_SIGNAL 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 ]) + +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 @@ -149,36 +95,34 @@ dnl 64bit platform check 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 -------------------------------------------------------------------------- @@ -232,19 +176,20 @@ AC_ARG_WITH(cracklib, 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]) ] ) @@ -278,9 +223,11 @@ AC_ARG_ENABLE(debug, 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]) ] ) @@ -387,6 +334,9 @@ AC_CHECK_QUOTA 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([ @@ -1022,49 +972,153 @@ AC_ARG_ENABLE(overwrite, ) 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 + #include + ],[ + 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 + #include + ],[ + 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" @@ -1166,6 +1220,7 @@ if test "x$neta_cv_eas_sys_found" = "xyes" ; then 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], @@ -1210,7 +1265,8 @@ AM_CONDITIONAL(COMPILE_TIMELORD, test x$compile_timelord = xyes) 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) @@ -1231,14 +1287,15 @@ AM_CONDITIONAL(USE_GENTOO, test x$sysv_style = xgentoo) 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 @@ -1257,10 +1314,7 @@ AC_OUTPUT([Makefile 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 @@ -1285,6 +1339,7 @@ AC_OUTPUT([Makefile libatalk/adouble/Makefile libatalk/asp/Makefile libatalk/atp/Makefile + libatalk/bstring/Makefile libatalk/cnid/Makefile libatalk/cnid/cdb/Makefile libatalk/cnid/last/Makefile @@ -1295,7 +1350,6 @@ AC_OUTPUT([Makefile libatalk/nbp/Makefile libatalk/netddp/Makefile libatalk/util/Makefile - libatalk/util/test/Makefile libatalk/tdb/Makefile libatalk/unicode/Makefile libatalk/unicode/charsets/Makefile @@ -1316,6 +1370,8 @@ AC_OUTPUT([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_*] ) diff --git a/contrib/ICDumpSuffixMap b/contrib/ICDumpSuffixMap deleted file mode 100644 index 3a8283fc..00000000 --- a/contrib/ICDumpSuffixMap +++ /dev/null @@ -1 +0,0 @@ -#!perl # # ICDumpMap # --- Dump suffix mappings from your Internet Config extension. # # iNOUE Koich! # use Mac::InternetConfig; open MAP, ">AppleVolumes"; printf MAP "%-9s \"%4s\" \"%4s\" %-30s %-25s %-15s\n\n", ".", "TEXT", "ttxt", "ASCII Text", "SimpleText", "text/plain"; print MAP "\# The following lines are extracted from Internet Config Preference.\n\n"; for my $entry (keys %InternetConfigMap) { next unless $entry->extension =~ /^\./; $_ = sprintf "%-9s \"%4s\" \"%4s\" %-30s %-25s %-15s", $entry->extension, $entry->file_type, $entry->file_creator, $entry->entry_name, $entry->creator_app_name, $entry->MIME_type; s/\s*$/\n/; print MAP; } close MAP; \ No newline at end of file diff --git a/contrib/Makefile.am b/contrib/Makefile.am index ae0ea4f0..5652b1bb 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -1,17 +1,15 @@ # 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 diff --git a/contrib/misc/make-precompose.h.pl b/contrib/misc/make-precompose.h.pl old mode 100644 new mode 100755 index 9e85815c..11cb8d9b --- a/contrib/misc/make-precompose.h.pl +++ b/contrib/misc/make-precompose.h.pl @@ -1,6 +1,19 @@ #!/usr/bin/perl - +# # usage: make-precompose.h.pl UnicodeData.txt > precompose.h +# +# (c) 2008-2011 by HAT +# +# 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 @@ -9,13 +22,14 @@ # 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 (){ +open(COMPOSE_TEMP, ">compose.TEMP"); +open(COMPOSE_SP_TEMP, ">compose_sp.TEMP"); + +while () { chop; ( $code0, @@ -33,48 +47,168 @@ while (){ $Simple_Uppercase_Mapping12, $Simple_Lowercase_Mapping13, $Simple_Titlecase_Mapping14 - ) = split(/\;/); + ) = split(/\;/); if (($Decomposition_Mapping5 ne "") && ($Decomposition_Mapping5 !~ /\ 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, ") { + 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, ") { + 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"); @@ -97,6 +231,30 @@ system("cat decompose.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 ("\} 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 diff --git a/contrib/patches/README b/contrib/patches/README deleted file mode 100644 index 731933bb..00000000 --- a/contrib/patches/README +++ /dev/null @@ -1,2 +0,0 @@ -This directory contains patches that are under consideration or being -tested. diff --git a/contrib/patches/patch.afp_vfs b/contrib/patches/patch.afp_vfs deleted file mode 100644 index f2c08ee8..00000000 --- a/contrib/patches/patch.afp_vfs +++ /dev/null @@ -1,1678 +0,0 @@ -First try for a netatalk vfs layer - -current schemes -adouble=v1,v2 classic adouble format -adouble=osx ._ 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 -+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<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 -+#endif -+ -+#include -+ -+#include -+#include -+#include -+ -+#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); diff --git a/contrib/patches/patch.mangled_trash_with_ip b/contrib/patches/patch.mangled_trash_with_ip deleted file mode 100644 index 38976fcd..00000000 --- a/contrib/patches/patch.mangled_trash_with_ip +++ /dev/null @@ -1,252 +0,0 @@ -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.." -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 diff --git a/contrib/patches/patch.samba.3.0.5pre2-SVN b/contrib/patches/patch.samba.3.0.5pre2-SVN deleted file mode 100644 index 8b17608b..00000000 --- a/contrib/patches/patch.samba.3.0.5pre2-SVN +++ /dev/null @@ -1,453 +0,0 @@ -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 */ diff --git a/contrib/patches/patch.samba.3.0a20 b/contrib/patches/patch.samba.3.0a20 deleted file mode 100644 index 6d846210..00000000 --- a/contrib/patches/patch.samba.3.0a20 +++ /dev/null @@ -1,407 +0,0 @@ -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 - }; - - /**************************************************************************** diff --git a/contrib/patches/patch.vfs b/contrib/patches/patch.vfs deleted file mode 100644 index 06295d07..00000000 --- a/contrib/patches/patch.vfs +++ /dev/null @@ -1,1115 +0,0 @@ -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 /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 diff --git a/contrib/permtest/add_permtest.patch b/contrib/permtest/add_permtest.patch deleted file mode 100644 index 6fdf38c1..00000000 --- a/contrib/permtest/add_permtest.patch +++ /dev/null @@ -1,38 +0,0 @@ -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) diff --git a/contrib/permtest/permtest.cfg b/contrib/permtest/permtest.cfg deleted file mode 100644 index 941d4e62..00000000 --- a/contrib/permtest/permtest.cfg +++ /dev/null @@ -1,25 +0,0 @@ -# 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 diff --git a/contrib/permtest/permtest.pl.in b/contrib/permtest/permtest.pl.in deleted file mode 100755 index 87de19a0..00000000 --- a/contrib/permtest/permtest.pl.in +++ /dev/null @@ -1,224 +0,0 @@ -#!@PERL@ -w - -########################################################################### -# -# Characterization testing netatalks permission model -# -# (c) 2008 by 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. -# -########################################################################### - -########################################################################### -# -# 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 () { - 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); - } -} diff --git a/contrib/shell_utils/Makefile.am b/contrib/shell_utils/Makefile.am index bb2f1496..c8cefe1e 100644 --- a/contrib/shell_utils/Makefile.am +++ b/contrib/shell_utils/Makefile.am @@ -6,7 +6,6 @@ GENERATED_FILES = lp2pap.sh TEMPLATE_FILES = lp2pap.sh.tmpl PERLSCRIPTS = \ afpd-mtab.pl \ - apple_cp apple_mv apple_rm \ asip-status.pl \ apple_dump diff --git a/contrib/shell_utils/apple_cp.in b/contrib/shell_utils/apple_cp.in deleted file mode 100755 index b8a75ab5..00000000 --- a/contrib/shell_utils/apple_cp.in +++ /dev/null @@ -1,67 +0,0 @@ -#!@PERL@ -# -# $Id: apple_cp.in,v 1.1 2002-01-17 05:59:25 srittau Exp $ - -$USAGE = < 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; -} - - diff --git a/contrib/shell_utils/apple_mv.in b/contrib/shell_utils/apple_mv.in deleted file mode 100755 index 79cf579f..00000000 --- a/contrib/shell_utils/apple_mv.in +++ /dev/null @@ -1,79 +0,0 @@ -#!@PERL@ -# -# $Id: apple_mv.in,v 1.1 2002-01-17 05:59:25 srittau Exp $ - -$USAGE = < 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; -} - - diff --git a/contrib/shell_utils/apple_rm.in b/contrib/shell_utils/apple_rm.in deleted file mode 100755 index 6595937b..00000000 --- a/contrib/shell_utils/apple_rm.in +++ /dev/null @@ -1,42 +0,0 @@ -#!@PERL@ -# -# $Id: apple_rm.in,v 1.1 2002-01-17 05:59:25 srittau Exp $ - -$USAGE = < -and was later maintained by late Joel Klecker and -David Huggins-Daines . It was repackaged by its current -maintainer Sebastian Rittau . - - 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 diff --git a/distrib/debian/changelog b/distrib/debian/changelog deleted file mode 100644 index d194da82..00000000 --- a/distrib/debian/changelog +++ /dev/null @@ -1,576 +0,0 @@ -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 . - 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 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 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 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 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 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 Sun, 7 Oct 2001 12:46:15 +0200 - -netatalk (1.5pre7-5) unstable; urgency=low - - * More patches by Jonas Smedegaard : - + 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 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 : - + 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 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 Sun, 23 Sep 2001 19:08:43 +0200 - -netatalk (1.5pre7-2) unstable; urgency=low - - * Integrated a lot of patches by Jonas Smedegaard : - + 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 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 - for actually being affected by this bug, and - more importantly - - finding the problem. (Closes: #109310) - - -- Sebastian Rittau 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 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 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 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 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 . - * 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 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 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 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 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 Wed, 8 Mar 2001 00:03:30 +0100 - -netatalk (1.5pre5-1) unstable; urgency=low - - * New upstream version. - - -- Sebastian Rittau 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 .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 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 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 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 Fri, 30 Jan 1998 07:50:00 -0800 - -netatalk (1.4b2-4.3) unstable; urgency=low - - * Non-maintainer release. - * Fixed dependencies. - - -- Joel Klecker 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 Wed, 7 Jan 1998 00:00:00 -0800 - -netatalk (1.4b2-4.1) unstable; urgency=low - - * Non-maintainer libc6 compile. - - -- Joel Klecker 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 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 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 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 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 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 Wed, 2 Oct 1996 10:18:14 -0700 - -netatalk (1.3.3-3); - - * Fixed location of include files. - - -- Klee Dienes Mon Jan 8 10:46:52 MST 1996 - -netatalk (1.3.3-2); - - * Fixed bug in postrm script. - - -- Klee Dienes Thu Dec 21 08:22:24 MST 1995 - -netatalk (1.3.3-1); - - * Initial Release. - - -- Klee Dienes Wed Dec 13 22:58:31 MST 1995 diff --git a/distrib/debian/control b/distrib/debian/control deleted file mode 100644 index fa3de944..00000000 --- a/distrib/debian/control +++ /dev/null @@ -1,31 +0,0 @@ -Source: netatalk -Section: non-US -Priority: extra -Maintainer: Sebastian Rittau -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. - diff --git a/distrib/debian/copyright b/distrib/debian/copyright deleted file mode 100644 index c284410c..00000000 --- a/distrib/debian/copyright +++ /dev/null @@ -1,62 +0,0 @@ -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 . - -The sources were obtained from CVS: cvs.netatalk.sourceforge.net. -See the Netatalk homepage at 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 diff --git a/distrib/debian/cvs2deb.sh b/distrib/debian/cvs2deb.sh deleted file mode 100755 index 699131f8..00000000 --- a/distrib/debian/cvs2deb.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/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 diff --git a/distrib/debian/logcheck/ignore.d.server b/distrib/debian/logcheck/ignore.d.server deleted file mode 100644 index bf7b562a..00000000 --- a/distrib/debian/logcheck/ignore.d.server +++ /dev/null @@ -1,27 +0,0 @@ -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 diff --git a/distrib/debian/logcheck/violations.ignore.d b/distrib/debian/logcheck/violations.ignore.d deleted file mode 100644 index 98638a75..00000000 --- a/distrib/debian/logcheck/violations.ignore.d +++ /dev/null @@ -1,4 +0,0 @@ -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 diff --git a/distrib/debian/netatalk-dev.docs b/distrib/debian/netatalk-dev.docs deleted file mode 100644 index 976f2246..00000000 --- a/distrib/debian/netatalk-dev.docs +++ /dev/null @@ -1 +0,0 @@ -doc/DEVELOPER diff --git a/distrib/debian/netatalk-dev.files b/distrib/debian/netatalk-dev.files deleted file mode 100644 index a4f3f2bb..00000000 --- a/distrib/debian/netatalk-dev.files +++ /dev/null @@ -1,39 +0,0 @@ -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 diff --git a/distrib/debian/netatalk.dirs b/distrib/debian/netatalk.dirs deleted file mode 100644 index b8ce9783..00000000 --- a/distrib/debian/netatalk.dirs +++ /dev/null @@ -1,4 +0,0 @@ -etc/default -etc/logcheck/ignore.d.server -etc/logcheck/ignore.d.workstation -etc/logcheck/violations.ignore.d diff --git a/distrib/debian/netatalk.docs b/distrib/debian/netatalk.docs deleted file mode 100644 index c8a19c91..00000000 --- a/distrib/debian/netatalk.docs +++ /dev/null @@ -1,8 +0,0 @@ -BUGS -CHANGES -CONTRIBUTORS -TODO -doc/CONFIGURE -doc/FAQ -doc/README.hidden-items -doc/README.platforms diff --git a/distrib/debian/netatalk.examples b/distrib/debian/netatalk.examples deleted file mode 100644 index 43e3fc46..00000000 --- a/distrib/debian/netatalk.examples +++ /dev/null @@ -1 +0,0 @@ -contrib/printing/add_netatalk_printer diff --git a/distrib/debian/netatalk.files b/distrib/debian/netatalk.files deleted file mode 100644 index 4fddf685..00000000 --- a/distrib/debian/netatalk.files +++ /dev/null @@ -1,94 +0,0 @@ -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 diff --git a/distrib/debian/netatalk.init b/distrib/debian/netatalk.init deleted file mode 100644 index fe5e1c78..00000000 --- a/distrib/debian/netatalk.init +++ /dev/null @@ -1,89 +0,0 @@ -#!/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 - diff --git a/distrib/debian/netatalk.links b/distrib/debian/netatalk.links deleted file mode 100644 index cc129330..00000000 --- a/distrib/debian/netatalk.links +++ /dev/null @@ -1 +0,0 @@ -etc/logcheck/ignore.d.server/netatalk etc/logcheck/ignore.d.workstation/netatalk diff --git a/distrib/debian/netatalk.undocumented b/distrib/debian/netatalk.undocumented deleted file mode 100644 index eb6a7b8d..00000000 --- a/distrib/debian/netatalk.undocumented +++ /dev/null @@ -1,9 +0,0 @@ -binheader.1 -cleanappledouble.pl.1 -cnid_didname_verify.1 -macusers.1 -makecode.1 -nadheader.1 -nu.1 -parsecode.1 -psa.8 diff --git a/distrib/debian/patches/add_printer.patch b/distrib/debian/patches/add_printer.patch deleted file mode 100644 index bbfb908f..00000000 --- a/distrib/debian/patches/add_printer.patch +++ /dev/null @@ -1,51 +0,0 @@ ---- 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 diff --git a/distrib/debian/patches/etc2ps.sh.patch b/distrib/debian/patches/etc2ps.sh.patch deleted file mode 100644 index c9c76d7f..00000000 --- a/distrib/debian/patches/etc2ps.sh.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- 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 diff --git a/distrib/debian/patches/filterdir.patch b/distrib/debian/patches/filterdir.patch deleted file mode 100644 index 65855d90..00000000 --- a/distrib/debian/patches/filterdir.patch +++ /dev/null @@ -1,27 +0,0 @@ ---- 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 - - # diff --git a/distrib/debian/patches/netatalk.conf.patch b/distrib/debian/patches/netatalk.conf.patch deleted file mode 100644 index 5487084e..00000000 --- a/distrib/debian/patches/netatalk.conf.patch +++ /dev/null @@ -1,18 +0,0 @@ ---- 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 diff --git a/distrib/debian/patches/netatalk.pamd.patch b/distrib/debian/patches/netatalk.pamd.patch deleted file mode 100644 index b1758d18..00000000 --- a/distrib/debian/patches/netatalk.pamd.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- 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 diff --git a/distrib/debian/patches/psf.8.patch b/distrib/debian/patches/psf.8.patch deleted file mode 100644 index 3cc2ab8e..00000000 --- a/distrib/debian/patches/psf.8.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- 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 diff --git a/distrib/debian/rules b/distrib/debian/rules deleted file mode 100755 index 824b41dc..00000000 --- a/distrib/debian/rules +++ /dev/null @@ -1,99 +0,0 @@ -#! /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 diff --git a/distrib/initscripts/rc.atalk.bsd.tmpl b/distrib/initscripts/rc.atalk.bsd.tmpl index 05010b7b..6a0f489a 100755 --- a/distrib/initscripts/rc.atalk.bsd.tmpl +++ b/distrib/initscripts/rc.atalk.bsd.tmpl @@ -15,6 +15,24 @@ ## /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} @@ -26,8 +44,8 @@ if [ -x :SBINDIR:/atalkd ]; then 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 @@ -37,12 +55,14 @@ if [ -x :SBINDIR:/papd -a X"${PAPD_RUN}" != X"no" ]; then 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 diff --git a/distrib/initscripts/rc.atalk.debian.tmpl b/distrib/initscripts/rc.atalk.debian.tmpl index b9654764..a9fafd13 100644 --- a/distrib/initscripts/rc.atalk.debian.tmpl +++ b/distrib/initscripts/rc.atalk.debian.tmpl @@ -18,22 +18,29 @@ NAME=netatalk 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 @@ -46,7 +53,7 @@ atalk_startup() { # 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. @@ -86,7 +93,7 @@ atalk_startup() { 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 @@ -104,16 +111,20 @@ case "$1" in 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 "." ;; diff --git a/distrib/initscripts/rc.atalk.gentoo.tmpl b/distrib/initscripts/rc.atalk.gentoo.tmpl index 200b0c24..5ed475c5 100644 --- a/distrib/initscripts/rc.atalk.gentoo.tmpl +++ b/distrib/initscripts/rc.atalk.gentoo.tmpl @@ -5,6 +5,24 @@ # 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 @@ -61,7 +79,7 @@ atalk_startup () { 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 diff --git a/distrib/initscripts/rc.atalk.redhat.tmpl b/distrib/initscripts/rc.atalk.redhat.tmpl index dfeed4ca..79b613bd 100644 --- a/distrib/initscripts/rc.atalk.redhat.tmpl +++ b/distrib/initscripts/rc.atalk.redhat.tmpl @@ -19,6 +19,25 @@ ATALK_SBIN=:SBINDIR: # 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 @@ -39,21 +58,16 @@ atalk_startup() { 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=$? @@ -125,7 +139,7 @@ atalk_startup() { 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 diff --git a/distrib/initscripts/rc.atalk.suse.tmpl b/distrib/initscripts/rc.atalk.suse.tmpl index c9a0bec3..886591bc 100755 --- a/distrib/initscripts/rc.atalk.suse.tmpl +++ b/distrib/initscripts/rc.atalk.suse.tmpl @@ -30,6 +30,25 @@ test -f /etc/rc.status && . /etc/rc.status 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 @@ -39,8 +58,8 @@ atalk_startup() { :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 @@ -74,7 +93,7 @@ atalk_startup() { 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 diff --git a/distrib/initscripts/rc.atalk.sysv.tmpl b/distrib/initscripts/rc.atalk.sysv.tmpl index f21c7ec9..85ec0748 100755 --- a/distrib/initscripts/rc.atalk.sysv.tmpl +++ b/distrib/initscripts/rc.atalk.sysv.tmpl @@ -18,7 +18,26 @@ killproc() { [ "$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 @@ -67,7 +86,7 @@ atalk_startup() { 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 diff --git a/distrib/rpm/buildrpm b/distrib/rpm/buildrpm deleted file mode 100755 index 5ef90f33..00000000 --- a/distrib/rpm/buildrpm +++ /dev/null @@ -1,53 +0,0 @@ -#!/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 diff --git a/distrib/rpm/netatalk-asun.spec.old b/distrib/rpm/netatalk-asun.spec.old deleted file mode 100644 index 4e3d9925..00000000 --- a/distrib/rpm/netatalk-asun.spec.old +++ /dev/null @@ -1,297 +0,0 @@ -Summary: AppleTalk networking programs -Name: netatalk -Version: 1.4b2+asun2.1.4 -Release: pre39 -Packager: iNOUE Koichi -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' \ - /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! -- /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! -- 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! -- [pre-asun2.1.4-30] -* Sun Jun 20 1999 iNOUE Koich! -- [pre-asun2.1.4-28] -* Thu Jun 3 1999 iNOUE Koich! -- [pre-asun2.1.4-22] -* Wed May 19 1999 iNOUE Koich! -- [pre-asun2.1.4-15] - Make BerkleyDB=/usr. -* Sun May 2 1999 iNOUE Koich! -- [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! -- [pre-asun2.1.4-9] - Move %chengelog section last. -* Wed Mar 31 1999 iNOUE Koich! -- Comment out -DNEED_QUOTA_WRAPPER in sys/linux/Makefile. -* Sat Mar 20 1999 iNOUE Koich! -- Correct symbolic links to psf. - Remove asciize function from nbplkup so as to display Japanese hostname. -* Thu Mar 11 1999 iNOUE Koich! -- Included MacPerl 5 script ICDumpSuffixMap which dumps suffix mapping - containd in Internet Config Preference. -* Tue Mar 2 1999 iNOUE Koich! -- [asun2.1.3] -* Mon Feb 15 1999 iNOUE Koich! -- [pre-asun2.1.2-8] -* Sun Feb 7 1999 iNOUE Koich! -- [pre-asun2.1.2-6] -* Mon Jan 25 1999 iNOUE Koichi -- [pre-asun2.1.2-3] -* Thu Dec 17 1998 INOUE Koichi -- [pre-asun2.1.2] - Remove crlf patch. It is now a server's option. -* Thu Dec 3 1998 INOUE Koichi -- Use stable version source netatalk-1.4b2+asun2.1.1.tar.gz - Add uams directory -* Sat Nov 28 1998 INOUE Koichi -- Use pre-asun2.1.1-3 source. -* Mon Nov 23 1998 INOUE Koichi -- Use pre-asun2.1.1-2 source. -* Mon Nov 16 1998 INOUE Koichi -- Fix rcX.d's symbolic links. -* Wed Oct 28 1998 INOUE Koichi -- Use pre-asun2.1.0a-2 source. Remove '%exclusiveos linux' line. -* Sat Oct 24 1998 INOUE Koichi -- Use stable version source netatalk-1.4b2+asun2.1.0.tar.gz. -* Mon Oct 5 1998 INOUE Koichi -- Use pre-asun2.1.0-10a source. -* Thu Sep 19 1998 INOUE Koichi -- Use pre-asun2.1.0-8 source. Add chkconfig support. -* Sat Sep 12 1998 INOUE Koichi -- Comment out -DCRLF. Use RPM_OPT_FLAGS. -* Mon Sep 8 1998 INOUE Koichi -- Use pre-asun2.1.0-7 source. Rename atalk.init to atalk. -* Mon Aug 22 1998 INOUE Koichi -- Use pre-asun2.1.0-6 source. -* Mon Jul 27 1998 INOUE Koichi -- Use pre-asun2.1.0-5 source. -* Tue Jul 21 1998 INOUE Koichi -- Use pre-asun2.1.0-3 source. -* Tue Jul 7 1998 INOUE Koichi -- Add afpovertcp entries to /etc/services -- Remove BuildRoot in man8 pages -* Mon Jun 29 1998 INOUE Koichi -- Use modified sources 1.4b2+asun2.1.0 produced by Adrian Sun - to provide an AppleShareIP file server -- Included AppleVolumes.system file maintained by Johnson - -* Mon Aug 25 1997 David Gibson -- 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 -- 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 -Updated for /etc/pam.d diff --git a/distrib/rpm/netatalk-fedora.spec b/distrib/rpm/netatalk-fedora.spec deleted file mode 100644 index ca31a1b8..00000000 --- a/distrib/rpm/netatalk-fedora.spec +++ /dev/null @@ -1,194 +0,0 @@ -#################################################### 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 - -############################################################## 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' \ - /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 - - Modify redhat spec file for Fedora Core. - -* Sat Jan 04 2002 Steven N. Hirsch - - Fix RedHat RPM build. - - Build Apple2 boot support. - -* Thu Apr 12 2001 rufus t firefly - - v1.5pre6-1 - - pre-release 6 for sourceforge - -* Wed Mar 07 2001 rufus t firefly - - v1.5pre5-1 - - pre-release 5 for sourceforge - -* Fri Feb 23 2001 rufus t firefly - - v1.5pre5-0 - - pre-release 5 for sourceforge (prebuild) - -* Tue Feb 20 2001 rufus t firefly - - v1.5pre4-1 - - pre-release 4 for sourceforge - - modified/split mandrake spec for redhat 7 build - -* Mon Dec 18 2000 rufus t firefly - - v1.5pre3-1mdk - - pre-release 3 for sourceforge - - moved away from 1.4.99 ... - -* Wed Nov 08 2000 rufus t firefly - - v1.4.99-0.20001108mdk - - pre-release 2 for sourceforge - -* Wed Sep 27 2000 rufus t firefly - - v1.4.99-0.20000927mdk - - pre-release 1 for sourceforge diff --git a/distrib/rpm/netatalk-mandrake.spec b/distrib/rpm/netatalk-mandrake.spec deleted file mode 100644 index caa0cd80..00000000 --- a/distrib/rpm/netatalk-mandrake.spec +++ /dev/null @@ -1,182 +0,0 @@ -#################################################### 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 -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 - - v1.5pre6-1mdk - - pre-release 6 for sourceforge - -* Wed Mar 07 2001 rufus t firefly - - v1.5pre5-1mdk - - pre-release 5 for sourceforge - - sync with redhat package - -* Mon Dec 18 2000 rufus t firefly - - v1.5pre3-1mdk - - pre-release 3 for sourceforge - - moved away from 1.4.99 ... - -* Wed Nov 08 2000 rufus t firefly - - v1.4.99-0.20001108mdk - - pre-release 2 for sourceforge - -* Wed Sep 27 2000 rufus t firefly - - 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' \ - /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 diff --git a/distrib/rpm/netatalk-redhat.spec b/distrib/rpm/netatalk-redhat.spec deleted file mode 100644 index b2c8a3e7..00000000 --- a/distrib/rpm/netatalk-redhat.spec +++ /dev/null @@ -1,194 +0,0 @@ -#################################################### 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 -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 - - Fix RedHat RPM build. - - Build Apple2 boot support. - -* Thu Apr 12 2001 rufus t firefly - - v1.5pre6-1 - - pre-release 6 for sourceforge - -* Wed Mar 07 2001 rufus t firefly - - v1.5pre5-1 - - pre-release 5 for sourceforge - -* Fri Feb 23 2001 rufus t firefly - - v1.5pre5-0 - - pre-release 5 for sourceforge (prebuild) - -* Tue Feb 20 2001 rufus t firefly - - v1.5pre4-1 - - pre-release 4 for sourceforge - - modified/split mandrake spec for redhat 7 build - -* Mon Dec 18 2000 rufus t firefly - - v1.5pre3-1mdk - - pre-release 3 for sourceforge - - moved away from 1.4.99 ... - -* Wed Nov 08 2000 rufus t firefly - - v1.4.99-0.20001108mdk - - pre-release 2 for sourceforge - -* Wed Sep 27 2000 rufus t firefly - - 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' \ - /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 diff --git a/distrib/rpm/netatalk-rpmbuild.patch b/distrib/rpm/netatalk-rpmbuild.patch deleted file mode 100644 index f965b2f4..00000000 --- a/distrib/rpm/netatalk-rpmbuild.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- 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 diff --git a/doc/DEVELOPER b/doc/DEVELOPER index 39eecd24..9ba1765b 100644 --- a/doc/DEVELOPER +++ b/doc/DEVELOPER @@ -144,16 +144,82 @@ Author: Andrew Morgan 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 . 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; +} diff --git a/doc/FAQ b/doc/FAQ deleted file mode 100644 index 5092ac7a..00000000 --- a/doc/FAQ +++ /dev/null @@ -1,636 +0,0 @@ -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. diff --git a/doc/Makefile.am b/doc/Makefile.am index e242667c..05eaf841 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,10 +1,3 @@ # Makefile.am for INSTALL/ -EXTRA_DIST = \ - DEVELOPER \ - FAQ \ - README.documentation \ - README.hidden-items \ - README.ids \ - README.AppleTalk \ - README.ACLs +EXTRA_DIST = DEVELOPER README.AppleTalk diff --git a/doc/README.ACLs b/doc/README.ACLs deleted file mode 100644 index 916ddbed..00000000 --- a/doc/README.ACLs +++ /dev/null @@ -1,86 +0,0 @@ - - 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 diff --git a/doc/README.documentation b/doc/README.documentation deleted file mode 100644 index db58a079..00000000 --- a/doc/README.documentation +++ /dev/null @@ -1,4 +0,0 @@ - ATTENTION - -The Netatalk documentation is now maintained in Docbook XML format. -It is kept in the separate Git module 'netatalk-docs'. diff --git a/doc/README.hidden-items b/doc/README.hidden-items deleted file mode 100644 index f07b1254..00000000 --- a/doc/README.hidden-items +++ /dev/null @@ -1,130 +0,0 @@ - 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. diff --git a/doc/README.ids b/doc/README.ids deleted file mode 100644 index a8539c38..00000000 --- a/doc/README.ids +++ /dev/null @@ -1,102 +0,0 @@ -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 . His developer notes can be found -libatalk/cnid/README file. It was later picked up and modernized by Uwe Hees -. Then, Joe Marcus Clarke -started fixing bugs and adding additional features. The Concurrent -Datastore support was subsequently added by Dan Wilga . -The CNID code is currently maintained by Joe Marcus Clarke. diff --git a/doc/TODO2.1 b/doc/TODO2.1 deleted file mode 100644 index 8296a104..00000000 --- a/doc/TODO2.1 +++ /dev/null @@ -1,118 +0,0 @@ -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. diff --git a/etc/Makefile.am b/etc/Makefile.am index 7ed56ed6..6ad182a5 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -1,3 +1,7 @@ # 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 diff --git a/etc/afpd/Makefile.am b/etc/afpd/Makefile.am index 54ae84fe..7e93dec2 100644 --- a/etc/afpd/Makefile.am +++ b/etc/afpd/Makefile.am @@ -5,33 +5,73 @@ pkgconfdir = @PKGCONFDIR@ 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 diff --git a/etc/afpd/acl_mappings.h b/etc/afpd/acl_mappings.h index e2d9f3d2..b7c4b6d2 100644 --- a/etc/afpd/acl_mappings.h +++ b/etc/afpd/acl_mappings.h @@ -15,7 +15,10 @@ #ifndef ACL_MAPPINGS #define ACL_MAPPINGS +#ifdef HAVE_SOLARIS_ACLS #include +#endif + #include "acls.h" /* @@ -32,6 +35,7 @@ struct ace_rights_map { 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}, @@ -93,5 +97,6 @@ struct darwin_to_nfsv4_flags_map darwin_to_nfsv4_flags[] = { {DARWIN_ACE_FLAGS_INHERITED, ACE_INHERITED_ACE}, {0,0} }; +#endif /* HAVE_SOLARIS_ACLS */ #endif /* ACL_MAPPINGS */ diff --git a/etc/afpd/acls.c b/etc/afpd/acls.c index e7ee37ff..a82d326f 100644 --- a/etc/afpd/acls.c +++ b/etc/afpd/acls.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2008,2009 Frank Lahm + Copyright (c) 2008, 2009, 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 @@ -23,8 +23,15 @@ #include #include #include +#ifdef HAVE_SOLARIS_ACLS #include +#endif +#ifdef HAVE_POSIX_ACLS +#include +#include +#endif +#include #include #include #include @@ -33,49 +40,129 @@ #include #include #include +#include +#include #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; } /* @@ -83,51 +170,42 @@ static int check_group(char *name, uid_t uid, gid_t pgid, gid_t path_gid) 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 */ @@ -159,6 +237,8 @@ static int map_aces_solaris_to_darwin(ace_t *aces, darwin_ace_t *darwin_aces, in } return count; +EC_CLEANUP: + EC_EXIT; } /* @@ -166,14 +246,17 @@ static int map_aces_solaris_to_darwin(ace_t *aces, darwin_ace_t *darwin_aces, in 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; @@ -183,25 +266,22 @@ int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces, ace_t *nfsv4_aces, int 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; @@ -241,33 +321,488 @@ int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces, ace_t *nfsv4_aces, int } 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; break; @@ -277,20 +812,22 @@ static int map_acl(int type, ace_t *nfsv4_aces, darwin_ace_t *buf, int ace_count 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 */ @@ -298,38 +835,72 @@ static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen) *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; } /* @@ -340,19 +911,21 @@ static int remove_acl_vfs(const struct vol *vol,const char *path, int dir) 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; @@ -369,11 +942,10 @@ static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibu } /* 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 */ @@ -391,13 +963,15 @@ static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibu 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++) { @@ -412,203 +986,197 @@ static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibu /* 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; } /******************************************************** @@ -625,8 +1193,6 @@ int afp_access(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size struct path *s_path; uuidp_t uuid; - LOG(log_debug9, logtype_afpd, "afp_access: BEGIN"); - *rbuflen = 0; ibuf += 2; @@ -668,9 +1234,8 @@ int afp_access(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size 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; } @@ -752,7 +1317,11 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size /* 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"); @@ -811,14 +1380,14 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size /* 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; } @@ -826,19 +1395,24 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size /* 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 { @@ -852,115 +1426,81 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size 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; } diff --git a/etc/afpd/acls.h b/etc/afpd/acls.h index a1b1e52d..c89fe9c8 100644 --- a/etc/afpd/acls.h +++ b/etc/afpd/acls.h @@ -1,5 +1,4 @@ /* - $Id: acls.h,v 1.3 2009-11-20 17:45:47 franklahm Exp $ Copyright (c) 2008,2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -16,8 +15,11 @@ #ifndef AFPD_ACLS_H #define AFPD_ACLS_H +#ifdef HAVE_SOLARIS_ACLS #include -#include /* for uuid_t */ +#endif + +#include /* for atalk_uuid_t */ /* * This is what Apple says about ACL flags in sys/kauth.h: @@ -32,8 +34,10 @@ * 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 { @@ -53,13 +57,13 @@ 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 \ @@ -89,7 +93,7 @@ enum { /* 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; @@ -108,4 +112,7 @@ int afp_setacl (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rb /* 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 diff --git a/etc/afpd/afp_avahi.c b/etc/afpd/afp_avahi.c new file mode 100644 index 00000000..28c72e22 --- /dev/null +++ b/etc/afpd/afp_avahi.c @@ -0,0 +1,356 @@ +/* + * Author: Daniel S. Haischt + * Purpose: Avahi based Zeroconf support + * Docs: http://avahi.org/download/doxygen/ + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_AVAHI + +#include + +#include + +#include +#include +#include +#include + +#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 */ + diff --git a/etc/afpd/afp_avahi.h b/etc/afpd/afp_avahi.h new file mode 100644 index 00000000..2c6ca706 --- /dev/null +++ b/etc/afpd/afp_avahi.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */ +/* + * Author: Daniel S. Haischt + * Purpose: Avahi based Zeroconf support + * Docs: http://avahi.org/download/doxygen/ + * + */ + +#ifndef AFPD_AVAHI_H +#define AFPD_AVAHI_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#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 */ diff --git a/etc/afpd/afp_config.c b/etc/afpd/afp_config.c index 86ee093a..648eed71 100644 --- a/etc/afpd/afp_config.c +++ b/etc/afpd/afp_config.c @@ -11,33 +11,19 @@ #include #include #include - -/* STDC check */ -#if STDC_HEADERS #include -#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 -#endif /* HAVE_UNISTD_H */ #include -#include -#include - #include #include #include +#ifdef USE_SRVLOC +#include +#endif /* USE_SRVLOC */ + +#include +#include #include #include #include @@ -45,10 +31,8 @@ char *strchr (), *strrchr (); #include #include #include -#ifdef USE_SRVLOC -#include -#endif /* USE_SRVLOC */ -#ifdef HAVE_NFSv4_ACLS + +#ifdef HAVE_LDAP #include #endif @@ -56,6 +40,8 @@ char *strchr (), *strrchr (); #include "afp_config.h" #include "uam_auth.h" #include "status.h" +#include "volume.h" +#include "afp_zeroconf.h" #define LINESIZE 1024 @@ -94,6 +80,9 @@ void configfree(AFPConfig *configs, const AFPConfig *config) } free(p); } + + /* the master loaded the volumes for zeroconf, get rid of that */ + unload_volumes_and_extmap(); } #ifdef USE_SRVLOC @@ -155,9 +144,9 @@ static char * srvloc_encode(const struct afp_options *options, const char *name) } #endif /* USE_SRVLOC */ -#ifdef USE_SRVLOC static void dsi_cleanup(const AFPConfig *config) { +#ifdef USE_SRVLOC SLPError err; SLPError callbackerr; SLPHandle hslp; @@ -190,8 +179,8 @@ static void dsi_cleanup(const AFPConfig *config) srvloc_dereg_err: dsi->srvloc_url[0] = '\0'; SLPClose(hslp); -} #endif /* USE_SRVLOC */ +} #ifndef NO_DDP static void asp_cleanup(const AFPConfig *config) @@ -453,7 +442,6 @@ srvloc_reg_err: } #endif /* USE_SRVLOC */ - config->fd = dsi->serversock; config->obj.handle = dsi; config->obj.config = config; @@ -476,7 +464,7 @@ srvloc_reg_err: /* 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; @@ -543,13 +531,6 @@ AFPConfig *configinit(struct afp_options *cmdline) 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) { @@ -558,8 +539,6 @@ AFPConfig *configinit(struct afp_options *cmdline) return AFPConfigInit(cmdline, cmdline); } - LOG(log_debug, logtype_afpd, "Loading ConfigFile"); - /* scan in the configuration file */ len = 0; while (!feof(fp)) { @@ -585,13 +564,7 @@ AFPConfig *configinit(struct afp_options *cmdline) 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; @@ -600,11 +573,20 @@ AFPConfig *configinit(struct afp_options *cmdline) } } +#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; } diff --git a/etc/afpd/afp_dsi.c b/etc/afpd/afp_dsi.c index 8bcfb9c6..963a2912 100644 --- a/etc/afpd/afp_dsi.c +++ b/etc/afpd/afp_dsi.c @@ -1,6 +1,4 @@ /* - * $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. @@ -37,6 +35,7 @@ #include "switch.h" #include "auth.h" #include "fork.h" +#include "dircache.h" #ifdef FORCE_UIDGID #warning UIDGID @@ -85,8 +84,9 @@ static void afp_dsi_close(AFPObj *obj) if (obj->logout) (*obj->logout)(); - LOG(log_info, logtype_afpd, "%.2fKB read, %.2fKB written", + LOG(log_info, 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); } @@ -175,8 +175,7 @@ static void afp_dsi_timedown(int sig _U_) /* --------------------------------- * SIGHUP reload configuration file - * FIXME here or we wait ? -*/ + */ volatile int reload_request = 0; static void afp_dsi_reload(int sig _U_) @@ -376,6 +375,9 @@ void afp_over_dsi(AFPObj *obj) } #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; @@ -385,23 +387,27 @@ void afp_over_dsi(AFPObj *obj) if (reload_request) { reload_request = 0; load_volumes(child.obj); + 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) { @@ -446,6 +452,9 @@ void afp_over_dsi(AFPObj *obj) LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s", AfpNum2name(function), AfpErr2name(err)); + + dir_free_invalid_q(); + #ifdef FORCE_UIDGID /* bring everything back to old euid, egid */ if (obj->force_uid) diff --git a/etc/afpd/afp_options.c b/etc/afpd/afp_options.c index 418e044d..407a594a 100644 --- a/etc/afpd/afp_options.c +++ b/etc/afpd/afp_options.c @@ -12,48 +12,33 @@ #include #include - -/* STDC check */ -#if STDC_HEADERS #include -#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 -#ifdef HAVE_UNISTD_H #include -#endif /* HAVE_UNISTD_H */ #include #include #include #include #include + #ifdef HAVE_NETDB_H #include #endif /* HAVE_NETDB_H */ +#ifdef ADMIN_GRP +#include +#include +#endif /* ADMIN_GRP */ + #include #include +#include + #include "globals.h" #include "status.h" #include "auth.h" - -#include - -#ifdef ADMIN_GRP -#include -#include -#endif /* ADMIN_GRP */ +#include "dircache.h" #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -152,6 +137,8 @@ void afp_options_free(struct afp_options *opt, 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 */ @@ -164,6 +151,7 @@ void afp_options_init(struct afp_options *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"; @@ -194,6 +182,9 @@ void afp_options_init(struct afp_options *options) /* 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 @@ -219,7 +210,10 @@ int afp_options_parseline(char *buf, struct afp_options *options) 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")) @@ -236,6 +230,8 @@ int afp_options_parseline(char *buf, struct afp_options *options) 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")) @@ -345,6 +341,7 @@ int afp_options_parseline(char *buf, struct afp_options *options) char *optstr; if ((optstr = getoption(c, "-setuplog"))) { setuplog(optstr); + options->logconfig = optstr; /* at least store the last (possibly only) one */ c += sizeof("-setuplog"); } } @@ -462,6 +459,9 @@ int afp_options_parseline(char *buf, struct afp_options *options) if ((c = getoption(buf, "-ntseparator")) && (opt = strdup(c))) options->ntseparator = opt; + if ((c = getoption(buf, "-dircachesize"))) + options->dircachesize = atoi(c); + return 1; } @@ -487,11 +487,14 @@ static void show_version( void ) puts( "No" ); #endif - printf( " Transport layers:\t" ); + printf( " TCP/IP Support:\t" ); + puts( "Yes" ); + + 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" ); @@ -538,6 +541,13 @@ static void show_version_extended(void ) 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" ); @@ -586,6 +596,23 @@ static void show_version_extended(void ) #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 } /* @@ -594,11 +621,18 @@ static void show_version_extended(void ) 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); } /* diff --git a/etc/afpd/afp_zeroconf.c b/etc/afpd/afp_zeroconf.c new file mode 100644 index 00000000..84f18712 --- /dev/null +++ b/etc/afpd/afp_zeroconf.c @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */ +/* + * Author: Daniel S. Haischt + * Purpose: Zeroconf facade, that abstracts access to a + * particular Zeroconf implementation + * Doc: http://www.dns-sd.org/ + * + */ + +#ifdef HAVE_CONFIG_H +#include +#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 +} diff --git a/etc/afpd/afp_zeroconf.h b/etc/afpd/afp_zeroconf.h new file mode 100644 index 00000000..e2711afb --- /dev/null +++ b/etc/afpd/afp_zeroconf.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */ +/* + * Author: Daniel S. Haischt + * 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 */ diff --git a/etc/afpd/appl.c b/etc/afpd/appl.c index 37850f62..fe9ff250 100644 --- a/etc/afpd/appl.c +++ b/etc/afpd/appl.c @@ -1,5 +1,5 @@ /* - * $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. @@ -20,6 +20,8 @@ #include #include +#include +#include #include "volume.h" #include "globals.h" @@ -122,7 +124,7 @@ static int copyapplfile(int sfd, int dfd, char *mpath, u_short mplen) * 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; @@ -130,16 +132,65 @@ makemacpath(char *mpath, int mpathlen, struct dir *dir, char *path) 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 } @@ -197,7 +248,7 @@ int afp_addappl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, siz 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; } @@ -280,7 +331,7 @@ int afp_rmvappl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, siz 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 ; } @@ -418,7 +469,7 @@ int afp_getappl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t 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 ); } diff --git a/etc/afpd/auth.c b/etc/afpd/auth.c index 77e946ce..6027b851 100644 --- a/etc/afpd/auth.c +++ b/etc/afpd/auth.c @@ -26,9 +26,6 @@ #include #include #include -#include -#include -#include #ifdef TRU64 #include @@ -39,6 +36,10 @@ extern void afp_get_cmdline( int *ac, char ***av ); #endif /* TRU64 */ +#include +#include +#include + #include "globals.h" #include "auth.h" #include "uam_auth.h" @@ -46,7 +47,7 @@ extern void afp_get_cmdline( int *ac, char ***av ); #include "status.h" #include "fork.h" #include "extattrs.h" -#ifdef HAVE_NFSv4_ACLS +#ifdef HAVE_ACLS #include "acls.h" #endif @@ -208,11 +209,11 @@ static int set_auth_switch(int expired) afp_switch = postauth_switch; switch (afp_version) { 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); @@ -959,6 +960,7 @@ int afp_getuserinfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, u_int8_t thisuser; u_int32_t id; u_int16_t bitmap; + char *bitmapp; LOG(log_debug, logtype_afpd, "begin afp_getuserinfo:"); @@ -977,8 +979,9 @@ int afp_getuserinfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, 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); @@ -997,29 +1000,27 @@ int afp_getuserinfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, *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; diff --git a/etc/afpd/catsearch.c b/etc/afpd/catsearch.c index a1e96dfc..0faeef94 100644 --- a/etc/afpd/catsearch.c +++ b/etc/afpd/catsearch.c @@ -1,6 +1,7 @@ /* * Netatalk 2002 (c) * Copyright (C) 1990, 1993 Regents of The University of Michigan + * Copyright (C) 2010 Frank Lahm * All Rights Reserved. See COPYRIGHT */ @@ -18,6 +19,10 @@ * * Initial version written by Rafal Lewczuk * + * 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 @@ -30,29 +35,22 @@ #include #include #include - -#if STDC_HEADERS #include -#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 #include #include #include #include -#ifdef CNID_DB #include -#endif /* CNID_DB */ +#include #include +#include +#include #include "desktop.h" #include "directory.h" +#include "dircache.h" #include "file.h" #include "volume.h" #include "globals.h" @@ -144,6 +142,7 @@ static int addstack(char *uname, struct dir *dir, int pidx) /* 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) { @@ -176,6 +175,7 @@ static int reducestack(void) while (dsidx > 0) { if (dstack[dsidx-1].checked) { dsidx--; + dstack[dsidx].dir->d_flags &= ~DIRF_CACHELOCK; free(dstack[dsidx].path); } else return dsidx - 1; @@ -189,6 +189,7 @@ static void clearstack(void) save_cidx = -1; while (dsidx > 0) { dsidx--; + dstack[dsidx].dir->d_flags &= ~DIRF_CACHELOCK; free(dstack[dsidx].path); } } @@ -479,18 +480,29 @@ static int rslt_add ( struct vol *vol, struct path *path, char **buf, int ext) #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. */ @@ -503,7 +515,6 @@ static int catsearch(struct vol *vol, struct dir *dir, char *rrbuf = rbuf; time_t start_time; int num_rounds = NUM_ROUNDS; - int cached; int cwd = -1; int error; @@ -540,14 +551,14 @@ static int catsearch(struct vol *vol, struct dir *dir, 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: @@ -594,18 +605,16 @@ static int catsearch(struct vol *vol, struct dir *dir, 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; @@ -667,6 +676,156 @@ catsearch_end: /* Exiting catsearch: error condition */ 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) @@ -683,7 +842,8 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen, size_t len; u_int16_t namelen; u_int16_t flags; - char tmppath[256]; + char tmppath[256]; + char *uname; *rbuflen = 0; @@ -858,7 +1018,9 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen, 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; @@ -869,7 +1031,15 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen, /* 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); diff --git a/etc/afpd/desktop.c b/etc/afpd/desktop.c index 5db36f71..9f105e13 100644 --- a/etc/afpd/desktop.c +++ b/etc/afpd/desktop.c @@ -1,5 +1,5 @@ /* - * $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. * @@ -600,7 +600,8 @@ char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8) u_int16_t flags; if ( *mpath == '\0' ) { - return( "." ); + strcpy(upath, "."); + return upath; } /* set conversion flags */ @@ -700,7 +701,7 @@ static int ad_addcomment(struct vol *vol, struct path *path, char *ibuf) 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; } diff --git a/etc/afpd/dircache.c b/etc/afpd/dircache.c new file mode 100644 index 00000000..52ef7478 --- /dev/null +++ b/etc/afpd/dircache.c @@ -0,0 +1,665 @@ +/* + 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. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/etc/afpd/dircache.h b/etc/afpd/dircache.h new file mode 100644 index 00000000..69c5f9ab --- /dev/null +++ b/etc/afpd/dircache.h @@ -0,0 +1,41 @@ +/* + 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. + */ + +#ifndef DIRCACHE_H +#define DIRCACHE_H + +#include + +#include +#include + +/* 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 */ diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c index 61339b08..55918eae 100644 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@ -1,40 +1,22 @@ /* * 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 -#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 -#endif #include #include - #include #include #include +#include #include #include +#include #include #include @@ -44,8 +26,11 @@ char *strchr (), *strrchr (); #include #include #include +#include +#include #include "directory.h" +#include "dircache.h" #include "desktop.h" #include "volume.h" #include "fork.h" @@ -56,583 +41,564 @@ char *strchr (), *strrchr (); #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; -int afp_errno; -#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 - */ - -static struct dir * -vol_tree_root(const struct vol *vol, u_int32_t did) -{ - struct dir *dir; +/******************************************************************************************* + * Globals + ******************************************************************************************/ - if (vol->v_curdir && vol->v_curdir->d_did == did) { - dir = vol->v_curdir; - } - else { - dir = vol->v_root; - } - return dir; -} +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 */ +}; /* - * redid did assignment for directories. now we use red-black trees. - * how exciting. + * 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. */ -struct dir * -dirsearch(const struct vol *vol, u_int32_t did) -{ - struct dir *dir; +q_t *invalid_dircache_entries; - /* 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 ); - } +/******************************************************************************************* + * Locals + ******************************************************************************************/ - 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; - } - return NULL; -} - -/* ------------------- */ -int get_afp_errno(const int param) +/* ------------------------- + appledouble mkdir afp error code. +*/ +static int netatalk_mkdir(const struct vol *vol, const char *name) { - if (afp_errno != AFPERR_DID1) - return afp_errno; - return param; -} + int ret; + struct stat st; -/* ------------------- */ -struct dir * -dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name) -{ - struct dir *dir = NULL; + 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); - if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) { - struct dir key; - hnode_t *hn; + ret = mkdir(name, mode); + } else { + ret = ad_mkdir(name, DIRBITS | 0777); + } - key.d_parent = cdir; - key.d_u_name = name; - hn = hash_lookup(vol->v_hash, &key); - if (hn) { - dir = hnode_get(hn); + 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 dir; + return AFP_OK; } -/* ----------------------------------------- - * 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) +/* ------------------- */ +static int deletedir(int dirfd, char *dir) { - 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 path[MAXPATHLEN + 1]; + DIR *dp; + struct dirent *de; + struct stat st; + size_t len; + int err = AFP_OK; + size_t remain; - ret = dirsearch(vol, did); - if (ret != NULL || afp_errno == AFPERR_PARAM) - return ret; + if ((len = strlen(dir)) +2 > sizeof(path)) + 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; - } - len = strlen(mpath); - pathlen = len; /* no 0 in the last part */ + /* already gone */ + if ((dp = opendirat(dirfd, dir)) == NULL) + return AFP_OK; + + strcpy(path, dir); + strcat(path, "/"); len++; - strcpy(ptr - len, mpath); - ptr -= len; - while (1) { - ret = dirsearch(vol,id); - if (ret != NULL) { + 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; } - 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; + strcpy(path + len, de->d_name); + if (lstatat(dirfd, path, &st)) { + continue; } - - len = strlen(mpath) + 1; - pathlen += len; - if (pathlen > maxpath) { - afp_errno = AFPERR_PARAM; - return NULL; + if (S_ISDIR(st.st_mode)) { + err = deletedir(dirfd, path); + } else { + err = netatalk_unlinkat(dirfd, path); } - strcpy(ptr - len, mpath); - ptr -= len; - } - - /* fill the cache, another place where we know about the path type */ - if (utf8) { - u_int16_t temp16; - u_int32_t temp; - - ptr -= 2; - temp16 = htons(pathlen); - memcpy(ptr, &temp16, sizeof(temp16)); - - temp = htonl(kTextEncodingUTF8); - ptr -= 4; - memcpy(ptr, &temp, sizeof(temp)); - ptr--; - *ptr = 3; - } - else { - ptr--; - *ptr = (unsigned char)pathlen; - ptr--; - *ptr = 2; } - /* cname is not efficient */ - if (cname( vol, ret, &ptr ) == NULL ) - return NULL; - - return dirsearch(vol, did); -} + closedir(dp); -/* child addition/removal */ -static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b) -{ - 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); + /* 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; } -static void dirchildremove(struct dir *a,struct dir *b) -{ - 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; -} - -/* --------------------------- */ -/* rotate the tree to the left */ -static void dir_leftrotate(struct vol *vol, struct dir *dir) +/* do a recursive copy. */ +static int copydir(const struct vol *vol, int dirfd, char *src, char *dst) { - struct dir *right = dir->d_right; + 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; - /* 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; + /* 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; - if (right != SENTINEL) { - right->d_back = dir->d_back; - right->d_left = dir; + /* try to create the destination directory */ + if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) { + closedir(dp); + return err; } - 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 */ + /* set things up to copy */ + strcpy(spath, src); + strcat(spath, "/"); + slen++; + srem = sizeof(spath) - slen -1; - /* re-insert dir on the left tree */ - if (dir != SENTINEL) - dir->d_back = right; -} + 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); -/* rotate the tree to the right */ -static void dir_rightrotate(struct vol *vol, struct dir *dir) -{ - struct dir *left = dir->d_left; + if (lstatat(dirfd, spath, &st) == 0) { + if (strlen(de->d_name) > drem) { + err = AFPERR_PARAM; + break; + } + strcpy(dpath + dlen, de->d_name); - /* 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; + 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; - if (left != SENTINEL) { - left->d_back = dir->d_back; - left->d_right = dir; + } else { + /* keep the same time stamp. */ + ut.actime = ut.modtime = st.st_mtime; + utime(dpath, &ut); + } + } } - 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 */ + /* keep the same time stamp. */ + if (lstatat(dirfd, src, &st) == 0) { + ut.actime = ut.modtime = st.st_mtime; + utime(dst, &ut); + } - /* re-insert dir on the right tree */ - if (dir != SENTINEL) - dir->d_back = left; +copydir_done: + closedir(dp); + return err; } -#if 0 -/* recolor after a removal */ -static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir) +/* --------------------- + * is our cached offspring count valid? + */ +static int diroffcnt(struct dir *dir, struct stat *st) { - 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; - } + return st->st_ctime == dir->ctime; +} + +/* --------------------- */ +static int invisible_dots(const struct vol *vol, const char *name) +{ + return vol_inv_dots(vol) && *name == '.' && strcmp(name, ".") && strcmp(name, ".."); +} - /* 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 */ +/* ------------------ */ +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; +} + +/*! + * @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) +{ + 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 { - 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; + /* toUTF8 */ + if (mtoUTF8(vol, ret->m_name, strlen(ret->m_name), temp, MAXPATHLEN) == (size_t)-1) { + afp_errno = AFPERR_PARAM; + return -1; } - 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; + strcpy(ret->m_name, temp); } + } - /* 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; + /* 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 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; + } } - dir->d_color = DIRTREE_COLOR_BLACK; - return dir; + return 0; } -#endif /* 0 */ -/* --------------------- */ -static void dir_hash_del(const 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) { - hnode_t *hn; - - 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); - } -} + if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT) + return NULL; -/* 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. */ + switch (afp_errno) { -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; - } + case AFPERR_ACCESS: + if (movecwd( vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */ + return NULL; - /* get that child */ - leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right; + 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); + } - /* 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; - } + ret->d_dir = dir; - /* 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; + 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); - memcpy(&save, dir, sizeof(save)); - memcpy(dir, node, sizeof(struct dir)); + return ret; - /* 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; + case AFPERR_NOOBJ: + if (movecwd(vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */ + return NULL; - if (node == vol->v_dir) {/* we may need to fix up this pointer */ - vol->v_dir = dir; - rootpar.d_child = vol->v_dir; + 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 { - /* 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; + 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); } - /* fix up children. */ - tmp = dir->d_child; - while (tmp) { - tmp->d_parent = dir; - tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next; - } + ret->d_dir = NULL; /* 4 */ + dir_remove(vol, dir); /* 5 */ + return ret; - if (node == curdir) /* another pointer to fixup */ - curdir = dir; + default: + return NULL; + } - /* 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; - } - } + /* DEADC0DE: never get here */ + return NULL; +} - /* 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; - } - if (node->d_color == DIRTREE_COLOR_BLACK) - dir_rmrecolor(vol, leaf); +/********************************************************************************************* + * Interface + ********************************************************************************************/ - 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); - } - free(node->d_m_name); - free(node); -#endif /* ! REMOVE_NODES */ +int get_afp_errno(const int param) +{ + if (afp_errno != AFPERR_DID1) + return afp_errno; + return param; } -/* --------------------------------------- - * remove the node and its childs from the tree +/*! + * @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. * - * 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. + * @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. */ -static void dir_invalidate( struct vol *vol, struct dir *dir) +struct dir *dirlookup(const struct vol *vol, cnid_t did) { - 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); - } + 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)); + + /* 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; } - /* 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; + /* 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; } - pdir = pdir->d_right; + /* DEADC0DE */ + return NULL; + } + return ret; + } + + utf8 = utf8_encoding(); + maxpath = (utf8) ? MAXPATHLEN - 7 : 255; + + /* 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; + + /* + * 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; + } + + /* build the fullpath */ + if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL + || bconchar(fullpath, '/') != BSTR_OK + || bcatcstr(fullpath, upath) != BSTR_OK) { + err = 1; + goto exit; + } + + /* 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; + } + + 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; @@ -712,635 +678,374 @@ caseenumerate(const struct vol *vol, struct path *path, struct dir *dir) } -/* - * 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); - - 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; - } - /* 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); + /* Get CNID */ + if ((id = get_id(vol, adp, &path->st, dir->d_did, path->u_name, len)) == 0) { /* 2 */ + err = 1; + 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 (adp) + ad_close_metadata(adp); - if ( dir->d_left != SENTINEL ) { - dirfree( dir->d_left ); - } - if ( dir->d_right != SENTINEL ) { - dirfree( dir->d_right ); + /* 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; + } } - if (dir != SENTINEL) { - dirfreename(dir); - free( dir ); + /* 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; } -} - -/* -------------------------------------------- - * most of the time mac name and unix name are the same - */ -struct dir *dirnew(const char *m_name, const char *u_name) -{ - 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; + /* 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 (m_name == u_name || !strcmp(m_name, u_name)) { - dir->d_u_name = dir->d_m_name; + if ((dircache_add(cdir)) != 0) { /* 4 */ + LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstr(fullpath)); + exit(EXITERR_SYS); } - else if ((dir->d_u_name = strdup(u_name)) == NULL) { - free(dir->d_m_name); - free(dir); - return NULL; + +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)); } - dir->d_m_name_ucs2 = NULL; - dir->d_left = dir->d_right = SENTINEL; - dir->d_next = dir->d_prev = dir; - return dir; + return(cdir); } -#if 0 -/* ------------------ */ -static hash_val_t hash_fun_dir(const void *key) +/*! + * Free the queue with invalid struct dirs + * + * This gets called at the end of every AFP func. + */ +void dir_free_invalid_q(void) { - 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; + struct dir *dir; + while (dir = (struct dir *)dequeue(invalid_dircache_entries)) + dir_free(dir); } -#endif -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif +/*! + * @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) +{ + AFP_ASSERT(vol); + AFP_ASSERT(dir); -#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_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT) + 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 (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; + } + + 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; + } -*/ -struct path * -cname(struct vol *vol, struct dir *dir, char **cpath) + 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"); + } + + 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; @@ -1357,7 +1062,6 @@ cname(struct vol *vol, struct dir *dir, char **cpath) len = ntohs(len16); data += 2; size = 7; - sep = 0; /* '/';*/ break; } /* else it's an error */ @@ -1366,248 +1070,217 @@ cname(struct vol *vol, struct dir *dir, char **cpath) 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--; } + *p = 0; /* Terminate string */ + ret.u_name = NULL; - /* 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; + if (cname_mtouname(vol, dir, &ret, toUTF8) != 0) { /* 7 */ + LOG(log_error, logtype_afpd, "cname('%s'): error from cname_mtouname", path); + return NULL; } - ret.u_name = NULL; - if (afp_version >= 30) { - char *t; - cnid_t fileid; - if (toUTF8) { - static char temp[ MAXPATHLEN + 1]; + LOG(log_maxdebug, logtype_afpd, "came('%s'): {node: '%s}", cfrombstr(dir->d_fullpath), ret.u_name); - 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); - } - } - /* 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 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 - 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: @@ -1615,11 +1288,11 @@ int movecwd(struct vol *vol, struct dir *dir) break; default: afp_errno = AFPERR_NOOBJ; - } return( -1 ); } - vol->v_curdir = curdir = dir; + + curdir = dir; return( 0 ); } @@ -1671,30 +1344,15 @@ void setdiroffcnt(struct dir *dir, struct stat *st, u_int32_t count) 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 @@ -1733,20 +1391,14 @@ int getdirparams(const struct vol *vol, 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 ) { @@ -1759,7 +1411,7 @@ int getdirparams(const struct vol *vol, 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; @@ -1771,6 +1423,8 @@ int getdirparams(const struct vol *vol, 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 : @@ -1803,7 +1457,7 @@ int getdirparams(const struct vol *vol, 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)); } @@ -1827,6 +1481,8 @@ int getdirparams(const struct vol *vol, 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 : @@ -1924,12 +1580,12 @@ int getdirparams(const struct vol *vol, 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 ); @@ -2014,33 +1670,7 @@ int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_ * * 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; @@ -2192,7 +1822,7 @@ int setdirparams(struct vol *vol, * 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)); } } @@ -2349,8 +1979,8 @@ setdirparam_done: 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); @@ -2359,7 +1989,7 @@ setdirparam_done: 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<ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) ); + vol->ad_path(cfrombstr(dir->d_u_name), ADFLAGS_DIR), strerror(errno) ); close(dfd); } @@ -2510,8 +2140,10 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_ 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; } @@ -2532,11 +2164,6 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_ 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 ); @@ -2558,9 +2185,7 @@ int renamedir(const struct vol *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 ) { @@ -2591,11 +2216,6 @@ int renamedir(const struct vol *vol, 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)) { @@ -2604,50 +2224,6 @@ int renamedir(const struct vol *vol, 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 ); } @@ -2662,7 +2238,7 @@ int deletecurdir(struct vol *vol) u_int16_t ashort; int err; - if ( curdir->d_parent == NULL ) { + if ( dirlookup(vol, curdir->d_pdid) == NULL ) { return( AFPERR_ACCESS ); } @@ -2703,14 +2279,13 @@ int deletecurdir(struct vol *vol) } } - 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 ); } @@ -2738,7 +2313,6 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r sfunc = (unsigned char) *ibuf++; *rbuflen = 0; - if (sfunc >= 3 && sfunc <= 6) { if (afp_version < 30) { return( AFPERR_PARAM ); @@ -2777,17 +2351,18 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r 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); @@ -2798,7 +2373,8 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r 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); @@ -2809,9 +2385,15 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r 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 ); } @@ -2865,7 +2447,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz 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 )) @@ -2874,7 +2455,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz len = ntohs(ulen); ibuf += 2; break; -#endif default : return( AFPERR_PARAM ); } @@ -2908,20 +2488,18 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz 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 ); diff --git a/etc/afpd/directory.h b/etc/afpd/directory.h index 5334be5d..76267e73 100644 --- a/etc/afpd/directory.h +++ b/etc/afpd/directory.h @@ -1,6 +1,4 @@ /* - * $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. * @@ -43,16 +41,14 @@ #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) @@ -76,10 +72,6 @@ #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 @@ -100,7 +92,6 @@ #define CNID(a,b) (((a)->st_ino & 0x7fffffff) | CNID_FILE(b)) #endif /* AFS */ - struct maccess { u_char ma_user; u_char ma_world; @@ -113,46 +104,42 @@ struct maccess { #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); diff --git a/etc/afpd/enumerate.c b/etc/afpd/enumerate.c index c06ef203..79869dbd 100644 --- a/etc/afpd/enumerate.c +++ b/etc/afpd/enumerate.c @@ -1,6 +1,4 @@ /* - * $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. */ @@ -21,8 +19,13 @@ #include #include #include +#include +#include +#include + #include "desktop.h" #include "directory.h" +#include "dircache.h" #include "volume.h" #include "globals.h" #include "file.h" @@ -266,8 +269,8 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, 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 */ @@ -370,19 +373,22 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, 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 ); diff --git a/etc/afpd/file.c b/etc/afpd/file.c index b10330c8..d3bf5600 100644 --- a/etc/afpd/file.c +++ b/etc/afpd/file.c @@ -1,6 +1,4 @@ /* - * $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. */ @@ -41,6 +39,7 @@ char *strchr (), *strrchr (); #include #include "directory.h" +#include "dircache.h" #include "desktop.h" #include "volume.h" #include "fork.h" @@ -201,9 +200,27 @@ char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t (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; @@ -213,9 +230,9 @@ restart: 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) { @@ -233,7 +250,7 @@ restart: /* 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"); @@ -267,9 +284,10 @@ restart: 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); } @@ -298,24 +316,50 @@ int getmetadata(struct vol *vol, 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 <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()); } @@ -348,11 +392,15 @@ int getmetadata(struct vol *vol, #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 : @@ -399,6 +447,8 @@ int getmetadata(struct vol *vol, 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 : @@ -563,10 +613,6 @@ int getfilparams(struct vol *vol, int opened = 0; int rc; -#ifdef DEBUG - LOG(log_debug9, logtype_default, "begin getfilparams:"); -#endif /* DEBUG */ - opened = PARAM_NEED_ADP(bitmap); adp = NULL; @@ -597,9 +643,6 @@ int getfilparams(struct vol *vol, if ( adp ) { ad_close_metadata( adp); } -#ifdef DEBUG - LOG(log_debug9, logtype_afpd, "end getfilparams:"); -#endif /* DEBUG */ return( rc ); } @@ -1945,6 +1988,10 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_ } if (NULL == ( dir = dirlookup( vol, id )) ) { + if (afp_errno == AFPERR_NOOBJ) { + err = AFPERR_NOOBJ; + goto delete; + } return( AFPERR_PARAM ); } @@ -1968,6 +2015,7 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_ 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: @@ -2188,12 +2236,11 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U /* 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)) || @@ -2287,5 +2334,11 @@ err_exchangefile: 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; } diff --git a/etc/afpd/file.h b/etc/afpd/file.h index 6cf4ae22..483f6be6 100644 --- a/etc/afpd/file.h +++ b/etc/afpd/file.h @@ -1,5 +1,5 @@ /* - * $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. @@ -127,8 +127,12 @@ extern void *get_finderinfo (const struct vol *, const char *, struct adouble *, 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); diff --git a/etc/afpd/filedir.c b/etc/afpd/filedir.c index 2a862abe..be7056a7 100644 --- a/etc/afpd/filedir.c +++ b/etc/afpd/filedir.c @@ -37,9 +37,12 @@ char *strchr (), *strrchr (); #include #include #include +#include +#include #include #include "directory.h" +#include "dircache.h" #include "desktop.h" #include "volume.h" #include "fork.h" @@ -51,22 +54,22 @@ char *strchr (), *strrchr (); #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)); @@ -118,7 +121,7 @@ more information */ adpath, strerror(errno)); ret = AFPERR_ACCESS; } - seteuid(uid); + seteuid(uid); } } /* end else if stat success */ @@ -131,13 +134,13 @@ more information */ 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; @@ -148,7 +151,7 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r 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 ); } @@ -167,11 +170,12 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r 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) { @@ -183,7 +187,9 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r of_statdir(vol, s_path); } if ( s_path->st_errno != 0 ) { - return( AFPERR_NOOBJ ); + if (afp_errno != AFPERR_ACCESS) { + return( AFPERR_NOOBJ ); + } } @@ -191,19 +197,19 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r 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 */ @@ -223,12 +229,12 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r 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; @@ -246,7 +252,7 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf ibuf += sizeof( did); if (NULL == ( dir = dirlookup( vol, did )) ) { - return afp_errno; + return afp_errno; } memcpy( &bitmap, ibuf, sizeof( bitmap )); @@ -254,7 +260,7 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf 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; @@ -266,7 +272,8 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf } if ( path->st_errno != 0 ) { - return( AFPERR_NOOBJ ); + if (afp_errno != AFPERR_ACCESS) + return( AFPERR_NOOBJ ); } /* * If ibuf is odd, make it even. @@ -287,7 +294,7 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf return( rc ); } -/* -------------------------------------------- +/* -------------------------------------------- Factorise some checks on a pathname */ int check_name(const struct vol *vol, char *name) @@ -362,14 +369,13 @@ static int moveandrename(const struct vol *vol, } } 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) @@ -410,13 +416,13 @@ static int moveandrename(const struct vol *vol, 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; @@ -454,6 +460,14 @@ static int moveandrename(const struct vol *vol, 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)); } @@ -467,13 +481,13 @@ exit: /* -------------------------------------------- */ 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; @@ -492,12 +506,12 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size 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; @@ -512,14 +526,14 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size } } 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 */ @@ -542,12 +556,12 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size /* ------------------------------- */ 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; @@ -563,22 +577,21 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size 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 { @@ -591,10 +604,16 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size } 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 ); } @@ -603,66 +622,51 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size /* ------------------------ */ 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; @@ -691,13 +695,13 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U /* 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) { @@ -705,7 +709,7 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U } 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 @@ -739,11 +743,13 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U } /* 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; @@ -786,8 +792,8 @@ int veto_file(const char*veto_str, const char*path) * 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; @@ -802,7 +808,7 @@ int veto_file(const char*veto_str, const char*path) } else { if (veto_str[i] != path[j]) { while ((veto_str[i] != '/') - && (veto_str[i] != '\0')) + && (veto_str[i] != '\0')) i++; j = 0; continue; diff --git a/etc/afpd/fork.c b/etc/afpd/fork.c index e8c019e1..f4d0d04c 100644 --- a/etc/afpd/fork.c +++ b/etc/afpd/fork.c @@ -73,7 +73,7 @@ static int getforkparams(struct ofork *ofork, u_int16_t bitmap, char *buf, size_ } 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 ); diff --git a/etc/afpd/fork.h b/etc/afpd/fork.h index 5a42afc7..ab75f1f7 100644 --- a/etc/afpd/fork.h +++ b/etc/afpd/fork.h @@ -1,6 +1,4 @@ /* - * $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. */ @@ -17,35 +15,33 @@ #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) diff --git a/etc/afpd/globals.h b/etc/afpd/globals.h index 40af2fe1..e37ebf64 100644 --- a/etc/afpd/globals.h +++ b/etc/afpd/globals.h @@ -35,6 +35,8 @@ #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 */ @@ -54,7 +56,7 @@ struct afp_volume_name { }; struct afp_options { - int connections, transports, tickleval, timeout, server_notif, flags; + int connections, transports, tickleval, timeout, server_notif, flags, dircachesize; unsigned char passwdbits, passwdminlen, loginmaxfail; u_int32_t server_quantum; char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile; @@ -62,6 +64,7 @@ struct afp_options { char *uampath, *fqdn; char *pidfile; char *sigconffile; + char *uuidconf; struct afp_volume_name defaultvol, systemvol, uservol; int closevol; @@ -83,13 +86,15 @@ struct afp_options { /* 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]; @@ -142,6 +147,9 @@ extern int parseline (int, char *); 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 */ diff --git a/etc/afpd/main.c b/etc/afpd/main.c index 8f63fc6e..30a32346 100644 --- a/etc/afpd/main.c +++ b/etc/afpd/main.c @@ -38,6 +38,7 @@ #include "status.h" #include "fork.h" #include "uam_auth.h" +#include "afp_zeroconf.h" #ifdef TRU64 #include @@ -55,6 +56,7 @@ static AFPConfig *configs; static server_child *server_children; static fd_set save_rfds; static int Ipc_fd = -1; +static sig_atomic_t reloadconfig = 0; #ifdef TRU64 void afp_get_cmdline( int *ac, char ***av) @@ -98,50 +100,30 @@ static void afp_goaway(int sig) dsi_kill(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); + afp_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; } @@ -165,9 +147,12 @@ 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); @@ -275,12 +260,12 @@ int main(int ac, char **av) #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); } - sigprocmask(SIG_UNBLOCK, &sigs, NULL); + pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); /* Register CNID */ cnid_init(); @@ -299,9 +284,34 @@ int main(int ac, char **av) * solution. */ while (1) { rfds = save_rfds; - sigprocmask(SIG_UNBLOCK, &sigs, NULL); + pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL); - sigprocmask(SIG_BLOCK, &sigs, NULL); + pthread_sigmask(SIG_BLOCK, &sigs, NULL); + int saveerrno = errno; + + if (reloadconfig) { + nologin++; + auth_unload(); + 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); + + /* 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); + nologin = 0; + reloadconfig = 0; + errno = saveerrno; + } + if (ret < 0) { if (errno == EINTR) continue; diff --git a/etc/afpd/mangle.c b/etc/afpd/mangle.c index c00b76ba..91533e61 100644 --- a/etc/afpd/mangle.c +++ b/etc/afpd/mangle.c @@ -1,5 +1,5 @@ /* - * $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. @@ -15,9 +15,13 @@ #include #include + +#include +#include + #include "mangle.h" #include "desktop.h" -#include + #define hextoint( c ) ( isdigit( c ) ? c - '0' : c + 10 - 'A' ) #define isuxdigit(x) (isdigit(x) || (isupper(x) && isxdigit(x))) @@ -38,7 +42,7 @@ static size_t mangle_extension(const struct vol *vol, const char* uname, 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 */ @@ -48,18 +52,18 @@ static char *demangle_checks ( const struct vol *vol, char* uname, char * mfilen /* 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 */ @@ -169,8 +173,8 @@ private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx } /* 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 */ @@ -178,13 +182,12 @@ private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx } 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) { diff --git a/etc/afpd/ofork.c b/etc/afpd/ofork.c index 49c014cc..f5f8a38d 100644 --- a/etc/afpd/ofork.c +++ b/etc/afpd/ofork.c @@ -1,5 +1,5 @@ /* - * $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. @@ -17,10 +17,12 @@ #include #include /* works around a bug */ #include -#include #include +#include #include +#include +#include #include "globals.h" #include "volume.h" @@ -32,9 +34,9 @@ #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*/ @@ -45,7 +47,7 @@ static unsigned long hashfn(const struct file_key *key) while (*name) { i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++; } -#endif +#endif return key->inode & (OFORK_HASHSIZE - 1); } @@ -72,7 +74,7 @@ static void of_unhash(struct ofork *of) #ifdef DEBUG1 void of_pforkdesc( FILE *f) { - int ofrefnum; + int ofrefnum; if (!oforks) return; @@ -87,14 +89,14 @@ void of_pforkdesc( FILE *f) 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) ); } } @@ -106,65 +108,51 @@ int of_rename(const struct vol *vol, 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) @@ -180,22 +168,22 @@ of_alloc(struct vol *vol, } } /* 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 ) { @@ -205,7 +193,7 @@ of_alloc(struct vol *vol, 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; } @@ -245,17 +233,7 @@ of_alloc(struct vol *vol, 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; @@ -279,16 +257,23 @@ struct ofork *of_find(const u_int16_t ofrefnum ) } /* -------------------------- */ -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) { @@ -309,10 +294,12 @@ 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 */ @@ -321,20 +308,28 @@ int ret; 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; } @@ -343,11 +338,11 @@ struct ofork *of_findname(struct path *path) { struct ofork *of; struct file_key key; - + if (!path->st_valid) { - of_stat(path); + of_stat(path); } - + if (path->st_errno) return NULL; @@ -404,14 +399,6 @@ void of_dealloc( struct ofork *of) 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 */ @@ -431,12 +418,12 @@ void of_dealloc( struct ofork *of) 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; @@ -447,10 +434,10 @@ int of_closefork(struct ofork *ofork) 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 ); } } } @@ -458,14 +445,14 @@ int of_closefork(struct ofork *ofork) 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; @@ -480,12 +467,12 @@ struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble * return adp; } -/* ---------------------- +/* ---------------------- close all forks for a volume */ void of_closevol(const struct vol *vol) { - int refnum; + int refnum; if (!oforks) return; diff --git a/etc/afpd/status.c b/etc/afpd/status.c index b264bbb9..8147a038 100644 --- a/etc/afpd/status.c +++ b/etc/afpd/status.c @@ -560,7 +560,7 @@ void set_signature(struct afp_options *options) { char *servername_conf; int header = 0; char buf[1024], *p; - FILE *fp, *randomp; + FILE *fp = NULL, *randomp; size_t len; char *server_tmp; @@ -663,14 +663,14 @@ server_signature_auto: 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; @@ -682,41 +682,9 @@ server_signature_auto: 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"); diff --git a/etc/afpd/switch.c b/etc/afpd/switch.c index 2e2c69e0..7dbd9ab7 100644 --- a/etc/afpd/switch.c +++ b/etc/afpd/switch.c @@ -1,6 +1,4 @@ /* - * $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. * @@ -46,7 +44,7 @@ #include "filedir.h" #include "status.h" #include "misc.h" -#ifdef HAVE_NFSv4_ACLS +#ifdef HAVE_ACLS #include "acls.h" #endif diff --git a/etc/afpd/unix.c b/etc/afpd/unix.c index 0df7ee54..9b68c3d4 100644 --- a/etc/afpd/unix.c +++ b/etc/afpd/unix.c @@ -1,6 +1,4 @@ /* - * $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. */ @@ -45,12 +43,10 @@ char *strchr (), *strrchr (); #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. */ @@ -173,9 +169,8 @@ mode_t mode; * 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) { @@ -184,9 +179,8 @@ struct stat sb; 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 } diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c index 9a5a4dc7..e53599b0 100644 --- a/etc/afpd/volume.c +++ b/etc/afpd/volume.c @@ -35,6 +35,7 @@ char *strchr (), *strrchr (); #include #include #include + #include #include #include @@ -43,7 +44,8 @@ char *strchr (), *strrchr (); #include #include #include -#include +#include + #ifdef CNID_DB #include #endif /* CNID_DB*/ @@ -56,6 +58,7 @@ char *strchr (), *strrchr (); #include "mangle.h" #include "fork.h" #include "hash.h" +#include "acls.h" extern int afprun(int root, char *cmd, int *outfd); @@ -74,6 +77,13 @@ 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"; @@ -173,7 +183,10 @@ static void volfree(struct vol_option *options, const struct vol_option *save) } -/* 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 @@ -187,17 +200,37 @@ static void volfree(struct vol_option *options, const struct vol_option *save) * $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 (! ((DSI *)obj->handle)->child) + afpmaster = 1; + + if (path && !volname) + /* cf above */ + xlatevolname = 1; if (!src) { return NULL; @@ -224,6 +257,8 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen, /* 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; @@ -231,6 +266,8 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen, q++; } } else if (is_var(p, "$c")) { + if (afpmaster && xlatevolname) + return NULL; if (obj->proto == AFPPROTO_ASP) { ASP asp = obj->handle; @@ -248,18 +285,26 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen, 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; @@ -278,13 +323,17 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen, 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; } @@ -445,8 +494,6 @@ static void volset(struct vol_option *options, struct vol_option *save, 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) @@ -457,7 +504,13 @@ static void volset(struct vol_option *options, struct vol_option *save, 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, ","); } @@ -559,6 +612,8 @@ static int creatvol(AFPObj *obj, struct passwd *pwd, 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 */ @@ -599,7 +654,7 @@ static int creatvol(AFPObj *obj, struct passwd *pwd, 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 */ @@ -625,7 +680,7 @@ static int creatvol(AFPObj *obj, struct passwd *pwd, 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 ) { @@ -678,14 +733,16 @@ static int creatvol(AFPObj *obj, struct passwd *pwd, /* 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; @@ -807,6 +864,19 @@ static int creatvol(AFPObj *obj, struct passwd *pwd, 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; @@ -1074,7 +1144,10 @@ static int volfile_changed(struct afp_volume_name *p) /* ---------------------- * 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. @@ -1082,8 +1155,9 @@ static int volfile_changed(struct afp_volume_name *p) * [] [allow:,<@group>,...] \ * [codepage:] [casefold:] * 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]; @@ -1092,12 +1166,12 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us 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; @@ -1129,6 +1203,8 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us 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 ); @@ -1174,29 +1250,16 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us /* 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'; @@ -1208,27 +1271,32 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us 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 (!((DSI *)obj->handle)->child + || + (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 (((DSI *)obj->handle)->child + && + ((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); @@ -1274,6 +1342,8 @@ static void volume_free(struct vol *vol) free(vol->v_forceuid); free(vol->v_forcegid); #endif /* FORCE_UIDGID */ + if (vol->v_uuid) + free(vol->v_uuid); } /* ------------------------------- */ @@ -1669,6 +1739,12 @@ void load_volumes(AFPObj *obj) free_volumes(); } + if (! ((DSI *)obj->handle)->child) { + 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); @@ -1837,23 +1913,46 @@ static int volume_openDB(struct vol *volume) 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 */ @@ -1870,11 +1969,11 @@ static int volume_openDB(struct vol *volume) 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) { @@ -2039,7 +2138,7 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t if ((tmp = strdup(volume->v_path)) == NULL) { free(volume->v_path); return AFPERR_MISC; - } + } free(volume->v_path); volume->v_path = tmp; #endif @@ -2059,9 +2158,6 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t volume->max_filename = MACFILELEN; } - volume->v_dir = volume->v_root = NULL; - volume->v_hash = NULL; - volume->v_flags |= AFPVOL_OPEN; volume->v_cdb = NULL; @@ -2080,22 +2176,23 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t 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); @@ -2134,16 +2231,15 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t } 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; @@ -2161,9 +2257,8 @@ static void closevol(struct vol *vol) 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; @@ -2204,7 +2299,7 @@ static void deletevol(struct vol *vol) 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; } } @@ -2231,6 +2326,7 @@ int afp_closevol(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_ } deletevol(vol); + current_vol = NULL; return( AFP_OK ); } @@ -2253,6 +2349,8 @@ struct vol *getvolbyvid(const u_int16_t vid ) set_uidgid ( vol ); #endif /* FORCE_UIDGID */ + current_vol = vol; + return( vol ); } @@ -2510,7 +2608,7 @@ static int create_special_folder (const struct vol *vol, const struct _special_f free(q); return (-1); } - + ad_setname(&ad, folder->name); ad_getattr(&ad, &attr); @@ -2544,3 +2642,111 @@ static void handle_special_folders (const struct vol * vol) } } +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); +} diff --git a/etc/afpd/volume.h b/etc/afpd/volume.h index 63d09003..b33e9c31 100644 --- a/etc/afpd/volume.h +++ b/etc/afpd/volume.h @@ -1,6 +1,4 @@ /* - * $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. */ @@ -21,13 +19,21 @@ #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); @@ -39,4 +45,6 @@ int afp_closevol (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size /* netatalk functions */ extern void close_all_vol (void); +struct vol *current_vol; /* last volume from getvolbyvid() */ + #endif diff --git a/etc/cnid_dbd/Makefile.am b/etc/cnid_dbd/Makefile.am index 3f254a3b..802ad3ab 100644 --- a/etc/cnid_dbd/Makefile.am +++ b/etc/cnid_dbd/Makefile.am @@ -10,11 +10,11 @@ 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 \ @@ -26,7 +26,7 @@ dbd_SOURCES = cmd_dbd.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 diff --git a/etc/cnid_dbd/cmd_dbd.c b/etc/cnid_dbd/cmd_dbd.c index dc4c3923..0b81b8cc 100644 --- a/etc/cnid_dbd/cmd_dbd.c +++ b/etc/cnid_dbd/cmd_dbd.c @@ -1,6 +1,4 @@ /* - $Id: cmd_dbd.c,v 1.26 2010-04-20 16:46:20 hat001 Exp $ - Copyright (c) 2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -84,6 +82,7 @@ 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 */ @@ -92,6 +91,8 @@ static struct db_param db_param = { NULL, /* Volume dirpath */ 1, /* bdb logfile autoremove */ 64 * 1024, /* bdb cachesize (64 MB) */ + 5000, /* maxlocks */ + 5000, /* maxlockobjs */ -1, /* not used ... */ -1, "", @@ -99,7 +100,7 @@ static struct db_param db_param = { -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 @@ -279,7 +280,6 @@ int main(int argc, char **argv) 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) { @@ -380,12 +380,25 @@ int main(int argc, char **argv) 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 */ @@ -412,7 +425,7 @@ int main(int argc, char **argv) /* Prepare upgrade ? */ if (prep_upgrade) { - if (dbif_prep_upgrade(dbpath)) + if (dbif_env_remove(dbpath)) goto exit_failure; goto exit_success; } @@ -420,9 +433,13 @@ int main(int argc, char **argv) /* 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."); } @@ -445,11 +462,6 @@ int main(int argc, char **argv) dbif_close(dbd); goto exit_failure; } - - if (dbd_stamp(dbd) < 0) { - dbif_close(dbd); - goto exit_failure; - } } /* Now execute given command scan|rebuild|dump */ diff --git a/etc/cnid_dbd/cmd_dbd.h b/etc/cnid_dbd/cmd_dbd.h index a1d51b67..ac6cde42 100644 --- a/etc/cnid_dbd/cmd_dbd.h +++ b/etc/cnid_dbd/cmd_dbd.h @@ -25,8 +25,6 @@ typedef unsigned int dbd_flags_t; 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); diff --git a/etc/cnid_dbd/cmd_dbd_scanvol.c b/etc/cnid_dbd/cmd_dbd_scanvol.c index 45d67ae1..240d4150 100644 --- a/etc/cnid_dbd/cmd_dbd_scanvol.c +++ b/etc/cnid_dbd/cmd_dbd_scanvol.c @@ -44,11 +44,9 @@ #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; @@ -85,22 +83,22 @@ static char *utompath(char *upath) 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; } @@ -126,17 +124,17 @@ static char *mtoupath(char *mpath) } /* 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; } @@ -147,11 +145,11 @@ static char *mtoupath(char *mpath) 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; } @@ -224,8 +222,8 @@ static int check_symlink(const char *name, int *adflags) 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; } @@ -306,7 +304,7 @@ static int check_adfile(const char *fname, const struct stat *st) 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) { @@ -322,7 +320,7 @@ static int check_adfile(const char *fname, const struct stat *st) 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", @@ -342,7 +340,7 @@ static int check_adfile(const char *fname, const struct stat *st) 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; @@ -472,10 +470,10 @@ static int check_addir(int volroot) } /* 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); @@ -494,7 +492,7 @@ static int check_addir(int volroot) } /* 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)); @@ -515,7 +513,7 @@ static int check_addir(int volroot) 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; @@ -534,7 +532,7 @@ static int check_eafile_in_adouble(const char *name) 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) { @@ -677,8 +675,8 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi /* 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) @@ -712,7 +710,7 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi 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; @@ -769,12 +767,12 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi 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)); @@ -808,11 +806,11 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi 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)); @@ -956,7 +954,7 @@ static int dbd_readdir(int volroot, cnid_t did) } /* Check EA files */ - if (volinfo->v_vfs_ea == AFPVOL_EA_AD) + if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) check_eafiles(ep->d_name); /************************************************************************** @@ -1003,22 +1001,22 @@ static int scanvol(struct volinfo *vi, dbd_flags_t flags) } /* 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 */ @@ -1073,7 +1071,12 @@ static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags) 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++; } @@ -1089,10 +1092,14 @@ static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags) 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++; } @@ -1108,7 +1115,7 @@ static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags) 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 */ @@ -1119,7 +1126,7 @@ cleanup: /* 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 }; @@ -1131,8 +1138,8 @@ int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags) 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; } @@ -1156,7 +1163,7 @@ int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags) goto exit_cleanup; /* Got signal, jump from dbd_readdir */ /* scanvol */ - if ( (scanvol(volinfo, flags)) != 0) + if ( (scanvol(vi, flags)) != 0) return -1; if (! nocniddb) { diff --git a/etc/cnid_dbd/cnid_metad.c b/etc/cnid_dbd/cnid_metad.c index dc13fb75..85ea7a4f 100644 --- a/etc/cnid_dbd/cnid_metad.c +++ b/etc/cnid_dbd/cnid_metad.c @@ -1,6 +1,7 @@ /* * Copyright (C) Joerg Lenneis 2003 - * Copyright (C) Frank Lahm 2010 + * Copyright (C) Frank Lahm 2009, 2010 + * * All Rights Reserved. See COPYING. */ @@ -20,6 +21,8 @@ Result: via TCP socket 4. afpd -------> cnid_dbd + + cnid_metad and cnid_dbd have been converted to non-blocking IO in 2010. */ @@ -35,18 +38,10 @@ #include #include #include -#ifdef HAVE_SYS_TYPES_H #include -#endif -#ifdef HAVE_SYS_TIME_H #include -#endif -#ifdef HAVE_SYS_WAIT_H #include -#endif -#ifdef HAVE_SYS_UIO_H #include -#endif #include #define _XPG4_2 1 #include @@ -94,8 +89,8 @@ #include #include #include +#include -#include "db_param.h" #include "usockfd.h" #define DBHOME ".AppleDB" @@ -113,7 +108,7 @@ static volatile sig_atomic_t sigchild = 0; #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 */ @@ -144,11 +139,11 @@ static void sigterm_handler(int sig) 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]; } } @@ -209,7 +204,7 @@ static int send_cred(int socket, int fd) } /* -------------------- */ -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; @@ -218,11 +213,11 @@ static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn) 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) { @@ -232,17 +227,18 @@ static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn) 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; } } @@ -250,8 +246,7 @@ static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn) 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 */ && @@ -311,11 +306,11 @@ static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn) /* 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)); @@ -331,12 +326,12 @@ static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn) } /* ------------------ */ -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); @@ -465,14 +460,13 @@ static void set_signal(void) /* ------------------ */ 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; @@ -483,6 +477,7 @@ int main(int argc, char *argv[]) char *loglevel = NULL; char *logfile = NULL; sigset_t set; + struct volinfo *volinfo; set_processname("cnid_metad"); @@ -543,7 +538,7 @@ int main(int argc, char *argv[]) } /* 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 */ @@ -602,9 +597,8 @@ int main(int argc, char *argv[]) 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; @@ -626,7 +620,7 @@ int main(int argc, char *argv[]) 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; @@ -635,17 +629,21 @@ int main(int argc, char *argv[]) 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); diff --git a/etc/cnid_dbd/comm.c b/etc/cnid_dbd/comm.c index 284ecae4..89355d76 100644 --- a/etc/cnid_dbd/comm.c +++ b/etc/cnid_dbd/comm.c @@ -1,7 +1,7 @@ /* - * $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. */ @@ -13,37 +13,19 @@ #include #include #include - -#ifdef HAVE_UNISTD_H #include -#endif - #include -#define _XPG4_2 1 -#include - -#ifdef HAVE_SYS_TYPES_H #include -#endif - -#ifdef HAVE_SYS_TIME_H #include -#endif - -#ifdef HAVE_SYS_UIO_H #include -#endif - -#ifdef HAVE_SYS_SOCKET_H +#define _XPG4_2 1 #include -#endif - #include - #include #include #include +#include #include #include "db_param.h" @@ -264,8 +246,16 @@ int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask 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)); @@ -274,7 +264,8 @@ int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask 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; @@ -283,6 +274,8 @@ int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask needs zero terminated strings. */ rqst->name[rqst->namelen] = '\0'; + LOG(log_maxdebug, logtype_cnid, "comm_rcv: got %u bytes", b + rqst->namelen); + return 1; } diff --git a/etc/cnid_dbd/comm.h b/etc/cnid_dbd/comm.h index 614bd27d..46d91fc1 100644 --- a/etc/cnid_dbd/comm.h +++ b/etc/cnid_dbd/comm.h @@ -1,6 +1,4 @@ /* - * $Id: comm.h,v 1.5 2009-10-19 08:09:07 didg Exp $ - * * Copyright (C) Joerg Lenneis 2003 * All Rights Reserved. See COPYING. */ @@ -8,6 +6,8 @@ #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 diff --git a/etc/cnid_dbd/db_param.c b/etc/cnid_dbd/db_param.c index 029c21b0..e31e7827 100644 --- a/etc/cnid_dbd/db_param.c +++ b/etc/cnid_dbd/db_param.c @@ -26,7 +26,9 @@ #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" @@ -66,6 +68,8 @@ static void default_params(struct db_param *dbp, char *dir) { 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) { @@ -98,7 +102,7 @@ static int parse_int(char *val) 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]; @@ -145,33 +149,32 @@ struct db_param *db_param_read(char *dir, enum identity id) 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; } diff --git a/etc/cnid_dbd/db_param.h b/etc/cnid_dbd/db_param.h index ebcea6b8..67ec62e4 100644 --- a/etc/cnid_dbd/db_param.h +++ b/etc/cnid_dbd/db_param.h @@ -1,7 +1,6 @@ /* - * $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. */ @@ -11,15 +10,12 @@ #include #include -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]; @@ -28,8 +24,7 @@ struct db_param { 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 */ diff --git a/etc/cnid_dbd/dbd.h b/etc/cnid_dbd/dbd.h index 0dbff51d..b4f7e5d4 100644 --- a/etc/cnid_dbd/dbd.h +++ b/etc/cnid_dbd/dbd.h @@ -1,8 +1,6 @@ /* - * $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. */ @@ -14,7 +12,6 @@ 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 *); @@ -23,6 +20,7 @@ extern int dbd_update(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 */ diff --git a/etc/cnid_dbd/dbd_add.c b/etc/cnid_dbd/dbd_add.c index 85676b2e..6a818a3b 100644 --- a/etc/cnid_dbd/dbd_add.c +++ b/etc/cnid_dbd/dbd_add.c @@ -1,7 +1,6 @@ /* - * $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. */ @@ -83,21 +82,35 @@ int add_cnid(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply) /* ---------------------- */ 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) { @@ -105,12 +118,10 @@ int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply) 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; @@ -120,44 +131,6 @@ int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply) 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) diff --git a/etc/cnid_dbd/dbd_getstamp.c b/etc/cnid_dbd/dbd_getstamp.c index b7fc936a..ea75c4ab 100644 --- a/etc/cnid_dbd/dbd_getstamp.c +++ b/etc/cnid_dbd/dbd_getstamp.c @@ -1,3 +1,4 @@ + /* * $Id: dbd_getstamp.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $ * diff --git a/etc/cnid_dbd/dbd_resolve.c b/etc/cnid_dbd/dbd_resolve.c index 6008d152..17a42de0 100644 --- a/etc/cnid_dbd/dbd_resolve.c +++ b/etc/cnid_dbd/dbd_resolve.c @@ -50,8 +50,8 @@ int dbd_resolve(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply 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); diff --git a/etc/cnid_dbd/dbd_search.c b/etc/cnid_dbd/dbd_search.c new file mode 100644 index 00000000..a2efc33c --- /dev/null +++ b/etc/cnid_dbd/dbd_search.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) Frank Lahm 2010 + * All Rights Reserved. See COPYING. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include + +#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; +} diff --git a/etc/cnid_dbd/dbif.c b/etc/cnid_dbd/dbif.c index ea22816b..2b5b91ac 100644 --- a/etc/cnid_dbd/dbif.c +++ b/etc/cnid_dbd/dbif.c @@ -11,36 +11,33 @@ #include #include #include -#ifdef HAVE_SYS_TYPES_H -#include -#endif /* HAVE_SYS_TYPES_H */ #include #include #include #include #include -#include + #include + +#include +#include + #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) { @@ -51,30 +48,167 @@ static int upgrade_required(const DBD *dbd) /* 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; + + 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; + + 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 */ + ret = 0; + break; + default: 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, as does dbif_getversion + * @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 + * + * For now this does nothing, as upgrading from ver. 0 to 1 is done in dbif_open + */ +static int dbif_upgrade(DBD *dbd) +{ + uint32_t version; + + if (dbif_getversion(dbd, &version) == -1) + return -1; + + /* + * Do upgrade stuff ... + */ + + /* Write current version to database */ + if (version != CNID_VERSION) { + if (dbif_setversion(dbd, CNID_VERSION) != 0) + return -1; } - return (ret < 0 ? ret : found); + + LOG(log_debug, logtype_cnid, "Finished CNID database version upgrade check"); + + return 0; } /* --------------- */ @@ -172,42 +306,6 @@ exit: 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) { @@ -238,14 +336,19 @@ 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; } @@ -253,7 +356,7 @@ DBD *dbif_init(const char *envhome, const char *filename) /* 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. */ @@ -261,12 +364,6 @@ int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags) { 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)); @@ -324,6 +421,22 @@ int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags) 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)); @@ -406,7 +519,7 @@ int dbif_open(DBD *dbd, struct db_param *dbp, int reindex) 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) { @@ -495,6 +608,34 @@ int dbif_open(DBD *dbd, struct db_param *dbp, int reindex) } if (reindex) LOG(log_info, logtype_cnid, "... done."); + + if (reindex) + LOG(log_info, logtype_cnid, "Reindexing name index..."); + + 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; } @@ -548,7 +689,7 @@ int dbif_close(DBD *dbd) 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; @@ -715,8 +856,10 @@ int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags) 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)); @@ -725,6 +868,60 @@ int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags) 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; @@ -871,7 +1068,7 @@ int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd) 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; @@ -898,11 +1095,15 @@ int dbif_dump(DBD *dbd, int dumpindexes) /* 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); diff --git a/etc/cnid_dbd/dbif.h b/etc/cnid_dbd/dbif.h index e2903b28..9c886fb1 100644 --- a/etc/cnid_dbd/dbif.h +++ b/etc/cnid_dbd/dbif.h @@ -58,11 +58,12 @@ #include #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 { @@ -80,7 +81,7 @@ 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 */ @@ -88,14 +89,14 @@ extern DBD *dbif_init(const char *envhome, const char *dbname); extern int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags); extern int dbif_open(DBD *dbd, struct db_param *dbp, int reindex); 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 *); diff --git a/etc/cnid_dbd/main.c b/etc/cnid_dbd/main.c index 42cf2848..ec0793cb 100644 --- a/etc/cnid_dbd/main.c +++ b/etc/cnid_dbd/main.c @@ -1,6 +1,4 @@ /* - * $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. @@ -34,6 +32,7 @@ #include #include #include +#include #include "db_param.h" #include "dbif.h" @@ -48,8 +47,10 @@ */ #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) @@ -176,12 +177,15 @@ static int loop(struct db_param *dbp) 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; @@ -270,6 +274,7 @@ static int get_lock(void) 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)); @@ -321,7 +326,7 @@ int main(int argc, char *argv[]) struct db_param *dbp; int err = 0; int lockfd, ctrlfd, clntfd; - char *dir, *logconfig; + char *logconfig; set_processname("cnid_dbd"); @@ -331,26 +336,43 @@ int main(int argc, char *argv[]) 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"); @@ -367,12 +389,6 @@ int main(int argc, char *argv[]) } 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); @@ -384,7 +400,7 @@ int main(int argc, char *argv[]) if (dbif_close(dbd) < 0) err++; - if (dbif_prep_upgrade(dir) < 0) + if (dbif_env_remove(dbpath) < 0) err++; free_lock(lockfd); diff --git a/etc/cnid_dbd/pack.c b/etc/cnid_dbd/pack.c index 1b4a15be..26124694 100644 --- a/etc/cnid_dbd/pack.c +++ b/etc/cnid_dbd/pack.c @@ -1,7 +1,6 @@ /* - * $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. */ @@ -12,16 +11,20 @@ #include #include -#ifdef HAVE_SYS_TYPES_H -#include -#endif /* HAVE_SYS_TYPES_H */ +#include #include #include #include +#include +#include +#include #include #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 @@ -72,6 +75,29 @@ int devino(DB *dbp _U_, const DBT *pkey _U_, const DBT *pdata, DBT *skey) 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. */ diff --git a/etc/cnid_dbd/pack.h b/etc/cnid_dbd/pack.h index fb698499..e94ef0d9 100644 --- a/etc/cnid_dbd/pack.h +++ b/etc/cnid_dbd/pack.h @@ -1,7 +1,6 @@ /* - * $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. */ @@ -23,5 +22,6 @@ 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 */ diff --git a/etc/cnid_dbd/usockfd.c b/etc/cnid_dbd/usockfd.c index 1817c0bc..ceb6ff21 100644 --- a/etc/cnid_dbd/usockfd.c +++ b/etc/cnid_dbd/usockfd.c @@ -1,6 +1,4 @@ /* - * $Id: usockfd.c,v 1.6 2009-11-05 14:38:07 franklahm Exp $ - * * Copyright (C) Joerg Lenneis 2003 * All Rights Reserved. See COPYING. */ diff --git a/include/atalk/Makefile.am b/include/atalk/Makefile.am index 69000564..2bf09187 100644 --- a/include/atalk/Makefile.am +++ b/include/atalk/Makefile.am @@ -4,8 +4,8 @@ atalkincludedir = $(includedir)/atalk 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 diff --git a/include/atalk/acl.h b/include/atalk/acl.h index c49ed0f4..53d10b19 100644 --- a/include/atalk/acl.h +++ b/include/atalk/acl.h @@ -1,5 +1,4 @@ /* - $Id: acl.h,v 1.1 2009-10-14 15:04:01 franklahm Exp $ Copyright (c) 2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -20,22 +19,28 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#ifdef HAVE_NFSv4_ACLS +#ifdef HAVE_ACLS + +#ifdef HAVE_SOLARIS_ACLS #include -#endif /* HAVE_NFSv4_ACLS */ +#endif /* HAVE_SOLARIS_ACLS */ -/* Solaris NFSv4 ACL stuff */ -#ifdef HAVE_NFSv4_ACLS +#ifdef HAVE_POSIX_ACLS +#include +#include +#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 */ diff --git a/include/atalk/adouble.h b/include/atalk/adouble.h index 1769c7b9..53337c78 100644 --- a/include/atalk/adouble.h +++ b/include/atalk/adouble.h @@ -56,11 +56,10 @@ 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 #include diff --git a/include/atalk/bstradd.h b/include/atalk/bstradd.h new file mode 100644 index 00000000..310349b2 --- /dev/null +++ b/include/atalk/bstradd.h @@ -0,0 +1,47 @@ +/* + 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. +*/ + +/*! + * @file + * Additional functions for bstrlib. + */ + +#ifndef ATALK_BSTRADD_H +#define ATALK_BSTRADD_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#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 */ diff --git a/include/atalk/bstrlib.h b/include/atalk/bstrlib.h new file mode 100644 index 00000000..0cd37197 --- /dev/null +++ b/include/atalk/bstrlib.h @@ -0,0 +1,300 @@ +/* + * 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 +#include +#include +#include + +#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 diff --git a/include/atalk/cnid.h b/include/atalk/cnid.h index 8dcb33fb..d7df70e1 100644 --- a/include/atalk/cnid.h +++ b/include/atalk/cnid.h @@ -1,9 +1,8 @@ -/* - * $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 - * + * 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 @@ -11,10 +10,10 @@ * */ -/* - * 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. @@ -33,8 +32,8 @@ #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 */ @@ -50,29 +49,30 @@ * 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. */ @@ -88,10 +88,10 @@ struct cnid_open_args { * 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; @@ -107,98 +107,22 @@ struct _cnid_db *cnid_open(const char *volpath, 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). - * - */ - diff --git a/include/atalk/cnid_dbd_private.h b/include/atalk/cnid_dbd_private.h index 4e4847a7..36dee3d8 100644 --- a/include/atalk/cnid_dbd_private.h +++ b/include/atalk/cnid_dbd_private.h @@ -24,21 +24,26 @@ #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; }; @@ -46,7 +51,7 @@ struct cnid_dbd_rply { int result; cnid_t cnid; cnid_t did; - char *name; + char *name; size_t namelen; }; diff --git a/include/atalk/cnid_private.h b/include/atalk/cnid_private.h index 6525ce48..4e0e0b55 100644 --- a/include/atalk/cnid_private.h +++ b/include/atalk/cnid_private.h @@ -18,12 +18,12 @@ #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) @@ -38,13 +38,21 @@ #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" \ @@ -54,4 +62,15 @@ "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 + +/* Current CNID version */ +#define CNID_VERSION CNID_VERSION_1 + #endif diff --git a/include/atalk/directory.h b/include/atalk/directory.h index 17e5d087..d15c0489 100644 --- a/include/atalk/directory.h +++ b/include/atalk/directory.h @@ -1,6 +1,4 @@ /* - * $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. * @@ -30,9 +28,11 @@ #include #include #include +#include #include -#include +#include +#include /* setgid directories */ #ifndef DIRBITS @@ -43,30 +43,33 @@ # 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; @@ -83,5 +86,4 @@ static inline int path_isadir(struct path *o_path) #endif } - #endif /* ATALK_DIRECTORY_H */ diff --git a/include/atalk/dsi.h b/include/atalk/dsi.h index 82da7a15..0616774d 100644 --- a/include/atalk/dsi.h +++ b/include/atalk/dsi.h @@ -94,6 +94,10 @@ typedef struct DSI { char srvloc_url[512]; #endif +#ifdef USE_ZEROCONF + int zeroconf_registered; +#endif + /* buffer for OSX deadlock */ char *buffer; char *start; diff --git a/include/atalk/ea.h b/include/atalk/ea.h index 00d987de..c0165a1a 100644 --- a/include/atalk/ea.h +++ b/include/atalk/ea.h @@ -20,7 +20,7 @@ #include #endif -#ifdef HAVE_NFSv4_ACLS +#ifdef HAVE_SOLARIS_ACLS #include #endif diff --git a/include/atalk/errchk.h b/include/atalk/errchk.h new file mode 100644 index 00000000..64a12764 --- /dev/null +++ b/include/atalk/errchk.h @@ -0,0 +1,163 @@ +/* + 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. + */ + +#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 */ diff --git a/include/atalk/ftw.h b/include/atalk/ftw.h new file mode 100644 index 00000000..157efc50 --- /dev/null +++ b/include/atalk/ftw.h @@ -0,0 +1,109 @@ +/* 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 +#include + +/* 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 */ diff --git a/include/atalk/ldapconfig.h b/include/atalk/ldapconfig.h index 2fb2ca29..70f9f628 100644 --- a/include/atalk/ldapconfig.h +++ b/include/atalk/ldapconfig.h @@ -1,4 +1,4 @@ -#ifdef HAVE_NFSv4_ACLS +#ifdef HAVE_ACLS #ifndef LDAPCONFIG_H #define LDAPCONFIG_H @@ -37,6 +37,6 @@ extern struct ldap_pref ldap_prefs[]; extern struct pref_array prefs_array[]; extern int ldap_config_valid; -#endif +#endif /* LDAPCONFIG_H */ -#endif +#endif /* HAVE_ACLS */ diff --git a/include/atalk/queue.h b/include/atalk/queue.h new file mode 100644 index 00000000..a3f433eb --- /dev/null +++ b/include/atalk/queue.h @@ -0,0 +1,41 @@ +/* + 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 */ diff --git a/include/atalk/util.h b/include/atalk/util.h index 13929953..63da2935 100644 --- a/include/atalk/util.h +++ b/include/atalk/util.h @@ -1,7 +1,3 @@ -/* - * $Id: util.h,v 1.21 2010-02-28 22:29:16 didg Exp $ - */ - /*! * @file * Netatalk utility functions @@ -28,6 +24,26 @@ #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); @@ -44,9 +60,9 @@ extern int atalk_aton (char *, struct at_addr *); 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 */ @@ -122,6 +138,7 @@ extern int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2); *****************************************************************/ 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 */ diff --git a/include/atalk/uuid.h b/include/atalk/uuid.h index 25db56ec..e3b2be11 100644 --- a/include/atalk/uuid.h +++ b/include/atalk/uuid.h @@ -1,5 +1,4 @@ /* - $Id: uuid.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $ Copyright (c) 2008,2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -19,10 +18,10 @@ #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 */ @@ -42,8 +41,8 @@ extern char *ldap_uid_attr; ********************************************************/ 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 uuid_bin2string( uuidp_t uuidp, char **uuidstring); +extern int getnamefromuuid( const uuidp_t uuidp, char **name, uuidtype_t *type); +extern const char *uuid_bin2string(unsigned char *uuid); extern void uuid_string2bin( const char *uuidstring, uuidp_t uuid); #endif /* AFP_UUID_H */ diff --git a/include/atalk/vfs.h b/include/atalk/vfs.h index de1c008c..0bd50796 100644 --- a/include/atalk/vfs.h +++ b/include/atalk/vfs.h @@ -27,6 +27,7 @@ #include #include +#include #define VFS_FUNC_ARGS_VALIDUPATH const struct vol *vol, const char *name #define VFS_FUNC_VARS_VALIDUPATH vol, name @@ -61,8 +62,14 @@ #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 @@ -101,9 +108,11 @@ struct vfs_ops { 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); diff --git a/include/atalk/volinfo.h b/include/atalk/volinfo.h index 410b6b2a..0f3a4024 100644 --- a/include/atalk/volinfo.h +++ b/include/atalk/volinfo.h @@ -1,7 +1,3 @@ -/* - * $Id: volinfo.h,v 1.10 2009-12-03 12:47:37 franklahm Exp $ - */ - #ifndef _ATALK_VOLINFO_H #define _ATALK_VOLINFO_H 1 @@ -18,6 +14,9 @@ typedef struct { } vol_opt_name_t; struct volinfo { + int retaincount; + int malloced; + char *v_name; char *v_path; int v_flags; @@ -33,10 +32,13 @@ struct volinfo { 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); diff --git a/include/atalk/volume.h b/include/atalk/volume.h index f1b2084b..77966459 100644 --- a/include/atalk/volume.h +++ b/include/atalk/volume.h @@ -1,5 +1,5 @@ /* - * $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. @@ -27,9 +27,7 @@ struct vol { 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; @@ -87,7 +85,7 @@ struct vol { 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; @@ -108,7 +106,7 @@ struct vol { /* 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 */ @@ -135,8 +133,13 @@ struct vol { #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 */ diff --git a/libatalk/Makefile.am b/libatalk/Makefile.am index 5fcf4483..4c7500d6 100644 --- a/libatalk/Makefile.am +++ b/libatalk/Makefile.am @@ -1,16 +1,18 @@ # 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 \ @@ -18,12 +20,14 @@ libatalk_la_LIBADD = \ 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 \ @@ -31,7 +35,7 @@ libatalk_la_DEPENDENCIES = \ util/libutil.la \ tdb/libtdb.la \ unicode/libunicode.la \ - vfs/libvfs.la @LIBATALK_ACLS@ + vfs/libvfs.la libatalk_la_LDFLAGS = -static diff --git a/libatalk/acl/Makefile.am b/libatalk/acl/Makefile.am index fe9f6301..e7f9753a 100644 --- a/libatalk/acl/Makefile.am +++ b/libatalk/acl/Makefile.am @@ -2,17 +2,12 @@ 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 diff --git a/libatalk/acl/aclldap.h b/libatalk/acl/aclldap.h index 085ed0b4..b4adea9f 100644 --- a/libatalk/acl/aclldap.h +++ b/libatalk/acl/aclldap.h @@ -1,5 +1,4 @@ /* - $Id: aclldap.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $ Copyright (c) 2008,2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -22,20 +21,7 @@ * 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 */ diff --git a/libatalk/acl/cache.c b/libatalk/acl/cache.c index 854c10d9..c4635b57 100644 --- a/libatalk/acl/cache.c +++ b/libatalk/acl/cache.c @@ -48,22 +48,19 @@ static int dumpcache() { 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); } } @@ -71,15 +68,14 @@ static int dumpcache() { 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); } } @@ -102,7 +98,7 @@ static unsigned char hashstring(unsigned char *str) { 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; diff --git a/libatalk/acl/ldap.c b/libatalk/acl/ldap.c index 379ee737..724ac4d5 100644 --- a/libatalk/acl/ldap.c +++ b/libatalk/acl/ldap.c @@ -1,5 +1,4 @@ /* - $Id: ldap.c,v 1.7 2010-04-23 11:37:06 franklahm Exp $ Copyright (c) 2008,2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -17,11 +16,14 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ +#ifdef HAVE_LDAP + #include #include #include #include #include +#define LDAP_DEPRECATED 1 #include #include @@ -247,9 +249,16 @@ cleanup: * 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; @@ -258,6 +267,9 @@ int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string) 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; @@ -283,13 +295,22 @@ int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string) * 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) { @@ -315,3 +336,4 @@ int ldap_getnamefromuuid( char *uuidstr, char **name, uuidtype_t *type) { return -1; } +#endif /* HAVE_LDAP */ diff --git a/libatalk/acl/ldap_config.c b/libatalk/acl/ldap_config.c index c302751b..da37fb97 100644 --- a/libatalk/acl/ldap_config.c +++ b/libatalk/acl/ldap_config.c @@ -1,5 +1,4 @@ /* - $Id: ldap_config.c,v 1.4 2009-11-28 11:10:37 franklahm Exp $ Copyright (c) 2009 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -17,9 +16,10 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#ifdef HAVE_NFSv4_ACLS +#ifdef HAVE_LDAP #include +#include #include #include #include @@ -124,7 +124,7 @@ int acl_ldap_readconfig(char *name) 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; } @@ -133,16 +133,16 @@ int acl_ldap_readconfig(char *name) 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 */ diff --git a/libatalk/acl/unix.c b/libatalk/acl/unix.c index c08ae8d6..be422aac 100644 --- a/libatalk/acl/unix.c +++ b/libatalk/acl/unix.c @@ -16,7 +16,7 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ -#ifdef HAVE_NFSv4_ACLS +#ifdef HAVE_SOLARIS_ACLS #include #include @@ -43,13 +43,24 @@ int get_nfsv4_acl(const char *name, ace_t **retAces) *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", @@ -241,10 +252,9 @@ exit: 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 */ diff --git a/libatalk/acl/uuid.c b/libatalk/acl/uuid.c index e8b96504..8993acc2 100644 --- a/libatalk/acl/uuid.c +++ b/libatalk/acl/uuid.c @@ -20,6 +20,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -29,7 +33,7 @@ #include "aclldap.h" #include "cache.h" -char *uuidtype[] = {"NULL","USER", "GROUP"}; +char *uuidtype[] = {"NULL","USER", "GROUP", "LOCAL"}; /******************************************************** * Public helper function @@ -68,45 +72,41 @@ void uuid_string2bin( const char *uuidstring, uuidp_t uuid) { } -/* - * 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; } /******************************************************** * Interface ********************************************************/ +static unsigned char local_group_uuid[] = {0xab, 0xcd, 0xef, + 0xab, 0xcd, 0xef, + 0xab, 0xcd, 0xef, + 0xab, 0xcd, 0xef}; + +static unsigned char local_user_uuid[] = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, + 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa}; + /* * name: give me his name * type: and type (UUID_USER or UUID_GROUP) @@ -115,69 +115,108 @@ int uuid_bin2string( uuidp_t uuid, char **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) { + memcpy(uuid, local_user_uuid, 12); + struct passwd *pwd; + if ((pwd = getpwnam(name)) == NULL) { + LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user", + name, uuidtype[type]); + goto cleanup; + } + uint32_t id = pwd->pw_uid; + id = htonl(id); + memcpy(uuid + 12, &id, 4); + } else { + memcpy(uuid, &local_group_uuid, 12); + struct group *grp; + if ((grp = getgrnam(name)) == NULL) { + LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user", + name, uuidtype[type]); + goto cleanup; + } + uint32_t id = grp->gr_gid; + id = htonl(id); + memcpy(uuid + 12, &id, 4); + } + 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; } diff --git a/libatalk/bstring/.gitignore b/libatalk/bstring/.gitignore new file mode 100644 index 00000000..0d0371d7 --- /dev/null +++ b/libatalk/bstring/.gitignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +*.lo +*.la +.deps +.libs diff --git a/libatalk/bstring/Makefile.am b/libatalk/bstring/Makefile.am new file mode 100644 index 00000000..39f78c3e --- /dev/null +++ b/libatalk/bstring/Makefile.am @@ -0,0 +1,6 @@ +# Makefile.am for libatalk/adouble/ + +noinst_LTLIBRARIES = libbstring.la + +libbstring_la_SOURCES = bstrlib.c bstradd.c + diff --git a/libatalk/bstring/bstradd.c b/libatalk/bstring/bstradd.c new file mode 100644 index 00000000..7b59b2e8 --- /dev/null +++ b/libatalk/bstring/bstradd.c @@ -0,0 +1,208 @@ +/* + $Id: bstradd.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $ + 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. +*/ + +/*! + * @file + * Additional functions for bstrlib + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* 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; +} diff --git a/libatalk/bstring/bstrlib.c b/libatalk/bstring/bstrlib.c new file mode 100644 index 00000000..29e113e3 --- /dev/null +++ b/libatalk/bstring/bstrlib.c @@ -0,0 +1,2956 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +/* 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 diff --git a/libatalk/cnid/cdb/Makefile.am b/libatalk/cnid/cdb/Makefile.am index 75b41c11..cd3ca24f 100644 --- a/libatalk/cnid/cdb/Makefile.am +++ b/libatalk/cnid/cdb/Makefile.am @@ -11,7 +11,7 @@ libcnid_cdb_la_SOURCES = cnid_cdb_add.c \ 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 diff --git a/libatalk/cnid/cdb/cnid_cdb_open.c b/libatalk/cnid/cdb/cnid_cdb_open.c index bf501fd5..10ecc78d 100644 --- a/libatalk/cnid/cdb/cnid_cdb_open.c +++ b/libatalk/cnid/cdb/cnid_cdb_open.c @@ -1,7 +1,4 @@ /* - - * $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. * @@ -40,6 +37,8 @@ #endif /* HAVE_CONFIG_H */ #ifdef CNID_BACKEND_CDB + +#include #include "cnid_cdb_private.h" #ifndef MIN @@ -55,14 +54,6 @@ #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 @@ -355,42 +346,27 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args) 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; diff --git a/libatalk/cnid/cnid.c b/libatalk/cnid/cnid.c index 9231cf55..68136f9f 100644 --- a/libatalk/cnid/cnid.c +++ b/libatalk/cnid/cnid.c @@ -1,6 +1,4 @@ /* - * $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 * @@ -166,7 +164,7 @@ struct _cnid_db *cnid_open(const char *volpath, mode_t mask, char *type, int fla static void block_signal( u_int32_t flags) { if ((flags & CNID_FLAG_BLOCK)) { - sigprocmask(SIG_BLOCK, &sigblockset, NULL); + pthread_sigmask(SIG_BLOCK, &sigblockset, NULL); } } @@ -174,7 +172,7 @@ static void block_signal( u_int32_t flags) static void unblock_signal(u_int32_t flags) { if ((flags & CNID_FLAG_BLOCK)) { - sigprocmask(SIG_UNBLOCK, &sigblockset, NULL); + pthread_sigmask(SIG_UNBLOCK, &sigblockset, NULL); } } @@ -216,7 +214,7 @@ u_int32_t flags; /* --------------- */ 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; @@ -272,9 +270,9 @@ time_t t; /* --------------- */ 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)); @@ -282,6 +280,22 @@ cnid_t ret; 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) { diff --git a/libatalk/cnid/dbd/cnid_dbd.c b/libatalk/cnid/dbd/cnid_dbd.c index dadc4e12..990248e4 100644 --- a/libatalk/cnid/dbd/cnid_dbd.c +++ b/libatalk/cnid/dbd/cnid_dbd.c @@ -156,7 +156,7 @@ static int tsock_getfd(const char *host, const char *port) strerror(errno)); } close(sock); - sock=-1; + sock = -1; continue; } } else { @@ -454,6 +454,7 @@ static struct _cnid_db *cnid_dbd_new(const char *volpath) 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; @@ -679,11 +680,9 @@ char *cnid_dbd_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t le 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; @@ -694,7 +693,7 @@ char *cnid_dbd_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t le 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: @@ -791,6 +790,61 @@ cnid_t cnid_dbd_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t 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) diff --git a/libatalk/cnid/dbd/cnid_dbd.h b/libatalk/cnid/dbd/cnid_dbd.h index 6d43f9a0..1f645908 100644 --- a/libatalk/cnid/dbd/cnid_dbd.h +++ b/libatalk/cnid/dbd/cnid_dbd.h @@ -1,7 +1,6 @@ /* - * $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. */ @@ -19,19 +18,21 @@ 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 */ diff --git a/libatalk/compat/pselect.c b/libatalk/compat/pselect.c index 608996b2..0f887e45 100644 --- a/libatalk/compat/pselect.c +++ b/libatalk/compat/pselect.c @@ -63,7 +63,7 @@ pselect(int count, fd_set * restrict rfds, fd_set * restrict wfds, tvp = 0; if (mask != 0) { - rv = sigprocmask(SIG_SETMASK, mask, &omask); + rv = pthread_sigmask(SIG_SETMASK, mask, &omask); if (rv != 0) return rv; } @@ -71,7 +71,7 @@ pselect(int count, fd_set * restrict rfds, fd_set * restrict wfds, 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; } diff --git a/libatalk/dsi/dsi_tcp.c b/libatalk/dsi/dsi_tcp.c index 1bb81b4c..9c44116a 100644 --- a/libatalk/dsi/dsi_tcp.c +++ b/libatalk/dsi/dsi_tcp.c @@ -218,7 +218,7 @@ static int dsi_tcp_open(DSI *dsi) #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; @@ -250,11 +250,11 @@ static void guess_interface(DSI *dsi, const char *hostname) 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) " @@ -408,7 +408,7 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address, freeaddrinfo(servinfo); interfaces: - guess_interface(dsi, hostname); + guess_interface(dsi, hostname, port ? port : "548"); return 1; } diff --git a/libatalk/unicode/precompose.h b/libatalk/unicode/precompose.h index 9eba0692..c925b00d 100644 --- a/libatalk/unicode/precompose.h +++ b/libatalk/unicode/precompose.h @@ -1,9 +1,30 @@ -/* 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; @@ -1009,6 +1030,9 @@ static const struct { { 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 */ @@ -2029,6 +2053,9 @@ static const struct { { 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 */ @@ -2044,4 +2071,50 @@ static const struct { /*{ 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 */ diff --git a/libatalk/unicode/utf8.c b/libatalk/unicode/utf8.c index 26342cb7..c3146d0c 100644 --- a/libatalk/unicode/utf8.c +++ b/libatalk/unicode/utf8.c @@ -71,13 +71,12 @@ struct charset_functions charset_utf8_mac = 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) { @@ -115,10 +114,8 @@ static size_t utf8_pull(void *cd _U_, char **inbuf, size_t *inbytesleft, } 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; @@ -150,13 +147,13 @@ badseq: 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) { @@ -205,9 +202,8 @@ static size_t utf8_push(void *cd _U_, char **inbuf, size_t *inbytesleft, 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); diff --git a/libatalk/unicode/util_unistr.c b/libatalk/unicode/util_unistr.c index 45ffcd93..ef48f21b 100644 --- a/libatalk/unicode/util_unistr.c +++ b/libatalk/unicode/util_unistr.c @@ -17,18 +17,9 @@ #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) @@ -78,16 +69,16 @@ ucs2_t tolower_w(ucs2_t val) ********************************************************************/ 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; } /******************************************************************* @@ -96,16 +87,16 @@ int strlower_w(ucs2_t *s) ********************************************************************/ 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; } @@ -179,18 +170,18 @@ ucs2_t *strcasechr_w(const ucs2_t *s, ucs2_t c) 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; } /******************************************************************* @@ -236,8 +227,8 @@ case insensitive string comparison ********************************************************************/ 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)); } /******************************************************************* @@ -245,9 +236,9 @@ case insensitive string comparison, lenght limited ********************************************************************/ 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; } /******************************************************************* @@ -256,24 +247,24 @@ duplicate string /* 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); } /******************************************************************* @@ -282,16 +273,16 @@ copy a string with max len 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; } @@ -301,261 +292,383 @@ append a string of len bytes and add a terminator 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 */ - 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 */ - 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; /* */ - 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; } /* */ 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) @@ -575,43 +688,42 @@ size_t utf8_charlen ( char* utf8 ) 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); } - diff --git a/libatalk/util/Makefile.am b/libatalk/util/Makefile.am index f8eeb6b4..b844dc5a 100644 --- a/libatalk/util/Makefile.am +++ b/libatalk/util/Makefile.am @@ -1,7 +1,5 @@ # Makefile.am for libatalk/util/ -SUBDIRS = . test - noinst_LTLIBRARIES = libutil.la AM_CFLAGS = -I$(top_srcdir)/sys @@ -10,10 +8,12 @@ libutil_la_SOURCES = \ 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 \ diff --git a/libatalk/util/fault.c b/libatalk/util/fault.c index 7ef4fb8a..2b9ef644 100644 --- a/libatalk/util/fault.c +++ b/libatalk/util/fault.c @@ -22,8 +22,6 @@ #include "config.h" #endif -#ifdef DEBUG1 - #include #ifdef HAVE_UNISTD_H #include @@ -84,65 +82,28 @@ static void (*CatchSignal(int signum,void (*handler)(int )))(int) 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 - } @@ -153,15 +114,16 @@ static void fault_report(int sig) { 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); @@ -173,7 +135,7 @@ static void fault_report(int sig) #endif return; /* this should cause a core dump */ } - exit(1); + abort(); } /**************************************************************************** @@ -199,4 +161,3 @@ void fault_setup(void (*fn)(void *)) #endif } -#endif diff --git a/libatalk/util/ftw.c b/libatalk/util/ftw.c new file mode 100644 index 00000000..3a32c12e --- /dev/null +++ b/libatalk/util/ftw.c @@ -0,0 +1,826 @@ +/* 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 , 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 +#endif + +#include +#define NAMLEN(dirent) strlen ((dirent)->d_name) +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_SYS_PARAM_H +# include +#endif + +#include + +#define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N))) + +#define NDEBUG 1 +#include + +#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); +} + diff --git a/libatalk/util/queue.c b/libatalk/util/queue.c new file mode 100644 index 00000000..114e6401 --- /dev/null +++ b/libatalk/util/queue.c @@ -0,0 +1,116 @@ +/* + 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. +*/ + +/*! + * @file + * Netatalk utility functions: queue + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +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; +} + diff --git a/libatalk/util/server_child.c b/libatalk/util/server_child.c index 85c150c4..b21bae35 100644 --- a/libatalk/util/server_child.c +++ b/libatalk/util/server_child.c @@ -141,12 +141,12 @@ int server_child_add(server_child *children, const int forkid, * chance to add the child in. */ sigemptyset(&sig); sigaddset(&sig, SIGCHLD); - sigprocmask(SIG_BLOCK, &sig, &oldsig); + pthread_sigmask(SIG_BLOCK, &sig, &oldsig); /* it's possible that the child could have already died before the - * sigprocmask. we need to check for this. */ + * pthread_sigmask. we need to check for this. */ if (kill(pid, 0) < 0) { - sigprocmask(SIG_SETMASK, &oldsig, NULL); + pthread_sigmask(SIG_SETMASK, &oldsig, NULL); return 1; } @@ -154,13 +154,13 @@ int server_child_add(server_child *children, const int forkid, /* if we already have an entry. just return. */ if (resolve_child(fork->table, pid)) { - sigprocmask(SIG_SETMASK, &oldsig, NULL); + pthread_sigmask(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); + pthread_sigmask(SIG_SETMASK, &oldsig, NULL); return -1; } @@ -169,7 +169,7 @@ int server_child_add(server_child *children, const int forkid, child->killed = 0; hash_child(fork->table, child); children->count++; - sigprocmask(SIG_SETMASK, &oldsig, NULL); + pthread_sigmask(SIG_SETMASK, &oldsig, NULL); return 0; } @@ -427,6 +427,6 @@ void server_reset_signal(void) sigaddset(&sigs, SIGHUP); sigaddset(&sigs, SIGUSR1); sigaddset(&sigs, SIGCHLD); - sigprocmask(SIG_UNBLOCK, &sigs, NULL); + pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); } diff --git a/libatalk/util/socket.c b/libatalk/util/socket.c index 96472bae..9afaa3eb 100644 --- a/libatalk/util/socket.c +++ b/libatalk/util/socket.c @@ -1,5 +1,4 @@ /* - $Id: socket.c,v 1.6 2010-01-05 19:05:52 franklahm Exp $ Copyright (c) 2009 Frank Lahm This program is free software; you can redistribute it and/or modify diff --git a/libatalk/util/test/.gitignore b/libatalk/util/test/.gitignore deleted file mode 100644 index 3dd6d8e2..00000000 --- a/libatalk/util/test/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -Makefile -Makefile.in -logger_test -test.log -.deps -.libs - -.gitignore -logger_test.o diff --git a/libatalk/util/test/Makefile.am b/libatalk/util/test/Makefile.am deleted file mode 100644 index 569936be..00000000 --- a/libatalk/util/test/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -bin_PROGRAMS = logger_test - -logger_test_SOURCES = logger_test.c -logger_test_LDADD = $(top_builddir)/libatalk/util/libutil.la diff --git a/libatalk/util/test/logger_test.c b/libatalk/util/test/logger_test.c deleted file mode 100644 index a968cce3..00000000 --- a/libatalk/util/test/logger_test.c +++ /dev/null @@ -1,94 +0,0 @@ -#include - -#include -#include - -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; -} - - diff --git a/libatalk/util/unix.c b/libatalk/util/unix.c index 6231a037..9e71fcb4 100644 --- a/libatalk/util/unix.c +++ b/libatalk/util/unix.c @@ -1,5 +1,4 @@ /* - $Id: unix.c,v 1.6 2010-02-28 22:29:16 didg Exp $ Copyright (c) 2010 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -31,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -56,6 +57,26 @@ const char *getcwdpath(void) 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 * @@ -129,3 +150,33 @@ int lchdir(const char *dir) 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; +} diff --git a/libatalk/util/volinfo.c b/libatalk/util/volinfo.c index ef438c5d..62afbc65 100644 --- a/libatalk/util/volinfo.c +++ b/libatalk/util/volinfo.c @@ -139,16 +139,32 @@ static char * make_path_absolute(char *path, size_t bufsize) 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'; } @@ -272,14 +288,16 @@ static int parseline ( char *buf, struct volinfo *vol) } 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; @@ -328,7 +346,7 @@ int loadvolinfo (char *path, struct volinfo *vol) char volinfofile[MAXPATHLEN]; char buf[MAXPATHLEN]; struct flock lock; - int fd; + int fd, len; FILE *fp; if ( !path || !vol) @@ -342,9 +360,16 @@ int loadvolinfo (char *path, struct volinfo *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)); @@ -387,10 +412,63 @@ int loadvolinfo (char *path, struct volinfo *vol) 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. diff --git a/libatalk/vfs/Makefile.am b/libatalk/vfs/Makefile.am index d48caa31..9b8d1437 100644 --- a/libatalk/vfs/Makefile.am +++ b/libatalk/vfs/Makefile.am @@ -1,10 +1,9 @@ - -# 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 diff --git a/libatalk/vfs/acl.c b/libatalk/vfs/acl.c index d9c7d1f6..1396ce34 100644 --- a/libatalk/vfs/acl.c +++ b/libatalk/vfs/acl.c @@ -1,5 +1,6 @@ /* Copyright (c) 2009 Frank Lahm + 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 @@ -22,14 +23,17 @@ #include #include #include -#include #include #include #include +#include +#include + +#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; @@ -81,3 +85,51 @@ exit: 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 */ diff --git a/libatalk/vfs/unix.c b/libatalk/vfs/unix.c index a60b08a4..a43794e1 100644 --- a/libatalk/vfs/unix.c +++ b/libatalk/vfs/unix.c @@ -1,9 +1,6 @@ /* - * $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 @@ -215,7 +212,7 @@ int copy_file(int dirfd, const char *src, const char *dst, mode_t mode) 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; diff --git a/libatalk/vfs/vfs.c b/libatalk/vfs/vfs.c index dadedc5b..3156afe3 100644 --- a/libatalk/vfs/vfs.c +++ b/libatalk/vfs/vfs.c @@ -21,9 +21,13 @@ #include "config.h" #endif /* HAVE_CONFIG_H */ +#include +#include +#include #include #include #include +#include #include #include @@ -35,6 +39,9 @@ #include #include #include +#include +#include +#include struct perm { uid_t uid; @@ -320,7 +327,67 @@ static int RF_renamefile_adouble(VFS_FUNC_ARGS_RENAMEFILE) 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]; @@ -358,20 +425,74 @@ static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL) 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 *********************************************************************************/ @@ -839,8 +960,10 @@ VFS_MFUNC(setdirowner, VFS_FUNC_ARGS_SETDIROWNER, VFS_FUNC_VARS_SETDIROWNER) 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) @@ -868,8 +991,10 @@ static struct vfs_ops vfs_master_funcs = { vfs_deletefile, vfs_renamefile, vfs_copyfile, +#ifdef HAVE_ACLS vfs_acl, vfs_remove_acl, +#endif vfs_ea_getsize, vfs_ea_getcontent, vfs_ea_list, @@ -892,7 +1017,7 @@ static struct vfs_ops netatalk_adouble = { /* vfs_setdirowner: */ RF_setdirowner_adouble, /* vfs_deletefile: */ RF_deletefile_adouble, /* vfs_renamefile: */ RF_renamefile_adouble, - /* vfs_copyfile: */ NULL, + /* vfs_copyfile: */ RF_copyfile_adouble, NULL }; @@ -943,8 +1068,10 @@ static struct vfs_ops netatalk_ea_adouble = { /* 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, @@ -964,8 +1091,10 @@ static struct vfs_ops netatalk_ea_sys = { /* 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, @@ -977,7 +1106,7 @@ static struct vfs_ops netatalk_ea_sys = { * 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, @@ -996,6 +1125,25 @@ static struct vfs_ops netatalk_solaris_acl_adouble = { }; #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) { @@ -1027,7 +1175,11 @@ 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 + } diff --git a/macros/db3-check.m4 b/macros/db3-check.m4 index 7b3c64ed..081d27be 100644 --- a/macros/db3-check.m4 +++ b/macros/db3-check.m4 @@ -161,8 +161,10 @@ AC_DEFUN([AC_PATH_BDB],[ 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" diff --git a/macros/summary.m4 b/macros/summary.m4 index 78ffb3ea..7673d482 100644 --- a/macros/summary.m4 +++ b/macros/summary.m4 @@ -16,7 +16,6 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [ 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:]) @@ -48,20 +47,22 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [ 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]) diff --git a/macros/zeroconf.m4 b/macros/zeroconf.m4 new file mode 100644 index 00000000..788a1930 --- /dev/null +++ b/macros/zeroconf.m4 @@ -0,0 +1,71 @@ +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) +]) diff --git a/man/man1/Makefile.am b/man/man1/Makefile.am index a9e39c15..b3e6bdb5 100644 --- a/man/man1/Makefile.am +++ b/man/man1/Makefile.am @@ -12,35 +12,34 @@ SUFFIXES= .tmpl . -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) diff --git a/man/man1/achfile.1 b/man/man1/achfile.1 deleted file mode 100644 index 3acef044..00000000 --- a/man/man1/achfile.1 +++ /dev/null @@ -1,47 +0,0 @@ -'\" t -.\" Title: achfile -.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] -.\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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) diff --git a/man/man1/ad.1 b/man/man1/ad.1 index 597b4a4e..c0c3817a 100644 --- a/man/man1/ad.1 +++ b/man/man1/ad.1 @@ -2,12 +2,12 @@ .\" Title: ad .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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 .\" ----------------------------------------------------------------- @@ -22,7 +22,7 @@ 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 @@ -31,43 +31,204 @@ files in \&.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]: + + +FinderFlags (valid for (f)iles and/or (d)irectories): - + 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 \&. diff --git a/man/man1/apple_cp.1.tmpl b/man/man1/apple_cp.1.tmpl deleted file mode 100644 index 59dd84b0..00000000 --- a/man/man1/apple_cp.1.tmpl +++ /dev/null @@ -1,50 +0,0 @@ -'\" t -.\" Title: apple_cp -.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] -.\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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 \&. -.SH "SEE ALSO" -.PP -\fBapple_mv\fR(1), -\fBapple_rm\fR(1)\&. diff --git a/man/man1/apple_mv.1.tmpl b/man/man1/apple_mv.1.tmpl deleted file mode 100644 index e91d28ce..00000000 --- a/man/man1/apple_mv.1.tmpl +++ /dev/null @@ -1,50 +0,0 @@ -'\" t -.\" Title: apple_mv -.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] -.\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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 \&. -.SH "SEE ALSO" -.PP -\fBapple_cp\fR(1), -\fBapple_rm\fR(1)\&. diff --git a/man/man1/apple_rm.1.tmpl b/man/man1/apple_rm.1.tmpl deleted file mode 100644 index 90dc0a98..00000000 --- a/man/man1/apple_rm.1.tmpl +++ /dev/null @@ -1,46 +0,0 @@ -'\" t -.\" Title: apple_rm -.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] -.\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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 \&. -.SH "SEE ALSO" -.PP -\fBapple_cp\fR(1), -\fBapple_mv\fR(1)\&. diff --git a/man/man1/dbd.1 b/man/man1/dbd.1 index 54d40ee6..caff2548 100644 --- a/man/man1/dbd.1 +++ b/man/man1/dbd.1 @@ -2,12 +2,12 @@ .\" Title: dbd .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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 .\" ----------------------------------------------------------------- @@ -135,7 +135,6 @@ Options: .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 diff --git a/man/man3/Makefile.am b/man/man3/Makefile.am index 1569cfd7..198f9772 100644 --- a/man/man3/Makefile.am +++ b/man/man3/Makefile.am @@ -1,5 +1,9 @@ # 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) diff --git a/man/man4/Makefile.am b/man/man4/Makefile.am index 944c3602..d0295d41 100644 --- a/man/man4/Makefile.am +++ b/man/man4/Makefile.am @@ -1,5 +1,9 @@ # 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) diff --git a/man/man5/AppleVolumes.default.5.tmpl b/man/man5/AppleVolumes.default.5.tmpl index 03bf9143..c754aeea 100644 --- a/man/man5/AppleVolumes.default.5.tmpl +++ b/man/man5/AppleVolumes.default.5.tmpl @@ -2,12 +2,12 @@ .\" Title: AppleVolumes.default .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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 .\" ----------------------------------------------------------------- @@ -234,11 +234,9 @@ options:\fI[option]\fR .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 @@ -355,9 +353,11 @@ rwlist:\fI[users/groups]\fR 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 diff --git a/man/man5/Makefile.am b/man/man5/Makefile.am index a1e28886..e3d258c7 100644 --- a/man/man5/Makefile.am +++ b/man/man5/Makefile.am @@ -13,13 +13,24 @@ SUFFIXES = .tmpl . -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 @@ -27,4 +38,4 @@ man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS) CLEANFILES = $(GENERATED_MANS) -EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS) +EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS) $(ATALK_MANS) diff --git a/man/man5/afpd.conf.5.tmpl b/man/man5/afpd.conf.5.tmpl index 0b440d05..f0613040 100644 --- a/man/man5/afpd.conf.5.tmpl +++ b/man/man5/afpd.conf.5.tmpl @@ -2,12 +2,12 @@ .\" Title: afpd.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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 .\" ----------------------------------------------------------------- @@ -354,7 +354,14 @@ Immediately unmount volumes removed from AppleVolumes files on SIGHUP sent to th .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 diff --git a/man/man5/netatalk.conf.5.tmpl b/man/man5/netatalk.conf.5.tmpl index 138fb625..8adcb66c 100644 --- a/man/man5/netatalk.conf.5.tmpl +++ b/man/man5/netatalk.conf.5.tmpl @@ -2,12 +2,12 @@ .\" Title: netatalk.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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 .\" ----------------------------------------------------------------- @@ -20,11 +20,21 @@ .\" ----------------------------------------------------------------- .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: diff --git a/man/man8/Makefile.am b/man/man8/Makefile.am index 99d41e39..26273d43 100644 --- a/man/man8/Makefile.am +++ b/man/man8/Makefile.am @@ -14,12 +14,17 @@ SUFFIXES = .tmpl . <$< >$@ 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) diff --git a/man/man8/afp_acls.8.tmpl b/man/man8/afp_acls.8.tmpl deleted file mode 100644 index c7c0c4ea..00000000 --- a/man/man8/afp_acls.8.tmpl +++ /dev/null @@ -1,134 +0,0 @@ -'\" t -.\" Title: afp_acls -.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] -.\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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) diff --git a/man/man8/cnid_dbd.8.tmpl b/man/man8/cnid_dbd.8.tmpl index 5fc967a5..090ab772 100644 --- a/man/man8/cnid_dbd.8.tmpl +++ b/man/man8/cnid_dbd.8.tmpl @@ -2,12 +2,12 @@ .\" Title: cnid_dbd .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" 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 .\" ----------------------------------------------------------------- @@ -22,7 +22,7 @@ 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 @@ -41,7 +41,7 @@ backend\&. \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 @@ -53,11 +53,6 @@ database library and uses transactionally protected updates\&. The 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 @@ -105,7 +100,7 @@ database subsystem will create files named log\&.xxxxxxxxxx in the database home \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 @@ -180,7 +175,11 @@ exits\&. Default: 600\&. Set this to 0 to disable the timeout\&. .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 \{\ @@ -239,10 +238,6 @@ Again using the new BerkeleyDB utilities run .\} 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), diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 00000000..282522db --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 00000000..bfd92886 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = afpd diff --git a/test/afpd/.gitignore b/test/afpd/.gitignore new file mode 100644 index 00000000..4d6beeae --- /dev/null +++ b/test/afpd/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +.deps +.libs +test +test.conf +test.default \ No newline at end of file diff --git a/test/afpd/Makefile.am b/test/afpd/Makefile.am new file mode 100644 index 00000000..2bb6b3e9 --- /dev/null +++ b/test/afpd/Makefile.am @@ -0,0 +1,65 @@ +# 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 diff --git a/test/afpd/afpfunc_helpers.c b/test/afpd/afpfunc_helpers.c new file mode 100644 index 00000000..b5b8165f --- /dev/null +++ b/test/afpd/afpfunc_helpers.c @@ -0,0 +1,237 @@ +/* + $Id: afpfunc_helpers.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $ + 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. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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< + + 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 */ diff --git a/test/afpd/subtests.c b/test/afpd/subtests.c new file mode 100644 index 00000000..7049a7c7 --- /dev/null +++ b/test/afpd/subtests.c @@ -0,0 +1,71 @@ +/* + $Id: subtests.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $ + 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. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/test/afpd/subtests.h b/test/afpd/subtests.h new file mode 100644 index 00000000..1ce10a06 --- /dev/null +++ b/test/afpd/subtests.h @@ -0,0 +1,45 @@ +/* + $Id: subtests.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $ + 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. +*/ + +#ifndef SUBTESTS_H +#define SUBTESTS_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 */ diff --git a/test/afpd/test.c b/test/afpd/test.c new file mode 100644 index 00000000..c554260c --- /dev/null +++ b/test/afpd/test.c @@ -0,0 +1,110 @@ +/* + 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. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/test/afpd/test.h b/test/afpd/test.h new file mode 100644 index 00000000..802120a2 --- /dev/null +++ b/test/afpd/test.h @@ -0,0 +1,77 @@ +/* + $Id: test.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $ + 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. +*/ + +#ifndef TEST_H +#define TEST_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 */ diff --git a/test/afpd/test.sh b/test/afpd/test.sh new file mode 100755 index 00000000..80066b6e --- /dev/null +++ b/test/afpd/test.sh @@ -0,0 +1,24 @@ +#!/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 < test.default <