summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/zonefs/super.c347
-rw-r--r--fs/zonefs/zonefs.h9
2 files changed, 257 insertions, 99 deletions
diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c
index 270ded209dde..7d70c327883e 100644
--- a/fs/zonefs/super.c
+++ b/fs/zonefs/super.c
@@ -243,6 +243,7 @@ static void zonefs_inode_update_mode(struct inode *inode)
}
z->z_flags &= ~ZONEFS_ZONE_INIT_MODE;
+ z->z_mode = inode->i_mode;
}
struct zonefs_ioerr_data {
@@ -578,144 +579,283 @@ static int zonefs_inode_setattr(struct user_namespace *mnt_userns,
setattr_copy(&init_user_ns, inode, iattr);
+ if (S_ISREG(inode->i_mode)) {
+ struct zonefs_zone *z = zonefs_inode_zone(inode);
+
+ z->z_mode = inode->i_mode;
+ z->z_uid = inode->i_uid;
+ z->z_gid = inode->i_gid;
+ }
+
return 0;
}
-static const struct inode_operations zonefs_dir_inode_operations = {
- .lookup = simple_lookup,
+static const struct inode_operations zonefs_file_inode_operations = {
.setattr = zonefs_inode_setattr,
};
-static void zonefs_init_dir_inode(struct inode *parent, struct inode *inode,
- enum zonefs_ztype ztype)
+static long zonefs_fname_to_fno(const struct qstr *fname)
{
- struct super_block *sb = parent->i_sb;
+ const char *name = fname->name;
+ unsigned int len = fname->len;
+ long fno = 0, shift = 1;
+ const char *rname;
+ char c = *name;
+ unsigned int i;
- inode->i_ino = bdev_nr_zones(sb->s_bdev) + ztype + 1;
- inode_init_owner(&init_user_ns, inode, parent, S_IFDIR | 0555);
- inode->i_op = &zonefs_dir_inode_operations;
- inode->i_fop = &simple_dir_operations;
- set_nlink(inode, 2);
- inc_nlink(parent);
-}
+ /*
+ * File names are always a base-10 number string without any
+ * leading 0s.
+ */
+ if (!isdigit(c))
+ return -ENOENT;
-static const struct inode_operations zonefs_file_inode_operations = {
- .setattr = zonefs_inode_setattr,
-};
+ if (len > 1 && c == '0')
+ return -ENOENT;
-static void zonefs_init_file_inode(struct inode *inode,
- struct zonefs_zone *z)
+ if (len == 1)
+ return c - '0';
+
+ for (i = 0, rname = name + len - 1; i < len; i++, rname--) {
+ c = *rname;
+ if (!isdigit(c))
+ return -ENOENT;
+ fno += (c - '0') * shift;
+ shift *= 10;
+ }
+
+ return fno;
+}
+
+static struct inode *zonefs_get_file_inode(struct inode *dir,
+ struct dentry *dentry)
{
- struct super_block *sb = inode->i_sb;
+ struct zonefs_zone_group *zgroup = dir->i_private;
+ struct super_block *sb = dir->i_sb;
struct zonefs_sb_info *sbi = ZONEFS_SB(sb);
+ struct zonefs_zone *z;
+ struct inode *inode;
+ ino_t ino;
+ long fno;
- inode->i_private = z;
+ /* Get the file number from the file name */
+ fno = zonefs_fname_to_fno(&dentry->d_name);
+ if (fno < 0)
+ return ERR_PTR(fno);
+
+ if (!zgroup->g_nr_zones || fno >= zgroup->g_nr_zones)
+ return ERR_PTR(-ENOENT);
- inode->i_ino = z->z_sector >> sbi->s_zone_sectors_shift;
- inode->i_mode = S_IFREG | sbi->s_perm;
- inode->i_uid = sbi->s_uid;
- inode->i_gid = sbi->s_gid;
+ z = &zgroup->g_zones[fno];
+ ino = z->z_sector >> sbi->s_zone_sectors_shift;
+ inode = iget_locked(sb, ino);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW)) {
+ WARN_ON_ONCE(inode->i_private != z);
+ return inode;
+ }
+
+ inode->i_ino = ino;
+ inode->i_mode = z->z_mode;
+ inode->i_ctime = inode->i_mtime = inode->i_atime = dir->i_ctime;
+ inode->i_uid = z->z_uid;
+ inode->i_gid = z->z_gid;
inode->i_size = z->z_wpoffset;
inode->i_blocks = z->z_capacity >> SECTOR_SHIFT;
+ inode->i_private = z;
inode->i_op = &zonefs_file_inode_operations;
inode->i_fop = &zonefs_file_operations;
inode->i_mapping->a_ops = &zonefs_file_aops;
/* Update the inode access rights depending on the zone condition */
- z->z_flags |= ZONEFS_ZONE_INIT_MODE;
zonefs_inode_update_mode(inode);
+
+ unlock_new_inode(inode);
+
+ return inode;
}
-static struct dentry *zonefs_create_inode(struct dentry *parent,
- const char *name,
- struct zonefs_zone *z,
- enum zonefs_ztype ztype)
+static struct inode *zonefs_get_zgroup_inode(struct super_block *sb,
+ enum zonefs_ztype ztype)
{
- struct inode *dir = d_inode(parent);
- struct dentry *dentry;
+ struct inode *root = d_inode(sb->s_root);
+ struct zonefs_sb_info *sbi = ZONEFS_SB(sb);
struct inode *inode;
- int ret = -ENOMEM;
+ ino_t ino = bdev_nr_zones(sb->s_bdev) + ztype + 1;
- dentry = d_alloc_name(parent, name);
- if (!dentry)
- return ERR_PTR(ret);
-
- inode = new_inode(parent->d_sb);
+ inode = iget_locked(sb, ino);
if (!inode)
- goto dput;
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+
+ inode->i_ino = ino;
+ inode_init_owner(&init_user_ns, inode, root, S_IFDIR | 0555);
+ inode->i_size = sbi->s_zgroup[ztype].g_nr_zones;
+ inode->i_ctime = inode->i_mtime = inode->i_atime = root->i_ctime;
+ inode->i_private = &sbi->s_zgroup[ztype];
+ set_nlink(inode, 2);
- inode->i_ctime = inode->i_mtime = inode->i_atime = dir->i_ctime;
- if (z)
- zonefs_init_file_inode(inode, z);
- else
- zonefs_init_dir_inode(dir, inode, ztype);
+ inode->i_op = &zonefs_dir_inode_operations;
+ inode->i_fop = &zonefs_dir_operations;
+
+ unlock_new_inode(inode);
- d_add(dentry, inode);
- dir->i_size++;
+ return inode;
+}
- return dentry;
-dput:
- dput(dentry);
+static struct inode *zonefs_get_dir_inode(struct inode *dir,
+ struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct zonefs_sb_info *sbi = ZONEFS_SB(sb);
+ const char *name = dentry->d_name.name;
+ enum zonefs_ztype ztype;
+
+ /*
+ * We only need to check for the "seq" directory and
+ * the "cnv" directory if we have conventional zones.
+ */
+ if (dentry->d_name.len != 3)
+ return ERR_PTR(-ENOENT);
+
+ for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) {
+ if (sbi->s_zgroup[ztype].g_nr_zones &&
+ memcmp(name, zonefs_zgroup_name(ztype), 3) == 0)
+ break;
+ }
+ if (ztype == ZONEFS_ZTYPE_MAX)
+ return ERR_PTR(-ENOENT);
- return ERR_PTR(ret);
+ return zonefs_get_zgroup_inode(sb, ztype);
}
-struct zonefs_zone_data {
- struct super_block *sb;
- unsigned int nr_zones[ZONEFS_ZTYPE_MAX];
- sector_t cnv_zone_start;
- struct blk_zone *zones;
-};
+static struct dentry *zonefs_lookup(struct inode *dir, struct dentry *dentry,
+ unsigned int flags)
+{
+ struct inode *inode;
-/*
- * Create the inodes for a zone group.
- */
-static int zonefs_create_zgroup_inodes(struct super_block *sb,
- enum zonefs_ztype ztype)
+ if (dentry->d_name.len > ZONEFS_NAME_MAX)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ if (dir == d_inode(dir->i_sb->s_root))
+ inode = zonefs_get_dir_inode(dir, dentry);
+ else
+ inode = zonefs_get_file_inode(dir, dentry);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
+
+ return d_splice_alias(inode, dentry);
+}
+
+static int zonefs_readdir_root(struct file *file, struct dir_context *ctx)
{
+ struct inode *inode = file_inode(file);
+ struct super_block *sb = inode->i_sb;
struct zonefs_sb_info *sbi = ZONEFS_SB(sb);
- struct zonefs_zone_group *zgroup = &sbi->s_zgroup[ztype];
- struct dentry *dir, *dent;
- char *file_name;
- int i, ret = 0;
+ enum zonefs_ztype ztype = ZONEFS_ZTYPE_CNV;
+ ino_t base_ino = bdev_nr_zones(sb->s_bdev) + 1;
- if (!zgroup)
- return -ENOMEM;
+ if (ctx->pos >= inode->i_size)
+ return 0;
- /* If the group is empty, there is nothing to do */
- if (!zgroup->g_nr_zones)
+ if (!dir_emit_dots(file, ctx))
return 0;
- file_name = kmalloc(ZONEFS_NAME_MAX, GFP_KERNEL);
- if (!file_name)
- return -ENOMEM;
+ if (ctx->pos == 2) {
+ if (!sbi->s_zgroup[ZONEFS_ZTYPE_CNV].g_nr_zones)
+ ztype = ZONEFS_ZTYPE_SEQ;
- dir = zonefs_create_inode(sb->s_root, zonefs_zgroup_name(ztype),
- NULL, ztype);
- if (IS_ERR(dir)) {
- ret = PTR_ERR(dir);
- goto free;
+ if (!dir_emit(ctx, zonefs_zgroup_name(ztype), 3,
+ base_ino + ztype, DT_DIR))
+ return 0;
+ ctx->pos++;
}
- for (i = 0; i < zgroup->g_nr_zones; i++) {
- /* Use the zone number within its group as the file name */
- snprintf(file_name, ZONEFS_NAME_MAX - 1, "%u", i);
- dent = zonefs_create_inode(dir, file_name,
- &zgroup->g_zones[i], ztype);
- if (IS_ERR(dent)) {
- ret = PTR_ERR(dent);
+ if (ctx->pos == 3 && ztype != ZONEFS_ZTYPE_SEQ) {
+ ztype = ZONEFS_ZTYPE_SEQ;
+ if (!dir_emit(ctx, zonefs_zgroup_name(ztype), 3,
+ base_ino + ztype, DT_DIR))
+ return 0;
+ ctx->pos++;
+ }
+
+ return 0;
+}
+
+static int zonefs_readdir_zgroup(struct file *file,
+ struct dir_context *ctx)
+{
+ struct inode *inode = file_inode(file);
+ struct zonefs_zone_group *zgroup = inode->i_private;
+ struct super_block *sb = inode->i_sb;
+ struct zonefs_sb_info *sbi = ZONEFS_SB(sb);
+ struct zonefs_zone *z;
+ int fname_len;
+ char *fname;
+ ino_t ino;
+ int f;
+
+ /*
+ * The size of zone group directories is equal to the number
+ * of zone files in the group and does note include the "." and
+ * ".." entries. Hence the "+ 2" here.
+ */
+ if (ctx->pos >= inode->i_size + 2)
+ return 0;
+
+ if (!dir_emit_dots(file, ctx))
+ return 0;
+
+ fname = kmalloc(ZONEFS_NAME_MAX, GFP_KERNEL);
+ if (!fname)
+ return -ENOMEM;
+
+ for (f = ctx->pos - 2; f < zgroup->g_nr_zones; f++) {
+ z = &zgroup->g_zones[f];
+ ino = z->z_sector >> sbi->s_zone_sectors_shift;
+ fname_len = snprintf(fname, ZONEFS_NAME_MAX - 1, "%u", f);
+ if (!dir_emit(ctx, fname, fname_len, ino, DT_REG))
break;
- }
+ ctx->pos++;
}
-free:
- kfree(file_name);
+ kfree(fname);
- return ret;
+ return 0;
}
+static int zonefs_readdir(struct file *file, struct dir_context *ctx)
+{
+ struct inode *inode = file_inode(file);
+
+ if (inode == d_inode(inode->i_sb->s_root))
+ return zonefs_readdir_root(file, ctx);
+
+ return zonefs_readdir_zgroup(file, ctx);
+}
+
+const struct inode_operations zonefs_dir_inode_operations = {
+ .lookup = zonefs_lookup,
+ .setattr = zonefs_inode_setattr,
+};
+
+const struct file_operations zonefs_dir_operations = {
+ .llseek = generic_file_llseek,
+ .read = generic_read_dir,
+ .iterate_shared = zonefs_readdir,
+};
+
+struct zonefs_zone_data {
+ struct super_block *sb;
+ unsigned int nr_zones[ZONEFS_ZTYPE_MAX];
+ sector_t cnv_zone_start;
+ struct blk_zone *zones;
+};
+
static int zonefs_get_zone_info_cb(struct blk_zone *zone, unsigned int idx,
void *data)
{
@@ -875,6 +1015,17 @@ static int zonefs_init_zgroup(struct super_block *sb,
zone->capacity << SECTOR_SHIFT);
z->z_wpoffset = zonefs_check_zone_condition(sb, z, zone);
+ z->z_mode = S_IFREG | sbi->s_perm;
+ z->z_uid = sbi->s_uid;
+ z->z_gid = sbi->s_gid;
+
+ /*
+ * Let zonefs_inode_update_mode() know that we will need
+ * special initialization of the inode mode the first time
+ * it is accessed.
+ */
+ z->z_flags |= ZONEFS_ZONE_INIT_MODE;
+
sb->s_maxbytes = max(z->z_capacity, sb->s_maxbytes);
sbi->s_blocks += z->z_capacity >> sb->s_blocksize_bits;
sbi->s_used_blocks += z->z_wpoffset >> sb->s_blocksize_bits;
@@ -1057,7 +1208,7 @@ static int zonefs_fill_super(struct super_block *sb, void *data, int silent)
{
struct zonefs_sb_info *sbi;
struct inode *inode;
- enum zonefs_ztype t;
+ enum zonefs_ztype ztype;
int ret;
if (!bdev_is_zoned(sb->s_bdev)) {
@@ -1122,7 +1273,7 @@ static int zonefs_fill_super(struct super_block *sb, void *data, int silent)
if (ret)
goto cleanup;
- /* Create root directory inode */
+ /* Create the root directory inode */
ret = -ENOMEM;
inode = new_inode(sb);
if (!inode)
@@ -1132,20 +1283,20 @@ static int zonefs_fill_super(struct super_block *sb, void *data, int silent)
inode->i_mode = S_IFDIR | 0555;
inode->i_ctime = inode->i_mtime = inode->i_atime = current_time(inode);
inode->i_op = &zonefs_dir_inode_operations;
- inode->i_fop = &simple_dir_operations;
+ inode->i_fop = &zonefs_dir_operations;
+ inode->i_size = 2;
set_nlink(inode, 2);
+ for (ztype = 0; ztype < ZONEFS_ZTYPE_MAX; ztype++) {
+ if (sbi->s_zgroup[ztype].g_nr_zones) {
+ inc_nlink(inode);
+ inode->i_size++;
+ }
+ }
sb->s_root = d_make_root(inode);
if (!sb->s_root)
goto cleanup;
- /* Create and populate files in zone groups directories */
- for (t = 0; t < ZONEFS_ZTYPE_MAX; t++) {
- ret = zonefs_create_zgroup_inodes(sb, t);
- if (ret)
- goto cleanup;
- }
-
ret = zonefs_sysfs_register(sb);
if (ret)
goto cleanup;
@@ -1168,12 +1319,10 @@ static void zonefs_kill_super(struct super_block *sb)
{
struct zonefs_sb_info *sbi = ZONEFS_SB(sb);
- if (sb->s_root)
- d_genocide(sb->s_root);
+ kill_block_super(sb);
zonefs_sysfs_unregister(sb);
zonefs_free_zgroups(sb);
- kill_block_super(sb);
kfree(sbi);
}
diff --git a/fs/zonefs/zonefs.h b/fs/zonefs/zonefs.h
index 2d626e18b141..f88466a4158b 100644
--- a/fs/zonefs/zonefs.h
+++ b/fs/zonefs/zonefs.h
@@ -64,6 +64,11 @@ struct zonefs_zone {
/* Write pointer offset in the zone (sequential zones only, bytes) */
loff_t z_wpoffset;
+
+ /* Saved inode uid, gid and access rights */
+ umode_t z_mode;
+ kuid_t z_uid;
+ kgid_t z_gid;
};
/*
@@ -265,6 +270,10 @@ static inline void zonefs_io_error(struct inode *inode, bool write)
mutex_unlock(&zi->i_truncate_mutex);
}
+/* In super.c */
+extern const struct inode_operations zonefs_dir_inode_operations;
+extern const struct file_operations zonefs_dir_operations;
+
/* In file.c */
extern const struct address_space_operations zonefs_file_aops;
extern const struct file_operations zonefs_file_operations;