summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2018-12-17 09:26:29 -0500
committerKent Overstreet <kent.overstreet@gmail.com>2018-12-17 09:26:29 -0500
commitf9ba96e16535a653b97f7f1f953fabf477737a96 (patch)
tree012aed6c10dbdcdb1ec2a223d7070f3eb6249139
parenta10a41fa2b1a917b0f3b34d20175867f968b2d12 (diff)
Update bcachefs sources to 57ea7773cb bcachefs: Fix duplicate ioctl nr
-rw-r--r--.bcachefs_revision2
-rw-r--r--libbcachefs/bcachefs_format.h74
-rw-r--r--libbcachefs/bcachefs_ioctl.h6
-rw-r--r--libbcachefs/fs-ioctl.c101
-rw-r--r--libbcachefs/fs.c182
-rw-r--r--libbcachefs/fs.h57
-rw-r--r--libbcachefs/inode.c32
-rw-r--r--libbcachefs/inode.h44
-rw-r--r--libbcachefs/opts.c16
-rw-r--r--libbcachefs/opts.h23
-rw-r--r--libbcachefs/quota.c7
-rw-r--r--libbcachefs/quota.h8
-rw-r--r--libbcachefs/quota_types.h6
-rw-r--r--libbcachefs/xattr.c203
14 files changed, 552 insertions, 209 deletions
diff --git a/.bcachefs_revision b/.bcachefs_revision
index 0779c530..d41e3b03 100644
--- a/.bcachefs_revision
+++ b/.bcachefs_revision
@@ -1 +1 @@
-f7670cba39ead5fcc99da93b46024bd6355c0663
+57ea7773cb4368dc98ec68385c3d0941c246ad70
diff --git a/libbcachefs/bcachefs_format.h b/libbcachefs/bcachefs_format.h
index efda901e..9245465d 100644
--- a/libbcachefs/bcachefs_format.h
+++ b/libbcachefs/bcachefs_format.h
@@ -689,38 +689,48 @@ struct bch_inode_generation {
__le32 pad;
} __attribute__((packed, aligned(8)));
-#define BCH_INODE_FIELDS() \
- BCH_INODE_FIELD(bi_atime, 64) \
- BCH_INODE_FIELD(bi_ctime, 64) \
- BCH_INODE_FIELD(bi_mtime, 64) \
- BCH_INODE_FIELD(bi_otime, 64) \
- BCH_INODE_FIELD(bi_size, 64) \
- BCH_INODE_FIELD(bi_sectors, 64) \
- BCH_INODE_FIELD(bi_uid, 32) \
- BCH_INODE_FIELD(bi_gid, 32) \
- BCH_INODE_FIELD(bi_nlink, 32) \
- BCH_INODE_FIELD(bi_generation, 32) \
- BCH_INODE_FIELD(bi_dev, 32) \
- BCH_INODE_FIELD(bi_data_checksum, 8) \
- BCH_INODE_FIELD(bi_compression, 8) \
- BCH_INODE_FIELD(bi_project, 32) \
- BCH_INODE_FIELD(bi_background_compression, 8) \
- BCH_INODE_FIELD(bi_data_replicas, 8) \
- BCH_INODE_FIELD(bi_promote_target, 16) \
- BCH_INODE_FIELD(bi_foreground_target, 16) \
- BCH_INODE_FIELD(bi_background_target, 16) \
- BCH_INODE_FIELD(bi_erasure_code, 16)
-
-#define BCH_INODE_FIELDS_INHERIT() \
- BCH_INODE_FIELD(bi_data_checksum) \
- BCH_INODE_FIELD(bi_compression) \
- BCH_INODE_FIELD(bi_project) \
- BCH_INODE_FIELD(bi_background_compression) \
- BCH_INODE_FIELD(bi_data_replicas) \
- BCH_INODE_FIELD(bi_promote_target) \
- BCH_INODE_FIELD(bi_foreground_target) \
- BCH_INODE_FIELD(bi_background_target) \
- BCH_INODE_FIELD(bi_erasure_code)
+#define BCH_INODE_FIELDS() \
+ x(bi_atime, 64) \
+ x(bi_ctime, 64) \
+ x(bi_mtime, 64) \
+ x(bi_otime, 64) \
+ x(bi_size, 64) \
+ x(bi_sectors, 64) \
+ x(bi_uid, 32) \
+ x(bi_gid, 32) \
+ x(bi_nlink, 32) \
+ x(bi_generation, 32) \
+ x(bi_dev, 32) \
+ x(bi_data_checksum, 8) \
+ x(bi_compression, 8) \
+ x(bi_project, 32) \
+ x(bi_background_compression, 8) \
+ x(bi_data_replicas, 8) \
+ x(bi_promote_target, 16) \
+ x(bi_foreground_target, 16) \
+ x(bi_background_target, 16) \
+ x(bi_erasure_code, 16) \
+ x(bi_fields_set, 16)
+
+/* subset of BCH_INODE_FIELDS */
+#define BCH_INODE_OPTS() \
+ x(data_checksum, 8) \
+ x(compression, 8) \
+ x(project, 32) \
+ x(background_compression, 8) \
+ x(data_replicas, 8) \
+ x(promote_target, 16) \
+ x(foreground_target, 16) \
+ x(background_target, 16) \
+ x(erasure_code, 16)
+
+enum inode_opt_id {
+#define x(name, ...) \
+ Inode_opt_##name,
+ BCH_INODE_OPTS()
+#undef x
+ Inode_opt_nr,
+};
enum {
/*
diff --git a/libbcachefs/bcachefs_ioctl.h b/libbcachefs/bcachefs_ioctl.h
index 73e5d887..fb595dff 100644
--- a/libbcachefs/bcachefs_ioctl.h
+++ b/libbcachefs/bcachefs_ioctl.h
@@ -70,7 +70,11 @@ struct bch_ioctl_incremental {
#define BCH_IOCTL_USAGE _IOWR(0xbc, 11, struct bch_ioctl_usage)
#define BCH_IOCTL_READ_SUPER _IOW(0xbc, 12, struct bch_ioctl_read_super)
#define BCH_IOCTL_DISK_GET_IDX _IOW(0xbc, 13, struct bch_ioctl_disk_get_idx)
-#define BCH_IOCTL_DISK_RESIZE _IOW(0xbc, 13, struct bch_ioctl_disk_resize)
+#define BCH_IOCTL_DISK_RESIZE _IOW(0xbc, 14, struct bch_ioctl_disk_resize)
+
+/* ioctl below act on a particular file, not the filesystem as a whole: */
+
+#define BCHFS_IOC_REINHERIT_ATTRS _IOR(0xbc, 64, const char __user *)
/*
* BCH_IOCTL_QUERY_UUID: get filesystem UUID
diff --git a/libbcachefs/fs-ioctl.c b/libbcachefs/fs-ioctl.c
index 0eb0a011..7b9acafb 100644
--- a/libbcachefs/fs-ioctl.c
+++ b/libbcachefs/fs-ioctl.c
@@ -2,6 +2,7 @@
#include "bcachefs.h"
#include "chardev.h"
+#include "dirent.h"
#include "fs.h"
#include "fs-ioctl.h"
#include "quota.h"
@@ -107,21 +108,12 @@ static int bch2_set_projid(struct bch_fs *c,
u32 projid)
{
struct bch_qid qid = inode->ei_qid;
- int ret;
-
- if (projid == inode->ei_qid.q[QTYP_PRJ])
- return 0;
qid.q[QTYP_PRJ] = projid;
- return bch2_quota_transfer(c, 1 << QTYP_PRJ, qid, inode->ei_qid,
- inode->v.i_blocks +
- inode->ei_quota_reserved);
- if (ret)
- return ret;
-
- inode->ei_qid.q[QTYP_PRJ] = projid;
- return 0;
+ return bch2_fs_quota_transfer(c, inode, qid,
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_PREALLOC);
}
static int fssetxattr_inode_update_fn(struct bch_inode_info *inode,
@@ -130,7 +122,14 @@ static int fssetxattr_inode_update_fn(struct bch_inode_info *inode,
{
struct flags_set *s = p;
- bi->bi_project = s->projid;
+ if (s->projid != bi->bi_project) {
+ if (s->projid)
+ bi->bi_fields_set |= 1U << Inode_opt_project;
+ else
+ bi->bi_fields_set &= ~(1U << Inode_opt_project);
+
+ bi->bi_project = s->projid;
+ }
return bch2_inode_flags_set(inode, bi, p);
}
@@ -178,6 +177,75 @@ err:
return ret;
}
+static int bch2_ioc_reinherit_attrs(struct bch_fs *c,
+ struct file *file,
+ struct bch_inode_info *src,
+ const char __user *name)
+{
+ struct bch_inode_info *dst;
+ struct inode *vinode = NULL;
+ char *kname = NULL;
+ struct qstr qstr;
+ int ret = 0;
+ u64 inum;
+
+ kname = kmalloc(BCH_NAME_MAX + 1, GFP_KERNEL);
+ if (!kname)
+ return -ENOMEM;
+
+ ret = strncpy_from_user(kname, name, BCH_NAME_MAX);
+ if (unlikely(ret < 0))
+ goto err1;
+
+ qstr.hash_len = ret;
+ qstr.name = kname;
+
+ ret = -ENOENT;
+ inum = bch2_dirent_lookup(c, src->v.i_ino,
+ &src->ei_str_hash,
+ &qstr);
+ if (!inum)
+ goto err1;
+
+ vinode = bch2_vfs_inode_get(c, inum);
+ ret = PTR_ERR_OR_ZERO(vinode);
+ if (ret)
+ goto err1;
+
+ dst = to_bch_ei(vinode);
+
+ ret = mnt_want_write_file(file);
+ if (ret)
+ goto err2;
+
+ bch2_lock_inodes(src, dst);
+
+ if (inode_attr_changing(src, dst, Inode_opt_project)) {
+ ret = bch2_fs_quota_transfer(c, dst,
+ src->ei_qid,
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_PREALLOC);
+ if (ret)
+ goto err3;
+ }
+
+ ret = bch2_write_inode(c, dst, bch2_reinherit_attrs_fn, src, 0);
+err3:
+ bch2_unlock_inodes(src, dst);
+
+ /* return true if we did work */
+ if (ret >= 0)
+ ret = !ret;
+
+ mnt_drop_write_file(file);
+err2:
+ iput(vinode);
+err1:
+ kfree(kname);
+
+ return ret;
+}
+
long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
struct bch_inode_info *inode = file_bch_inode(file);
@@ -194,7 +262,12 @@ long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
case FS_IOC_FSGETXATTR:
return bch2_ioc_fsgetxattr(inode, (void __user *) arg);
case FS_IOC_FSSETXATTR:
- return bch2_ioc_fssetxattr(c, file, inode, (void __user *) arg);
+ return bch2_ioc_fssetxattr(c, file, inode,
+ (void __user *) arg);
+
+ case BCHFS_IOC_REINHERIT_ATTRS:
+ return bch2_ioc_reinherit_attrs(c, file, inode,
+ (void __user *) arg);
case FS_IOC_GETVERSION:
return -ENOTTY;
diff --git a/libbcachefs/fs.c b/libbcachefs/fs.c
index 13670a63..300348b6 100644
--- a/libbcachefs/fs.c
+++ b/libbcachefs/fs.c
@@ -47,30 +47,6 @@ static void journal_seq_copy(struct bch_inode_info *dst,
} while ((v = cmpxchg(&dst->ei_journal_seq, old, journal_seq)) != old);
}
-static inline int ptrcmp(void *l, void *r)
-{
- return (l > r) - (l < r);
-}
-
-#define __bch2_lock_inodes(_lock, ...) \
-do { \
- struct bch_inode_info *a[] = { NULL, __VA_ARGS__ }; \
- unsigned i; \
- \
- bubble_sort(&a[1], ARRAY_SIZE(a) - 1 , ptrcmp); \
- \
- for (i = ARRAY_SIZE(a) - 1; a[i]; --i) \
- if (a[i] != a[i - 1]) { \
- if (_lock) \
- mutex_lock_nested(&a[i]->ei_update_lock, i);\
- else \
- mutex_unlock(&a[i]->ei_update_lock); \
- } \
-} while (0)
-
-#define bch2_lock_inodes(...) __bch2_lock_inodes(true, __VA_ARGS__)
-#define bch2_unlock_inodes(...) __bch2_lock_inodes(false, __VA_ARGS__)
-
/*
* I_SIZE_DIRTY requires special handling:
*
@@ -119,7 +95,6 @@ void bch2_inode_update_after_write(struct bch_fs *c,
inode->v.i_ctime = bch2_time_to_timespec(c, bi->bi_ctime);
inode->ei_inode = *bi;
- inode->ei_qid = bch_qid(bi);
bch2_inode_flags_to_vfs(inode);
}
@@ -197,7 +172,68 @@ retry:
return ret < 0 ? ret : 0;
}
-static struct inode *bch2_vfs_inode_get(struct bch_fs *c, u64 inum)
+int bch2_fs_quota_transfer(struct bch_fs *c,
+ struct bch_inode_info *inode,
+ struct bch_qid new_qid,
+ unsigned qtypes,
+ enum quota_acct_mode mode)
+{
+ unsigned i;
+ int ret;
+
+ qtypes &= enabled_qtypes(c);
+
+ for (i = 0; i < QTYP_NR; i++)
+ if (new_qid.q[i] == inode->ei_qid.q[i])
+ qtypes &= ~(1U << i);
+
+ if (!qtypes)
+ return 0;
+
+ mutex_lock(&inode->ei_quota_lock);
+
+ ret = bch2_quota_transfer(c, qtypes, new_qid,
+ inode->ei_qid,
+ inode->v.i_blocks +
+ inode->ei_quota_reserved,
+ mode);
+ if (!ret)
+ for (i = 0; i < QTYP_NR; i++)
+ if (qtypes & (1 << i))
+ inode->ei_qid.q[i] = new_qid.q[i];
+
+ mutex_unlock(&inode->ei_quota_lock);
+
+ return ret;
+}
+
+int bch2_reinherit_attrs_fn(struct bch_inode_info *inode,
+ struct bch_inode_unpacked *bi,
+ void *p)
+{
+ struct bch_inode_info *dir = p;
+ u64 src, dst;
+ unsigned id;
+ int ret = 1;
+
+ for (id = 0; id < Inode_opt_nr; id++) {
+ if (bi->bi_fields_set & (1 << id))
+ continue;
+
+ src = bch2_inode_opt_get(&dir->ei_inode, id);
+ dst = bch2_inode_opt_get(bi, id);
+
+ if (src == dst)
+ continue;
+
+ bch2_inode_opt_set(bi, id, src);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+struct inode *bch2_vfs_inode_get(struct bch_fs *c, u64 inum)
{
struct bch_inode_unpacked inode_u;
struct bch_inode_info *inode;
@@ -274,14 +310,13 @@ __bch2_create(struct bch_inode_info *dir, struct dentry *dentry,
bch2_inode_init(c, &inode_u, 0, 0, 0, rdev, &dir->ei_inode);
bch2_inode_init_owner(&inode_u, &dir->v, mode);
- inode_u.bi_project = dir->ei_qid.q[QTYP_PRJ];
-
hash_info = bch2_hash_info_init(c, &inode_u);
if (tmpfile)
inode_u.bi_flags |= BCH_INODE_UNLINKED;
- ret = bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, 1, KEY_TYPE_QUOTA_PREALLOC);
+ ret = bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, 1,
+ KEY_TYPE_QUOTA_PREALLOC);
if (ret)
return ERR_PTR(ret);
@@ -664,6 +699,7 @@ static int inode_update_for_rename_fn(struct bch_inode_info *inode,
void *p)
{
struct rename_info *info = p;
+ int ret;
if (inode == info->src_dir) {
bi->bi_nlink -= S_ISDIR(info->src_inode->v.i_mode);
@@ -678,6 +714,19 @@ static int inode_update_for_rename_fn(struct bch_inode_info *inode,
S_ISDIR(info->dst_inode->v.i_mode);
}
+ if (inode == info->src_inode) {
+ ret = bch2_reinherit_attrs_fn(inode, bi, info->dst_dir);
+
+ BUG_ON(!ret && S_ISDIR(info->src_inode->v.i_mode));
+ }
+
+ if (inode == info->dst_inode &&
+ info->mode == BCH_RENAME_EXCHANGE) {
+ ret = bch2_reinherit_attrs_fn(inode, bi, info->src_dir);
+
+ BUG_ON(!ret && S_ISDIR(info->dst_inode->v.i_mode));
+ }
+
if (inode == info->dst_inode &&
info->mode == BCH_RENAME_OVERWRITE) {
BUG_ON(bi->bi_nlink &&
@@ -742,6 +791,39 @@ static int bch2_rename2(struct inode *src_vdir, struct dentry *src_dentry,
i.dst_inode);
bch2_trans_init(&trans, c);
+
+ if (S_ISDIR(i.src_inode->v.i_mode) &&
+ inode_attrs_changing(i.dst_dir, i.src_inode)) {
+ ret = -EXDEV;
+ goto err;
+ }
+
+ if (i.mode == BCH_RENAME_EXCHANGE &&
+ S_ISDIR(i.dst_inode->v.i_mode) &&
+ inode_attrs_changing(i.src_dir, i.dst_inode)) {
+ ret = -EXDEV;
+ goto err;
+ }
+
+ if (inode_attr_changing(i.dst_dir, i.src_inode, Inode_opt_project)) {
+ ret = bch2_fs_quota_transfer(c, i.src_inode,
+ i.dst_dir->ei_qid,
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_PREALLOC);
+ if (ret)
+ goto err;
+ }
+
+ if (i.mode == BCH_RENAME_EXCHANGE &&
+ inode_attr_changing(i.src_dir, i.dst_inode, Inode_opt_project)) {
+ ret = bch2_fs_quota_transfer(c, i.dst_inode,
+ i.src_dir->ei_qid,
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_PREALLOC);
+ if (ret)
+ goto err;
+ }
+
retry:
bch2_trans_begin(&trans);
i.now = bch2_current_time(c);
@@ -792,6 +874,17 @@ retry:
ATTR_CTIME);
err:
bch2_trans_exit(&trans);
+
+ bch2_fs_quota_transfer(c, i.src_inode,
+ bch_qid(&i.src_inode->ei_inode),
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_NOCHECK);
+ if (i.dst_inode)
+ bch2_fs_quota_transfer(c, i.dst_inode,
+ bch_qid(&i.dst_inode->ei_inode),
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_NOCHECK);
+
bch2_unlock_inodes(i.src_dir,
i.dst_dir,
i.src_inode,
@@ -838,36 +931,26 @@ static int inode_update_for_setattr_fn(struct bch_inode_info *inode,
static int bch2_setattr_nonsize(struct bch_inode_info *inode, struct iattr *iattr)
{
struct bch_fs *c = inode->v.i_sb->s_fs_info;
- struct bch_qid qid = inode->ei_qid;
+ struct bch_qid qid;
struct btree_trans trans;
struct bch_inode_unpacked inode_u;
struct posix_acl *acl = NULL;
- unsigned qtypes = 0;
int ret;
mutex_lock(&inode->ei_update_lock);
- if (c->opts.usrquota &&
- (iattr->ia_valid & ATTR_UID) &&
- !uid_eq(iattr->ia_uid, inode->v.i_uid)) {
- qid.q[QTYP_USR] = from_kuid(&init_user_ns, iattr->ia_uid),
- qtypes |= 1 << QTYP_USR;
- }
+ qid = inode->ei_qid;
- if (c->opts.grpquota &&
- (iattr->ia_valid & ATTR_GID) &&
- !gid_eq(iattr->ia_gid, inode->v.i_gid)) {
+ if (iattr->ia_valid & ATTR_UID)
+ qid.q[QTYP_USR] = from_kuid(&init_user_ns, iattr->ia_uid);
+
+ if (iattr->ia_valid & ATTR_GID)
qid.q[QTYP_GRP] = from_kgid(&init_user_ns, iattr->ia_gid);
- qtypes |= 1 << QTYP_GRP;
- }
- if (qtypes) {
- ret = bch2_quota_transfer(c, qtypes, qid, inode->ei_qid,
- inode->v.i_blocks +
- inode->ei_quota_reserved);
- if (ret)
- goto err;
- }
+ ret = bch2_fs_quota_transfer(c, inode, qid, ~0,
+ KEY_TYPE_QUOTA_PREALLOC);
+ if (ret)
+ goto err;
bch2_trans_init(&trans, c);
retry:
@@ -1228,6 +1311,7 @@ static void bch2_vfs_inode_init(struct bch_fs *c,
inode->ei_journal_seq = 0;
inode->ei_quota_reserved = 0;
inode->ei_str_hash = bch2_hash_info_init(c, bi);
+ inode->ei_qid = bch_qid(bi);
inode->v.i_mapping->a_ops = &bch_address_space_operations;
diff --git a/libbcachefs/fs.h b/libbcachefs/fs.h
index a434c757..7dc0453a 100644
--- a/libbcachefs/fs.h
+++ b/libbcachefs/fs.h
@@ -29,6 +29,30 @@ struct bch_inode_info {
#define to_bch_ei(_inode) \
container_of_or_null(_inode, struct bch_inode_info, v)
+static inline int ptrcmp(void *l, void *r)
+{
+ return (l > r) - (l < r);
+}
+
+#define __bch2_lock_inodes(_lock, ...) \
+do { \
+ struct bch_inode_info *a[] = { NULL, __VA_ARGS__ }; \
+ unsigned i; \
+ \
+ bubble_sort(&a[1], ARRAY_SIZE(a) - 1 , ptrcmp); \
+ \
+ for (i = ARRAY_SIZE(a) - 1; a[i]; --i) \
+ if (a[i] != a[i - 1]) { \
+ if (_lock) \
+ mutex_lock_nested(&a[i]->ei_update_lock, i);\
+ else \
+ mutex_unlock(&a[i]->ei_update_lock); \
+ } \
+} while (0)
+
+#define bch2_lock_inodes(...) __bch2_lock_inodes(true, __VA_ARGS__)
+#define bch2_unlock_inodes(...) __bch2_lock_inodes(false, __VA_ARGS__)
+
static inline struct bch_inode_info *file_bch_inode(struct file *file)
{
return to_bch_ei(file_inode(file));
@@ -49,10 +73,39 @@ static inline u64 bch2_current_time(struct bch_fs *c)
return timespec_to_bch2_time(c, current_kernel_time64());
}
+static inline bool inode_attr_changing(struct bch_inode_info *dir,
+ struct bch_inode_info *inode,
+ enum inode_opt_id id)
+{
+ return !(inode->ei_inode.bi_fields_set & (1 << id)) &&
+ bch2_inode_opt_get(&dir->ei_inode, id) !=
+ bch2_inode_opt_get(&inode->ei_inode, id);
+}
+
+static inline bool inode_attrs_changing(struct bch_inode_info *dir,
+ struct bch_inode_info *inode)
+{
+ unsigned id;
+
+ for (id = 0; id < Inode_opt_nr; id++)
+ if (inode_attr_changing(dir, inode, id))
+ return true;
+
+ return false;
+}
+
struct bch_inode_unpacked;
#ifndef NO_BCACHEFS_FS
+int bch2_fs_quota_transfer(struct bch_fs *,
+ struct bch_inode_info *,
+ struct bch_qid,
+ unsigned,
+ enum quota_acct_mode);
+
+struct inode *bch2_vfs_inode_get(struct bch_fs *, u64);
+
/* returns 0 if we want to do the update, or error is passed up */
typedef int (*inode_set_fn)(struct bch_inode_info *,
struct bch_inode_unpacked *, void *);
@@ -68,6 +121,10 @@ int __must_check bch2_write_inode_trans(struct btree_trans *,
int __must_check bch2_write_inode(struct bch_fs *, struct bch_inode_info *,
inode_set_fn, void *, unsigned);
+int bch2_reinherit_attrs_fn(struct bch_inode_info *,
+ struct bch_inode_unpacked *,
+ void *);
+
void bch2_vfs_exit(void);
int bch2_vfs_init(void);
diff --git a/libbcachefs/inode.c b/libbcachefs/inode.c
index 8c3d4431..f851e3b7 100644
--- a/libbcachefs/inode.c
+++ b/libbcachefs/inode.c
@@ -12,7 +12,12 @@
#include <asm/unaligned.h>
-#define FIELD_BYTES() \
+const char * const bch2_inode_opts[] = {
+#define x(name, ...) #name,
+ BCH_INODE_OPTS()
+#undef x
+ NULL,
+};
static const u8 byte_table[8] = { 1, 2, 3, 4, 6, 8, 10, 13 };
static const u8 bits_table[8] = {
@@ -97,7 +102,7 @@ void bch2_inode_pack(struct bkey_inode_buf *packed,
packed->inode.v.bi_flags = cpu_to_le32(inode->bi_flags);
packed->inode.v.bi_mode = cpu_to_le16(inode->bi_mode);
-#define BCH_INODE_FIELD(_name, _bits) \
+#define x(_name, _bits) \
out += inode_encode_field(out, end, 0, inode->_name); \
nr_fields++; \
\
@@ -107,7 +112,7 @@ void bch2_inode_pack(struct bkey_inode_buf *packed,
}
BCH_INODE_FIELDS()
-#undef BCH_INODE_FIELD
+#undef x
out = last_nonzero_field;
nr_fields = last_nonzero_fieldnr;
@@ -129,9 +134,9 @@ void bch2_inode_pack(struct bkey_inode_buf *packed,
BUG_ON(unpacked.bi_hash_seed != inode->bi_hash_seed);
BUG_ON(unpacked.bi_mode != inode->bi_mode);
-#define BCH_INODE_FIELD(_name, _bits) BUG_ON(unpacked._name != inode->_name);
+#define x(_name, _bits) BUG_ON(unpacked._name != inode->_name);
BCH_INODE_FIELDS()
-#undef BCH_INODE_FIELD
+#undef x
}
}
@@ -149,7 +154,7 @@ int bch2_inode_unpack(struct bkey_s_c_inode inode,
unpacked->bi_flags = le32_to_cpu(inode.v->bi_flags);
unpacked->bi_mode = le16_to_cpu(inode.v->bi_mode);
-#define BCH_INODE_FIELD(_name, _bits) \
+#define x(_name, _bits) \
if (fieldnr++ == INODE_NR_FIELDS(inode.v)) { \
memset(&unpacked->_name, 0, \
sizeof(*unpacked) - \
@@ -168,7 +173,7 @@ int bch2_inode_unpack(struct bkey_s_c_inode inode,
in += ret;
BCH_INODE_FIELDS()
-#undef BCH_INODE_FIELD
+#undef x
/* XXX: signal if there were more fields than expected? */
@@ -219,10 +224,10 @@ void bch2_inode_to_text(struct printbuf *out, struct bch_fs *c,
return;
}
-#define BCH_INODE_FIELD(_name, _bits) \
+#define x(_name, _bits) \
pr_buf(out, #_name ": %llu ", (u64) unpacked._name);
BCH_INODE_FIELDS()
-#undef BCH_INODE_FIELD
+#undef x
}
const char *bch2_inode_generation_invalid(const struct bch_fs *c,
@@ -254,7 +259,8 @@ void bch2_inode_init(struct bch_fs *c, struct bch_inode_unpacked *inode_u,
/* ick */
inode_u->bi_flags |= c->opts.str_hash << INODE_STR_HASH_OFFSET;
- get_random_bytes(&inode_u->bi_hash_seed, sizeof(inode_u->bi_hash_seed));
+ get_random_bytes(&inode_u->bi_hash_seed,
+ sizeof(inode_u->bi_hash_seed));
inode_u->bi_mode = mode;
inode_u->bi_uid = uid;
@@ -266,9 +272,9 @@ void bch2_inode_init(struct bch_fs *c, struct bch_inode_unpacked *inode_u,
inode_u->bi_otime = now;
if (parent) {
-#define BCH_INODE_FIELD(_name) inode_u->_name = parent->_name;
- BCH_INODE_FIELDS_INHERIT()
-#undef BCH_INODE_FIELD
+#define x(_name, ...) inode_u->bi_##_name = parent->bi_##_name;
+ BCH_INODE_OPTS()
+#undef x
}
}
diff --git a/libbcachefs/inode.h b/libbcachefs/inode.h
index 44855e1a..0d609985 100644
--- a/libbcachefs/inode.h
+++ b/libbcachefs/inode.h
@@ -5,6 +5,8 @@
#include <linux/math64.h>
+extern const char * const bch2_inode_opts[];
+
const char *bch2_inode_invalid(const struct bch_fs *, struct bkey_s_c);
void bch2_inode_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
@@ -29,17 +31,17 @@ struct bch_inode_unpacked {
u32 bi_flags;
u16 bi_mode;
-#define BCH_INODE_FIELD(_name, _bits) u##_bits _name;
+#define x(_name, _bits) u##_bits _name;
BCH_INODE_FIELDS()
-#undef BCH_INODE_FIELD
+#undef x
};
struct bkey_inode_buf {
struct bkey_i_inode inode;
-#define BCH_INODE_FIELD(_name, _bits) + 8 + _bits / 8
+#define x(_name, _bits) + 8 + _bits / 8
u8 _pad[0 + BCH_INODE_FIELDS()];
-#undef BCH_INODE_FIELD
+#undef x
} __attribute__((packed, aligned(8)));
void bch2_inode_pack(struct bkey_inode_buf *, const struct bch_inode_unpacked *);
@@ -79,39 +81,41 @@ static inline struct bch_io_opts bch2_inode_opts_get(struct bch_inode_unpacked *
{
struct bch_io_opts ret = { 0 };
-#define BCH_INODE_OPT(_name, _bits) \
+#define x(_name, _bits) \
if (inode->bi_##_name) \
opt_set(ret, _name, inode->bi_##_name - 1);
BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
return ret;
}
-static inline void __bch2_inode_opt_set(struct bch_inode_unpacked *inode,
- enum bch_opt_id id, u64 v)
+static inline void bch2_inode_opt_set(struct bch_inode_unpacked *inode,
+ enum inode_opt_id id, u64 v)
{
switch (id) {
-#define BCH_INODE_OPT(_name, ...) \
- case Opt_##_name: \
+#define x(_name, ...) \
+ case Inode_opt_##_name: \
inode->bi_##_name = v; \
break;
BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
default:
BUG();
}
}
-static inline void bch2_inode_opt_set(struct bch_inode_unpacked *inode,
- enum bch_opt_id id, u64 v)
-{
- return __bch2_inode_opt_set(inode, id, v + 1);
-}
-
-static inline void bch2_inode_opt_clear(struct bch_inode_unpacked *inode,
- enum bch_opt_id id)
+static inline u64 bch2_inode_opt_get(struct bch_inode_unpacked *inode,
+ enum inode_opt_id id)
{
- return __bch2_inode_opt_set(inode, id, 0);
+ switch (id) {
+#define x(_name, ...) \
+ case Inode_opt_##_name: \
+ return inode->bi_##_name;
+ BCH_INODE_OPTS()
+#undef x
+ default:
+ BUG();
+ }
}
#ifdef CONFIG_BCACHEFS_DEBUG
diff --git a/libbcachefs/opts.c b/libbcachefs/opts.c
index 449cd5bf..4739f782 100644
--- a/libbcachefs/opts.c
+++ b/libbcachefs/opts.c
@@ -376,40 +376,40 @@ no_val:
struct bch_io_opts bch2_opts_to_inode_opts(struct bch_opts src)
{
struct bch_io_opts ret = { 0 };
-#define BCH_INODE_OPT(_name, _bits) \
+#define x(_name, _bits) \
if (opt_defined(src, _name)) \
opt_set(ret, _name, src._name);
BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
return ret;
}
struct bch_opts bch2_inode_opts_to_opts(struct bch_io_opts src)
{
struct bch_opts ret = { 0 };
-#define BCH_INODE_OPT(_name, _bits) \
+#define x(_name, _bits) \
if (opt_defined(src, _name)) \
opt_set(ret, _name, src._name);
BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
return ret;
}
void bch2_io_opts_apply(struct bch_io_opts *dst, struct bch_io_opts src)
{
-#define BCH_INODE_OPT(_name, _bits) \
+#define x(_name, _bits) \
if (opt_defined(src, _name)) \
opt_set(*dst, _name, src._name);
BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
}
bool bch2_opt_is_inode_opt(enum bch_opt_id id)
{
static const enum bch_opt_id inode_opt_list[] = {
-#define BCH_INODE_OPT(_name, _bits) Opt_##_name,
+#define x(_name, _bits) Opt_##_name,
BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
};
unsigned i;
diff --git a/libbcachefs/opts.h b/libbcachefs/opts.h
index 5c744018..8a4e2e50 100644
--- a/libbcachefs/opts.h
+++ b/libbcachefs/opts.h
@@ -184,7 +184,10 @@ enum opt_type {
NO_SB_OPT, false) \
BCH_OPT(version_upgrade, u8, OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false)
+ NO_SB_OPT, false) \
+ BCH_OPT(project, u8, OPT_INTERNAL, \
+ OPT_BOOL(), \
+ NO_SB_OPT, false) \
struct bch_opts {
#define BCH_OPT(_name, _bits, ...) unsigned _name##_defined:1;
@@ -277,24 +280,14 @@ int bch2_parse_mount_opts(struct bch_opts *, char *);
/* inode opts: */
-#define BCH_INODE_OPTS() \
- BCH_INODE_OPT(data_checksum, 8) \
- BCH_INODE_OPT(compression, 8) \
- BCH_INODE_OPT(background_compression, 8) \
- BCH_INODE_OPT(data_replicas, 8) \
- BCH_INODE_OPT(promote_target, 16) \
- BCH_INODE_OPT(foreground_target, 16) \
- BCH_INODE_OPT(background_target, 16) \
- BCH_INODE_OPT(erasure_code, 16)
-
struct bch_io_opts {
-#define BCH_INODE_OPT(_name, _bits) unsigned _name##_defined:1;
+#define x(_name, _bits) unsigned _name##_defined:1;
BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
-#define BCH_INODE_OPT(_name, _bits) u##_bits _name;
+#define x(_name, _bits) u##_bits _name;
BCH_INODE_OPTS()
-#undef BCH_INODE_OPT
+#undef x
};
struct bch_io_opts bch2_opts_to_inode_opts(struct bch_opts);
diff --git a/libbcachefs/quota.c b/libbcachefs/quota.c
index 95ff0cae..44aacd40 100644
--- a/libbcachefs/quota.c
+++ b/libbcachefs/quota.c
@@ -269,7 +269,8 @@ static void __bch2_quota_transfer(struct bch_memquota *src_q,
int bch2_quota_transfer(struct bch_fs *c, unsigned qtypes,
struct bch_qid dst,
- struct bch_qid src, u64 space)
+ struct bch_qid src, u64 space,
+ enum quota_acct_mode mode)
{
struct bch_memquota_type *q;
struct bch_memquota *src_q[3], *dst_q[3];
@@ -295,13 +296,13 @@ int bch2_quota_transfer(struct bch_fs *c, unsigned qtypes,
ret = bch2_quota_check_limit(c, i, dst_q[i], &msgs, Q_SPC,
dst_q[i]->c[Q_SPC].v + space,
- KEY_TYPE_QUOTA_PREALLOC);
+ mode);
if (ret)
goto err;
ret = bch2_quota_check_limit(c, i, dst_q[i], &msgs, Q_INO,
dst_q[i]->c[Q_INO].v + 1,
- KEY_TYPE_QUOTA_PREALLOC);
+ mode);
if (ret)
goto err;
}
diff --git a/libbcachefs/quota.h b/libbcachefs/quota.h
index 0c3eb697..eabb94c1 100644
--- a/libbcachefs/quota.h
+++ b/libbcachefs/quota.h
@@ -14,12 +14,6 @@ void bch2_quota_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
.val_to_text = bch2_quota_to_text, \
}
-enum quota_acct_mode {
- KEY_TYPE_QUOTA_PREALLOC,
- KEY_TYPE_QUOTA_WARN,
- KEY_TYPE_QUOTA_NOCHECK,
-};
-
static inline struct bch_qid bch_qid(struct bch_inode_unpacked *u)
{
return (struct bch_qid) {
@@ -42,7 +36,7 @@ int bch2_quota_acct(struct bch_fs *, struct bch_qid, enum quota_counters,
s64, enum quota_acct_mode);
int bch2_quota_transfer(struct bch_fs *, unsigned, struct bch_qid,
- struct bch_qid, u64);
+ struct bch_qid, u64, enum quota_acct_mode);
void bch2_fs_quota_exit(struct bch_fs *);
void bch2_fs_quota_init(struct bch_fs *);
diff --git a/libbcachefs/quota_types.h b/libbcachefs/quota_types.h
index bcaed4ea..e978dc54 100644
--- a/libbcachefs/quota_types.h
+++ b/libbcachefs/quota_types.h
@@ -7,6 +7,12 @@ struct bch_qid {
u32 q[QTYP_NR];
};
+enum quota_acct_mode {
+ KEY_TYPE_QUOTA_PREALLOC,
+ KEY_TYPE_QUOTA_WARN,
+ KEY_TYPE_QUOTA_NOCHECK,
+};
+
struct memquota_counter {
u64 v;
u64 hardlimit;
diff --git a/libbcachefs/xattr.c b/libbcachefs/xattr.c
index 85d8bdd3..407177b2 100644
--- a/libbcachefs/xattr.c
+++ b/libbcachefs/xattr.c
@@ -197,40 +197,83 @@ int bch2_xattr_set(struct btree_trans *trans, u64 inum,
return ret;
}
-static size_t bch2_xattr_emit(struct dentry *dentry,
- const struct bch_xattr *xattr,
- char *buffer, size_t buffer_size)
+static void __bch2_xattr_emit(const char *prefix,
+ const char *name, size_t name_len,
+ char **buffer, size_t *buffer_size,
+ ssize_t *ret)
+{
+ const size_t prefix_len = strlen(prefix);
+ const size_t total_len = prefix_len + name_len + 1;
+
+ if (*buffer) {
+ if (total_len > *buffer_size) {
+ *ret = -ERANGE;
+ return;
+ }
+
+ memcpy(*buffer, prefix, prefix_len);
+ memcpy(*buffer + prefix_len,
+ name, name_len);
+ (*buffer)[prefix_len + name_len] = '\0';
+
+ *buffer += total_len;
+ *buffer_size -= total_len;
+ }
+
+ *ret += total_len;
+}
+
+static void bch2_xattr_emit(struct dentry *dentry,
+ const struct bch_xattr *xattr,
+ char **buffer, size_t *buffer_size,
+ ssize_t *ret)
{
const struct xattr_handler *handler =
bch2_xattr_type_to_handler(xattr->x_type);
- if (handler && (!handler->list || handler->list(dentry))) {
- const char *prefix = handler->prefix ?: handler->name;
- const size_t prefix_len = strlen(prefix);
- const size_t total_len = prefix_len + xattr->x_name_len + 1;
+ if (handler && (!handler->list || handler->list(dentry)))
+ __bch2_xattr_emit(handler->prefix ?: handler->name,
+ xattr->x_name, xattr->x_name_len,
+ buffer, buffer_size, ret);
+}
- if (buffer && total_len <= buffer_size) {
- memcpy(buffer, prefix, prefix_len);
- memcpy(buffer + prefix_len,
- xattr->x_name, xattr->x_name_len);
- buffer[prefix_len + xattr->x_name_len] = '\0';
- }
+static void bch2_xattr_list_bcachefs(struct bch_fs *c,
+ struct bch_inode_info *inode,
+ char **buffer,
+ size_t *buffer_size,
+ ssize_t *ret,
+ bool all)
+{
+ const char *prefix = all ? "bcachefs_effective." : "bcachefs.";
+ unsigned id;
+ u64 v;
- return total_len;
- } else {
- return 0;
+ for (id = 0; id < Inode_opt_nr; id++) {
+ v = bch2_inode_opt_get(&inode->ei_inode, id);
+ if (!v)
+ continue;
+
+ if (!all &&
+ !(inode->ei_inode.bi_fields_set & (1 << id)))
+ continue;
+
+ __bch2_xattr_emit(prefix,
+ bch2_inode_opts[id],
+ strlen(bch2_inode_opts[id]),
+ buffer, buffer_size, ret);
+ if (*ret < 0)
+ break;
}
}
ssize_t bch2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size)
{
struct bch_fs *c = dentry->d_sb->s_fs_info;
+ struct bch_inode_info *inode = to_bch_ei(dentry->d_inode);
struct btree_iter iter;
struct bkey_s_c k;
- const struct bch_xattr *xattr;
u64 inum = dentry->d_inode->i_ino;
ssize_t ret = 0;
- size_t len;
for_each_btree_key(&iter, c, BTREE_ID_XATTRS, POS(inum, 0), 0, k) {
BUG_ON(k.k->p.inode < inum);
@@ -241,23 +284,25 @@ ssize_t bch2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size)
if (k.k->type != KEY_TYPE_xattr)
continue;
- xattr = bkey_s_c_to_xattr(k).v;
-
- len = bch2_xattr_emit(dentry, xattr, buffer, buffer_size);
- if (buffer) {
- if (len > buffer_size) {
- bch2_btree_iter_unlock(&iter);
- return -ERANGE;
- }
+ bch2_xattr_emit(dentry, bkey_s_c_to_xattr(k).v,
+ &buffer, &buffer_size, &ret);
+ if (ret < 0)
+ break;
+ }
+ bch2_btree_iter_unlock(&iter);
- buffer += len;
- buffer_size -= len;
- }
+ if (ret < 0)
+ return ret;
- ret += len;
+ bch2_xattr_list_bcachefs(c, inode, &buffer,
+ &buffer_size, &ret, false);
+ if (ret < 0)
+ return ret;
- }
- bch2_btree_iter_unlock(&iter);
+ bch2_xattr_list_bcachefs(c, inode, &buffer,
+ &buffer_size, &ret, true);
+ if (ret < 0)
+ return ret;
return ret;
}
@@ -316,27 +361,48 @@ static const struct xattr_handler bch_xattr_security_handler = {
#ifndef NO_BCACHEFS_FS
-static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
- struct dentry *dentry, struct inode *vinode,
- const char *name, void *buffer, size_t size)
+static int opt_to_inode_opt(int id)
+{
+ switch (id) {
+#define x(name, ...) \
+ case Opt_##name: return Inode_opt_##name;
+ BCH_INODE_OPTS()
+#undef x
+ default:
+ return -1;
+ }
+}
+
+static int __bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *vinode,
+ const char *name, void *buffer, size_t size,
+ bool all)
{
struct bch_inode_info *inode = to_bch_ei(vinode);
struct bch_fs *c = inode->v.i_sb->s_fs_info;
struct bch_opts opts =
bch2_inode_opts_to_opts(bch2_inode_opts_get(&inode->ei_inode));
const struct bch_option *opt;
- int id;
+ int id, inode_opt_id;
u64 v;
id = bch2_opt_lookup(name);
if (id < 0 || !bch2_opt_is_inode_opt(id))
return -EINVAL;
+ inode_opt_id = opt_to_inode_opt(id);
+ if (inode_opt_id < 0)
+ return -EINVAL;
+
opt = bch2_opt_table + id;
if (!bch2_opt_defined_by_id(&opts, id))
return -ENODATA;
+ if (!all &&
+ !(inode->ei_inode.bi_fields_set & (1 << inode_opt_id)))
+ return -ENODATA;
+
v = bch2_opt_get_by_id(&opts, id);
if (!buffer) {
@@ -357,6 +423,14 @@ static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
}
}
+static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *vinode,
+ const char *name, void *buffer, size_t size)
+{
+ return __bch2_xattr_bcachefs_get(handler, dentry, vinode,
+ name, buffer, size, false);
+}
+
struct inode_opt_set {
int id;
u64 v;
@@ -370,9 +444,12 @@ static int inode_opt_set_fn(struct bch_inode_info *inode,
struct inode_opt_set *s = p;
if (s->defined)
- bch2_inode_opt_set(bi, s->id, s->v);
+ bi->bi_fields_set |= 1U << s->id;
else
- bch2_inode_opt_clear(bi, s->id);
+ bi->bi_fields_set &= ~(1U << s->id);
+
+ bch2_inode_opt_set(bi, s->id, s->v);
+
return 0;
}
@@ -386,33 +463,51 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
const struct bch_option *opt;
char *buf;
struct inode_opt_set s;
- int ret;
+ int opt_id, inode_opt_id, ret;
+
+ opt_id = bch2_opt_lookup(name);
+ if (opt_id < 0)
+ return -EINVAL;
- s.id = bch2_opt_lookup(name);
- if (s.id < 0 || !bch2_opt_is_inode_opt(s.id))
+ opt = bch2_opt_table + opt_id;
+
+ inode_opt_id = opt_to_inode_opt(opt_id);
+ if (inode_opt_id < 0)
return -EINVAL;
- opt = bch2_opt_table + s.id;
+ s.id = inode_opt_id;
if (value) {
+ u64 v = 0;
+
buf = kmalloc(size + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy(buf, value, size);
buf[size] = '\0';
- ret = bch2_opt_parse(c, opt, buf, &s.v);
+ ret = bch2_opt_parse(c, opt, buf, &v);
kfree(buf);
if (ret < 0)
return ret;
- ret = bch2_opt_check_may_set(c, s.id, s.v);
+ ret = bch2_opt_check_may_set(c, opt_id, v);
if (ret < 0)
return ret;
+ s.v = v + 1;
s.defined = true;
} else {
+ if (!IS_ROOT(dentry)) {
+ struct bch_inode_info *dir =
+ to_bch_ei(d_inode(dentry->d_parent));
+
+ s.v = bch2_inode_opt_get(&dir->ei_inode, inode_opt_id);
+ } else {
+ s.v = 0;
+ }
+
s.defined = false;
}
@@ -421,8 +516,8 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
mutex_unlock(&inode->ei_update_lock);
if (value &&
- (s.id == Opt_background_compression ||
- s.id == Opt_background_target))
+ (opt_id == Opt_background_compression ||
+ opt_id == Opt_background_target))
bch2_rebalance_add_work(c, inode->v.i_blocks);
return ret;
@@ -434,6 +529,21 @@ static const struct xattr_handler bch_xattr_bcachefs_handler = {
.set = bch2_xattr_bcachefs_set,
};
+static int bch2_xattr_bcachefs_get_effective(
+ const struct xattr_handler *handler,
+ struct dentry *dentry, struct inode *vinode,
+ const char *name, void *buffer, size_t size)
+{
+ return __bch2_xattr_bcachefs_get(handler, dentry, vinode,
+ name, buffer, size, true);
+}
+
+static const struct xattr_handler bch_xattr_bcachefs_effective_handler = {
+ .prefix = "bcachefs_effective.",
+ .get = bch2_xattr_bcachefs_get_effective,
+ .set = bch2_xattr_bcachefs_set,
+};
+
#endif /* NO_BCACHEFS_FS */
const struct xattr_handler *bch2_xattr_handlers[] = {
@@ -444,6 +554,7 @@ const struct xattr_handler *bch2_xattr_handlers[] = {
&bch_xattr_security_handler,
#ifndef NO_BCACHEFS_FS
&bch_xattr_bcachefs_handler,
+ &bch_xattr_bcachefs_effective_handler,
#endif
NULL
};