Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions src/libltfs/ltfs_fsops.c
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,13 @@ int ltfs_fsops_unlink(const char *path, ltfs_file_id *id, struct ltfs_volume *vo
}
parent = d->parent;

/* Acquire parent->meta_lock up front: the shared out: path releases it
* via fs_release_dentry_unlocked(), so every goto out (including the WORM
* and non-empty-directory error paths below) must hold it. Ordering is
* preserved: parent->contents_lock (held by fs_path_lookup) before
* parent->meta_lock before the child d->meta_lock. */
acquirewrite_mrsw(&parent->meta_lock);

if (parent->is_immutable || parent->is_appendonly) {
ltfsmsg(LTFS_ERR, 17237E, "unlink: parent is WORM");
ret = -LTFS_WORM_ENABLED;
Expand All @@ -482,7 +489,6 @@ int ltfs_fsops_unlink(const char *path, ltfs_file_id *id, struct ltfs_volume *vo
goto out;
}

acquirewrite_mrsw(&parent->meta_lock);
acquirewrite_mrsw(&d->meta_lock);

if (dcache_initialized(vol)) {
Expand Down Expand Up @@ -640,18 +646,9 @@ int ltfs_fsops_rename(const char *from, const char *to, ltfs_file_id *id, struct
goto out_release;
}

if (fromdir->is_appendonly || fromdir->is_immutable ) {
ltfsmsg(LTFS_ERR, 17237E, "rename: parent is WORM");
ret = -LTFS_WORM_ENABLED;
acquirewrite_mrsw(&fromdir->meta_lock);
goto out_release;
}
if (todir->is_immutable || todir->is_appendonly) {
ltfsmsg(LTFS_ERR, 17237E, "rename: target dir is WORM");
ret = -LTFS_WORM_ENABLED;
acquirewrite_mrsw(&fromdir->meta_lock);
goto out_release;
}
/* The WORM state of the source and target directories is checked below,
* after both directory meta_locks are held (the fields are meta_lock
* protected, and the out_release path expects both meta_locks held). */

/* Take locks in the appropriate order and look up the source and destination dentries */
if (todir == fromdir || fs_is_predecessor(todir, fromdir)) {
Expand Down Expand Up @@ -760,6 +757,20 @@ int ltfs_fsops_rename(const char *from, const char *to, ltfs_file_id *id, struct
}
#endif

/* Reject renames into or out of a WORM directory. Checked here, with both
* directory meta_locks held, rather than right after lookup: the fields are
* meta_lock protected and the out_unlock/out_release path releases both
* directory meta_locks via fs_release_dentry_unlocked. */
if (fromdir->is_immutable || fromdir->is_appendonly ||
todir->is_immutable || todir->is_appendonly) {
ltfsmsg(LTFS_ERR, 17237E, "rename: source or target dir is WORM");
ret = -LTFS_WORM_ENABLED;
fs_release_dentry(fromdentry);
if (todentry && todentry != fromdentry)
fs_release_dentry(todentry);
goto out_unlock;
}

if (fromdentry->is_immutable || fromdentry->is_appendonly) {
ltfsmsg(LTFS_ERR, 17237E, "rename: src entry is WORM");
ret = -LTFS_WORM_ENABLED;
Expand Down Expand Up @@ -857,6 +868,10 @@ int ltfs_fsops_rename(const char *from, const char *to, ltfs_file_id *id, struct
fromdentry->name.percent_encode = fs_is_percent_encode_required(fromdentry->name.name);
fromdentry->platform_safe_name = to_filename_copy2;
fromdentry->matches_name_criteria = index_criteria_match(fromdentry, vol);
/* Ownership of both buffers has moved to fromdentry; clear the locals so
* the out_free path does not free them again if a later step fails. */
to_filename_copy = NULL;
to_filename_copy2 = NULL;

/* Add fromdentry to new directory */
todir->child_list = fs_add_key_to_hash_table(todir->child_list, fromdentry, &ret);
Expand Down