From 9873ab6bfe54103a973685922b76a5ca02109928 Mon Sep 17 00:00:00 2001 From: Frank Lahm Date: Tue, 5 Oct 2010 22:39:38 +0200 Subject: [PATCH] Use own ftw func in ad cp --- bin/cnid/Makefile.am | 6 +- bin/cnid/ad.c | 2 +- bin/cnid/ad.h | 26 +- bin/cnid/ad_cp.c | 173 ++++++-- bin/cnid/ad_ls.c | 2 - bin/cnid/ad_util.c | 87 ++++- include/atalk/Makefile.am | 4 +- include/atalk/ftw.h | 116 ++++++ include/atalk/queue.h | 1 + include/atalk/volinfo.h | 2 +- libatalk/util/Makefile.am | 1 + libatalk/util/ftw.c | 803 ++++++++++++++++++++++++++++++++++++++ libatalk/util/queue.c | 20 +- libatalk/util/volinfo.c | 33 +- 14 files changed, 1206 insertions(+), 70 deletions(-) create mode 100644 include/atalk/ftw.h create mode 100644 libatalk/util/ftw.c diff --git a/bin/cnid/Makefile.am b/bin/cnid/Makefile.am index 3aa59e93..0de86aa9 100644 --- a/bin/cnid/Makefile.am +++ b/bin/cnid/Makefile.am @@ -11,5 +11,9 @@ ad_SOURCES = ad.c ad_util.c \ ad_ls.c \ ad_cp.c -ad_LDADD = $(top_builddir)/libatalk/libatalk.la +ad_LDADD = \ + $(top_builddir)/libatalk/cnid/libcnid.la \ + $(top_builddir)/libatalk/libatalk.la \ + @ACL_LIBS@ + endif diff --git a/bin/cnid/ad.c b/bin/cnid/ad.c index 391a8465..e272f5b9 100644 --- a/bin/cnid/ad.c +++ b/bin/cnid/ad.c @@ -41,7 +41,7 @@ static void usage_main(void) int main(int argc, char **argv) { - setuplog("default log_info /dev/tty"); + setuplog("default log_note /dev/tty"); if (argc < 2) { usage_main(); diff --git a/bin/cnid/ad.h b/bin/cnid/ad.h index 0f2c4ea3..fe26bc51 100644 --- a/bin/cnid/ad.h +++ b/bin/cnid/ad.h @@ -20,8 +20,8 @@ #include #include #include -#include +#include #include #define STRCMP(a,b,c) (strcmp(a,c) b 0) @@ -29,13 +29,6 @@ #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; - enum logtype {STD, DBG}; #define SLOG(...) \ @@ -47,15 +40,26 @@ enum logtype {STD, DBG}; exit(1); \ } while (0) +typedef struct { + struct volinfo volinfo; +// char *basename; +} afpvol_t; + extern int log_verbose; /* Logging flag */ extern void _log(enum logtype lt, char *fmt, ...); -extern int newvol(const char *path, afpvol_t *vol); -extern void freevol(afpvol_t *vol); +extern struct volinfo svolinfo, dvolinfo; +extern struct vol svolume, dvolume; extern int ad_ls(int argc, char **argv); extern int ad_cp(int argc, char **argv); +/* ad_util.c */ +extern int newvol(const char *path, afpvol_t *vol); +extern void freevol(afpvol_t *vol); +extern cnid_t get_parent_cnid_for_path(const struct volinfo *vi, const struct vol *vol, const char *path); +extern char *utompath(const struct volinfo *volinfo, char *upath); + struct FTWELEM { const struct FTW *ftw; const char *ftw_path; @@ -74,7 +78,7 @@ extern PATH_T to; extern int fflag, iflag, lflag, nflag, pflag, vflag; extern volatile sig_atomic_t info; -extern int copy_file(const struct FTW *, const char *, const struct stat *, int); +extern int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int); extern int copy_link(const struct FTW *, const char *, const struct stat *, int); extern int setfile(const struct stat *, int); extern int preserve_dir_acls(const struct stat *, char *, char *); diff --git a/bin/cnid/ad_cp.c b/bin/cnid/ad_cp.c index 10351467..91cb762b 100644 --- a/bin/cnid/ad_cp.c +++ b/bin/cnid/ad_cp.c @@ -52,9 +52,7 @@ #include #include -// #include #include -#include #include #include #include @@ -62,7 +60,16 @@ #include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include "ad.h" @@ -77,10 +84,16 @@ PATH_T to = { to.p_path, emptystring, "" }; int fflag, iflag, lflag, nflag, pflag, vflag; mode_t mask; -static int Rflag, rflag; +struct volinfo svolinfo, dvolinfo; +struct vol svolume, dvolume; +cnid_t did = 0; /* current dir CNID */ + +static enum op type; +static int Rflag; volatile sig_atomic_t sigint; -static int type, badcp, rval; -static int ftw_options = FTW_MOUNT | FTW_PHYS; +static int badcp, rval; +static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL; +static q_t *cnidq; /* CNID dir stack */ enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; @@ -94,7 +107,6 @@ static char *netatalk_dirs[] = { static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf); static void siginfo(int _U_); - /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" Returns pointer to name or NULL. @@ -114,34 +126,39 @@ static const char *check_netatalk_dirs(const char *name) static void usage_cp(void) { printf( - "Usage: ad cp [-R [-L | -P]] [-pvf] \n" - "Usage: ad cp [-R [-L | -P]] [-pvf] \n" + "Usage: ad cp [-R [-P]] [-pvf] \n" + "Usage: ad cp [-R [-P]] [-pvfx] \n" ); + exit(EXIT_FAILURE); +} + +static void upfunc(void) +{ + if (cnidq) { + cnid_t *cnid = dequeue(cnidq); + if (cnid) { + did = *cnid; + free(cnid); + } + } } int ad_cp(int argc, char *argv[]) { struct stat to_stat, tmp_stat; - enum op type; - int Pflag, ch, r, have_trailing_slash; + int r, ch, have_trailing_slash; char *target; #if 0 afpvol_t srcvol; afpvol_t dstvol; #endif - Pflag = 0; - - while ((ch = getopt(argc, argv, "PRafilnprvx")) != -1) + while ((ch = getopt(argc, argv, "Rafilnpvx")) != -1) switch (ch) { - case 'P': - Pflag = 1; - break; case 'R': Rflag = 1; break; case 'a': - Pflag = 1; pflag = 1; Rflag = 1; break; @@ -163,10 +180,6 @@ int ad_cp(int argc, char *argv[]) case 'p': pflag = 1; break; - case 'r': - rflag = 1; - Pflag = 0; - break; case 'v': vflag = 1; break; @@ -183,14 +196,10 @@ int ad_cp(int argc, char *argv[]) if (argc < 2) usage_cp(); - if (Rflag && rflag) - ERROR("the -R and -r options may not be specified together"); - - if (rflag) - Rflag = 1; - (void)signal(SIGINT, siginfo); + cnid_init(); + /* Save the target base in "to". */ target = argv[--argc]; if ((strlcpy(to.p_path, target, PATH_MAX)) >= PATH_MAX) @@ -206,7 +215,7 @@ int ad_cp(int argc, char *argv[]) STRIP_TRAILING_SLASH(to); to.target_end = to.p_end; - /* Set end of argument list for fts(3). */ + /* Set end of argument list */ argv[argc] = NULL; /* @@ -269,13 +278,77 @@ int ad_cp(int argc, char *argv[]) mask = ~umask(0777); umask(~mask); +#if 0 + /* Inhereting perms in ad_mkdir etc requires this */ + ad_setfuid(0); +#endif + + /* Load .volinfo file for destination*/ + if (loadvolinfo(to.p_path, &dvolinfo) == 0) { + if (vol_load_charsets(&dvolinfo) == -1) + ERROR("Error loading charsets!"); + /* Sanity checks to ensure we can touch this volume */ + if (dvolinfo.v_vfs_ea != AFPVOL_EA_SYS) + ERROR("Unsupported Extended Attributes option: %u", dvolinfo.v_vfs_ea); + + /* initialize sufficient struct vol and initialize CNID connection */ + dvolume.v_adouble = AD_VERSION2; + dvolume.v_vfs_ea = AFPVOL_EA_SYS; + initvol_vfs(&dvolume); + int flags = 0; + if ((dvolinfo.v_flags & AFPVOL_NODEV)) + flags |= CNID_FLAG_NODEV; + + if ((dvolume.v_cdb = cnid_open(dvolinfo.v_path, + 0000, + "dbd", + flags, + dvolinfo.v_dbd_host, + dvolinfo.v_dbd_port)) == NULL) + ERROR("Cant initialize CNID database connection for %s", dvolinfo.v_path); + + /* setup a list for storing the CNID stack of dirs in destination path */ + if ((cnidq = queue_init()) == NULL) + ERROR("Cant initialize CNID stack"); + } + for (int i = 0; argv[i] != NULL; i++) { - if (nftw(argv[i], copy, 20, ftw_options) == -1) { - ERROR("nftw: %s", strerror(errno)); + /* Load .volinfo file for source */ + if (loadvolinfo(to.p_path, &svolinfo) == 0) { + if (vol_load_charsets(&svolinfo) == -1) + ERROR("Error loading charsets!"); + /* Sanity checks to ensure we can touch this volume */ + if (svolinfo.v_vfs_ea != AFPVOL_EA_SYS) + ERROR("Unsupported Extended Attributes option: %u", svolinfo.v_vfs_ea); + + /* initialize sufficient struct vol and initialize CNID connection */ + svolume.v_adouble = AD_VERSION2; + svolume.v_vfs_ea = AFPVOL_EA_SYS; + initvol_vfs(&svolume); + int flags = 0; + if ((svolinfo.v_flags & AFPVOL_NODEV)) + flags |= CNID_FLAG_NODEV; + + if ((svolume.v_cdb = cnid_open(svolinfo.v_path, + 0000, + "dbd", + flags, + svolinfo.v_dbd_host, + svolinfo.v_dbd_port)) == NULL) + ERROR("Cant initialize CNID database connection for %s", svolinfo.v_path); + } + + if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) { + ERROR("%s: %s", argv[i], strerror(errno)); exit(EXIT_FAILURE); } + + if (svolume.v_cdb) + cnid_close(svolume.v_cdb); + svolume.v_cdb = NULL; + } - return 0; + return rval; } static int copy(const char *path, @@ -289,6 +362,16 @@ static int copy(const char *path, const char *p; char *target_mid; + const char *dir = strrchr(path, '/'); + if (dir == NULL) + dir = path; + else + dir++; + if (check_netatalk_dirs(dir) != NULL) { + SLOG("Skipping Netatalk dir %s", path); + return FTW_SKIP_SIBLINGS; + } + /* * If we are in case (2) above, we need to append the * source name to the target name. @@ -320,7 +403,7 @@ static int copy(const char *path, if (strcmp(&path[base], "..") == 0) base += 1; } else - base = ftw->base; + base = strlen(path); } p = &path[base]; @@ -391,6 +474,30 @@ static int copy(const char *path, ERROR("%s", to.p_path); } + /* Create ad dir and copy ".Parent" */ + if (svolinfo.v_path && svolinfo.v_adouble == AD_VERSION2 && + dvolinfo.v_path && dvolinfo.v_adouble == AD_VERSION2) { + /* Create ".AppleDouble" dir */ + mode_t omask = umask(0); + bstring addir = bfromcstr(to.p_path); + bcatcstr(addir, "/.AppleDouble"); + mkdir(cfrombstring(addir), 02777); + + /* copy ".Parent" file */ + bcatcstr(addir, "/.Parent"); + bstring sdir = bfromcstr(path); + bcatcstr(sdir, "/.AppleDouble/.Parent"); + if (copy_file(-1, cfrombstring(sdir), cfrombstring(addir), 0666) != 0) { + SLOG("Error copying %s -> %s", cfrombstring(sdir), cfrombstring(addir)); + badcp = rval = 1; + break; + } + umask(omask); + + /* Get CNID of Parent and add new childir to CNID database */ + did = get_parent_cnid_for_path(&dvolinfo, &dvolume, to.p_path); + } + if (pflag) { if (setfile(statp, -1)) rval = 1; @@ -412,7 +519,7 @@ static int copy(const char *path, SLOG("%s is a FIFO (not copied).", path); break; default: - if (copy_file(ftw, path, statp, dne)) + if (ftw_copy_file(ftw, path, statp, dne)) badcp = rval = 1; break; } diff --git a/bin/cnid/ad_ls.c b/bin/cnid/ad_ls.c index cd6545e1..87834d86 100644 --- a/bin/cnid/ad_ls.c +++ b/bin/cnid/ad_ls.c @@ -1,6 +1,4 @@ /* - $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 diff --git a/bin/cnid/ad_util.c b/bin/cnid/ad_util.c index 1c9ff645..1814ae74 100644 --- a/bin/cnid/ad_util.c +++ b/bin/cnid/ad_util.c @@ -40,7 +40,6 @@ #include #include -#include #include #include #include @@ -87,17 +86,17 @@ void _log(enum logtype lt, char *fmt, ...) int newvol(const char *path, afpvol_t *vol) { -// char *pathdup; - + // 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); + // 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); @@ -118,7 +117,73 @@ void freevol(afpvol_t *vol) #endif } -int copy_file(const struct FTW *entp, +/* + Taken form afpd/desktop.c +*/ +char *utompath(const struct volinfo *volinfo, char *upath) +{ + static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */ + char *m, *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); +} + +/*! + * 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" + * + * 1) in case a) concatenate both paths + * 2) strip last element + * 3) strip volume root + * 4) start recursive CNID search with + * a) DID:2, "topdir" + * b) DID:2, "dir" + * 5) ...until we have the CNID for + * a) "/afp_volume/topdir/dir" + * b) "/afp_volume/dir" (no recursion required) + */ +cnid_t get_parent_cnid_for_path(const struct volinfo *vi, + const struct vol *vol, + const char *path) +{ + + return 0; +} + +int ftw_copy_file(const struct FTW *entp, const char *spath, const struct stat *sp, int dne) diff --git a/include/atalk/Makefile.am b/include/atalk/Makefile.am index 6a9f0b1b..2bf09187 100644 --- a/include/atalk/Makefile.am +++ b/include/atalk/Makefile.am @@ -2,10 +2,10 @@ atalkincludedir = $(includedir)/atalk atalkinclude_HEADERS = \ - adouble.h vfs.h aep.h afp.h asp.h atp.h boolean.h bstradd.h \ + adouble.h vfs.h aep.h afp.h asp.h atp.h boolean.h \ cnid.h compat.h ddp.h dsi.h ldapconfig.h list.h logger.h \ nbp.h netddp.h pap.h paths.h 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 bstrlib.h errchk.h +noinst_HEADERS = cnid_dbd_private.h cnid_private.h bstradd.h bstrlib.h errchk.h ftw.h diff --git a/include/atalk/ftw.h b/include/atalk/ftw.h new file mode 100644 index 00000000..6845402a --- /dev/null +++ b/include/atalk/ftw.h @@ -0,0 +1,116 @@ +/* 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 +#include + + +__BEGIN_DECLS + +/* 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); + +__END_DECLS + +#endif /* ATALK_FTW_H */ diff --git a/include/atalk/queue.h b/include/atalk/queue.h index 7ce49773..a3f433eb 100644 --- a/include/atalk/queue.h +++ b/include/atalk/queue.h @@ -35,6 +35,7 @@ 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/volinfo.h b/include/atalk/volinfo.h index 410b6b2a..2a95bef6 100644 --- a/include/atalk/volinfo.h +++ b/include/atalk/volinfo.h @@ -33,7 +33,7 @@ struct volinfo { int v_vfs_ea; char *(*ad_path)(const char *, int); char *v_dbd_host; - int v_dbd_port; + char *v_dbd_port; }; extern int loadvolinfo(char *path, struct volinfo *vol); diff --git a/libatalk/util/Makefile.am b/libatalk/util/Makefile.am index 52597e9c..f36fbdc1 100644 --- a/libatalk/util/Makefile.am +++ b/libatalk/util/Makefile.am @@ -10,6 +10,7 @@ libutil_la_SOURCES = \ atalk_addr.c \ bprint.c \ fault.c \ + ftw.c \ getiface.c \ locking.c \ logger.c \ diff --git a/libatalk/util/ftw.c b/libatalk/util/ftw.c new file mode 100644 index 00000000..2d33a375 --- /dev/null +++ b/libatalk/util/ftw.c @@ -0,0 +1,803 @@ +/* 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; + +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 | O_DIRECTORY | O_NDELAY); + 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, int d_type) +{ + 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 (d_type == DT_LNK) + flag = FTW_SLN; + 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), d->d_type); + 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, DT_UNKNOWN); + + 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 | O_DIRECTORY); + 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; + __tdestroy (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 index 2e7ff5ec..114e6401 100644 --- a/libatalk/util/queue.c +++ b/libatalk/util/queue.c @@ -1,5 +1,4 @@ /* - $Id: queue.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 @@ -51,6 +50,7 @@ q_t *queue_init(void) return queue; } +/* Insert at tail */ qnode_t *enqueue(q_t *q, void *data) { qnode_t *node; @@ -67,6 +67,24 @@ qnode_t *enqueue(q_t *q, void *data) 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; diff --git a/libatalk/util/volinfo.c b/libatalk/util/volinfo.c index ef438c5d..d4fdfab0 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)); - 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 (!S_ISDIR(st.st_mode)) { - if (NULL == (p=strrchr(abspath, '/')) ) + if (NULL == (p = strrchr(abspath, '/')) ) + /* single component `ad cp SOURCEFILE TARGETFILE`, use "." instead */ strcpy(abspath, "."); else + /* try without the last path element */ + *p = '\0'; + + if (stat(abspath, &st) != 0) { + return NULL; + } + } + + 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,7 +288,10 @@ static int parseline ( char *buf, struct volinfo *vol) } break; case CNIDDBDPORT: - vol->v_dbd_port = atoi(value); + if ((vol->v_dbd_port = strdup(value)) == NULL) { + fprintf (stderr, "strdup: %s", strerror(errno)); + return -1; + } break; case CNID_DBPATH: if ((vol->v_dbpath = strdup(value)) == NULL) { -- 2.39.2