In volinfo.c/h: translate vol options to ad options like afp/volume.c does it.
Rename dbd.c to cmd_dbd.c.
Makefile.in
cnid_metad
cnid_dbd
+dbd
.deps
.libs
cnid_metad_SOURCES = cnid_metad.c usockfd.c db_param.c
cnid_metad_LDADD = $(top_builddir)/libatalk/libatalk.la
-dbd_SOURCES = dbd.c dbif.c pack.c cmd_dbd_scanvol.c
+dbd_SOURCES = cmd_dbd.c cmd_dbd_scanvol.c cmd_dbd_lookup.c \
+ dbif.c pack.c \
+ dbd_delete.c dbd_update.c dbd_add.c dbd_lookup.c \
+ dbd_rebuild_add.c dbd_getstamp.c
dbd_CFLAGS = $(AM_CFLAGS)
dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@
--- /dev/null
+/*
+ $Id: cmd_dbd.c,v 1.1 2009-05-14 13:46:08 franklahm Exp $
+
+ Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+#include <atalk/logger.h>
+#include <atalk/cnid_dbd_private.h>
+#include <atalk/volinfo.h>
+#include "cmd_dbd.h"
+#include "dbif.h"
+#include "db_param.h"
+
+#define LOCKFILENAME "lock"
+#define DBOPTIONS (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN)
+
+static DBD *dbd;
+
+volatile sig_atomic_t alarmed;
+static int verbose; /* Logging flag */
+static int exclusive; /* Exclusive volume access */
+static struct db_param db_param = {
+ NULL, /* Volume dirpath */
+ 1, /* bdb logfile autoremove */
+ 16384, /* bdb cachesize */
+ -1, /* not used ... */
+ -1,
+ "",
+ -1,
+ -1,
+ -1
+};
+
+/*
+ Provide some logging
+ */
+void dbd_log(enum logtype lt, char *fmt, ...)
+{
+ int len;
+ static char logbuffer[1024];
+ va_list args;
+
+ if ( (lt == LOGSTD) || (verbose == 1)) {
+ va_start(args, fmt);
+ len = vsnprintf(logbuffer, 1023, fmt, args);
+ va_end(args);
+ logbuffer[1023] = 0;
+
+ printf("%s\n", logbuffer);
+ }
+}
+
+/*
+ SIGNAL handling:
+ catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+ */
+
+static void sig_handler(int signo)
+{
+ alarmed = 1;
+ return;
+}
+
+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) {
+ dbd_log( LOGSTD, "error in sigaction(SIGTERM): %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (sigaction(SIGINT, &sv, NULL) < 0) {
+ dbd_log( LOGSTD, "error in sigaction(SIGINT): %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&sv, 0, sizeof(struct sigaction));
+ sv.sa_handler = SIG_IGN;
+ sigemptyset(&sv.sa_mask);
+
+ if (sigaction(SIGABRT, &sv, NULL) < 0) {
+ dbd_log( LOGSTD, "error in sigaction(SIGABRT): %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (sigaction(SIGHUP, &sv, NULL) < 0) {
+ dbd_log( LOGSTD, "error in sigaction(SIGHUP): %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (sigaction(SIGQUIT, &sv, NULL) < 0) {
+ dbd_log( LOGSTD, "error in sigaction(SIGQUIT): %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+int get_lock(void)
+{
+ int lockfd;
+ struct flock lock;
+
+ if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
+ dbd_log( LOGSTD, "Error opening lockfile: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+ lock.l_type = F_WRLCK;
+
+ if (fcntl(lockfd, F_SETLK, &lock) < 0) {
+ if (errno == EACCES || errno == EAGAIN) {
+ if (exclusive) {
+ dbd_log( LOGSTD, "Database is in use and exlusive was requested", strerror(errno));
+ exit(EXIT_FAILURE);
+ };
+ } else {
+ dbd_log( LOGSTD, "Error getting fcntl F_WRLCK on lockfile: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return lockfd;
+}
+
+void free_lock(int lockfd)
+{
+ struct flock lock;
+
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+ lock.l_type = F_UNLCK;
+ fcntl(lockfd, F_SETLK, &lock);
+ close(lockfd);
+}
+
+static void usage ()
+{
+ printf("Usage: dbd [-e|-v|-x] -d [-i] | -s | -r [-f] <path to netatalk volume>\n"
+ "dbd can dump, scan, reindex and rebuild Netatalk dbd CNID databases.\n"
+ "dbd must be run with appropiate permissions i.e. as root.\n\n"
+ "Main commands are:\n"
+ " -d Dump CNID database\n"
+ " Option: -i dump indexes too\n"
+ " -s Scan volume:\n"
+ " 1. Compare database with volume\n"
+ " 2. Check if .AppleDouble dirs exist\n"
+ " 3. Check if AppleDouble file exist\n"
+ " 4. Report orphaned AppleDouble files\n"
+ " 5. Check for directories inside AppleDouble directories\n"
+ " 6. Check name encoding by roundtripping, log on error\n"
+ " -r Rebuild volume:\n"
+ " 1. Sync database with volume\n"
+ " 2. Make sure .AppleDouble dir exist, create if missing\n"
+ " 3. Make sure AppleDouble file exists, create if missing\n"
+ " 4. Delete orphaned AppleDouble files\n"
+ " 5. Check for directories inside AppleDouble directories\n"
+ " 6. Check name encoding by roundtripping, log on error\n"
+ " Option: -f wipe database and rebuild from IDs stored in AppleDouble files,\n"
+ " only available for volumes with 'cachecnid' option\n\n"
+ "General options:\n"
+ " -e only work on inactive volumes and lock them (exclusive)\n"
+ " -x rebuild indexes\n"
+ " -v verbose\n"
+ "\n"
+ "05-05-2009: -s and -r already check/repair the AppleDouble stuff and encoding,\n"
+ " no CNID database maintanance is done yet\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ int c, lockfd;
+ int dump=0, scan=0, rebuild=0, rebuildindexes=0, dumpindexes=0, force=0;
+ dbd_flags_t flags = 0;
+ char *volpath;
+ struct volinfo volinfo;
+
+ if (geteuid() != 0) {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+
+ while ((c = getopt(argc, argv, ":dsrvxife")) != -1) {
+ switch(c) {
+ case 'd':
+ dump = 1;
+ break;
+ case 'i':
+ dumpindexes = 1;
+ break;
+ case 's':
+ scan = 1;
+ flags = DBD_FLAGS_SCAN;
+ break;
+ case 'r':
+ rebuild = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'e':
+ exclusive = 1;
+ break;
+ case 'x':
+ rebuildindexes = 1;
+ break;
+ case 'f':
+ force = 1;
+ flags = DBD_FLAGS_FORCE;
+ break;
+ case ':':
+ case '?':
+ usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ if ((dump + scan + rebuild) != 1) {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+
+ if ( (optind + 1) != argc ) {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ volpath = argv[optind];
+
+ /* Put "/.AppleDB" at end of volpath */
+ if ( (strlen(volpath) + strlen("/.AppleDB")) > (PATH_MAX - 1) ) {
+ dbd_log( LOGSTD, "Volume pathname too long");
+ exit(EXIT_FAILURE);
+ }
+ char dbpath[PATH_MAX];
+ strncpy(dbpath, volpath, PATH_MAX - 1);
+ strcat(dbpath, "/.AppleDB");
+
+ /* Remember cwd */
+ int cdir;
+ if ((cdir = open(".", O_RDONLY)) < 0) {
+ dbd_log( LOGSTD, "Can't open dir: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ Before we do anything else, check if there is an instance of cnid_dbd
+ running already and silently exit if yes.
+ */
+ lockfd = get_lock();
+
+ /* Setup signal handling */
+ set_signal();
+
+ /* Setup logging. Should be portable among *NIXes */
+ if (!verbose)
+ setuplog("default log_info /dev/tty");
+ else
+ setuplog("default log_debug /dev/tty");
+
+ /* Load .volinfo file */
+ if (loadvolinfo(volpath, &volinfo) == -1) {
+ dbd_log( LOGSTD, "Unkown volume options!");
+ exit(EXIT_FAILURE);
+ }
+ if (vol_load_charsets(&volinfo) == -1) {
+ dbd_log( LOGSTD, "Error loading charsets!");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ Lets start with the BerkeleyDB stuff
+ */
+ if ((dbd = dbif_init(dbpath, "cnid2.db")) == NULL)
+ exit(EXIT_FAILURE);
+
+ if (dbif_env_open(dbd, &db_param, exclusive ? (DBOPTIONS | DB_RECOVER) : DBOPTIONS) < 0) {
+ dbd_log( LOGSTD, "error opening database!");
+ exit(EXIT_FAILURE);
+ }
+
+ if (exclusive)
+ dbd_log( LOGDEBUG, "Finished recovery.");
+
+ if (dbif_open(dbd, &db_param, rebuildindexes) < 0) {
+ dbif_close(dbd);
+ exit(EXIT_FAILURE);
+ }
+
+ if (dump) {
+ if (dbif_dump(dbd, dumpindexes) < 0) {
+ dbd_log( LOGSTD, "Error dumping database");
+ }
+ } else if (rebuild || scan) {
+ if (cmd_dbd_scanvol(dbd, &volinfo, flags) < 0) {
+ dbd_log( LOGSTD, "Error repairing database.");
+ }
+ }
+
+cleanup:
+ if (dbif_close(dbd) < 0) {
+ dbd_log( LOGSTD, "Error closing database");
+ exit(EXIT_FAILURE);
+ }
+
+ free_lock(lockfd);
+
+ if ((fchdir(cdir)) < 0)
+ dbd_log(LOGSTD, "fchdir: %s", strerror(errno));
+
+ return 0;
+}
#ifndef CMD_DBD_H
#define CMD_DBD_H
+#include <signal.h>
+#include <limits.h>
+
#include <atalk/volinfo.h>
#include "dbif.h"
#define STRCMP(a,b,c) \
(strcmp(a,c) b 0)
+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);
+/*
+ Functions for querying the database which couldn't be reused from the existing
+ funcs pool of dbd_* for one reason or another
+*/
+extern int cmd_dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag);
+extern int cmd_dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply);
#endif /* CMD_DBD_H */
--- /dev/null
+/*
+ * $Id: cmd_dbd_lookup.c,v 1.1 2009-05-14 13:46:08 franklahm Exp $
+ *
+ * Copyright (C) Frank Lahm 2009
+ * All Rights Reserved. See COPYING.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <netatalk/endian.h>
+#include <atalk/logger.h>
+#include <atalk/cnid_dbd_private.h>
+
+#include "pack.h"
+#include "dbif.h"
+#include "dbd.h"
+#include "cmd_dbd.h"
+
+/* Pull these in from dbd_add.c */
+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);
+
+/*
+ * This returns the CNID corresponding to a particular file and logs any inconsitencies.
+ * If roflags == 1 we only scan, if roflag == 0, we modify, but we do _not_ *add* as does
+ * dbd_lookup.
+ */
+
+int cmd_dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
+{
+ unsigned char *buf;
+ DBT key, devdata, diddata;
+ uint64_t dev = 0;
+ int devino = 1, didname = 1;
+ int rc;
+ cnid_t id_devino, id_didname;
+ u_int32_t type_devino = (unsigned)-1;
+ u_int32_t type_didname = (unsigned)-1;
+ int update = 0;
+
+ memset(&key, 0, sizeof(key));
+ memset(&diddata, 0, sizeof(diddata));
+ memset(&devdata, 0, sizeof(devdata));
+
+ rply->namelen = 0;
+ rply->cnid = 0;
+
+ buf = pack_cnid_data(rqst);
+
+ /* Look for a CNID. We have two options: dev/ino or did/name. If we
+ only get a match in one of them, that means a file has moved. */
+ key.data = buf + CNID_DEVINO_OFS;
+ key.size = CNID_DEVINO_LEN;
+
+ if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) {
+ dbd_log( LOGSTD, "dbd_search: Unable to get CNID %u, name %s",
+ ntohl(rqst->did), rqst->name);
+ rply->result = CNID_DBD_RES_ERR_DB;
+ return -1;
+ }
+ if (rc == 0) {
+ devino = 0;
+ }
+ else {
+ memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
+ memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
+ type_devino = ntohl(type_devino);
+ }
+
+ key.data = buf + CNID_DID_OFS;
+ key.size = CNID_DID_LEN + rqst->namelen + 1;
+
+ if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) {
+ dbd_log( LOGSTD, "dbd_search: Unable to get CNID %u, name %s",
+ ntohl(rqst->did), rqst->name);
+ rply->result = CNID_DBD_RES_ERR_DB;
+ return -1;
+ }
+ if (rc == 0) {
+ didname = 0;
+ }
+ else {
+ memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
+ memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
+ type_didname = ntohl(type_didname);
+ }
+
+ if (!devino && !didname) {
+ /* not found */
+ dbd_log( LOGDEBUG, "name: '%s/%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database",
+ cwdbuf, rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+ rply->result = CNID_DBD_RES_NOTFOUND;
+ return 1;
+ }
+
+ if (devino && didname && id_devino == id_didname && type_devino == rqst->type) {
+ /* the same */
+ rply->cnid = id_didname;
+ rply->result = CNID_DBD_RES_OK;
+ return 1;
+ }
+
+ if (didname) {
+ dbd_log( LOGSTD, "CNID resolve problem: changed dev/ino for '%s/%s'", cwdbuf, rqst->name);
+ rqst->cnid = id_didname;
+ /* we have a did/name.
+ If it's the same dev or not the same type, e.g. a remove followed by a new file
+ with the same name */
+ if (!memcmp(&dev, (char *)diddata.data + CNID_DEV_OFS, CNID_DEV_LEN) ||
+ type_didname != rqst->type) {
+ if (! roflag)
+ if (dbd_delete(dbd, rqst, rply) < 0)
+ return -1;
+ }
+ else {
+ update = 1;
+ }
+ }
+
+ if (devino) {
+ dbd_log( LOGSTD, "CNID resolve problem: server side rename oder reused inode for '%s/%s'", cwdbuf, rqst->name);
+ rqst->cnid = id_devino;
+ if (type_devino != rqst->type) {
+ /* same dev/inode but not same type: it's an inode reused, delete the record */
+ if (! roflag)
+ if (dbd_delete(dbd, rqst, rply) < 0)
+ return -1;
+ }
+ else {
+ update = 1;
+ }
+ }
+ if (!update || roflag) {
+ rply->result = CNID_DBD_RES_NOTFOUND;
+ return 1;
+ }
+ /* Fix up the database. assume it was a file move and rename */
+ rc = dbd_update(dbd, rqst, rply);
+ if (rc >0) {
+ rply->cnid = rqst->cnid;
+ }
+
+ dbd_log( LOGSTD, "CNID database needed update: dev/ino: 0x%llx/0x%llx, did: %u, name: '%s/%s' --> CNID: %u",
+ (unsigned long long)rqst->dev, (unsigned long long)rqst->ino,
+ ntohl(rqst->did), cwdbuf, rqst->name, ntohl(rply->cnid));
+
+ return rc;
+}
+
+/*
+ This is taken from dbd_add.c
+*/
+int cmd_dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
+{
+ if (get_cnid(dbd, rply) < 0) {
+ if (rply->result == CNID_DBD_RES_ERR_MAX) {
+ dbd_log( LOGSTD, "FATAL: CNID database has reached its limit.");
+ /* This will cause an abort/rollback if transactions are used */
+ return 0;
+ } else {
+ dbd_log( LOGSTD, "Failed to compute CNID for %s, error reading/updating Rootkey", rqst->name);
+ return -1;
+ }
+ }
+
+ if (add_cnid(dbd, rqst, rply) < 0) {
+ if (rply->result == CNID_DBD_RES_ERR_DUPLCNID) {
+ dbd_log( LOGSTD, "Cannot add CNID %u. Corrupt/invalid Rootkey?.", ntohl(rply->cnid));
+ /* abort/rollback, see above */
+ return 0;
+ } else {
+ dbd_log( LOGSTD, "Failed to add CNID for %s to database", rqst->name);
+ return -1;
+ }
+ }
+ dbd_log( LOGDEBUG, "Add to CNID database: did: %u, cnid: %u, name: '%s', dev/ino: 0x%llx/0x%llx",
+ ntohl(rqst->did), ntohl(rply->cnid), rqst->name,
+ (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+
+ rply->result = CNID_DBD_RES_OK;
+ return 1;
+}
-/*
- $Id: cmd_dbd_scanvol.c,v 1.1 2009-05-06 11:54:24 franklahm Exp $
-
- Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-*/
+/*
+ $Id: cmd_dbd_scanvol.c,v 1.2 2009-05-14 13:46:08 franklahm Exp $
+
+ Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-/*
- dbd specs and implementation progress
- =====================================
-
- St Type Check (Status of implementation progress, type: file/dir)
- -- ---- -----
- OK D Make sure .AppleDouble dir exist, create if missing. Error creating
- it is fatal as that shouldn't happen as root
- OK F Make sure ad file exists
- OK F/D Delete orphaned ad-files, log dirs in ad-dir
- OK F/D Check name encoding by roundtripping, log on error
- .. F/D try: read CNID from ad file (if cnid caching is on)
- try: fetch CNID from database
- on mismatch: keep CNID from file, update database
- on no CNID id ad file: write CNID from database to ad file
- on no CNID in database: add CNID from ad file to database
- on no CNID at all: create one and store in both places
+ 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.
*/
-/* FIXME: set id */
-#if 0
- ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
-#endif
+/*
+ dbd specs and implementation progress
+ =====================================
+
+ St := Status
+
+ Force option
+ ------------
+
+ St Spec
+ -- ----
+ -- If -f is requested, ensure -e is too.
+ Check if volumes is usign AFPVOL_CACHE, then wipe db from disk.
+
+ 1st pass: Scan volume
+ --------------------
+
+ St Type Check
+ -- ---- -----
+ OK F/D Make sure ad file exists
+ OK D Make sure .AppleDouble dir exist, create if missing. Error creating
+ it is fatal as that shouldn't happen as root.
+ OK F/D Delete orphaned ad-files, log dirs in ad-dir
+ OK F/D Check name encoding by roundtripping, log on error
+ OK F/D try: read CNID from ad file (if cnid caching is on)
+ try: fetch CNID from database
+ -> on mismatch: use CNID from file, update database (deleting both found CNIDs first)
+ -> if no CNID in ad file: write CNID from database to ad file
+ -> if no CNID in database: add CNID from ad file to database
+ -> on no CNID at all: create one and store in both places
+ OK F/D Add found CNID, DID, filename, dev/inode, stamp to rebuild database
+
+ 2nd pass: Delete unused CNIDs
+ -----------------------------
+
+ St Spec
+ -- ----
+ -- Step through dbd (the one on disk) and rebuild-db from pass 1 and delete any CNID from
+ dbd not in rebuild db.
+*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <fcntl.h>
#include <string.h>
#include <errno.h>
-
+#include <setjmp.h>
#include <atalk/adouble.h>
#include <atalk/unicode.h>
#include <atalk/volinfo.h>
-#include "cmd_dbd.h"
+#include <atalk/cnid_dbd_private.h>
+#include "cmd_dbd.h"
#include "dbif.h"
#include "db_param.h"
+#include "dbd.h"
+
+/* Some defines to ease code parsing */
+#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];
+DBD *dbd;
static DBD *dbd_rebuild;
-static struct volinfo *volinfo;
static dbd_flags_t dbd_flags;
-static char cwdbuf[MAXPATHLEN+1];
+static char stamp[CNID_DEV_LEN];
static char *netatalk_dirs[] = {
".AppleDB",
".AppleDesktop",
NULL
};
+static struct cnid_dbd_rqst rqst;
+static struct cnid_dbd_rply rply;
+static jmp_buf jmp;
-/*
- Taken form afpd/desktop.c
+/*
+ Taken form afpd/desktop.c
*/
static char *utompath(char *upath)
{
/* convert charsets */
if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
- CH_UTF8_MAC,
+ CH_UTF8_MAC,
volinfo->v_maccharset,
- u, outlen, mpath, MAXPATHLEN, &flags)) ) {
+ u, outlen, mpath, MAXPATHLEN, &flags)) ) {
dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
volinfo->v_volcodepage, volinfo->v_maccodepage, u);
return NULL;
return(m);
}
-/*
- Taken form afpd/desktop.c
+/*
+ Taken form afpd/desktop.c
*/
static char *mtoupath(char *mpath)
{
static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
- char *m, *u;
+ char *m, *u;
size_t inplen;
size_t outlen;
- u_int16_t flags = 0;
+ u_int16_t flags = 0;
if (!mpath)
return NULL;
-
+
if ( *mpath == '\0' ) {
return( "." );
}
m, inplen, u, outlen, &flags)) ) {
dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
volinfo->v_volcodepage, mpath);
- return NULL;
+ return NULL;
}
return( upath );
}
-/*
- Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
- We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
+/*
+ Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
+ We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
*/
static int check_name_encoding(char *uname)
{
roundtripped = mtoupath(utompath(uname));
if (!roundtripped) {
- dbd_log( LOGSTD, "Error checking encoding for %s/%s", cwdbuf, uname);
+ dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname);
return -1;
}
if ( STRCMP(uname, !=, roundtripped)) {
- dbd_log( LOGSTD, "Bad encoding for %s/%s", cwdbuf, uname);
+ dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname);
return -1;
}
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;
}
-/*
- Check for .AppleDouble file, create if missing
+/*
+ Check for .AppleDouble file, create if missing
*/
static int check_adfile(const char *fname, const struct stat *st)
{
- int ret;
+ int ret, adflags;
struct adouble ad;
char *adname;
- adname = volinfo->ad_path(fname, 0);
+ if (S_ISREG(st->st_mode))
+ adflags = 0;
+ else
+ adflags = ADFLAGS_DIR;
+
+ adname = volinfo->ad_path(fname, adflags);
if ((ret = access( adname, F_OK)) != 0) {
if (errno != ENOENT) {
if (dbd_flags & DBD_FLAGS_SCAN)
/* Scan only requested, dont change anything */
- return 0;
+ return -1;
/* Create ad file */
- ad_init(&ad, volinfo->v_adouble, volinfo->v_flags);
-
- if ((ret = ad_open_metadata( fname, 0, O_CREAT, &ad)) != 0) {
- dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
+ ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+
+ if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
+ dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
cwdbuf, adname, strerror(errno));
return -1;
}
return 0;
}
-/*
- Check for .AppleDouble folder, create if missing
+/*
+ Check for .AppleDouble folder and .Parent, create if missing
*/
static int check_addir(int volroot)
{
- int ret;
+ int addir_ok, adpar_ok;
struct stat st;
struct adouble ad;
- if ((ret = access(ADv2_DIRNAME, F_OK)) != 0) {
+ /* Check for ad-dir */
+ if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
if (errno != ENOENT) {
dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
return -1;
}
- /* Missing. Log and create it */
- dbd_log(LOGSTD, "Missing %s directory %s", ADv2_DIRNAME, cwdbuf);
+ dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
+ }
- if (dbd_flags & DBD_FLAGS_SCAN)
- /* Scan only requested, dont change anything */
- return 0;
+ /* Check for ".Parent" */
+ if ( (adpar_ok = access(volinfo->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));
+ return -1;
+ }
+ dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
+ }
+
+ /* Is one missing ? */
+ if ((addir_ok != 0) || (adpar_ok != 0)) {
+ /* Yes, but are we only scanning ? */
+ if (dbd_flags & DBD_FLAGS_SCAN) {
+ /* Yes: missing .Parent is not a problem, but missing ad-dir
+ causes later checking of ad-files to fail. So we have to return appropiately */
+ if (addir_ok != 0)
+ return -1;
+ else /* (adpar_ok != 0) */
+ return 0;
+ }
- /* Create ad dir and set name and id */
- ad_init(&ad, volinfo->v_adouble, volinfo->v_flags);
-
- if ((ret = ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad)) != 0) {
+ /* Create ad dir and set name */
+ ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+
+ if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
return -1;
}
ad_setname(&ad, mname);
ad_flush(&ad);
ad_close_metadata(&ad);
-
+
/* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
if ((stat(".", &st)) != 0) {
dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
DIR *dp;
struct dirent *ep;
struct stat st;
- static char fname[NAME_MAX] = "../";
+ static char fname[MAXPATHLEN] = "../";
if ((chdir(ADv2_DIRNAME)) != 0) {
dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
/* Check if its "." or ".." */
if (DIR_DOT_OR_DOTDOT(ep->d_name))
continue;
+ /* Skip ".Parent" */
+ if (STRCMP(ep->d_name, ==, ".Parent"))
+ continue;
if ((stat(ep->d_name, &st)) < 0) {
- dbd_log( LOGSTD, "Lost file while reading dir '%s/%s/%s', probably removed: %s",
+ dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
continue;
}
continue;
}
- /* Skip ".Parent" */
- if (STRCMP(ep->d_name, ==, ".Parent"))
- continue;
-
/* Check for data file */
strcpy(fname+3, ep->d_name);
if ((access( fname, F_OK)) != 0) {
if (errno != ENOENT) {
- dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
+ dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
cwdbuf, ep->d_name, strerror(errno));
continue;
}
return 0;
}
+
+static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok,
+ int adflags)
+{
+ int ret;
+ cnid_t db_cnid, ad_cnid;
+ struct adouble ad;
+
+ /* 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 (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
+ dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
+ return 0;
+ }
+ ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
+ if (ad_cnid == 0)
+ dbd_log( LOGSTD, "ZeroID '%s/%s'", cwdbuf, name);
+ ad_close_metadata(&ad);
+ }
+
+ /* Get CNID from database */
+
+ /* Prepare request data */
+ memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
+ memset(&rply, 0, sizeof(struct cnid_dbd_rply));
+ rqst.did = did;
+ if ( ! (volinfo->v_flags & AFPVOL_NODEV))
+ rqst.dev = st->st_dev;
+ rqst.ino = st->st_ino;
+ rqst.type = S_ISDIR(st->st_mode)?1:0;
+ rqst.name = (char *)name;
+ rqst.namelen = strlen(name);
+
+ /* Query the database */
+ ret = cmd_dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
+ dbif_txn_close(dbd, ret);
+ if (rply.result == CNID_DBD_RES_OK) {
+ db_cnid = rply.cnid;
+ } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
+ dbd_log( LOGSTD, "Missing CNID for: '%s/%s'", cwdbuf, name);
+ db_cnid = 0;
+ } else {
+ dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
+ db_cnid = 0;
+ }
+
+ /* Compare results from both CNID searches */
+ if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
+ /* Everything is fine */
+ return db_cnid;
+ } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
+ /* Mismatch ? Delete both from db and re-add data from file */
+ dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
+ if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
+ rqst.cnid = db_cnid;
+ ret = dbd_delete(dbd, &rqst, &rply);
+ dbif_txn_close(dbd, ret);
+
+ rqst.cnid = ad_cnid;
+ ret = dbd_delete(dbd, &rqst, &rply);
+ dbif_txn_close(dbd, ret);
+
+ ret = dbd_rebuild_add(dbd, &rqst, &rply);
+ dbif_txn_close(dbd, ret);
+ }
+ return ad_cnid;
+ } else if (ad_cnid && (db_cnid == 0)) {
+ /* in ad-file but not in db */
+ if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
+ dbd_log( LOGSTD, "CNID rebuild add for '%s/%s', adding with CNID from ad-file: %u", cwdbuf, name, ntohl(ad_cnid));
+ rqst.cnid = ad_cnid;
+ ret = dbd_delete(dbd, &rqst, &rply);
+ dbif_txn_close(dbd, ret);
+ ret = dbd_rebuild_add(dbd, &rqst, &rply);
+ dbif_txn_close(dbd, ret);
+ }
+ return ad_cnid;
+ } else if ((db_cnid == 0) && (ad_cnid == 0)) {
+ /* No CNID at all, we clearly have to allocat a fresh one... */
+ /* Note: the next test will use this new CNID too! */
+ if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
+ /* add to db */
+ dbd_log( LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
+ ret = cmd_dbd_add(dbd, &rqst, &rply);
+ dbif_txn_close(dbd, ret);
+ db_cnid = rply.cnid;
+ }
+ } else 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 ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
+ dbd_log( LOGSTD, "Setting CNID for ZeroID '%s/%s' from db: %u", cwdbuf, name, ntohl(db_cnid));
+ ad_init(&ad, volinfo->v_adouble, volinfo->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));
+ return 0;
+ }
+ ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
+ ad_flush(&ad);
+ ad_close_metadata(&ad);
+ }
+ }
+ return db_cnid;
+ } else {
+ dbd_log( LOGSTD, "Uncaught CNID mismatch for '%s/%s'", cwdbuf, name);
+ return 0;
+ }
+ /* Not reached */
+ return 0;
+}
+
+/*
+ This is called recursively for all dirs.
+ volroot=1 means we're in the volume root dir, 0 means we aren't.
+ We use this when checking for netatalk private folders like .AppleDB.
+ did is our parents CNID.
+*/
static int dbd_readdir(int volroot, cnid_t did)
{
- int cwd, ret = 0, encoding_ok;
+ int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
+ cnid_t cnid;
const char *name;
DIR *dp;
struct dirent *ep;
static struct stat st; /* Save some stack space */
- /* Check for .AppleDouble folder */
- if ((check_addir(volroot)) != 0)
- return -1;
+ /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
+ if ((addir_ok = check_addir(volroot)) != 0)
+ if ( ! (dbd_flags & DBD_FLAGS_SCAN))
+ /* Fatal on rebuild run, continue if only scanning ! */
+ return -1;
- /* Check AppleDouble files in AppleDouble folder */
- if ((ret = read_addir()) != 0)
- return -1;
+ /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
+ if (ADDIR_OK)
+ if ((read_addir()) != 0)
+ if ( ! (dbd_flags & DBD_FLAGS_SCAN))
+ /* Fatal on rebuild run, continue if only scanning ! */
+ return -1;
if ((dp = opendir (".")) == NULL) {
dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
}
while ((ep = readdir (dp))) {
+ /* Check if we got a termination signal */
+ if (alarmed)
+ longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
+
/* Check if its "." or ".." */
if (DIR_DOT_OR_DOTDOT(ep->d_name))
continue;
dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno));
continue;
}
+ if (S_ISREG(st.st_mode))
+ adflags = 0;
+ else
+ adflags = ADFLAGS_DIR;
/**************************************************************************
Tests
/* Check encoding */
if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
/* If its a file: skipp all other tests now ! */
- /* For dirs we skip tests too, but later */
- if (S_ISREG(st.st_mode))
- continue;
+ /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
+ continue;
}
- /* Check for appledouble file, create if missing */
- if (S_ISREG(st.st_mode)) {
- if ( 0 != check_adfile(ep->d_name, &st))
- continue;
+ /* Check for appledouble file, create if missing, but only if we have addir */
+ adfile_ok = -1;
+ if (ADDIR_OK)
+ adfile_ok = check_adfile(ep->d_name, &st);
+
+ /* Check CNIDs */
+ cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
+
+ /* Now add this object to our rebuild dbd */
+ if (cnid) {
+ rqst.cnid = rply.cnid;
+ dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
+ if (rply.result != CNID_DBD_RES_OK) {
+ dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
+ cnid, cwdbuf, ep->d_name);
+ exit(EXIT_FAILURE);
+ }
}
-
+
/**************************************************************************
Recursion
**************************************************************************/
- if (S_ISDIR(st.st_mode)) {
+ if (S_ISDIR(st.st_mode) && cnid) { /* If we have no cnid for it we cant recur */
strcat(cwdbuf, "/");
strcat(cwdbuf, ep->d_name);
dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
continue;
}
- ret = dbd_readdir(0, did);
+ ret = dbd_readdir(0, cnid);
fchdir(cwd);
close(cwd);
}
}
- /*
- Use results of previous checks
+ /*
+ Use results of previous checks
*/
-exit_cleanup:
- closedir(dp);
+ closedir(dp);
return ret;
}
chdir(volinfo->v_path);
/* Start recursion */
- if (dbd_readdir(1, 2) < 0) /* 2 = volumeroot CNID */
+ if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
return -1;
return 0;
}
-int cmd_dbd_scanvol(DBD *dbd, struct volinfo *volinfo, dbd_flags_t flags)
+int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
{
int ret = 0;
+ /* Make it accessible for all funcs */
+ 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);
return -1;
}
- /* open/create dbd, copy rootinfo key */
- if (NULL == (dbd_rebuild = dbif_init(NULL)))
+ /* Get volume stamp */
+ dbd_getstamp(dbd, &rqst, &rply);
+ if (rply.result != CNID_DBD_RES_OK)
+ goto exit_cleanup;
+ memcpy(stamp, rply.name, CNID_DEV_LEN);
+
+ /* open/create rebuild dbd, copy rootinfo key */
+ if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
return -1;
if (0 != (dbif_open(dbd_rebuild, NULL, 0)))
return -1;
- if (0 != (ret = dbif_copy_rootinfokey(dbd, dbd_rebuild)))
+ if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
goto exit_cleanup;
- dbd_log( LOGSTD, "dumping rebuilddb");
- dbif_dump(dbd_rebuild, 0);
+// dbif_dump(dbd_rebuild, 0);
+
+ if (setjmp(jmp) != 0)
+ goto exit_cleanup; /* Got signal, jump from dbd_readdir */
/* scanvol */
if ( (scanvol(volinfo, flags)) != 0)
return -1;
+// dbif_dump(dbd_rebuild, 0);
+
exit_cleanup:
dbif_close(dbd_rebuild);
return ret;
+++ /dev/null
-/*
- $Id: dbd.c,v 1.3 2009-05-06 11:54:24 franklahm Exp $
-
- Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <signal.h>
-#include <string.h>
-#include <errno.h>
-
-#include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
-#include <atalk/volinfo.h>
-#include "cmd_dbd.h"
-#include "dbif.h"
-#include "db_param.h"
-
-#define LOCKFILENAME "lock"
-#define DBOPTIONS (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN)
-
-static DBD *dbd;
-
-static volatile sig_atomic_t alarmed;
-static int verbose; /* Logging flag */
-static int exclusive; /* Exclusive volume access */
-static struct db_param db_param = {
- NULL, /* Volume dirpath */
- 1, /* bdb logfile autoremove */
- 16384, /* bdb cachesize */
- -1, /* not used ... */
- -1,
- "",
- -1,
- -1,
- -1
-};
-
-/*
- Provide some logging
- */
-void dbd_log(enum logtype lt, char *fmt, ...)
-{
- int len;
- static char logbuffer[1024];
- va_list args;
-
- if ( (lt == LOGSTD) || (verbose == 1)) {
- va_start(args, fmt);
- len = vsnprintf(logbuffer, 1023, fmt, args);
- va_end(args);
- logbuffer[1023] = 0;
-
- printf("%s\n", logbuffer);
- }
-}
-
-/*
- SIGNAL handling:
- ignore everything except SIGTERM which we catch and which causes
- a clean exit.
- */
-
-static void sig_handler(int signo)
-{
- alarmed = 1;
- return;
-}
-
-void set_signal(void)
-{
- struct sigaction sv;
-
- sv.sa_handler = sig_handler;
- sv.sa_flags = SA_RESTART;
- sigemptyset(&sv.sa_mask);
- sigaddset(&sv.sa_mask, SIGTERM);
- if (sigaction(SIGTERM, &sv, NULL) < 0) {
- dbd_log( LOGSTD, "error in sigaction(SIGTERM): %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- memset(&sv, 0, sizeof(struct sigaction));
- sv.sa_handler = SIG_IGN;
- sigemptyset(&sv.sa_mask);
-
- if (sigaction(SIGINT, &sv, NULL) < 0) {
- dbd_log( LOGSTD, "error in sigaction(SIGINT): %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
- if (sigaction(SIGABRT, &sv, NULL) < 0) {
- dbd_log( LOGSTD, "error in sigaction(SIGABRT): %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
- if (sigaction(SIGHUP, &sv, NULL) < 0) {
- dbd_log( LOGSTD, "error in sigaction(SIGHUP): %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
- if (sigaction(SIGQUIT, &sv, NULL) < 0) {
- dbd_log( LOGSTD, "error in sigaction(SIGQUIT): %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-}
-
-#if 0
-static void block_sigs_onoff(int block)
-{
- sigset_t set;
-
- sigemptyset(&set);
- sigaddset(&set, SIGINT);
- sigaddset(&set, SIGTERM);
- if (block)
- sigprocmask(SIG_BLOCK, &set, NULL);
- else
- sigprocmask(SIG_UNBLOCK, &set, NULL);
- return;
-}
-#endif
-
-int get_lock(void)
-{
- int lockfd;
- struct flock lock;
-
- if ((lockfd = open(LOCKFILENAME, O_RDWR | O_CREAT, 0644)) < 0) {
- dbd_log( LOGSTD, "Error opening lockfile: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- lock.l_start = 0;
- lock.l_whence = SEEK_SET;
- lock.l_len = 0;
- lock.l_type = F_WRLCK;
-
- if (fcntl(lockfd, F_SETLK, &lock) < 0) {
- if (errno == EACCES || errno == EAGAIN) {
- if (exclusive) {
- dbd_log( LOGSTD, "Database is in use and exlusive was requested", strerror(errno));
- exit(EXIT_FAILURE);
- };
- } else {
- dbd_log( LOGSTD, "Error getting fcntl F_WRLCK on lockfile: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
- }
-
- return lockfd;
-}
-
-void free_lock(int lockfd)
-{
- struct flock lock;
-
- lock.l_start = 0;
- lock.l_whence = SEEK_SET;
- lock.l_len = 0;
- lock.l_type = F_UNLCK;
- fcntl(lockfd, F_SETLK, &lock);
- close(lockfd);
-}
-
-static void usage ()
-{
- printf("Usage: dbd [-e|-v|-x] -d [-i] | -s | -r [-f] <path to netatalk volume>\n"
- "dbd can dump, scan, reindex and rebuild Netatalk dbd CNID databases.\n"
- "dbd must be run with appropiate permissions i.e. as root.\n\n"
- "Main commands are:\n"
- " -d Dump CNID database\n"
- " Option: -i dump indexes too\n"
- " -s Scan volume:\n"
- " 1. Compare database with volume\n"
- " 2. Check if .AppleDouble dirs exist\n"
- " 3. Check if AppleDouble file exist\n"
- " 4. Report orphaned AppleDouble files\n"
- " 5. Check for directories inside AppleDouble directories\n"
- " 6. Check name encoding by roundtripping, log on error\n"
- " -r Rebuild volume:\n"
- " 1. Sync database with volume\n"
- " 2. Make sure .AppleDouble dir exist, create if missing\n"
- " 3. Make sure AppleDouble file exists, create if missing\n"
- " 4. Delete orphaned AppleDouble files\n"
- " 5. Check for directories inside AppleDouble directories\n"
- " 6. Check name encoding by roundtripping, log on error\n"
- " Option: -f wipe database and rebuild from IDs stored in AppleDouble files,\n"
- " only available for volumes with 'cachecnid' option\n\n"
- "General options:\n"
- " -e only work on inactive volumes and lock them (exclusive)\n"
- " -x rebuild indexes\n"
- " -v verbose\n"
- "\n"
- "05-05-2009: -s and -r already check/repair the AppleDouble stuff and encoding,\n"
- " no CNID database maintanance is done yet\n"
- );
-}
-
-int main(int argc, char **argv)
-{
- int c, lockfd;
- int dump=0, scan=0, rebuild=0, rebuildindexes=0, dumpindexes=0, force=0;
- dbd_flags_t flags = 0;
- char *volpath;
- struct volinfo volinfo;
-
- if (geteuid() != 0) {
- usage();
- exit(EXIT_FAILURE);
- }
-
- while ((c = getopt(argc, argv, ":dsrvxife")) != -1) {
- switch(c) {
- case 'd':
- dump = 1;
- break;
- case 'i':
- dumpindexes = 1;
- break;
- case 's':
- scan = 1;
- flags = DBD_FLAGS_SCAN;
- break;
- case 'r':
- rebuild = 1;
- break;
- case 'v':
- verbose = 1;
- break;
- case 'e':
- exclusive = 1;
- break;
- case 'x':
- rebuildindexes = 1;
- break;
- case 'f':
- force = 1;
- flags = DBD_FLAGS_FORCE;
- break;
- case ':':
- case '?':
- usage();
- exit(EXIT_FAILURE);
- break;
- }
- }
-
- if ((dump + scan + rebuild) != 1) {
- usage();
- exit(EXIT_FAILURE);
- }
-
- if ( (optind + 1) != argc ) {
- usage();
- exit(EXIT_FAILURE);
- }
- volpath = argv[optind];
-
- /* Put "/.AppleDB" at end of volpath */
- if ( (strlen(volpath) + strlen("/.AppleDB")) > (PATH_MAX -1) ) {
- dbd_log( LOGSTD, "Volume pathname too long");
- exit(EXIT_FAILURE);
- }
- char dbpath[PATH_MAX];
- strncpy(dbpath, volpath, PATH_MAX - 1);
- strcat(dbpath, "/.AppleDB");
-
- /* cd to .AppleDB dir */
- int cdir;
- if ((cdir = open(".", O_RDONLY)) < 0) {
- dbd_log( LOGSTD, "Can't open dir: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
- if (chdir(dbpath) < 0) {
- dbd_log( LOGSTD, "chdir to %s failed: %s", dbpath, strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- /*
- Before we do anything else, check if there is an instance of cnid_dbd
- running already and silently exit if yes.
- */
- lockfd = get_lock();
-
- /* Ignore everything except SIGTERM */
- set_signal();
-
- /* Setup logging. Should be portable among *NIXes */
- if (!verbose)
- setuplog("default log_info /dev/tty");
- else
- setuplog("default log_debug /dev/tty");
-
- /* Load .volinfo file */
- if ( -1 == loadvolinfo(volpath, &volinfo)) {
- dbd_log( LOGSTD, "Unkown volume options!");
- exit(EXIT_FAILURE);
- }
- if ( -1 == vol_load_charsets(&volinfo)) {
- dbd_log( LOGSTD, "Error loading charsets!");
- exit(EXIT_FAILURE);
- }
-
- /*
- Lets start with the BerkeleyDB stuff
- */
- if (NULL == (dbd = dbif_init("cnid2.db")))
- exit(2);
-
- if (dbif_env_open(dbd, &db_param, DBOPTIONS) < 0) {
- dbd_log( LOGSTD, "error opening database!");
- exit(EXIT_FAILURE);
- }
-
- dbd_log( LOGDEBUG, "Finished opening BerkeleyDB databases including recovery.");
-
- if (dbif_open(dbd, &db_param, rebuildindexes) < 0) {
- dbif_close(dbd);
- exit(EXIT_FAILURE);
- }
-
- if ((fchdir(cdir)) < 0) {
- dbd_log(LOGSTD, "fchdir: %s", strerror(errno));
- goto cleanup;
- }
-
- if (dump) {
- if (dbif_dump(dbd, dumpindexes) < 0) {
- dbd_log( LOGSTD, "Error dumping database");
- }
- } else if (rebuild || scan) {
- if (cmd_dbd_scanvol(dbd, &volinfo, flags) < 0) {
- dbd_log( LOGSTD, "Fatal error repairing database. Please rm -r it.");
- }
- }
-
-cleanup:
- if (dbif_close(dbd) < 0)
- exit(EXIT_FAILURE);
-
- free_lock(lockfd);
-
- return 0;
-}
/*
- * $Id: dbd_add.c,v 1.5 2009-05-06 11:54:24 franklahm Exp $
+ * $Id: dbd_add.c,v 1.6 2009-05-14 13:46:08 franklahm Exp $
*
* Copyright (C) Joerg Lenneis 2003
* All Rights Reserved. See COPYING.
#include "pack.h"
#include "dbd.h"
-static int add_cnid(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
+int add_cnid(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
{
DBT key, data;
int rc;
}
/* ---------------------- */
-static int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
+int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
{
DBT rootinfo_key, rootinfo_data;
int rc;
return -1;
}
}
- LOG(log_info, logtype_cnid, "dbd_add: Added dev/ino 0x%llx/0x%llx did %u name %s cnid %u",
- ntoh64((unsigned long long int)rqst->dev), ntoh64((unsigned long long int)rqst->ino),
+ LOG(log_debug, logtype_cnid, "dbd_add: Added dev/ino 0x%llx/0x%llx did %u name %s cnid %u",
+ (unsigned long long)rqst->dev, (unsigned long long)rqst->ino,
ntohl(rqst->did), rqst->name, ntohl(rply->cnid));
rply->result = CNID_DBD_RES_OK;
/*
- * $Id: dbd_lookup.c,v 1.6 2009-05-06 11:54:24 franklahm Exp $
+ * $Id: dbd_lookup.c,v 1.7 2009-05-14 13:46:08 franklahm Exp $
*
* Copyright (C) Joerg Lenneis 2003
* All Rights Reserved. See COPYING.
unsigned char *buf;
DBT key, devdata, diddata;
char dev[CNID_DEV_LEN];
-#if 0
- char ino[CNID_INO_LEN];
-#endif
int devino = 1, didname = 1;
int rc;
cnid_t id_devino, id_didname;
buf = pack_cnid_data(rqst);
memcpy(dev, buf + CNID_DEV_OFS, CNID_DEV_LEN);
-#if 0
- /* FIXME: ino is not needed later on, remove? */
- memcpy(ino, buf + CNID_INO_OFS, CNID_INO_LEN);
-#endif
/* Look for a CNID. We have two options: dev/ino or did/name. If we
only get a match in one of them, that means a file has moved. */
/* not found */
LOG(log_debug, logtype_cnid, "cnid_lookup: dev/ino 0x%llx/0x%llx did %u name %s neither in devino nor didname",
- ntoh64((unsigned long long int)rqst->dev), ntoh64((unsigned long long int)rqst->ino), ntohl(rqst->did), rqst->name);
+ (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rqst->did), rqst->name);
rply->result = CNID_DBD_RES_NOTFOUND;
return 1;
/* the same */
LOG(log_debug, logtype_cnid, "cnid_lookup: Looked up dev/ino 0x%llx/0x%llx did %u name %s as %u",
- ntoh64((unsigned long long int)rqst->dev), ntoh64((unsigned long long int)rqst->ino), ntohl(rqst->did), rqst->name, ntohl(id_didname));
+ (unsigned long long )rqst->dev, (unsigned long long )rqst->ino, ntohl(rqst->did), rqst->name, ntohl(id_didname));
rply->cnid = id_didname;
rply->result = CNID_DBD_RES_OK;
}
LOG(log_debug, logtype_cnid, "cnid_lookup: Looked up dev/ino 0x%llx/0x%llx did %u name %s as %u (needed update)",
- ntoh64((unsigned long long int)rqst->dev), ntoh64((unsigned long long int)rqst->ino), ntohl(rqst->did), rqst->name, ntohl(rply->cnid));
+ (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rqst->did), rqst->name, ntohl(rply->cnid));
return rc;
}
/*
- * $Id: dbif.c,v 1.9 2009-05-10 08:08:28 franklahm Exp $
+ * $Id: dbif.c,v 1.10 2009-05-14 13:46:08 franklahm Exp $
*
* Copyright (C) Joerg Lenneis 2003
* Copyright (C) Frank Lahm 2009
}
/* --------------- */
-DBD *dbif_init(const char *filename)
+DBD *dbif_init(const char *envhome, const char *filename)
{
DBD *dbd;
/* filename == NULL means in memory db */
if (filename) {
+ if (! envhome)
+ return NULL;
+
+ dbd->db_envhome = strdup(envhome);
+ if (NULL == dbd->db_envhome) {
+ free(dbd);
+ return NULL;
+ }
+
dbd->db_filename = strdup(filename);
if (NULL == dbd->db_filename) {
+ free(dbd->db_envhome);
free(dbd);
return NULL;
}
dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog);
}
- dbd->db_env->set_verbose(dbd->db_env, DB_VERB_RECOVERY, 1);
-
if (dbenv_oflags & DB_RECOVER) {
+ dbd->db_env->set_verbose(dbd->db_env, DB_VERB_RECOVERY, 1);
/* Open the database for recovery using DB_PRIVATE option which is faster */
- if ((ret = dbd->db_env->open(dbd->db_env, ".", dbenv_oflags | DB_PRIVATE, 0))) {
+ if ((ret = dbd->db_env->open(dbd->db_env, dbd->db_envhome, dbenv_oflags | DB_PRIVATE, 0))) {
LOG(log_error, logtype_cnid, "error opening DB environment: %s",
db_strerror(ret));
dbd->db_env->close(dbd->db_env, 0);
dbd->db_env->set_errfile(dbd->db_env, dbd->db_errlog);
dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog);
}
- if ((ret = dbd->db_env->open(dbd->db_env, ".", dbenv_oflags, 0))) {
+ 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));
dbd->db_env->close(dbd->db_env, 0);
return 0;
}
+/*
+ ret = 1 -> commit txn
+ ret = 0 -> abort txn -> exit!
+ anything else -> exit!
+*/
+void dbif_txn_close(DBD *dbd, int ret)
+{
+ if (ret == 0) {
+ if (dbif_txn_abort(dbd) < 0) {
+ LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
+ exit(EXIT_FAILURE);
+ }
+ } else if (ret == 1) {
+ ret = dbif_txn_commit(dbd);
+ if ( ret < 0) {
+ LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
+ exit(EXIT_FAILURE);
+ }
+ } else
+ exit(EXIT_FAILURE);
+}
+
int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
{
int ret;
/*
- $Id: dbif.h,v 1.5 2009-05-06 11:54:24 franklahm Exp $
+ $Id: dbif.h,v 1.6 2009-05-14 13:46:08 franklahm Exp $
Copyright (C) Joerg Lenneis 2003
Copyright (C) Frank Lahm 2009
typedef struct {
DB_ENV *db_env;
DB_TXN *db_txn;
+ char *db_envhome;
char *db_filename;
FILE *db_errlog;
db_table db_table[3];
} DBD;
/* Functions */
-extern DBD *dbif_init(const char *dbname);
+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 do_truncate);
extern int dbif_close(DBD *dbd);
extern int dbif_txn_begin(DBD *);
extern int dbif_txn_commit(DBD *);
extern int dbif_txn_abort(DBD *);
+extern void dbif_txn_close(DBD *dbd, int ret); /* Switch between commit+abort */
extern int dbif_txn_checkpoint(DBD *, u_int32_t, u_int32_t, u_int32_t);
extern int dbif_dump(DBD *, int dumpindexes);
/*
- * $Id: main.c,v 1.6 2009-05-06 11:54:24 franklahm Exp $
+ * $Id: main.c,v 1.7 2009-05-14 13:46:08 franklahm Exp $
*
* Copyright (C) Joerg Lenneis 2003
* Copyright (c) Frank Lahm 2009
exit(1);
LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
- if (NULL == (dbd = dbif_init("cnid2.db")))
+ if (NULL == (dbd = dbif_init(".", "cnid2.db")))
exit(2);
if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
/*
- * $Id: volinfo.h,v 1.4 2009-04-28 13:01:24 franklahm Exp $
+ * $Id: volinfo.h,v 1.5 2009-05-14 13:46:08 franklahm Exp $
*/
#ifndef _ATALK_VOLINFO_H
char *v_maccodepage;
charset_t v_maccharset;
int v_adouble; /* default adouble format */
+ int v_ad_options;
char *(*ad_path)(const char *, int);
char *v_dbd_host;
int v_dbd_port;
lock.l_type = F_UNLCK;
fcntl(fd, F_SETLK, &lock);
+ /* Translate vol options to ad options like afp/volume.c does it */
+ vol->v_ad_options = 0;
+ if ((vol->v_flags & AFPVOL_NODEV))
+ vol->v_ad_options |= ADVOL_NODEV;
+ if ((vol->v_flags & AFPVOL_CACHE))
+ vol->v_ad_options |= ADVOL_CACHE;
+ if ((vol->v_flags & AFPVOL_UNIX_PRIV))
+ vol->v_ad_options |= ADVOL_UNIXPRIV;
+ if ((vol->v_flags & AFPVOL_INV_DOTS))
+ vol->v_ad_options |= ADVOL_INVDOTS;
+
fclose(fp);
return 0;
}