Skip to content

Commit

Permalink
fs/adfs: dir: update directory locking
Browse files Browse the repository at this point in the history
Update directory locking such that it covers the validation of the
directory, which could fail if another thread is concurrently writing
to the same directory.  Since we may sleep, we need to use a rwsem
rather than a rw spinlock.

Signed-off-by: Russell King <[email protected]>
Signed-off-by: Al Viro <[email protected]>
  • Loading branch information
Russell King authored and Al Viro committed Jan 21, 2020
1 parent c3c8149 commit deed1bf
Showing 1 changed file with 29 additions and 26 deletions.
55 changes: 29 additions & 26 deletions fs/adfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
/*
* For future. This should probably be per-directory.
*/
static DEFINE_RWLOCK(adfs_dir_lock);
static DECLARE_RWSEM(adfs_dir_rwsem);

int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
size_t len)
Expand Down Expand Up @@ -232,39 +232,40 @@ adfs_readdir(struct file *file, struct dir_context *ctx)
if (ctx->pos >> 32)
return 0;

down_read(&adfs_dir_rwsem);
ret = adfs_dir_read_inode(sb, inode, &dir);
if (ret)
return ret;
goto unlock;

if (ctx->pos == 0) {
if (!dir_emit_dot(file, ctx))
goto free_out;
goto unlock_relse;
ctx->pos = 1;
}
if (ctx->pos == 1) {
if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
goto free_out;
goto unlock_relse;
ctx->pos = 2;
}

read_lock(&adfs_dir_lock);

ret = ops->setpos(&dir, ctx->pos - 2);
if (ret)
goto unlock_out;
goto unlock_relse;
while (ops->getnext(&dir, &obj) == 0) {
if (!dir_emit(ctx, obj.name, obj.name_len,
obj.indaddr, DT_UNKNOWN))
break;
ctx->pos++;
}

unlock_out:
read_unlock(&adfs_dir_lock);

free_out:
unlock_relse:
up_read(&adfs_dir_rwsem);
adfs_dir_relse(&dir);
return ret;

unlock:
up_read(&adfs_dir_rwsem);
return ret;
}

int
Expand All @@ -281,13 +282,13 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
if (!ops->update)
return -EINVAL;

down_write(&adfs_dir_rwsem);
ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
if (ret)
goto out;
goto unlock;

write_lock(&adfs_dir_lock);
ret = ops->update(&dir, obj);
write_unlock(&adfs_dir_lock);
up_write(&adfs_dir_rwsem);

if (ret == 0)
adfs_dir_mark_dirty(&dir);
Expand All @@ -299,7 +300,10 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
}

adfs_dir_relse(&dir);
out:
return ret;

unlock:
up_write(&adfs_dir_rwsem);
#endif
return ret;
}
Expand Down Expand Up @@ -336,17 +340,14 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
u32 name_len;
int ret;

down_read(&adfs_dir_rwsem);
ret = adfs_dir_read_inode(sb, inode, &dir);
if (ret)
goto out;

obj->parent_id = inode->i_ino;

read_lock(&adfs_dir_lock);
goto unlock;

ret = ops->setpos(&dir, 0);
if (ret)
goto unlock_out;
goto unlock_relse;

ret = -ENOENT;
name = qstr->name;
Expand All @@ -357,13 +358,15 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
break;
}
}
obj->parent_id = inode->i_ino;

unlock_out:
read_unlock(&adfs_dir_lock);

free_out:
unlock_relse:
up_read(&adfs_dir_rwsem);
adfs_dir_relse(&dir);
out:
return ret;

unlock:
up_read(&adfs_dir_rwsem);
return ret;
}

Expand Down

0 comments on commit deed1bf

Please sign in to comment.