diff --git a/kmod/src/attr_x.c b/kmod/src/attr_x.c index 56718197..39e84a36 100644 --- a/kmod/src/attr_x.c +++ b/kmod/src/attr_x.c @@ -33,6 +33,10 @@ static int validate_attr_x_input(struct super_block *sb, struct scoutfs_ioctl_in (ret = scoutfs_fmt_vers_unsupported(sb, SCOUTFS_FORMAT_VERSION_FEAT_RETENTION))) return ret; + if ((iax->x_mask & SCOUTFS_IOC_IAX_PROJECT_ID) && + (ret = scoutfs_fmt_vers_unsupported(sb, SCOUTFS_FORMAT_VERSION_FEAT_PROJECT_ID))) + return ret; + return 0; } @@ -111,6 +115,8 @@ int scoutfs_get_attr_x(struct inode *inode, struct scoutfs_ioctl_inode_attr_x *i bits |= SCOUTFS_IOC_IAX_B_RETENTION; size = fill_attr(size, iax, SCOUTFS_IOC_IAX__BITS, bits, bits); } + size = fill_attr(size, iax, SCOUTFS_IOC_IAX_PROJECT_ID, + project_id, scoutfs_inode_get_proj(inode)); ret = size; unlock: @@ -230,6 +236,8 @@ int scoutfs_set_attr_x(struct inode *inode, struct scoutfs_ioctl_inode_attr_x *i (iax->bits & SCOUTFS_IOC_IAX_B_RETENTION) ? SCOUTFS_INO_FLAG_RETENTION : 0); } + if (iax->x_mask & SCOUTFS_IOC_IAX_PROJECT_ID) + scoutfs_inode_set_proj(inode, iax->project_id); scoutfs_update_inode_item(inode, lock, &ind_locks); ret = 0; diff --git a/kmod/src/dir.c b/kmod/src/dir.c index e382c78d..c8417bdf 100644 --- a/kmod/src/dir.c +++ b/kmod/src/dir.c @@ -672,6 +672,8 @@ retry: if (ret < 0) goto out; + scoutfs_inode_set_proj(inode, scoutfs_inode_get_proj(dir)); + ret = scoutfs_dirty_inode_item(dir, *dir_lock); out: if (ret) diff --git a/kmod/src/format.h b/kmod/src/format.h index 88abb822..7e283de8 100644 --- a/kmod/src/format.h +++ b/kmod/src/format.h @@ -12,6 +12,7 @@ #define SCOUTFS_FORMAT_VERSION_MAX_STR __stringify(SCOUTFS_FORMAT_VERSION_MAX) #define SCOUTFS_FORMAT_VERSION_FEAT_RETENTION 2 +#define SCOUTFS_FORMAT_VERSION_FEAT_PROJECT_ID 2 /* statfs(2) f_type */ #define SCOUTFS_SUPER_MAGIC 0x554f4353 /* "SCOU" */ @@ -861,8 +862,11 @@ struct scoutfs_inode { struct scoutfs_timespec ctime; struct scoutfs_timespec mtime; struct scoutfs_timespec crtime; + __le64 proj; }; +#define SCOUTFS_INODE_FMT_V1_BYTES offsetof(struct scoutfs_inode, proj) + /* * There are so few versions that we don't mind doing this work inline * so that both utils and kernel can share these. Mounting has already @@ -872,7 +876,10 @@ struct scoutfs_inode { /* Returns the native written inode size for the given format version, 0 for bad version */ static inline int scoutfs_inode_vers_bytes(__u64 fmt_vers) { - return sizeof(struct scoutfs_inode); + if (fmt_vers == 1) + return SCOUTFS_INODE_FMT_V1_BYTES; + else + return sizeof(struct scoutfs_inode); } /* * Returns true if bytes is a valid inode size to read from the given @@ -881,7 +888,8 @@ static inline int scoutfs_inode_vers_bytes(__u64 fmt_vers) */ static inline int scoutfs_inode_valid_vers_bytes(__u64 fmt_vers, int bytes) { - return bytes == sizeof(struct scoutfs_inode); + return (bytes == sizeof(struct scoutfs_inode) && fmt_vers == SCOUTFS_FORMAT_VERSION_MAX) || + (bytes == SCOUTFS_INODE_FMT_V1_BYTES); } #define SCOUTFS_INO_FLAG_TRUNCATE 0x1 diff --git a/kmod/src/inode.c b/kmod/src/inode.c index 4bd1492b..2203a8e9 100644 --- a/kmod/src/inode.c +++ b/kmod/src/inode.c @@ -278,6 +278,7 @@ static void load_inode(struct inode *inode, struct scoutfs_inode *cinode, int in si->flags = le32_to_cpu(cinode->flags); si->crtime.tv_sec = le64_to_cpu(cinode->crtime.sec); si->crtime.tv_nsec = le32_to_cpu(cinode->crtime.nsec); + si->proj = le64_to_cpu(cinode->proj); /* * i_blocks is initialized from online and offline and is then @@ -734,6 +735,29 @@ void scoutfs_inode_set_flags(struct inode *inode, u32 and, u32 or) write_sequnlock(&si->seqlock); } +u64 scoutfs_inode_get_proj(struct inode *inode) +{ + struct scoutfs_inode_info *si = SCOUTFS_I(inode); + unsigned seq; + u64 proj; + + do { + seq = read_seqbegin(&si->seqlock); + proj = si->proj; + } while (read_seqretry(&si->seqlock, seq)); + + return proj; +} + +void scoutfs_inode_set_proj(struct inode *inode, u64 proj) +{ + struct scoutfs_inode_info *si = SCOUTFS_I(inode); + + write_seqlock(&si->seqlock); + si->proj = proj; + write_sequnlock(&si->seqlock); +} + static int scoutfs_iget_test(struct inode *inode, void *arg) { struct scoutfs_inode_info *si = SCOUTFS_I(inode); @@ -879,6 +903,7 @@ static void store_inode(struct scoutfs_inode *cinode, struct inode *inode, int i cinode->crtime.sec = cpu_to_le64(si->crtime.tv_sec); cinode->crtime.nsec = cpu_to_le32(si->crtime.tv_nsec); memset(cinode->crtime.__pad, 0, sizeof(cinode->crtime.__pad)); + cinode->proj = cpu_to_le64(si->proj); } /* @@ -1527,6 +1552,7 @@ int scoutfs_new_inode(struct super_block *sb, struct inode *dir, umode_t mode, d si->offline_blocks = 0; si->next_readdir_pos = SCOUTFS_DIRENT_FIRST_POS; si->next_xattr_id = 0; + si->proj = 0; si->have_item = false; atomic64_set(&si->last_refreshed, lock->refresh_gen); scoutfs_lock_add_coverage(sb, lock, &si->ino_lock_cov); diff --git a/kmod/src/inode.h b/kmod/src/inode.h index 6ab7d220..e068cee5 100644 --- a/kmod/src/inode.h +++ b/kmod/src/inode.h @@ -21,6 +21,7 @@ struct scoutfs_inode_info { u64 data_version; u64 online_blocks; u64 offline_blocks; + u64 proj; u32 flags; struct kc_timespec crtime; @@ -122,6 +123,9 @@ u64 scoutfs_inode_data_version(struct inode *inode); void scoutfs_inode_get_onoff(struct inode *inode, s64 *on, s64 *off); u32 scoutfs_inode_get_flags(struct inode *inode); void scoutfs_inode_set_flags(struct inode *inode, u32 and, u32 or); +u64 scoutfs_inode_get_proj(struct inode *inode); +void scoutfs_inode_set_proj(struct inode *inode, u64 proj); + int scoutfs_complete_truncate(struct inode *inode, struct scoutfs_lock *lock); int scoutfs_inode_check_retention(struct inode *inode); diff --git a/kmod/src/ioctl.h b/kmod/src/ioctl.h index fbb04217..9a6b9b30 100644 --- a/kmod/src/ioctl.h +++ b/kmod/src/ioctl.h @@ -687,6 +687,7 @@ struct scoutfs_ioctl_inode_attr_x { __u64 crtime_sec; __u64 size; __u64 bits; + __u64 project_id; }; /* @@ -736,11 +737,12 @@ struct scoutfs_ioctl_inode_attr_x { #define SCOUTFS_IOC_IAX_CRTIME (1ULL << 6) #define SCOUTFS_IOC_IAX_SIZE (1ULL << 7) #define SCOUTFS_IOC_IAX_RETENTION (1ULL << 8) +#define SCOUTFS_IOC_IAX_PROJECT_ID (1ULL << 9) /* single bit attributes that are packed in the bits field as _B_ */ #define SCOUTFS_IOC_IAX__BITS (SCOUTFS_IOC_IAX_RETENTION) /* inverse of all the bits we understand */ -#define SCOUTFS_IOC_IAX__UNKNOWN (U64_MAX << 9) +#define SCOUTFS_IOC_IAX__UNKNOWN (U64_MAX << 10) #define SCOUTFS_IOC_GET_ATTR_X \ _IOW(SCOUTFS_IOCTL_MAGIC, 18, struct scoutfs_ioctl_inode_attr_x) diff --git a/utils/man/scoutfs.5 b/utils/man/scoutfs.5 index 839c8cd9..458d69a3 100644 --- a/utils/man/scoutfs.5 +++ b/utils/man/scoutfs.5 @@ -312,6 +312,19 @@ with sufficient priveledges to remove the retention restrictions on other modifications. .RE +.SH PROJECT IDs +All inodes have a project ID attribute that can be set via the +SET_ATTR_X ioctl and displayed with the GET_ATTR_X ioctl. Project IDs +are an unsigned 64bit value and the value of 0 is reserved to indicate +that no project ID is assigned. If a project ID is set on a directory +then all inodes created with it as the initial parent inheret that ID, +for all file types. This includes files initially unlinked from the +namespace when created with O_TMPFILE. Project IDs are only +automatically inherited from the parent dir on initial creation. +They're not changed as directory entry linkes to the inode are created +or renamed. +.RE + .SH FORMAT VERSION The format version defines the layout and use of structures stored on devices and passed over the network. The version is incremented for @@ -399,7 +412,8 @@ The defined format versions are: Initial format version. .TP .B 2 -Added retention mode by setting the retention attribute. +Added retention mode by setting the retention attribute. Added the +project ID inode attribute. .RE .SH CORRUPTION DETECTION diff --git a/utils/src/attr_x.c b/utils/src/attr_x.c index 979ac517..9674624e 100644 --- a/utils/src/attr_x.c +++ b/utils/src/attr_x.c @@ -82,6 +82,7 @@ static int do_attr_x(struct attr_x_args *args) pr(iax, CRTIME, "crtime", "%llu.%u", iax->crtime_sec, iax->crtime_nsec); pr(iax, SIZE, "size", "%llu", iax->size); prb(iax, RETENTION, "retention"); + pr(iax, PROJECT_ID, "project_id", "%llu", iax->project_id); } ret = 0; @@ -191,6 +192,14 @@ static int parse_opt(int key, char *arg, struct argp_state *state) args->iax.bits |= SCOUTFS_IOC_IAX_B_RETENTION; } break; + case 'p': + args->iax.x_mask |= SCOUTFS_IOC_IAX_PROJECT_ID; + if (arg) { + ret = parse_u64(arg, &args->iax.project_id); + if (ret) + return ret; + } + break; case ARGP_KEY_ARG: if (!args->filename) args->filename = strdup_or_error(state, arg); @@ -222,6 +231,7 @@ static struct argp_option set_options[] = { { "crtime", 'r', "SECS.NSECS", 0, "ScoutFS creation time"}, { "size", 's', "SIZE", 0, "Inode i_size field"}, { "retention", 't', "0|1", 0, "Retention flag"}, + { "project_id", 'p', "PROJECT_ID", 0, "Project ID"}, { NULL } }; diff --git a/utils/src/print.c b/utils/src/print.c index b977da46..e3f81780 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -49,7 +49,7 @@ static void print_inode(struct scoutfs_key *key, void *val, int val_len) { struct scoutfs_inode *inode = val; - printf(" inode: ino %llu size %llu version %llu nlink %u\n" + printf(" inode: ino %llu size %llu version %llu proj %llu nlink %u\n" " uid %u gid %u mode 0%o rdev 0x%x flags 0x%x\n" " next_readdir_pos %llu meta_seq %llu data_seq %llu data_version %llu\n" " atime %llu.%08u ctime %llu.%08u\n" @@ -57,6 +57,7 @@ static void print_inode(struct scoutfs_key *key, void *val, int val_len) le64_to_cpu(key->ski_ino), le64_to_cpu(inode->size), le64_to_cpu(inode->version), + le64_to_cpu(inode->proj), le32_to_cpu(inode->nlink), le32_to_cpu(inode->uid), le32_to_cpu(inode->gid), le32_to_cpu(inode->mode), le32_to_cpu(inode->rdev),