author | Alberto Bertogli
<albertito@gmail.com> 2004-11-26 05:21:26 UTC |
committer | Alberto Bertogli
<albertito@gmail.com> 2007-07-15 13:47:10 UTC |
parent | 99bb7f5c9dd91863ea7241c06efc44fff7de4b8f |
bindings/preload/libjio_preload.c | +1 | -1 |
bindings/python/libjio.c | +41 | -12 |
check.c | +40 | -17 |
common.c | +3 | -19 |
common.h | +2 | -1 |
jiofsck.c | +35 | -27 |
libjio.h | +4 | -2 |
trans.c | +72 | -2 |
diff --git a/bindings/preload/libjio_preload.c b/bindings/preload/libjio_preload.c index 5ff5f0d..6d201ec 100644 --- a/bindings/preload/libjio_preload.c +++ b/bindings/preload/libjio_preload.c @@ -380,7 +380,7 @@ int unlink(const char *pathname) printd("libjio\n"); rec_inc(); - jfsck_cleanup(pathname); + jfsck_cleanup(pathname, NULL); rec_dec(); r = (*c_unlink)(pathname); diff --git a/bindings/python/libjio.c b/bindings/python/libjio.c index 103f293..51d7745 100644 --- a/bindings/python/libjio.c +++ b/bindings/python/libjio.c @@ -283,6 +283,32 @@ static PyObject *jf_jsync(jfileobject *fp, PyObject *args) return PyLong_FromLong(rv); } +/* jmove_journal */ +PyDoc_STRVAR(jf_jmove_journal__doc, +"jmove_journal(newpath)\n\ +\n\ +Moves the journal directory to the new path; note that there MUST NOT BE\n\ +anything else operating on the file.\n\ +It's a wrapper to jmove_journal().\n"); + +static PyObject *jf_jmove_journal(jfileobject *fp, PyObject *args) +{ + long rv; + char *newpath; + + if (!PyArg_ParseTuple(args, "s:jmove_journal", &newpath)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + rv = jmove_journal(fp->fs, newpath); + Py_END_ALLOW_THREADS + + if (rv != 0) + return PyErr_SetFromErrno(PyExc_IOError); + + return PyLong_FromLong(rv); +} + /* new_trans */ PyDoc_STRVAR(jf_new_trans__doc, "new_trans()\n\ @@ -326,6 +352,7 @@ static PyMethodDef jfile_methods[] = { { "truncate", (PyCFunction)jf_truncate, METH_VARARGS, jf_truncate__doc }, { "lseek", (PyCFunction)jf_lseek, METH_VARARGS, jf_lseek__doc }, { "jsync", (PyCFunction)jf_jsync, METH_VARARGS, jf_jsync__doc }, + { "jmove_journal", (PyCFunction)jf_jmove_journal, METH_VARARGS, jf_jmove_journal__doc }, { "new_trans", (PyCFunction)jf_new_trans, METH_VARARGS, jf_new_trans__doc }, { NULL } }; @@ -514,21 +541,22 @@ static PyObject *jf_open(PyObject *self, PyObject *args) /* jfsck */ PyDoc_STRVAR(jf_jfsck__doc, -"jfsck(name)\n\ +"jfsck(name[, jdir])\n\ \n\ -Checks the integrity of the file with the given name; returns a dictionary\n\ -with all the different values of the check (equivalent to the 'struct\n\ -jfsck_result'), or None if there was nothing to check.\n\ +Checks the integrity of the file with the given name, using (optionally) jdir\n\ +as the journal directory; returns a dictionary with all the different values\n\ +of the check (equivalent to the 'struct jfsck_result'), or None if there was\n\ +nothing to check.\n\ It's a wrapper to jfsck().\n"); static PyObject *jf_jfsck(PyObject *self, PyObject *args) { int rv; - char *name; + char *name, *jdir; struct jfsck_result res; PyObject *dict; - if (!PyArg_ParseTuple(args, "s:jfsck", &name)) + if (!PyArg_ParseTuple(args, "s|s:jfsck", &name, &jdir)) return NULL; dict = PyDict_New(); @@ -536,7 +564,7 @@ static PyObject *jf_jfsck(PyObject *self, PyObject *args) return PyErr_NoMemory(); Py_BEGIN_ALLOW_THREADS - rv = jfsck(name, &res); + rv = jfsck(name, jdir, &res); Py_END_ALLOW_THREADS if (rv == J_ENOMEM) { @@ -558,21 +586,22 @@ static PyObject *jf_jfsck(PyObject *self, PyObject *args) /* jfsck_cleanup */ PyDoc_STRVAR(jf_jfsck_cleanup__doc, -"jfsck_cleanup()\n\ +"jfsck_cleanup(name[, jdir])\n\ \n\ -Clean the journal directory and leave it ready to use.\n\ +Clean the journal directory for the given file using (optionally) jdir as the\n\ +journal directory, and leave it ready to use.\n\ It's a wrapper to jfsck_cleanup().\n"); static PyObject *jf_jfsck_cleanup(PyObject *self, PyObject *args) { long rv; - char *name; + char *name, *jdir; - if (!PyArg_ParseTuple(args, "s:jfsck_cleanup", &name)) + if (!PyArg_ParseTuple(args, "s|s:jfsck_cleanup", &name, &jdir)) return NULL; Py_BEGIN_ALLOW_THREADS - rv = jfsck_cleanup(name); + rv = jfsck_cleanup(name, jdir); Py_END_ALLOW_THREADS return PyInt_FromLong(rv); diff --git a/check.c b/check.c index a7933e6..95dca6a 100644 --- a/check.c +++ b/check.c @@ -93,12 +93,12 @@ error: } /* check the journal and rollback incomplete transactions */ -int jfsck(const char *name, struct jfsck_result *res) +int jfsck(const char *name, const char *jdir, struct jfsck_result *res) { int tfd, rv, i, ret; unsigned int maxtid; uint32_t csum1, csum2; - char jdir[PATH_MAX], jlockfile[PATH_MAX], tname[PATH_MAX]; + char jlockfile[PATH_MAX], tname[PATH_MAX]; struct stat sinfo; struct jfs fs; struct jtrans *curts; @@ -113,6 +113,7 @@ int jfsck(const char *name, struct jfsck_result *res) dir = NULL; fs.fd = -1; fs.jfd = -1; + fs.jdir = NULL; fs.jdirfd = -1; fs.jmap = MAP_FAILED; map = NULL; @@ -134,17 +135,33 @@ int jfsck(const char *name, struct jfsck_result *res) fs.name = (char *) name; - if (!get_jdir(name, jdir)) { - ret = J_ENOMEM; - goto exit; + if (jdir == NULL) { + fs.jdir = (char *) malloc(PATH_MAX); + if (fs.jdir == NULL) { + ret = J_ENOMEM; + goto exit; + } + + if (!get_jdir(name, fs.jdir)) { + ret = J_ENOMEM; + goto exit; + } + } else { + fs.jdir = (char *) malloc(strlen(jdir) + 1); + if (fs.jdir == NULL) { + ret = J_ENOMEM; + goto exit; + } + strcpy(fs.jdir, jdir); } - rv = lstat(jdir, &sinfo); + + rv = lstat(fs.jdir, &sinfo); if (rv < 0 || !S_ISDIR(sinfo.st_mode)) { ret = J_ENOJOURNAL; goto exit; } - fs.jdirfd = open(jdir, O_RDONLY); + fs.jdirfd = open(fs.jdir, O_RDONLY); if (fs.jdirfd < 0) { ret = J_ENOJOURNAL; goto exit; @@ -152,7 +169,7 @@ int jfsck(const char *name, struct jfsck_result *res) /* open the lock file, which is only used to complete the jfs * structure */ - snprintf(jlockfile, PATH_MAX, "%s/%s", jdir, "lock"); + snprintf(jlockfile, PATH_MAX, "%s/%s", fs.jdir, "lock"); rv = open(jlockfile, O_RDWR | O_CREAT, 0600); if (rv < 0) { ret = J_ENOJOURNAL; @@ -167,7 +184,7 @@ int jfsck(const char *name, struct jfsck_result *res) goto exit; } - dir = opendir(jdir); + dir = opendir(fs.jdir); if (dir == NULL) { ret = J_ENOJOURNAL; goto exit; @@ -210,7 +227,7 @@ int jfsck(const char *name, struct jfsck_result *res) * really looping in order (recovering transaction in a * different order as they were applied means instant * corruption) */ - if (!get_jtfile(name, i, tname)) { + if (!get_jtfile(&fs, i, tname)) { ret = J_ENOMEM; goto exit; } @@ -290,6 +307,8 @@ exit: close(fs.jfd); if (fs.jdirfd >= 0) close(fs.jdirfd); + if (fs.jdir) + free(fs.jdir); if (dir != NULL) closedir(dir); if (fs.jmap != MAP_FAILED) @@ -300,16 +319,20 @@ exit: } /* remove all the files in the journal directory (if any) */ -int jfsck_cleanup(const char *name) +int jfsck_cleanup(const char *name, const char *jdir) { - char jdir[PATH_MAX], tfile[PATH_MAX*3]; + char path[PATH_MAX], tfile[PATH_MAX*3]; DIR *dir; struct dirent *dent; - if (!get_jdir(name, jdir)) - return 0; + if (jdir == NULL) { + if (!get_jdir(name, path)) + return 0; + } else { + strcpy(path, jdir); + } - dir = opendir(jdir); + dir = opendir(path); if (dir == NULL && errno == ENOENT) /* it doesn't exist, so it's clean */ return 1; @@ -324,7 +347,7 @@ int jfsck_cleanup(const char *name) /* build the full path to the transaction file */ memset(tfile, 0, PATH_MAX * 3); - strcat(tfile, jdir); + strcat(tfile, path); strcat(tfile, "/"); strcat(tfile, dent->d_name); @@ -339,7 +362,7 @@ int jfsck_cleanup(const char *name) } closedir(dir); - rmdir(jdir); + rmdir(path); return 1; } diff --git a/common.c b/common.c index a2e9bf0..739b5e4 100644 --- a/common.c +++ b/common.c @@ -15,6 +15,7 @@ #include <stdio.h> #include <stdlib.h> +#include "libjio.h" #include "common.h" @@ -126,26 +127,9 @@ int get_jdir(const char *filename, char *jdir) } /* build the filename of a given transaction */ -int get_jtfile(const char *filename, unsigned int tid, char *jtfile) +int get_jtfile(struct jfs *fs, unsigned int tid, char *jtfile) { - char *base, *baset; - char *dir, *dirt; - - baset = strdup(filename); - if (baset == NULL) - return 0; - base = basename(baset); - - dirt = strdup(filename); - if (dirt == NULL) - return 0; - dir = dirname(dirt); - - snprintf(jtfile, PATH_MAX, "%s/.%s.jio/%u", dir, base, tid); - - free(baset); - free(dirt); - + snprintf(jtfile, PATH_MAX, "%s/%u", fs->jdir, tid); return 1; } diff --git a/common.h b/common.h index 586ceb5..76a3e3a 100644 --- a/common.h +++ b/common.h @@ -12,6 +12,7 @@ #include <sys/types.h> /* for ssize_t and off_t */ #include <stdint.h> /* for uint*_t */ +#include "libjio.h" /* for struct jfs */ #define _F_READ 0x00001 #define _F_WRITE 0x00010 @@ -30,7 +31,7 @@ off_t plockf(int fd, int cmd, off_t offset, off_t len); ssize_t spread(int fd, void *buf, size_t count, off_t offset); ssize_t spwrite(int fd, const void *buf, size_t count, off_t offset); int get_jdir(const char *filename, char *jdir); -int get_jtfile(const char *filename, unsigned int tid, char *jtfile); +int get_jtfile(struct jfs *fs, unsigned int tid, char *jtfile); int checksum(int fd, size_t len, uint32_t *csum); uint32_t checksum_map(uint8_t *map, size_t count); diff --git a/jiofsck.c b/jiofsck.c index db3f6ba..d2cf7e4 100644 --- a/jiofsck.c +++ b/jiofsck.c @@ -1,5 +1,5 @@ -/* +/* * jiofsck - A journal checker and recovery tool for libjio * Alberto Bertogli (albertogli@telpin.com.ar) */ @@ -11,42 +11,52 @@ void usage() { - printf("Use: jiofsck [clean] FILE\n\n"); - printf("Where \"FILE\" is the name of the file " - "which you want to check the journal from,\n" - "and the optional parameter \"clean\" makes " - "jiofsck to clean up the journal after\n" - "recovery.\n"); + printf("\ +Use: jiofsck [clean=1] [dir=DIR] FILE\n\ +\n\ +Where \"FILE\" is the name of the file you want to check the journal from,\n\ +and the optional parameter \"clean\" makes jiofsck to clean up the journal\n\ +after recovery.\n\ +The parameter \"dir=DIR\", also optional, is used to indicate the position\n\ +of the journal directory.\n\ +\n\ +Examples:\n\ +# jiofsck file\n\ +# jiofsck clean=1 file\n\ +# jiofsck dir=/tmp/journal file\n\ +# jiofsck clean=1 dir=/tmp/journal file\n\ +\n"); } int main(int argc, char **argv) { - int rv, do_cleanup; - char *file; + int i, rv, do_cleanup; + char *file, *jdir; struct jfsck_result res; - - if (argc != 2 && argc != 3) { + + file = jdir = NULL; + do_cleanup = 0; + + if (argc < 2) { usage(); return 1; } - if (argc == 3) { - if (strcmp("clean", argv[1]) != 0 ) { - usage(); - return 1; + for (i = 1; i < argc; i++) { + if (strcmp("clean=1", argv[i]) == 0) { + do_cleanup = 1; + } else if (strncmp("dir=", argv[i], 4) == 0) { + jdir = argv[i] + 4; + } else { + file = argv[i]; } - file = argv[2]; - do_cleanup = 1; - } else { - file = argv[1]; - do_cleanup = 0; } memset(&res, 0, sizeof(res)); - + printf("Checking journal: "); fflush(stdout); - rv = jfsck(file, &res); + rv = jfsck(file, jdir, &res); if (rv == J_ENOENT) { printf("No such file or directory\n"); @@ -62,7 +72,7 @@ int main(int argc, char **argv) if (do_cleanup) { printf("Cleaning journal: "); fflush(stdout); - if (!jfsck_cleanup(file)) { + if (!jfsck_cleanup(file, jdir)) { printf("Error cleaning journal\n"); return 1; } @@ -76,14 +86,12 @@ int main(int argc, char **argv) printf("Total:\t\t %d\n", res.total); printf("Invalid:\t %d\n", res.invalid); printf("In progress:\t %d\n", res.in_progress); - printf("Broken head:\t %d\n", res.broken_head); - printf("Broken body:\t %d\n", res.broken_body); - printf("Load error:\t %d\n", res.load_error); + printf("Broken:\t\t %d\n", res.broken); printf("Corrupt:\t %d\n", res.corrupt); printf("Apply error:\t %d\n", res.apply_error); printf("Reapplied:\t %d\n", res.reapplied); printf("\n"); - + if (!do_cleanup) { printf("You can now safely remove the journal directory " "completely\nto start a new journal.\n"); diff --git a/libjio.h b/libjio.h index 7c24796..32519d5 100644 --- a/libjio.h +++ b/libjio.h @@ -34,6 +34,7 @@ extern "C" { struct jfs { int fd; /* main file descriptor */ char *name; /* and its name */ + char *jdir; /* journal directory */ int jdirfd; /* journal directory file descriptor */ int jfd; /* journal's lock file descriptor */ unsigned int *jmap; /* journal's lock file mmap area */ @@ -110,12 +111,13 @@ ssize_t jtrans_commit(struct jtrans *ts); ssize_t jtrans_rollback(struct jtrans *ts); void jtrans_free(struct jtrans *ts); int jsync(struct jfs *fs); +int jmove_journal(struct jfs *fs, const char *newpath); int jclose(struct jfs *fs); /* journal checker */ -int jfsck(const char *name, struct jfsck_result *res); -int jfsck_cleanup(const char *name); +int jfsck(const char *name, const char *jdir, struct jfsck_result *res); +int jfsck_cleanup(const char *name, const char *jdir); /* UNIX API wrappers */ ssize_t jread(struct jfs *fs, void *buf, size_t count); diff --git a/trans.c b/trans.c index bb1daff..7ee2978 100644 --- a/trans.c +++ b/trans.c @@ -71,7 +71,7 @@ static void free_tid(struct jfs *fs, unsigned int tid) /* this can fail if we're low on mem, but we don't * care checking here because the problem will come * out later and we can fail more properly */ - get_jtfile(fs->name, i, name); + get_jtfile(fs, i, name); if (access(name, R_OK | W_OK) == 0) { curid = i; break; @@ -221,7 +221,7 @@ ssize_t jtrans_commit(struct jtrans *ts) goto exit; /* open the transaction file */ - if (!get_jtfile(ts->fs->name, id, name)) + if (!get_jtfile(ts->fs, id, name)) goto exit; fd = open(name, O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd < 0) @@ -533,6 +533,7 @@ int jopen(struct jfs *fs, const char *name, int flags, int mode, int jflags) fs->fd = -1; fs->jfd = -1; + fs->jdir = NULL; fs->jdirfd = -1; fs->jmap = MAP_FAILED; @@ -588,6 +589,11 @@ int jopen(struct jfs *fs, const char *name, int flags, int mode, int jflags) if (rv < 0 || !S_ISDIR(sinfo.st_mode)) goto error_exit; + fs->jdir = (char *) malloc(strlen(jdir) + 1); + if (fs->jdir == NULL) + goto error_exit; + strcpy(fs->jdir, jdir); + /* open the directory, we will use it to flush transaction files' * metadata in jtrans_commit() */ fs->jdirfd = open(jdir, O_RDONLY); @@ -660,6 +666,68 @@ int jsync(struct jfs *fs) return 0; } +/* change the location of the journal directory */ +int jmove_journal(struct jfs *fs, const char *newpath) +{ + int ret; + char *oldpath, jlockfile[PATH_MAX]; + + /* we try to be sure that all lingering transactions have been + * applied, so when we try to remove the journal directory, only the + * lockfile is there; however, we do this just to be nice, but the + * caller must be sure there are no in-flight transactions or any + * other kind of operation around when he calls this function */ + jsync(fs); + + oldpath = fs->jdir; + + fs->jdir = (char *) malloc(strlen(newpath + 1)); + if (fs->jdir == NULL) + return -1; + strcpy(fs->jdir, newpath); + + ret = rename(oldpath, newpath); + if (ret == -1 && (errno == ENOTEMPTY || errno == EEXIST) ) { + /* rename() failed, the dest. directory is not empty, so we + * have to reload everything */ + + close(fs->jdirfd); + fs->jdirfd = open(newpath, O_RDONLY); + if (fs->jdirfd < 0) { + ret = -1; + goto exit; + } + + close(fs->jfd); + snprintf(jlockfile, PATH_MAX, "%s/%s", newpath, "lock"); + fs->jfd = open(jlockfile, O_RDWR | O_CREAT, 0600); + if (fs->jfd < 0) + goto exit; + + munmap(fs->jmap, sizeof(unsigned int)); + fs->jmap = (unsigned int *) mmap(NULL, sizeof(unsigned int), + PROT_READ | PROT_WRITE, MAP_SHARED, fs->jfd, 0); + if (fs->jmap == MAP_FAILED) + goto exit; + + /* remove the journal directory, if possible */ + snprintf(jlockfile, PATH_MAX, "%s/%s", oldpath, "lock"); + unlink(jlockfile); + ret = rmdir(oldpath); + if (ret == -1) { + /* we couldn't remove it, something went wrong + * (possible it had some files left) */ + goto exit; + } + + ret = 0; + } + +exit: + free(oldpath); + return ret; +} + /* close a file */ int jclose(struct jfs *fs) { @@ -683,6 +751,8 @@ int jclose(struct jfs *fs) if (fs->name) /* allocated by strdup() in jopen() */ free(fs->name); + if (fs->jdir) + free(fs->jdir); pthread_mutex_destroy(&(fs->lock)); return ret;