author | Alberto Bertogli
<albertito@blitiri.com.ar> 2009-04-23 22:01:46 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2009-04-23 22:18:56 UTC |
parent | f98796be2c98a5d80ba35b8de850538a43b81b88 |
UPGRADING | +3 | -0 |
bindings/preload/libjio_preload.c | +2 | -1 |
bindings/python/libjio.c | +15 | -43 |
doc/guide.rst | +2 | -4 |
doc/libjio.rst | +0 | -5 |
libjio/check.c | +65 | -53 |
libjio/doxygen/Makefile | +1 | -1 |
libjio/jiofsck.c | +10 | -13 |
libjio/libjio.3 | +26 | -31 |
libjio/libjio.h | +45 | -31 |
samples/full.c | +1 | -2 |
tests/behaviour/tf.py | +0 | -2 |
tests/performance/performance.c | +1 | -2 |
tests/performance/random.c | +1 | -2 |
tests/stress/jiostress | +0 | -2 |
diff --git a/UPGRADING b/UPGRADING index 6cc3e10..24c2fae 100644 --- a/UPGRADING +++ b/UPGRADING @@ -15,6 +15,9 @@ take much effort. When it's mandatory, it will be noted. (which are an indication of a severe underlying condition). - jtrans_add() returns 0 on success and -1 on errors (it used to return 1 on success and 0 on errors). + - jfsck() now has an additional "flags" parameter, which should be set to 0 + to select the default behaviour. + - jfsck_cleanup() was removed, and now jfsck() cleans up by default. -> 0.25 - It is no longer necessary to pass O_SYNC to jopen() if lingering diff --git a/bindings/preload/libjio_preload.c b/bindings/preload/libjio_preload.c index 2562b81..ab61434 100644 --- a/bindings/preload/libjio_preload.c +++ b/bindings/preload/libjio_preload.c @@ -385,6 +385,7 @@ int close(int fd) int unlink(const char *pathname) { int r; + struct jfsck_result res; if (called) { printd("orig\n"); @@ -394,7 +395,7 @@ int unlink(const char *pathname) printd("libjio\n"); rec_inc(); - jfsck_cleanup(pathname, NULL); + r = jfsck(pathname, NULL, &res, 0); rec_dec(); r = (*c_unlink)(pathname); diff --git a/bindings/python/libjio.c b/bindings/python/libjio.c index 4577267..7fb926b 100644 --- a/bindings/python/libjio.c +++ b/bindings/python/libjio.c @@ -27,9 +27,8 @@ * several operations that get added by its add() method. It gets committed * with commit(), and rolled back with rollback(). * - * There rest of the module's functions are related to file checking, called - * jfsck() and jfsck_cleanup(), which are just wrappers to the real C - * functions. + * There rest of the module's functions are related to file checking, at the + * moment only jfsck(), which is just a wrapper to the real C functions. */ /* @@ -636,23 +635,26 @@ static PyObject *jf_open(PyObject *self, PyObject *args) /* jfsck */ PyDoc_STRVAR(jf_jfsck__doc, -"jfsck(name[, jdir])\n\ +"jfsck(name[, jdir] [, flags])\n\ \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'). If the path is\n\ -incorrect, or there is no journal associated with it, an IOError will be\n\ -raised.\n\ +as the journal directory and the given flags; returns a dictionary with all\n\ +the different values of the check (equivalent to the 'struct jfsck_result').\n\ +If the path is incorrect, or there is no journal associated with it, an\n\ +IOError will be raised.\n\ It's a wrapper to jfsck().\n"); -static PyObject *jf_jfsck(PyObject *self, PyObject *args) +static PyObject *jf_jfsck(PyObject *self, PyObject *args, PyObject *kw) { int rv; + unsigned int flags; char *name, *jdir = NULL; struct jfsck_result res; PyObject *dict; + char *keywords[] = { "name", "jdir", "flags", NULL }; - if (!PyArg_ParseTuple(args, "s|s:jfsck", &name, &jdir)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "s|sI:jfsck", + keywords, &name, &jdir, &flags)) return NULL; dict = PyDict_New(); @@ -660,7 +662,7 @@ static PyObject *jf_jfsck(PyObject *self, PyObject *args) return PyErr_NoMemory(); Py_BEGIN_ALLOW_THREADS - rv = jfsck(name, jdir, &res); + rv = jfsck(name, jdir, &res, flags); Py_END_ALLOW_THREADS if (rv == J_ENOMEM) { @@ -683,40 +685,10 @@ static PyObject *jf_jfsck(PyObject *self, PyObject *args) return dict; } -/* jfsck_cleanup */ -PyDoc_STRVAR(jf_jfsck_cleanup__doc, -"jfsck_cleanup(name[, jdir])\n\ -\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, *jdir = NULL; - - if (!PyArg_ParseTuple(args, "s|s:jfsck_cleanup", &name, &jdir)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - rv = jfsck_cleanup(name, jdir); - Py_END_ALLOW_THREADS - - if (rv != 1) { - PyErr_SetObject(PyExc_IOError, PyLong_FromLong(rv)); - return NULL; - } - - return PyLong_FromLong(rv); -} - - static PyMethodDef module_methods[] = { { "open", jf_open, METH_VARARGS, jf_open__doc }, - { "jfsck", jf_jfsck, METH_VARARGS, jf_jfsck__doc }, - { "jfsck_cleanup", jf_jfsck_cleanup, METH_VARARGS, - jf_jfsck_cleanup__doc }, + { "jfsck", (PyCFunction) jf_jfsck, METH_VARARGS | METH_KEYWORDS, + jf_jfsck__doc }, { NULL, NULL, 0, NULL }, }; diff --git a/doc/guide.rst b/doc/guide.rst index e65fd1e..84af707 100644 --- a/doc/guide.rst +++ b/doc/guide.rst @@ -140,12 +140,10 @@ checks the integrity of a file and makes sure that everything is consistent. It must be called "offline", that is when you are not actively committing and rollbacking; it is normally done before calling *jopen()* and is **very, very -important**. Another good practise is to call *jfsck_cleanup()* after calling -*jfsck()*, to make sure we're starting up with a fresh clean journal. After -both calls, it is safe to assume that the file is and ready to use. +important**. You can also do this manually with an utility named jiofsck, which can be used -from the shell to perform the checking and cleanup. +from the shell to perform the checking. Rollback diff --git a/doc/libjio.rst b/doc/libjio.rst index 32de123..6bc4f3c 100644 --- a/doc/libjio.rst +++ b/doc/libjio.rst @@ -163,11 +163,6 @@ from the commit procedure, we only apply the transaction after saving it in the journal, so there is really nothing left to be done. So if the transaction is complete, we only need to rollback. -In any case, after making the recovery you can simply remove the journal -entirely and let the library create a new one, and you can be sure that -transaction atomicity was preserved. You can use jfsck_cleanup() for that -purpose. - UNIX-alike API -------------- diff --git a/libjio/check.c b/libjio/check.c index 48d8605..1d93f0d 100644 --- a/libjio/check.c +++ b/libjio/check.c @@ -95,8 +95,66 @@ error: return 0; } +/** Remove all the files in the journal directory (if any). + * + * @param name path to the file + * @param jdir path to the journal directory, use NULL for the default + * @returns 0 on success, < 0 on error + */ +static int jfsck_cleanup(const char *name, const char *jdir) +{ + char path[PATH_MAX], tfile[PATH_MAX*3]; + DIR *dir; + struct dirent *dent; + + if (jdir == NULL) { + if (!get_jdir(name, path)) + return -1; + } else { + strcpy(path, jdir); + } + + dir = opendir(path); + if (dir == NULL && errno == ENOENT) + /* it doesn't exist, so it's clean */ + return 0; + else if (dir == NULL) + return -1; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + /* we only care about transactions (named as numbers > 0) and + * the lockfile (named "lock"); ignore everything else */ + if (strcmp(dent->d_name, "lock") && atoi(dent->d_name) <= 0) + continue; + + /* build the full path to the transaction file */ + memset(tfile, 0, PATH_MAX * 3); + strcat(tfile, path); + strcat(tfile, "/"); + strcat(tfile, dent->d_name); + + if (strlen(tfile) > PATH_MAX) { + closedir(dir); + return -1; + } + + if (unlink(tfile) != 0) { + closedir(dir); + return -1; + } + } + if (closedir(dir) != 0) + return -1; + + if (rmdir(path) != 0) + return -1; + + return 0; +} + /* Check the journal and fix the incomplete transactions */ -int jfsck(const char *name, const char *jdir, struct jfsck_result *res) +enum jfsck_return jfsck(const char *name, const char *jdir, + struct jfsck_result *res, unsigned int flags) { int tfd, rv, i, ret; unsigned int maxtid; @@ -306,6 +364,12 @@ loop: res->total++; } + if ( !(flags & J_NOCLEANUP) ) { + if (jfsck_cleanup(name, jdir) < 0) { + ret = J_ECLEANUP; + } + } + exit: if (fs.fd >= 0) close(fs.fd); @@ -324,55 +388,3 @@ exit: } -/* Remove all the files in the journal directory (if any) */ -int jfsck_cleanup(const char *name, const char *jdir) -{ - char path[PATH_MAX], tfile[PATH_MAX*3]; - DIR *dir; - struct dirent *dent; - - if (jdir == NULL) { - if (!get_jdir(name, path)) - return 0; - } else { - strcpy(path, jdir); - } - - dir = opendir(path); - if (dir == NULL && errno == ENOENT) - /* it doesn't exist, so it's clean */ - return 1; - else if (dir == NULL) - return 0; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - /* we only care about transactions (named as numbers > 0) and - * the lockfile (named "lock"); ignore everything else */ - if (strcmp(dent->d_name, "lock") && atoi(dent->d_name) <= 0) - continue; - - /* build the full path to the transaction file */ - memset(tfile, 0, PATH_MAX * 3); - strcat(tfile, path); - strcat(tfile, "/"); - strcat(tfile, dent->d_name); - - if (strlen(tfile) > PATH_MAX) { - closedir(dir); - return 0; - } - - if (unlink(tfile) != 0) { - closedir(dir); - return 0; - } - } - if (closedir(dir) != 0) - return 0; - - if (rmdir(path) != 0) - return 0; - - return 1; -} - diff --git a/libjio/doxygen/Makefile b/libjio/doxygen/Makefile index b70d3ce..3b020d0 100644 --- a/libjio/doxygen/Makefile +++ b/libjio/doxygen/Makefile @@ -17,7 +17,7 @@ internal: $(NICE_DOXYGEN) Doxyfile.internal clean: - rm -r doc.internal doc.public + rm -rf doc.internal doc.public .PHONY: all clean default doxygen internal public diff --git a/libjio/jiofsck.c b/libjio/jiofsck.c index a22dcd7..8cfe00f 100644 --- a/libjio/jiofsck.c +++ b/libjio/jiofsck.c @@ -30,9 +30,13 @@ Examples:\n\ int main(int argc, char **argv) { - int i, rv, do_cleanup; + int i, do_cleanup; + unsigned int flags; char *file, *jdir; struct jfsck_result res; + enum jfsck_return rv; + + file = jdir = NULL; do_cleanup = 0; @@ -54,9 +58,13 @@ int main(int argc, char **argv) memset(&res, 0, sizeof(res)); + flags = 0; + if (!do_cleanup) + flags |= J_NOCLEANUP; + printf("Checking journal: "); fflush(stdout); - rv = jfsck(file, jdir, &res); + rv = jfsck(file, jdir, &res, flags); if (rv == J_ENOENT) { printf("No such file or directory\n"); @@ -69,17 +77,6 @@ int main(int argc, char **argv) printf("done\n"); - if (do_cleanup) { - printf("Cleaning journal: "); - fflush(stdout); - if (!jfsck_cleanup(file, jdir)) { - printf("Error cleaning journal\n"); - return 1; - } - - printf("done\n"); - } - printf("Journal checking results\n"); printf("------------------------\n\n"); diff --git a/libjio/libjio.3 b/libjio/libjio.3 index fde6aad..2b8bf53 100644 --- a/libjio/libjio.3 +++ b/libjio/libjio.3 @@ -34,9 +34,8 @@ libjio - A library for Journaled I/O .BI "int jfs_autosync_stop(jfs_t *" fs ");" .BI "int jmove_journal(jfs_t *" fs ", const char *" newpath ");" -.BI "int jfsck(const char *" name ", const char *" jdir "," -.BI " jfs_tck_result *" res ");" -.BI "int jfsck_cleanup(const char *" name ", const char *" jdir ");" +.BI "enum jfsck_return jfsck(const char *" name ", const char *" jdir "," +.BI " jfsck_result *" res ", unsigned int " flags ");" .BR "struct jfsck_result" " {" int total; /* total transactions files we looked at */ @@ -48,6 +47,15 @@ libjio - A library for Journaled I/O ... }; +.BR "enum jfsck_return" " {" + J_ESUCCESS = 0, /* Success */ + J_ENOENT = -1, /* No such file or directory */ + J_ENOJOURNAL = -2, /* No journal associated with the given file */ + J_ENOMEM = -3, /* Not enough free memory */ + J_ECLEANUP = -4, /* Error cleaning the journal directory */ +}; + + .SH DESCRIPTION libjio is a library to do transaction-oriented journaled I/O. This manpage @@ -62,7 +70,7 @@ completion and < 0 upon failure, unless otherwise noted. The common functions provide functionality common to the other two: .BR jopen() " and " jclose() to open and close files in order to use them with the library, and -.BR "jfsck() " and " jfsck_cleanup()" +.B jfsck() to provide integrity checking. The UNIX-alike API mimics the traditional UNIX API by providing similar @@ -121,17 +129,17 @@ The thread is also stopped automatically when .B jclose() is called. -There are two functions that differ from the rest, which are -.BR jfsck() " and " jfsck_cleanup() . -Both take as the first two parameters the path to the file to check and the -path to the journal directory (usually NULL for the default, unless you've -changed it manually using -.BR jmove_journal() ). - -The first one, -.BR jfsck() , -is used to perform journal checking and recovery in case of a crash. It must -be performed when nobody else is using the file (like in the case of a +.B jfsck() +takes as the first two parameters the path to the file to check and the path +to the journal directory (usually NULL for the default, unless you've changed +it manually using +.BR jmove_journal() ), +and optionally a flags parameter, which can be 0 for the default behaviour, or +J_NOCLEANUP to indicate that the journal should not be cleaned up after +successful recovery. + +It is used to perform journal checking and recovery in case of a crash. It +must be performed when nobody else is using the file (like in the case of a filesystem which can't be mounted), and it returns 0 if success or an error code != 0 in case of a failure. If it succeeded, it will fill jfsck_result summarizing the outcome of the operation. The error codes can be either @@ -140,25 +148,12 @@ summarizing the outcome of the operation. The error codes can be either .I J_ENOJOURNAL (no journal associated with that file) or .I J_ENOMEM -(not enough free memory). There is also a program named +(not enough free memory), and +.I J_ECLEANUP +(error cleaning the journal directory). There is also a program named .I jiofsck which is just a simple human frontend to this function. -The second, -.BR jfsck_cleanup() , -is intended to be used after -.B jfsck() -by programs wanting to remove all the stall transaction files and leave the -journal directory ready to use. After calling -.BR jfsck() , -those transaction files will no longer be needed, so by cleaning up the -directory you make sure you're starting over with a clean journal. It returns -0 if there was an error, or 1 if it succeeded. The aforementioned -.I jiofsck -can also optionally invoke this function after performing the regular checks. -Calling it after -.B jfsck() -is highly recommended. .SS UNIX-alike API diff --git a/libjio/libjio.h b/libjio/libjio.h index d4919aa..143a305 100644 --- a/libjio/libjio.h +++ b/libjio/libjio.h @@ -27,10 +27,10 @@ * Opaque types, the API does not expose these */ -/** An open file, similar to a file descriptor */ +/** An open file, similar to a file descriptor. */ typedef struct jfs jfs_t; -/** A single transaction */ +/** A single transaction. */ typedef struct jtrans jtrans_t; @@ -38,7 +38,9 @@ typedef struct jtrans jtrans_t; * Public types */ -/** The result of a jfsck() run +/** The result of a jfsck() run. + * + * @see jfsck() * @ingroup check */ struct jfsck_result { @@ -64,6 +66,29 @@ struct jfsck_result { int reapplied; }; +/** jfsck() return values. + * + * @see jfsck() + * @ingroup check + */ +enum jfsck_return { + /** Success */ + J_ESUCCESS = 0, + + /** No such file or directory */ + J_ENOENT = -1, + + /** No journal associated with the given file */ + J_ENOJOURNAL = -2, + + /** Not enough free memory */ + J_ENOMEM = -3, + + /** Error cleaning the journal directory */ + J_ECLEANUP = -4, +}; + + /* * Core functions @@ -226,22 +251,26 @@ int jfs_autosync_stop(jfs_t *fs); */ /** Check and repair the given path. + * + * The file MUST NOT be in use by any other thread or process. This + * requirement will be lifted in future releases. * * @param name path to the file to check * @param jdir journal directory of the given file, use NULL for the default * @param res structure where to store the result - * @see struct jfsck_result, jfsck_cleanup() - * @return 0 on success, < 0 on error, with the following possible negative - * values: J_ENOENT if there was no such file with the given name, - * J_ENOJOURNAL if there was no journal at the given jdir, J_ENOMEM if - * memory could not be allocated. + * @param flags flags that change the checking behaviour, currently only + * J_NOCLEANUP is supported, which avoids cleaning up the journal + * directory after a successful recovery + * @see struct jfsck_result + * @returns 0 on success, < 0 on error, with the following possible negative + * values from enum jfsck_return: J_ENOENT if there was no such file with + * the given name, J_ENOJOURNAL if there was no journal at the given + * jdir, J_ENOMEM if memory could not be allocated, J_ECLEANUP if there + * was an error cleaning the journal. * @ingroup check */ -int jfsck(const char *name, const char *jdir, struct jfsck_result *res); - -/** Remove all the files in the journal directory, and clean it up. - * TODO: this should be merged with jfsck() and removed */ -int jfsck_cleanup(const char *name, const char *jdir); +enum jfsck_return jfsck(const char *name, const char *jdir, + struct jfsck_result *res, unsigned int flags); /* @@ -373,7 +402,7 @@ FILE *jfsopen(jfs_t *stream, const char *mode); /* - * jopen() and jtrans_create() flags + * Miscelaneous flags */ /** Don't lock the file before operating on it */ @@ -397,23 +426,8 @@ FILE *jfsopen(jfs_t *stream, const char *mode); /** Marks a file as read-only */ #define J_RDONLY 64 - -/* - * jfsck() constants (return values) - */ - -/** Success, shouldn't be used */ -#define J_ESUCCESS 0 - -/** No such file or directory */ -#define J_ENOENT -1 - -/** No journal associated with the given file */ -#define J_ENOJOURNAL -2 - -/** Not enough free memory */ -#define J_ENOMEM -3 - +/** Do not perform a journal cleanup. Used in jfsck(). */ +#define J_NOCLEANUP 128 #endif diff --git a/samples/full.c b/samples/full.c index e36f0f2..116e23f 100644 --- a/samples/full.c +++ b/samples/full.c @@ -17,8 +17,7 @@ int main(void) struct jfsck_result result; /* check the file is OK */ - jfsck(FILENAME, NULL, &result); - jfsck_cleanup(FILENAME, NULL); + jfsck(FILENAME, NULL, &result, 0); /* and open it */ file = jopen(FILENAME, O_RDWR | O_CREAT | O_TRUNC, 0600, 0); diff --git a/tests/behaviour/tf.py b/tests/behaviour/tf.py index 7dbd2eb..786f24c 100644 --- a/tests/behaviour/tf.py +++ b/tests/behaviour/tf.py @@ -136,8 +136,6 @@ def fsck_verify(n, **kwargs): raise AssertionError, k + ' does not match: ' + \ str(res) - libjio.jfsck_cleanup(n) - def cleanup(path): """Unlinks the path and its temporary libjio directory. The libjio directory must only have the 'lock' file in it.""" diff --git a/tests/performance/performance.c b/tests/performance/performance.c index a2c48bc..eb4f9ca 100644 --- a/tests/performance/performance.c +++ b/tests/performance/performance.c @@ -130,7 +130,7 @@ int main(int argc, char **argv) } jclose(fs); - jfsck(FILENAME, NULL, &ckres); + jfsck(FILENAME, NULL, &ckres, 0); if (ckres.total != 0) { fprintf(stderr, "There were %d errors during the test\n", ckres.total); @@ -139,7 +139,6 @@ int main(int argc, char **argv) return 1; } - jfsck_cleanup(FILENAME, NULL); return 0; } diff --git a/tests/performance/random.c b/tests/performance/random.c index b21ba27..67c30cb 100644 --- a/tests/performance/random.c +++ b/tests/performance/random.c @@ -133,7 +133,7 @@ int main(int argc, char **argv) } jclose(fs); - jfsck(FILENAME, NULL, &ckres); + jfsck(FILENAME, NULL, &ckres, 0); if (ckres.total != 0) { fprintf(stderr, "There were %d errors during the test\n", ckres.total); @@ -142,7 +142,6 @@ int main(int argc, char **argv) return 1; } - jfsck_cleanup(FILENAME, NULL); return 0; } diff --git a/tests/stress/jiostress b/tests/stress/jiostress index 16bc77c..76c3d6b 100755 --- a/tests/stress/jiostress +++ b/tests/stress/jiostress @@ -130,7 +130,6 @@ class Stresser: self.jf = None r = libjio.jfsck(self.fname) self.verify() - libjio.jfsck_cleanup(self.fname) self.jf = libjio.open(self.fname, libjio.O_RDWR | libjio.O_CREAT, 0o600) @@ -227,7 +226,6 @@ def main(): r = libjio.jfsck(fname) assert r['total'] == 0 - libjio.jfsck_cleanup(fname) print("Final check completed") #os.unlink(fname)