diff --git a/cd b/cd deleted file mode 100644 index e69de29b..00000000 diff --git a/contrib/fssim/src/.gitignore b/contrib/fssim/src/.gitignore new file mode 100644 index 00000000..6722cd96 --- /dev/null +++ b/contrib/fssim/src/.gitignore @@ -0,0 +1 @@ +*.xml diff --git a/src/libltfs/Makefile.am b/src/libltfs/Makefile.am index b3ae064d..9b9ab438 100644 --- a/src/libltfs/Makefile.am +++ b/src/libltfs/Makefile.am @@ -62,6 +62,7 @@ libltfs_la_SOURCES = \ config_file.c \ plugin.c \ periodic_sync.c \ + inc_journal.c \ arch/uuid_internal.c \ arch/filename_handling.c \ arch/time_internal.c \ diff --git a/src/libltfs/fs.c b/src/libltfs/fs.c index 01909508..38bd65a0 100644 --- a/src/libltfs/fs.c +++ b/src/libltfs/fs.c @@ -650,6 +650,99 @@ void fs_split_path(char *path, char **filename, size_t len) } } +int fs_path_clean(const char *path, struct ltfs_index *idx) +{ + int ret = 0; + struct dentry *d = NULL, *parent = NULL; + char *tmp_path, *start, *end; + + CHECK_ARG_NULL(path, -LTFS_NULL_ARG); + CHECK_ARG_NULL(idx, -LTFS_NULL_ARG); + + tmp_path = strdup(path); + if (! tmp_path) { + ltfsmsg(LTFS_ERR, 10001E, "fs_path_clean: tmp_path"); + return -LTFS_NO_MEMORY; + } + + /* Get a reference count on the root dentry. Either it will be returned immediately, or it + * will be disposed later after the first path lookup. */ + acquirewrite_mrsw(&idx->root->meta_lock); + ++idx->root->numhandles; + releasewrite_mrsw(&idx->root->meta_lock); + + if (idx->root->dirty) + idx->root->dirty = false; + + /* Did the caller ask for the root dentry? */ + if (*path == '\0' || ! strcmp(path, "/")) { + goto out; + } + + start = tmp_path + 1; + end = tmp_path; + d = idx->root; + + while (end) { + end = strstr(start, "/"); + if (end) + *end = '\0'; + + acquireread_mrsw(&d->contents_lock); + + if (parent) + releaseread_mrsw(&parent->contents_lock); + parent = d; + d = NULL; + + ret = fs_directory_lookup(parent, start, &d); + if (ret < 0 || ! d) { + releaseread_mrsw(&parent->contents_lock); + fs_release_dentry(parent); + + if (ret == 0) + ret = -LTFS_NO_DENTRY; + goto out; + } + + /* Release the parent if we aren't keeping any locks on it. + * Since we know 'parent' has a child (d), it's guaranteed that parent is still linked + * into the file system tree. Therefore, fs_release_dentry is just a fancy way of + * decrementing the handle count... so do that. */ + acquirewrite_mrsw(&parent->meta_lock); + --parent->numhandles; + releasewrite_mrsw(&parent->meta_lock); + + if (d->dirty) + d->dirty = false; + + if (end) + start = end + 1; + } + + releaseread_mrsw(&parent->contents_lock); + +out: + free(tmp_path); + + return ret; +} + +int fs_dir_clean(struct dentry *d) +{ + struct name_list *list_ptr = NULL, *list_tmp = NULL; + CHECK_ARG_NULL(d, -LTFS_NULL_ARG); + + if (d->isdir) { + HASH_ITER(hh, d->child_list, list_ptr, list_tmp) { + fs_dir_clean(list_ptr->d); + } + } else + d->dirty = false; + + return 0; +} + /** * Dispose a dentry and all resources used by it, including the struct dentry itself. * @param dentry dentry to dispose. diff --git a/src/libltfs/fs.h b/src/libltfs/fs.h index 781c79d9..4fe904b0 100644 --- a/src/libltfs/fs.h +++ b/src/libltfs/fs.h @@ -146,4 +146,26 @@ int fs_path_lookup(const char *path, int flags, struct dentry **dentry, struct l */ void fs_split_path(char *path, char **filename, size_t len); +/** + * Cleanup d->dirty flag in the provided path + * + * @param path Path to search for, in UTF-8 NFC. The path should be checked + * for invalid characters by the caller. This function validates the length of each + * path component. If path points to an empty string, this function returns the + * root dentry. + * @param idx LTFS index to search. + * @return 0 on success (dentry found), -LTFS_NO_DENTRY if no dentry was found, -LTFS_NAMETOOLONG + * if any component of the path is too long, or another negative value if an internal + * (unexpected) error occurs. + */ +int fs_path_clean(const char *path, struct ltfs_index *idx); + +/** + * Cleanup d->dirty flag under the provided directory (dentry) recursively + * + * @param d dentry structure to clean. Expect a directory. + * @return 0 on success, otherwise on error + */ +int fs_dir_clean(struct dentry *d); + #endif /* __fs_helper_h */ diff --git a/src/libltfs/inc_journal.c b/src/libltfs/inc_journal.c new file mode 100644 index 00000000..84793016 --- /dev/null +++ b/src/libltfs/inc_journal.c @@ -0,0 +1,450 @@ +/* +** +** OO_Copyright_BEGIN +** +** +** Copyright 2010, 2024 IBM Corp. All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. Neither the name of the copyright holder nor the names of its +** contributors may be used to endorse or promote products derived from +** this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +** +** OO_Copyright_END +** +************************************************************************************* +** +** COMPONENT NAME: IBM Linear Tape File System +** +** FILE NAME: inc_journal.c +** +** DESCRIPTION: Journal handling for incremental index +** +** AUTHORS: Atsushi Abe +** IBM Tokyo Lab., Japan +** piste@jp.ibm.com +** +** ORIGINAL LOGIC: David Pease +** pease@coati.com +** +************************************************************************************* +*/ + +#include "ltfs.h" +#include "fs.h" +#include "inc_journal.h" + +static int _allocate_jentry(struct jentry **e, char *path, struct dentry* d) +{ + struct jentry *ent = NULL; + *e = NULL; + + ent = calloc(1, sizeof(struct jentry)); + if (!ent) { + ltfsmsg(LTFS_ERR, 11168E); + return -LTFS_NO_MEMORY; + } + + ent->id.full_path = path; + ent->id.uid = d->uid; + *e = ent; + + return 0; +} + +static int _dispose_jentry(struct jentry *ent) +{ + if (ent) { + if (ent->id.full_path) + free(ent->id.full_path); + free(ent); + } + + return 0; +} + +/** + * Handle created object in the tree. + * + * Caller need to grab vol->index->dirty_lock outside of this function. + * + * @param ppath parent path name of the object + * @param d dentry created + * @param vol pointer to the LTFS volume + */ +int incj_create(char *ppath, struct dentry *d, struct ltfs_volume *vol) +{ + int ret = -1, len = -1; + char *full_path = NULL; + struct jentry *ent = NULL; + struct jcreated_entry *jdir = NULL, *jd = NULL; + + /* Skip journal modification because of an error */ + if (vol->journal_err) { + return 0; + } + + /* Skip if an ancestor is already created in this session */ + TAILQ_FOREACH(jd, &vol->created_dirs, list) { + char* cp = jd->path; + if (strstr(ppath, cp) == ppath) { + return 0; + } + } + + /* Create full path of created object and jentry */ + len = asprintf(&full_path, "%s/%s", ppath, d->name.name); + if (len < 0) { + ltfsmsg(LTFS_ERR, 11168E); + vol->journal_err = true; + return -LTFS_NO_MEMORY; + } + + ret = _allocate_jentry(&ent, full_path, d); + if (ret < 0) { + vol->journal_err = true; + free(full_path); + return ret; + } + + ent->reason = CREATE; + ent->dentry = d; + + HASH_ADD(hh, vol->journal, id, sizeof(struct jentry), ent); + + if (d->isdir) { + jdir = calloc(1, sizeof(struct jcreated_entry)); + if (!jdir) { + ltfsmsg(LTFS_ERR, 11168E); + return -LTFS_NO_MEMORY; + } + + /* NOTE: Use same pointer of ent because it's life is same */ + jdir->path = ent->id.full_path; + + TAILQ_INSERT_TAIL(&vol->created_dirs, jdir, list); + } + + return 0; +} + +/** + * Handle modified file in the tree. + * + * Caller need to grab vol->index->dirty_lock outside of this function. + * + * @param path path name of the object + * @param d dentry to be modified (for recording uid) + * @param vol pointer to the LTFS volume + */ +int incj_modify(char *path, struct dentry *d, struct ltfs_volume *vol) +{ + int ret = -1; + struct jentry *ent = NULL; + struct jcreated_entry *jd = NULL; + + /* Skip journal modification because of an error */ + if (vol->journal_err) { + return 0; + } + + /* Skip journal modification because it is already existed */ + HASH_FIND(hh, vol->journal, &ent->id, sizeof(struct jentry), ent); + if (ent) { + return 0; + } + + /* Skip if an ancestor is already created in this session */ + TAILQ_FOREACH(jd, &vol->created_dirs, list) { + char *cp = jd->path; + if (strstr(path, cp) == path) { + return 0; + } + } + + ret = _allocate_jentry(&ent, path, d); + if (ret < 0) { + vol->journal_err = true; + return ret; + } + + ent->reason = MODIFY; + ent->dentry = d; + + HASH_ADD(hh, vol->journal, id, sizeof(struct jentry), ent); + + return 0; +} + +/** + * Handle deleted file in the tree. + * + * Caller need to grab vol->index->dirty_lock outside of this function. + * + * @param path path name of the object + * @param d dentry to be removed (for recording uid) + * @param vol pointer to the LTFS volume + */ +int incj_rmfile(char *path, struct dentry *d, struct ltfs_volume *vol) +{ + int ret = -1; + char *full_path = NULL; + struct journal_id id; + struct jentry *ent = NULL; + struct jcreated_entry *jd = NULL; + + /* Skip journal modification because of an error */ + if (vol->journal_err) { + return 0; + } + + id.full_path = path; + id.uid = d->uid; + HASH_FIND(hh, vol->journal, &id, sizeof(struct jentry), ent); + if (ent) { + if (ent->reason == CREATE) { + /* + * Remove the entry because this file is newly created and deleted + * in one incremental index session + */ + HASH_DEL(vol->journal, ent); + return 0; + } else if (ent->reason == MODIFY) { + /* + * Override the existing entry to DELETE_FILE record. + */ + ent->reason = DELETE_FILE; + ent->dentry = NULL; + return 0; + } + } + + /* Skip if an ancestor is already created in this session */ + TAILQ_FOREACH(jd, &vol->created_dirs, list) { + char *cp = jd->path; + if (strstr(path, cp) == path) { + return 0; + } + } + + /* Create full path of created object and jentry */ + full_path = strdup(path); + if (!full_path) { + ltfsmsg(LTFS_ERR, 11168E); + vol->journal_err = true; + return -LTFS_NO_MEMORY; + } + + ret = _allocate_jentry(&ent, full_path, d); + if (ret < 0) { + vol->journal_err = true; + return ret; + } + + ent->reason = DELETE_FILE; + + HASH_ADD(hh, vol->journal, id, sizeof(struct jentry), ent); + + return 0; +} + +/** + * Handle deleted directory in the tree. + * + * Caller need to grab vol->index->dirty_lock outside of this function. + * + * @param path path name of the object + * @param d dentry to be removed (for recording uid) + * @param vol pointer to the LTFS volume + */ +int incj_rmdir(char *path, struct dentry *d, struct ltfs_volume *vol) +{ + int ret = -1; + char *full_path = NULL; + struct jentry *ent = NULL, *je = NULL, *tmp = NULL; + struct jcreated_entry *jd = NULL, *dtmp = NULL; + + /* Skip journal modification because of an error */ + if (vol->journal_err) { + return 0; + } + + /* + * 1. Remove entry from created_dirs if created directory is removed in a same session + * 2. Skip if an ancestor is already created in this session + */ + TAILQ_FOREACH_SAFE(jd, &vol->created_dirs, list, dtmp) { + char *cp = jd->path; + if (strstr(path, cp) == path) { + if (!strcmp(path, cp)) { + TAILQ_REMOVE(&vol->created_dirs, jd, list); + /* + * NOTE: + * Do not free jd->path because it shall be freed into _dispose_jentry. + * jentry::id.full_path and jd->path points the same address + */ + } else { + return 0; + } + } + } + + /* Need to find existing children under this directory */ + HASH_ITER(hh, vol->journal, je, tmp) { + if (strstr(je->id.full_path, path) == je->id.full_path) { + HASH_DEL(vol->journal, je); + _dispose_jentry(je); + } + } + + /* Create full path of created object and jentry */ + full_path = strdup(path); + if (!full_path) { + ltfsmsg(LTFS_ERR, 11168E); + vol->journal_err = true; + return -LTFS_NO_MEMORY; + } + + ret = _allocate_jentry(&ent, full_path, d); + if (ret < 0) { + vol->journal_err = true; + return ret; + } + + ent->reason = DELETE_DIRECTORY; + + HASH_ADD(hh, vol->journal, id, sizeof(struct jentry), ent); + + return 0; +} + +/** + * Clear all entries into the incremental journal + */ +int incj_clear(struct ltfs_volume *vol) +{ + struct jentry *je = NULL, *tmp = NULL; + struct jcreated_entry *jd = NULL, *dtmp = NULL; + + TAILQ_FOREACH_SAFE(jd, &vol->created_dirs, list, dtmp) { + TAILQ_REMOVE(&vol->created_dirs, jd, list); + } + + HASH_ITER(hh, vol->journal, je, tmp) { + HASH_DEL(vol->journal, je); + _dispose_jentry(je); + } + + return 0; +} + +/** + * Sort function for dump incremental journal + */ +static int _by_path(const struct jentry *a, const struct jentry *b) +{ + int ret = 0; + + ret = strcmp(a->id.full_path, b->id.full_path); + if (!ret) { + if (a->id.uid > b->id.uid) + ret = 1; + else + ret = -1; + } + + return ret; +} + +static inline int dig_path(char *p, struct ltfs_index *idx) +{ + int ret = 0; + char *path, *filename; + + path = strdup(p); + if (! path) { + ltfsmsg(LTFS_ERR, 10001E, "dig_path: path"); + return -LTFS_NO_MEMORY; + } + + ret = fs_path_clean(path, idx); + + free(path); + + return ret; +} + +/** + * This is a function for debug. Print contents of the journal and the created + * directory list to stdout. + */ +void incj_dump(struct ltfs_volume *vol) +{ + char *prev_parent = NULL, *parent, *filename; + struct jcreated_entry *jd = NULL, *dtmp = NULL; + struct jentry *ent = NULL, *tmp = NULL; + char *reason[] = { "CREATE", "MODIFY", "DELFILE", "DELDIR" }; + + printf("===============================================================================\n"); + TAILQ_FOREACH_SAFE(jd, &vol->created_dirs, list, dtmp) { + printf("CREATED_DIR: %s\n", jd->path); + TAILQ_REMOVE(&vol->created_dirs, jd, list); + } + + printf("--------------------------------------------------------------------------------\n"); + HASH_SORT(vol->journal, _by_path); + HASH_ITER(hh, vol->journal, ent, tmp) { + printf("JOURNAL: %s, %llu, %s, ", ent->id.full_path, ent->id.uid, reason[ent->reason]); + if (!ent->dentry) + printf("no-dentry\n"); + else { + if (ent->dentry->isdir) { + printf("dir\n"); + if (ent->reason == CREATE) + fs_dir_clean(ent->dentry); + } else + printf("file\n"); + + parent= strdup(ent->id.full_path); + fs_split_path(parent, &filename, strlen(parent) + 1); + + if (prev_parent) { + if (strcmp(prev_parent, parent)) { + dig_path(parent, vol->index); + } + free(prev_parent); + } else { + dig_path(parent, vol->index); + } + prev_parent = parent; + ent->dentry->dirty = false; + } + + HASH_DEL(vol->journal, ent); + _dispose_jentry(ent); + } + + if (prev_parent) free(prev_parent); + + return; +} diff --git a/src/libltfs/inc_journal.h b/src/libltfs/inc_journal.h new file mode 100644 index 00000000..1b4b1b2f --- /dev/null +++ b/src/libltfs/inc_journal.h @@ -0,0 +1,109 @@ +/* +** +** OO_Copyright_BEGIN +** +** +** Copyright 2010, 2024 IBM Corp. All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. Neither the name of the copyright holder nor the names of its +** contributors may be used to endorse or promote products derived from +** this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +** +** OO_Copyright_END +** +************************************************************************************* +** +** COMPONENT NAME: IBM Linear Tape File System +** +** FILE NAME: inc_journal.h +** +** DESCRIPTION: Journal handling for incremental index +** +** AUTHORS: Atsushi Abe +** IBM Tokyo Lab., Japan +** piste@jp.ibm.com +** +** ORIGINAL LOGIC: David Pease +** pease@coati.com +** +************************************************************************************* +*/ + +#include "queue.h" + +#ifndef __inc_journal_h__ +#define __inc_journal_h__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Enumeration of reasons for an entry + */ +enum journal_reason { + CREATE = 0, /**< Newly created, need to be 0 for debug */ + MODIFY, /**< Modified */ + DELETE_FILE, /**< File is deleted */ + DELETE_DIRECTORY, /**< Directory is deleted */ +}; + +/** + * Identifier of journal entry for handling multiple changes in one session + */ +struct journal_id { + char *full_path; /**< Full path name of the target */ + uint64_t uid; /**< i-node number of the target */ +}; + +/** + * Journal entry + */ +struct jentry { + struct journal_id id; /**< ID of the journal entry (key of the hash table) */ + enum journal_reason reason; /**< Reason of the entry */ + struct dentry *dentry; /**< Target dentry if required */ + UT_hash_handle hh; +}; + +/** + * Created directory list + */ +struct jcreated_entry { + TAILQ_ENTRY(jcreated_entry) list; /**< Pointers for linked list of requests */ + char *path; +}; + +int incj_create(char *ppath, struct dentry *d, struct ltfs_volume *vol); +int incj_modify(char *path, struct dentry *d, struct ltfs_volume *vol); +int incj_rmfile(char *path, struct dentry *d, struct ltfs_volume *vol); +int incj_rmdir(char *path, struct dentry *d, struct ltfs_volume *vol); +int incj_clear(struct ltfs_volume *vol); +void incj_dump(struct ltfs_volume *vol); + +#ifdef __cplusplus +} +#endif + +#endif /* __inc_journal_h__ */ diff --git a/src/libltfs/ltfs.c b/src/libltfs/ltfs.c index f901be8f..c6aae5d9 100644 --- a/src/libltfs/ltfs.c +++ b/src/libltfs/ltfs.c @@ -70,6 +70,7 @@ #include "xattr.h" #include "xml_libltfs.h" #include "label.h" +#include "inc_journal.h" #include "arch/version.h" #include "arch/filename_handling.h" #include "libltfs/arch/errormap.h" @@ -373,6 +374,9 @@ int ltfs_volume_alloc(const char *execname, struct ltfs_volume **volume) } } + newvol->journal = NULL; + TAILQ_INIT(&newvol->created_dirs); + *volume = newvol; return 0; @@ -2461,6 +2465,8 @@ int ltfs_write_index(char partition, char *reason, struct ltfs_volume *vol) return ret; } + incj_clear(vol); /* Clear incremental journal data */ + bc_print = _get_barcode(vol); if (write_perm) { @@ -4572,3 +4578,59 @@ int ltfs_get_rao_list(char *path, struct ltfs_volume *vol) tape_device_unlock(vol->device); return ret; } + +static inline char* _lay_dirs(char *p, struct dentry *d) +{ + int ret = 0; + char *path = NULL; + + if (p) { + if (d->parent) + ret = asprintf(&path, "%s/%s", d->name.name, p); + else { + /* + * root dir doesn't have the parent and d->name.name is "/". + * So directory separator, '/', is not needed. + */ + ret = asprintf(&path, "%s%s", d->name.name, p); + } + free(p); + } else { + ret = asprintf(&path, "%s", d->name.name); + } + + if (ret < 0) + return NULL; + + if (d->parent) { + path = _lay_dirs(path, d->parent); + if (!path) { + /* Memory allocation error */ + return NULL; + } + } + + return path; +} + +/** + * Build full path from a dentry + */ +int ltfs_build_fullpath(char **dest, struct dentry *d) +{ + int ret = 0; + char *path = NULL; + + CHECK_ARG_NULL(dest, -LTFS_NULL_ARG); + + path = _lay_dirs(path, d); + if (path) { + *dest = path; + } else { + ltfsmsg(LTFS_ERR, 10001E, __FUNCTION__); + *dest = NULL; + ret = -LTFS_NO_MEMORY; + } + + return ret; +} diff --git a/src/libltfs/ltfs.h b/src/libltfs/ltfs.h index e34ffe3e..83b8a56e 100644 --- a/src/libltfs/ltfs.h +++ b/src/libltfs/ltfs.h @@ -339,7 +339,7 @@ struct dentry { /* Take the iosched_lock before accessing iosched_priv. */ void *iosched_priv; /**< I/O scheduler private data. */ - struct name_list *child_list; /* for hash search */ + struct name_list *child_list; /**< Child list for hash search */ }; struct tape_attr { @@ -458,10 +458,15 @@ struct ltfs_volume { struct tape_attr *t_attr; /**< Tape Attribute data */ mam_lockval lock_status; /**< Total volume lock status from t_attr->vollock and index->vollock */ struct ltfs_timespec first_locate; /**< Time to first locate */ - int file_open_count; /**< Number of opened files */ + int file_open_count; /**< Number of opened files */ - const char *work_directory; + /* Journal structure for incremental index */ + struct jentry *journal; /**< Journal for incremental index */ + TAILQ_HEAD(jcreated_struct, jcreated_entry) created_dirs; + bool journal_err; /**< Journal error flag, write a full index next time forcibly */ + /* Misc */ + const char *work_directory; /**< work directory for profiler data, dump etc.*/ }; struct ltfs_label { @@ -723,6 +728,7 @@ void ltfs_enable_livelink_mode(struct ltfs_volume *vol); int ltfs_profiler_set(uint64_t source, struct ltfs_volume *vol); int ltfs_get_rao_list(char *path, struct ltfs_volume *vol); +int ltfs_build_fullpath(char **dest, struct dentry *d); #ifdef __cplusplus } diff --git a/src/libltfs/ltfs_fsops.c b/src/libltfs/ltfs_fsops.c index efd37869..83d38a66 100644 --- a/src/libltfs/ltfs_fsops.c +++ b/src/libltfs/ltfs_fsops.c @@ -66,6 +66,7 @@ #include "dcache.h" #include "pathname.h" #include "index_criteria.h" +#include "inc_journal.h" #include "arch/time_internal.h" int ltfs_fsops_open(const char *path, bool open_write, bool use_iosched, struct dentry **d, @@ -367,9 +368,10 @@ int ltfs_fsops_create(const char *path, bool isdir, bool readonly, bool overwrit if (! isdir) ++vol->index->file_count; ltfs_set_index_dirty(false, false, vol->index); + incj_create(path_norm, d, vol); d->dirty = true; ltfs_mutex_unlock(&vol->index->dirty_lock); - vol->file_open_count ++; + vol->file_open_count++; *dentry = d; ret = 0; @@ -522,8 +524,12 @@ int ltfs_fsops_unlink(const char *path, ltfs_file_id *id, struct ltfs_volume *vo releasewrite_mrsw(&d->meta_lock); ltfs_mutex_lock(&vol->index->dirty_lock); - if (! d->isdir) + if (d->isdir) { + incj_rmdir(path_norm, d, vol); + } else { + incj_rmfile(path_norm, d, vol); --vol->index->file_count; + } ltfs_set_index_dirty(false, false, vol->index); ltfs_mutex_unlock(&vol->index->dirty_lock); @@ -548,7 +554,7 @@ int ltfs_fsops_rename(const char *from, const char *to, ltfs_file_id *id, struct int ret; char *from_norm = NULL, *to_norm = NULL; char *from_norm_copy = NULL, *to_norm_copy = NULL; - char *from_filename, *to_filename; + char *from_filename = NULL, *to_filename = NULL, *to_filename_incj = NULL; char *to_filename_copy = NULL, *to_filename_copy2 = NULL; struct dentry *fromdir = NULL, *todir = NULL; struct dentry *fromdentry = NULL, *todentry = NULL; @@ -587,14 +593,12 @@ int ltfs_fsops_rename(const char *from, const char *to, ltfs_file_id *id, struct goto out_free; } - if (dcache_initialized(vol)) { - from_norm_copy = strdup(from_norm); - to_norm_copy = strdup(to_norm); - if (! from_norm_copy || ! to_norm_copy) { - ltfsmsg(LTFS_ERR, 10001E, "ltfs_fsops_rename: file name copy"); - ret = -LTFS_NO_MEMORY; - goto out_free; - } + from_norm_copy = strdup(from_norm); + to_norm_copy = strdup(to_norm); + if (! from_norm_copy || ! to_norm_copy) { + ltfsmsg(LTFS_ERR, 10001E, "ltfs_fsops_rename: file name copy"); + ret = -LTFS_NO_MEMORY; + goto out_free; } /* Split paths into directory and file name */ @@ -868,6 +872,16 @@ int ltfs_fsops_rename(const char *from, const char *to, ltfs_file_id *id, struct fromdentry->dirty = true; + /* Process the incremental journal */ + if (fromdentry->isdir) + incj_rmdir(from_norm_copy, fromdentry, vol); + else + incj_rmfile(from_norm_copy, fromdentry, vol); + + fs_split_path(to_norm_copy, &to_filename_incj, strlen(to_norm_copy) + 1); + incj_create(to_norm_copy, fromdentry, vol); + + /* Release dentry of source */ if (! iosched_initialized(vol)) fs_release_dentry_unlocked(fromdentry); else @@ -1565,7 +1579,7 @@ int ltfs_fsops_utimens(struct dentry *d, const struct ltfs_timespec ts[2], struc d->platform_safe_name, (unsigned long long)d->uid, (unsigned long long)ts[0].tv_sec); get_current_timespec(&d->change_time); ltfs_set_index_dirty(true, true, vol->index); - d->dirty = true; + ltfs_set_dentry_dirty(d, vol); } if (d->modify_time.tv_sec != ts[1].tv_sec || d->modify_time.tv_nsec != ts[1].tv_nsec) { d->modify_time = ts[1]; @@ -1575,7 +1589,7 @@ int ltfs_fsops_utimens(struct dentry *d, const struct ltfs_timespec ts[2], struc d->platform_safe_name, (unsigned long long)d->uid, (unsigned long long)ts[1].tv_sec); get_current_timespec(&d->change_time); ltfs_set_index_dirty(true, false, vol->index); - d->dirty = true; + ltfs_set_dentry_dirty(d, vol); } if (dcache_initialized(vol)) dcache_flush(d, FLUSH_METADATA, vol); @@ -1642,7 +1656,7 @@ int ltfs_fsops_utimens_all(struct dentry *d, const struct ltfs_timespec ts[4], s d->platform_safe_name, (unsigned long long)d->uid, (unsigned long long)ts[3].tv_sec); isctime=true; ltfs_set_index_dirty(true, false, vol->index); - d->dirty = true; + ltfs_set_dentry_dirty(d, vol); } if (ts[0].tv_sec != 0 || ts[0].tv_nsec != 0) { d->access_time = ts[0]; @@ -1652,7 +1666,7 @@ int ltfs_fsops_utimens_all(struct dentry *d, const struct ltfs_timespec ts[4], s d->platform_safe_name, (unsigned long long)d->uid, (unsigned long long)ts[0].tv_sec); if(!isctime) get_current_timespec(&d->change_time); ltfs_set_index_dirty(true, true, vol->index); - d->dirty = true; + ltfs_set_dentry_dirty(d, vol); } if (ts[1].tv_sec != 0 || ts[1].tv_nsec != 0) { d->modify_time = ts[1]; @@ -1662,7 +1676,7 @@ int ltfs_fsops_utimens_all(struct dentry *d, const struct ltfs_timespec ts[4], s d->platform_safe_name, (unsigned long long)d->uid, (unsigned long long)ts[1].tv_sec); if(!isctime) get_current_timespec(&d->change_time); ltfs_set_index_dirty(true, false, vol->index); - d->dirty = true; + ltfs_set_dentry_dirty(d, vol); } if (ts[2].tv_sec != 0 || ts[2].tv_nsec != 0) { d->creation_time = ts[2]; @@ -1672,7 +1686,7 @@ int ltfs_fsops_utimens_all(struct dentry *d, const struct ltfs_timespec ts[4], s d->platform_safe_name, (unsigned long long)d->uid, (unsigned long long)ts[2].tv_sec); if(!isctime) get_current_timespec(&d->change_time); ltfs_set_index_dirty(true, false, vol->index); - d->dirty = true; + ltfs_set_dentry_dirty(d, vol); } if (dcache_initialized(vol)) @@ -1684,7 +1698,6 @@ int ltfs_fsops_utimens_all(struct dentry *d, const struct ltfs_timespec ts[4], s return 0; } - int ltfs_fsops_set_readonly(struct dentry *d, bool readonly, struct ltfs_volume *vol) { int ret; @@ -1710,6 +1723,7 @@ int ltfs_fsops_set_readonly(struct dentry *d, bool readonly, struct ltfs_volume d->readonly = readonly; get_current_timespec(&d->change_time); ltfs_set_index_dirty(true, false, vol->index); + ltfs_set_dentry_dirty(d, vol); if (dcache_initialized(vol)) dcache_flush(d, FLUSH_METADATA, vol); } @@ -2000,86 +2014,87 @@ int ltfs_fsops_readlink_path(const char* path, char* buf, size_t size, ltfs_file int ltfs_fsops_target_absolute_path(const char* link, const char* target, char* buf, size_t size ) { - char *work_buf, *target_buf, *temp_buf, *token, *next_token; // work buffer for string - int len=0,len2=0; // work variables for string length + char *work_buf, *target_buf, *temp_buf, *token, *next_token; /* work buffers for string */ + int len=0, len2=0; /* work variables for string length */ CHECK_ARG_NULL(link, -LTFS_NULL_ARG); CHECK_ARG_NULL(target, -LTFS_NULL_ARG); - // need to set message and return code + /* need to set message and return code */ if (link[0]!='/') { return -LTFS_BAD_ARG; } + /* Check input target string is already absolute path or not */ len2 = strlen(target); - // input target string is already absolute path if ( (target[0]=='/') && !strstr(target,"./" ) ) { if ( size < (size_t)len2+1) { return -LTFS_SMALL_BUFFER; } - strcpy( buf, target ); + strcpy(buf, target); return 0; } len=strlen(link); - work_buf = malloc(len+len2+1); + work_buf = malloc(len + len2 + 1); if (!work_buf) { return -LTFS_NO_MEMORY; } - target_buf = malloc(len2+1); + target_buf = malloc(len2 + 1); if (!target_buf) { - free( work_buf ); + free(work_buf); return -LTFS_NO_MEMORY; } - if ( target[0]=='/' ) { - temp_buf=strstr( target, "/." ); // get "/../ccc/target.txt" of "/aaa/../ccc/target.txt" - strcpy(target_buf,temp_buf+1); // copy "../ccc/target.txt" - len=strlen(target_buf)+1; - len=len2-len; - strncpy( work_buf, target, len ); // copy "/aaa" + if (target[0]=='/') { + temp_buf = strstr(target, "/."); /* get "/../ccc/target.txt" of "/aaa/../ccc/target.txt" */ + strcpy(target_buf, temp_buf + 1); /* copy "../ccc/target.txt" */ + len = strlen(target_buf) + 1; + len = len2 - len; + strncpy(work_buf, target, len); /* copy "/aaa" */ } else { - strcpy( work_buf, link ); - strcpy( target_buf, target ); - - // split link file name then get current directory - temp_buf=strrchr( work_buf, '/' ); // get "/link.txt" from "/aaa/bbb/link.txt" - len = len-strlen(temp_buf); // length of "/aaa/bbb" - } - - // split target path directory then modify current directory with target path information - token = strtok( target_buf,"/" ); // get ".." from "../ccc/target.txt" - while ( token ) { - next_token = strtok( NULL,"/" ); // if next_token is NULL then token is filename - if ( !next_token ) break; - if ( strcmp( token,".." )==0 ) { - work_buf[len]='\0'; // "/aaa/bbb\0link.txt" - temp_buf=strrchr( work_buf, '/' ); // get "/bbb" + strcpy(work_buf, link); + strcpy(target_buf, target); + + /* Split link file name then get current directory */ + temp_buf = strrchr(work_buf, '/'); /* get "/link.txt" from "/aaa/bbb/link.txt" */ + len -= strlen(temp_buf); /* length of "/aaa/bbb" */ + } + + /* Split target path directory then modify current directory with target path information */ + token = strtok(target_buf, "/"); /* get ".." from "../ccc/target.txt" */ + while (token) { + next_token = strtok(NULL, "/"); /* if next_token is NULL then token is filename */ + if (!next_token) + break; + if (strcmp(token, "..") == 0) { + work_buf[len] = '\0'; /* "/aaa/bbb\0link.txt" */ + temp_buf = strrchr(work_buf, '/' ); /* get "/bbb" */ if (!temp_buf) { - *buf = '\0'; // out of ltfs range + *buf = '\0'; /* out of ltfs range */ return 0; } - len = len-strlen(temp_buf); // length of "/aaa" - } else if ( strcmp( token, "." ) ) { // have directory name - work_buf[len]='/'; // put '/ 'as "/aaa/" - strncpy( work_buf+len+1, token, strlen(token)+1 ); // "/aaa/ccc\0" - len=strlen(work_buf); + len -= strlen(temp_buf); /* length of "/aaa" */ + } else if (strcmp(token, "." )) { /* have directory name */ + work_buf[len] = '/'; /* put '/ 'as "/aaa/" */ + strncpy(work_buf+len+1, token, strlen(token)+1); /* "/aaa/ccc\0" */ + len = strlen(work_buf); } token = next_token; } - work_buf[len]='/'; // put '/ 'as "/aaa/ccc/" - strncpy( work_buf+len+1, token, strlen(token)+1 ); // "/aaa/ccc/target.txt\0" + work_buf[len] = '/'; /* put '/ 'as "/aaa/ccc/" */ + strncpy(work_buf+len+1, token, strlen(token)+1); /* "/aaa/ccc/target.txt\0" */ - if ( size < strlen(work_buf)+1 ) { - free( work_buf ); - free( target_buf ); + if (size < strlen(work_buf) + 1) { + free(work_buf); + free(target_buf); return -LTFS_SMALL_BUFFER; } - strcpy( buf, work_buf ); - free( work_buf ); - free( target_buf ); + strcpy(buf, work_buf); + free(work_buf); + free(target_buf); return 0; } diff --git a/src/libltfs/ltfs_fsops_raw.c b/src/libltfs/ltfs_fsops_raw.c index 8a2b6ca8..d45a86de 100644 --- a/src/libltfs/ltfs_fsops_raw.c +++ b/src/libltfs/ltfs_fsops_raw.c @@ -398,7 +398,7 @@ int _ltfs_fsraw_add_extent_unlocked(struct dentry *d, struct extent_info *ext, b * No need to mark at this time but reserve this value for fueture release */ d->extents_dirty = true; - d->dirty = true; + ltfs_set_dentry_dirty(d, vol); releasewrite_mrsw(&d->meta_lock); ltfs_set_index_dirty(true, false, vol->index); @@ -793,12 +793,12 @@ int ltfs_fsraw_truncate(struct dentry *d, off_t length, struct ltfs_volume *vol) d->realsize = new_realsize; get_current_timespec(&d->modify_time); d->change_time = d->modify_time; + ltfs_set_dentry_dirty(d, vol); releasewrite_mrsw(&d->meta_lock); releasewrite_mrsw(&d->contents_lock); ltfs_set_index_dirty(true, false, vol->index); - d->dirty = true; releaseread_mrsw(&vol->lock); return 0; diff --git a/src/libltfs/ltfs_internal.c b/src/libltfs/ltfs_internal.c index 2f449f4e..8467f86d 100644 --- a/src/libltfs/ltfs_internal.c +++ b/src/libltfs/ltfs_internal.c @@ -65,6 +65,7 @@ #include "iosched.h" #include "ltfs_fsops.h" #include "xattr.h" +#include "inc_journal.h" /** * Allocate an empty LTFS index. @@ -1416,7 +1417,7 @@ int ltfs_write_index_conditional(char partition, struct ltfs_volume *vol) return ret; } -int ltfs_split_symlink( struct ltfs_volume *vol ) +int ltfs_split_symlink(struct ltfs_volume *vol) { size_t i, size; struct dentry *d, *workd; @@ -1527,3 +1528,22 @@ int ltfs_split_symlink( struct ltfs_volume *vol ) free(path); return ret; } + +int ltfs_set_dentry_dirty(struct dentry *d, struct ltfs_volume *vol) +{ + int ret = 0; + char *full_path = NULL; + + if (!d->dirty) { + ret = ltfs_build_fullpath(&full_path, d); + if (!ret) { + incj_modify(full_path, d, vol); + } else { + vol->journal_err = true; + } + } + + d->dirty = true; + + return ret; +} diff --git a/src/libltfs/ltfs_internal.h b/src/libltfs/ltfs_internal.h index e02365ea..714aea5e 100644 --- a/src/libltfs/ltfs_internal.h +++ b/src/libltfs/ltfs_internal.h @@ -85,6 +85,7 @@ int ltfs_seek_index(char partition, tape_block_t *eod_pos, tape_block_t *index_e bool *fm_after, bool *blocks_after, bool recover_symlink, struct ltfs_volume *vol); void _ltfs_last_ref(struct dentry *d, tape_block_t *dp_last, tape_block_t *ip_last, struct ltfs_volume *vol); -int ltfs_split_symlink( struct ltfs_volume *vol ); +int ltfs_split_symlink(struct ltfs_volume *vol); +int ltfs_set_dentry_dirty(struct dentry *d, struct ltfs_volume *vol); #endif /* __ltfs_internal_h__ */ diff --git a/src/libltfs/xattr.c b/src/libltfs/xattr.c index 1588a619..f1328910 100644 --- a/src/libltfs/xattr.c +++ b/src/libltfs/xattr.c @@ -66,6 +66,7 @@ #include "pathname.h" #include "tape.h" #include "ltfs_internal.h" +#include "inc_journal.h" #include "arch/time_internal.h" /* Helper functions for formatting virtual EA output */ @@ -209,7 +210,9 @@ static int _xattr_set_time(struct dentry *d, struct ltfs_timespec *out, const ch acquirewrite_mrsw(&d->meta_lock); *out = t; - d->dirty = true; + + ltfs_set_dentry_dirty(d, vol); + releasewrite_mrsw(&d->meta_lock); ltfs_set_index_dirty(true, false, vol->index); @@ -492,6 +495,7 @@ static bool _xattr_is_virtual(struct dentry *d, const char *name, struct ltfs_vo || ! strcmp(name, "ltfs.vendor.IBM.rao") || ! strcmp(name, "ltfs.vendor.IBM.logPage") || ! strcmp(name, "ltfs.vendor.IBM.mediaMAM") + || ! strcmp(name, "ltfs.vendor.IBM.dumpincj") || ! strncmp(name, "ltfs.vendor", strlen("ltfs.vendor"))) return true; } @@ -868,6 +872,10 @@ static int _xattr_get_virtual(struct dentry *d, char *buf, size_t buf_size, cons ret = ltfs_mam(part, (unsigned char *)buf, buf_size, vol); + } else if (! strcmp(name, "ltfs.vendor.IBM.dumpincj")) { + incj_dump(vol); + ret = 0; + } else if (! strncmp(name, "ltfs.vendor", strlen("ltfs.vendor"))) { if (! strncmp(name + strlen("ltfs.vendor."), LTFS_VENDOR_NAME, strlen(LTFS_VENDOR_NAME))) { ret = _xattr_get_vendorunique_xattr(&val, name, vol); @@ -1472,9 +1480,11 @@ int xattr_set(struct dentry *d, const char *name, const char *value, size_t size } get_current_timespec(&d->change_time); - releasewrite_mrsw(&d->meta_lock); - d->dirty = true; + ltfs_set_index_dirty(true, false, vol->index); + ltfs_set_dentry_dirty(d, vol); + + releasewrite_mrsw(&d->meta_lock); if (write_idx) ret = ltfs_sync_index(SYNC_EA, false, vol); @@ -1734,8 +1744,8 @@ int xattr_remove(struct dentry *d, const char *name, struct ltfs_volume *vol) ltfsmsg(LTFS_INFO, 17238I, "appendonly", d->is_appendonly, d->name.name); } - d->dirty = true; ltfs_set_index_dirty(true, false, vol->index); + ltfs_set_dentry_dirty(d, vol); out_dunlk: _xattr_unlock_dentry(name, true, d, vol);