[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Thu, 7 Jul 2016 16:10:46 +0900
From: Wang Shilong <wangshilong1991@...il.com>
To: linux-ext4@...r.kernel.org, jack@...e.cz
Cc: sihara@....com, lixi@....com, Wang Shilong <wshilong@....com>
Subject: [PATCH] quota: add project support for quotacheck
From: Wang Shilong <wshilong@....com>
With prjquota mount option supported, quotacheck should be
aware of ext4 project quota.
This patch add support for it.
Signed-off-by: Wang Shilong <wshilong@....com>
---
mntopt.h | 2 +
quota.h | 3 +-
quotacheck.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++-------------
quotacheck.h | 17 +++++++
quotaio_v2.h | 2 +-
quotasys.c | 10 ++++
6 files changed, 159 insertions(+), 36 deletions(-)
diff --git a/mntopt.h b/mntopt.h
index f31abb7..b055f7a 100644
--- a/mntopt.h
+++ b/mntopt.h
@@ -32,6 +32,8 @@
#define MNTOPT_USRJQUOTA "usrjquota" /* enforce user quota */
#define MNTOPT_GRPQUOTA "grpquota" /* enforce group quota */
#define MNTOPT_GRPJQUOTA "grpjquota" /* enforce group quota */
+#define MNTOPT_PRJQUOTA "prjquota" /* enforce group quota */
+#define MNTOPT_PRJJQUOTA "prjjquota" /* enforce group quota */
#define MNTOPT_RSQUASH "rsquash" /* root as ordinary user */
#define MNTOPT_BIND "bind" /* binded mount */
#define MNTOPT_LOOP "loop" /* loopback mount */
diff --git a/quota.h b/quota.h
index 3628614..094130a 100644
--- a/quota.h
+++ b/quota.h
@@ -30,7 +30,8 @@ typedef int64_t qsize_t; /* Type in which we store size limitations */
*/
#define INITQMAGICS {\
0xd9c01f11, /* USRQUOTA */\
- 0xd9c01927 /* GRPQUOTA */\
+ 0xd9c01927, /* GRPQUOTA */\
+ 0xd9c03f14 /* PRJQUOTA */\
}
/* Size of blocks in which are counted size limits in generic utility parts */
diff --git a/quotacheck.c b/quotacheck.c
index 6f34cff..d895f77 100644
--- a/quotacheck.c
+++ b/quotacheck.c
@@ -61,7 +61,7 @@ struct dirs {
static dev_t cur_dev; /* Device we are working on */
static int files_done, dirs_done;
int flags, fmt = -1, cfmt; /* Options from command line; Quota format to use spec. by user; Actual format to check */
-static int uwant, gwant, ucheck, gcheck; /* Does user want to check user/group quota; Do we check user/group quota? */
+static int uwant, gwant, pwant, ucheck, gcheck, pcheck; /* Does user want to check quotas; Do we check quotas? */
static char *mntpoint; /* Mountpoint to check */
char *progname;
struct util_dqinfo old_info[MAXQUOTAS]; /* Loaded infos */
@@ -179,16 +179,18 @@ struct dquot *add_dquot(qid_t id, int type)
/*
* Add a number of blocks and inodes to a quota.
*/
-static void add_to_quota(int type, ino_t i_num, uid_t i_uid, gid_t i_gid, mode_t i_mode,
- nlink_t i_nlink, loff_t i_space, int need_remember)
+static void add_to_quota(int type, ino_t i_num, uid_t i_uid, gid_t i_gid, __u32 prjid,
+ mode_t i_mode, nlink_t i_nlink, loff_t i_space, int need_remember)
{
qid_t wanted;
struct dquot *lptr;
if (type == USRQUOTA)
wanted = i_uid;
- else
+ else if (type == PRJQUOTA)
wanted = i_gid;
+ else
+ wanted = prjid;
if ((lptr = lookup_dquot(wanted, type)) == NODQUOT)
lptr = add_dquot(wanted, type);
@@ -293,9 +295,10 @@ static inline void blit(const char *msg)
static void usage(void)
{
- printf(_("Utility for checking and repairing quota files.\n%s [-gucbfinvdmMR] [-F <quota-format>] filesystem|-a\n\n\
+ printf(_("Utility for checking and repairing quota files.\n%s [-gupcbfinvdmMR] [-F <quota-format>] filesystem|-a\n\n\
-u, --user check user files\n\
-g, --group check group files\n\
+-p, --project check group files\n\
-c, --create-files create new quota files\n\
-b, --backup create backups of old quota files\n\
-f, --force force check even if quotas are enabled\n\
@@ -327,6 +330,7 @@ static void parse_options(int argcnt, char **argstr)
{ "debug", 0, NULL, 'd' },
{ "user", 0, NULL, 'u' },
{ "group", 0, NULL, 'g' },
+ { "group", 0, NULL, 'p' },
{ "interactive", 0, NULL, 'i' },
{ "use-first-dquot", 0, NULL, 'n' },
{ "force", 0, NULL, 'f' },
@@ -338,7 +342,7 @@ static void parse_options(int argcnt, char **argstr)
{ NULL, 0, NULL, 0 }
};
- while ((ret = getopt_long(argcnt, argstr, "VhbcvugidnfF:mMRa", long_opts, NULL)) != -1) {
+ while ((ret = getopt_long(argcnt, argstr, "VhbcvugpidnfF:mMRa", long_opts, NULL)) != -1) {
switch (ret) {
case 'b':
flags |= FL_BACKUPS;
@@ -349,6 +353,9 @@ static void parse_options(int argcnt, char **argstr)
case 'u':
uwant = 1;
break;
+ case 'p':
+ pwant = 1;
+ break;
case 'd':
flags |= FL_DEBUG;
setlinebuf(stderr);
@@ -394,7 +401,7 @@ static void parse_options(int argcnt, char **argstr)
usage();
}
}
- if (!(uwant | gwant))
+ if (!(uwant | gwant | pwant))
uwant = 1;
if ((argcnt == optind && !(flags & FL_ALL)) || (argcnt > optind && flags & FL_ALL)) {
fputs(_("Bad number of arguments.\n"), stderr);
@@ -415,12 +422,14 @@ static int ext2_direct_scan(const char *device)
ext2_filsys fs;
errcode_t error;
ext2_inode_scan scan;
- struct ext2_inode inode;
+ struct ext2_inode *inode;
+ int inode_size;
int inode_buffer_blocks = 0;
ext2fs_inode_bitmap inode_used_map;
ext2fs_inode_bitmap inode_dir_map;
uid_t uid;
gid_t gid;
+ u32 projid = 0;
if ((error = ext2fs_open(device, 0, 0, 0, unix_io_manager, &fs))) {
errstr(_("error (%d) while opening %s\n"), (int)error, device);
@@ -441,38 +450,53 @@ static int ext2_direct_scan(const char *device)
errstr(_("error (%d) while opening inode scan\n"), (int)error);
return -1;
}
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ inode = e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
+ if (!inode) {
+ errstr(_("error (%d) while allocating inode\n"), -ENOMEM);
+ return -1;
+ }
- if ((error = ext2fs_get_next_inode(scan, &i_num, &inode))) {
+ if ((error = ext2fs_get_next_inode_full(scan, &i_num, inode, inode_size))) {
errstr(_("error (%d) while starting inode scan\n"), (int)error);
return -1;
}
while (i_num) {
+ /* keep off project quota inode here ? */
if ((i_num == EXT2_ROOT_INO ||
i_num >= EXT2_FIRST_INO(fs->super)) &&
- inode.i_links_count) {
+ inode->i_links_count) {
debug(FL_DEBUG, _("Found i_num %ld, blocks %ld\n"), (long)i_num, (long)inode.i_blocks);
if (flags & FL_VERBOSE)
blit(NULL);
- uid = inode.i_uid | (inode.i_uid_high << 16);
- gid = inode.i_gid | (inode.i_gid_high << 16);
- if (inode.i_uid_high | inode.i_gid_high)
+ uid = inode->i_uid | (inode->i_uid_high << 16);
+ gid = inode->i_gid | (inode->i_gid_high << 16);
+ if (inode->i_uid_high | inode->i_gid_high)
debug(FL_DEBUG, _("High uid detected.\n"));
+ inode_size = EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize;
+ if (inode_includes(inode_size, i_projid))
+ projid = inode->i_projid;
if (ucheck)
- add_to_quota(USRQUOTA, i_num, uid, gid,
- inode.i_mode, inode.i_links_count,
- ((loff_t)inode.i_blocks) << 9, 0);
+ add_to_quota(USRQUOTA, i_num, uid, gid, projid,
+ inode->i_mode, inode->i_links_count,
+ ((loff_t)inode->i_blocks) << 9, 0);
if (gcheck)
- add_to_quota(GRPQUOTA, i_num, uid, gid,
- inode.i_mode, inode.i_links_count,
- ((loff_t)inode.i_blocks) << 9, 0);
- if (S_ISDIR(inode.i_mode))
+ add_to_quota(GRPQUOTA, i_num, uid, gid, projid,
+ inode->i_mode, inode->i_links_count,
+ ((loff_t)inode->i_blocks) << 9, 0);
+ if (pcheck)
+ add_to_quota(PRJQUOTA, i_num, uid, gid, projid,
+ inode->i_mode, inode->i_links_count,
+ ((loff_t)inode->i_blocks) << 9, 0);
+ if (S_ISDIR(inode->i_mode))
dirs_done++;
else
files_done++;
}
- if ((error = ext2fs_get_next_inode(scan, &i_num, &inode))) {
+ if ((error = ext2fs_get_next_inode_full(scan, &i_num, inode, inode_size))) {
errstr(_("Something weird happened while scanning. Error %d\n"), (int)error);
return -1;
}
@@ -481,6 +505,29 @@ static int ext2_direct_scan(const char *device)
}
#endif
+static int fgetproject(const char *name, unsigned long *project)
+{
+#ifndef FS_IOC_FSGETXATTR
+ errno = EOPNOTSUPP;
+ return -1;
+#else
+ int fd, r, save_errno = 0;
+ struct fsxattr fsx;
+
+ fd = open (name, O_RDONLY);
+ if (fd == -1)
+ return -1;
+ r = ioctl (fd, FS_IOC_FSGETXATTR, &fsx);
+ if (r == 0)
+ *project = fsx.fsx_projid;
+ save_errno = errno;
+ close (fd);
+ if (save_errno)
+ errno = save_errno;
+ return r;
+#endif
+}
+
/*
* Scan a directory with the readdir systemcall. Stat the files and add the sizes
* of the files to the appropriate quotas. When we find a dir we recursivly call
@@ -493,6 +540,7 @@ static int scan_dir(const char *pathname)
struct dirent *de;
struct stat st;
loff_t qspace;
+ unsigned long projid;
DIR *dp;
int ret;
@@ -500,13 +548,20 @@ static int scan_dir(const char *pathname)
errstr(_("Cannot stat directory %s: %s\n"), pathname, strerror(errno));
goto out;
}
+
+ if (fgetproject(pathname, &projid))
+ projid = 0;
+
qspace = getqsize(pathname, &st);
if (ucheck)
- add_to_quota(USRQUOTA, st.st_ino, st.st_uid, st.st_gid, st.st_mode,
- st.st_nlink, qspace, 0);
+ add_to_quota(USRQUOTA, st.st_ino, st.st_uid, st.st_gid, projid,
+ st.st_mode, st.st_nlink, qspace, 0);
if (gcheck)
- add_to_quota(GRPQUOTA, st.st_ino, st.st_uid, st.st_gid, st.st_mode,
- st.st_nlink, qspace, 0);
+ add_to_quota(GRPQUOTA, st.st_ino, st.st_uid, st.st_gid, projid,
+ st.st_mode, st.st_nlink, qspace, 0);
+ if (pcheck)
+ add_to_quota(PRJQUOTA, st.st_ino, st.st_uid, st.st_gid, projid,
+ st.st_mode, st.st_nlink, qspace, 0);
if ((dp = opendir(pathname)) == (DIR *) NULL)
die(2, _("\nCan open directory %s: %s\n"), pathname, strerror(errno));
@@ -541,13 +596,18 @@ static int scan_dir(const char *pathname)
dir_stack = new_dir;
}
else {
+ if (fgetproject(de->d_name, &projid))
+ projid = 0;
qspace = getqsize(de->d_name, &st);
if (ucheck)
- add_to_quota(USRQUOTA, st.st_ino, st.st_uid, st.st_gid, st.st_mode,
- st.st_nlink, qspace, 1);
+ add_to_quota(USRQUOTA, st.st_ino, st.st_uid, st.st_gid, projid,
+ st.st_mode, st.st_nlink, qspace, 1);
if (gcheck)
- add_to_quota(GRPQUOTA, st.st_ino, st.st_uid, st.st_gid, st.st_mode,
- st.st_nlink, qspace, 1);
+ add_to_quota(GRPQUOTA, st.st_ino, st.st_uid, st.st_gid, projid,
+ st.st_mode, st.st_nlink, qspace, 1);
+ if (pcheck)
+ add_to_quota(PRJQUOTA, st.st_ino, st.st_uid, st.st_gid, projid,
+ st.st_mode, st.st_nlink, qspace, 1);
debug(FL_DEBUG, _("\tAdding %s size %lld ino %d links %d uid %u gid %u\n"), de->d_name,
(long long)st.st_size, (int)st.st_ino, (int)st.st_nlink, (int)st.st_uid, (int)st.st_gid);
files_done++;
@@ -930,7 +990,10 @@ static int check_dir(struct mount_entry *mnt)
if (gcheck)
if (process_file(mnt, GRPQUOTA) < 0)
gcheck = 0;
- if (!ucheck && !gcheck) /* Nothing to check? */
+ if (pcheck)
+ if (process_file(mnt, PRJQUOTA) < 0)
+ pcheck = 0;
+ if (!ucheck && !gcheck && !pcheck) /* Nothing to check? */
return 0;
if (!(flags & FL_NOREMOUNT)) {
/* Now we try to remount fs read-only to prevent races when scanning filesystem */
@@ -959,7 +1022,8 @@ Please stop all programs writing to filesystem or use -m flag to force checking.
start_scan:
debug(FL_VERBOSE | FL_DEBUG, _("Scanning %s [%s] "), mnt->me_devname, mnt->me_dir);
#if defined(EXT2_DIRECT)
- if (!strcmp(mnt->me_type, MNTTYPE_EXT2) || !strcmp(mnt->me_type, MNTTYPE_EXT3) || !strcmp(mnt->me_type, MNTTYPE_NEXT3)) {
+ if (!strcmp(mnt->me_type, MNTTYPE_EXT2) || !strcmp(mnt->me_type, MNTTYPE_EXT3) ||
+ !strcmp(mnt->me_type, MNTTYPE_NEXT3) || !strcmp(mnt->me_type, MNTTYPE_EXT4)) {
if ((failed = ext2_direct_scan(mnt->me_devname)) < 0)
goto out;
}
@@ -975,13 +1039,21 @@ start_scan:
dirs_done++;
if (flags & FL_VERBOSE || flags & FL_DEBUG)
fputs(_("done\n"), stdout);
+ /* skip this for project quota */
if (ucheck) {
failed |= sub_quota_file(mnt, USRQUOTA, USRQUOTA);
failed |= sub_quota_file(mnt, USRQUOTA, GRPQUOTA);
+ failed |= sub_quota_file(mnt, USRQUOTA, PRJQUOTA);
}
if (gcheck) {
failed |= sub_quota_file(mnt, GRPQUOTA, USRQUOTA);
failed |= sub_quota_file(mnt, GRPQUOTA, GRPQUOTA);
+ failed |= sub_quota_file(mnt, GRPQUOTA, PRJQUOTA);
+ }
+ if (pcheck) {
+ failed |= sub_quota_file(mnt, PRJQUOTA, USRQUOTA);
+ failed |= sub_quota_file(mnt, PRJQUOTA, GRPQUOTA);
+ failed |= sub_quota_file(mnt, PRJQUOTA, PRJQUOTA);
}
debug(FL_DEBUG | FL_VERBOSE, _("Checked %d directories and %d files\n"), dirs_done,
files_done);
@@ -994,6 +1066,8 @@ start_scan:
failed |= dump_to_file(mnt, USRQUOTA);
if (gcheck)
failed |= dump_to_file(mnt, GRPQUOTA);
+ if (pcheck)
+ failed |= dump_to_file(mnt, PRJQUOTA);
out:
remove_list();
return failed;
@@ -1022,13 +1096,20 @@ static int detect_filename_format(struct mount_entry *mnt, int type)
else if ((option = str_hasmntopt(mnt->me_opts, MNTOPT_QUOTA)))
option += strlen(MNTOPT_QUOTA);
}
- else {
+ else if (type == USRQUOTA){
if ((option = str_hasmntopt(mnt->me_opts, MNTOPT_GRPQUOTA)))
option += strlen(MNTOPT_GRPQUOTA);
else if ((option = str_hasmntopt(mnt->me_opts, MNTOPT_GRPJQUOTA))) {
journal = 1;
option += strlen(MNTOPT_GRPJQUOTA);
}
+ } else {
+ if ((option = str_hasmntopt(mnt->me_opts, MNTOPT_PRJQUOTA)))
+ option += strlen(MNTOPT_PRJQUOTA);
+ else if ((option = str_hasmntopt(mnt->me_opts, MNTOPT_PRJJQUOTA))) {
+ journal = 1;
+ option += strlen(MNTOPT_PRJJQUOTA);
+ }
}
if (!option)
die(2, _("Cannot find quota option on filesystem %s with quotas!\n"), mnt->me_dir);
@@ -1154,10 +1235,21 @@ static int check_all(void)
gcheck = 1;
else
gcheck = 0;
- if (!ucheck && !gcheck)
+ if (pwant && me_hasquota(mnt, PRJQUOTA))
+ pcheck = 1;
+ else
+ pcheck = 0;
+ if (!ucheck && !gcheck && !pcheck)
continue;
if (cfmt == -1) {
- cfmt = detect_filename_format(mnt, ucheck ? USRQUOTA : GRPQUOTA);
+ int qtype;
+ if (ucheck)
+ qtype = USRQUOTA;
+ else if (gcheck)
+ qtype = GRPQUOTA;
+ else
+ qtype = PRJQUOTA;
+ cfmt = detect_filename_format(mnt, qtype);
if (cfmt == -1) {
errstr(_("Cannot guess format from filename on %s. Please specify format on commandline.\n"),
mnt->me_devname);
@@ -1170,6 +1262,7 @@ static int check_all(void)
if (flags & (FL_VERBOSE | FL_DEBUG) &&
!str_hasmntopt(mnt->me_opts, MNTOPT_USRJQUOTA) &&
!str_hasmntopt(mnt->me_opts, MNTOPT_GRPJQUOTA) &&
+ !str_hasmntopt(mnt->me_opts, MNTOPT_PRJJQUOTA) &&
!warned &&
(!strcmp(mnt->me_type, MNTTYPE_EXT3) ||
!strcmp(mnt->me_type, MNTTYPE_EXT4) ||
diff --git a/quotacheck.h b/quotacheck.h
index 0abdaaa..4d5456b 100644
--- a/quotacheck.h
+++ b/quotacheck.h
@@ -35,6 +35,23 @@ extern size_t malloc_mem = 0;
extern size_t free_mem = 0;
#endif
+#if !defined(FS_IOC_FSGETXATTR)
+#define FS_IOC_FSGETXATTR _IOR('X', 31, struct fsxattr)
+#define FS_IOC_FSSETXATTR _IOW('X', 32, struct fsxattr)
+
+/*
+ * Structure for FS_IOC_FSGETXATTR and FS_IOC_FSSETXATTR.
+ */
+struct fsxattr {
+ __u32 fsx_xflags; /* xflags field value (get/set) */
+ __u32 fsx_extsize; /* extsize field value (get/set)*/
+ __u32 fsx_nextents; /* nextents field value (get) */
+ __u32 fsx_projid; /* project identifier (get/set) */
+ unsigned char fsx_pad[12];
+};
+#endif
+
+
void *xmalloc(size_t size);
void debug(int df, char *fmtstr, ...) __attribute__ ((__format__ (__printf__, 2, 3)));
int ask_yn(char *q, int def);
diff --git a/quotaio_v2.h b/quotaio_v2.h
index 197bb65..3b5bad1 100644
--- a/quotaio_v2.h
+++ b/quotaio_v2.h
@@ -11,7 +11,7 @@
#include "quota.h"
#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader) /* Offset of info header in file */
-#define INIT_V2_VERSIONS { 1, 1}
+#define INIT_V2_VERSIONS { 1, 1, 1}
struct v2_disk_dqheader {
u_int32_t dqh_magic; /* Magic number identifying file */
diff --git a/quotasys.c b/quotasys.c
index c78e02c..8560dca 100644
--- a/quotasys.c
+++ b/quotasys.c
@@ -783,6 +783,8 @@ static int hasquota(const char *dev, struct mntent *mnt, int type, int flags)
return QF_VFSUNKNOWN;
if ((type == GRPQUOTA) && (hasmntopt(mnt, MNTOPT_GRPQUOTA) || hasmntoptarg(mnt->mnt_opts, MNTOPT_GRPJQUOTA)))
return QF_VFSUNKNOWN;
+ if ((type == PRJQUOTA) && (hasmntopt(mnt, MNTOPT_PRJQUOTA) || hasmntoptarg(mnt->mnt_opts, MNTOPT_PRJJQUOTA)))
+ return QF_VFSUNKNOWN;
if ((type == USRQUOTA) && hasmntopt(mnt, MNTOPT_QUOTA))
return QF_VFSUNKNOWN;
return -1;
@@ -862,6 +864,14 @@ int get_qf_name(struct mount_entry *mnt, int type, int fmt, int flags, char **fi
has_quota_file_definition = 1;
pathname++;
}
+ } else if (type == PRJQUOTA && (option = str_hasmntopt(mnt->me_opts, MNTOPT_PRJQUOTA))) {
+ if (*(pathname = option + strlen(MNTOPT_PRJQUOTA)) == '=')
+ has_quota_file_definition = 1;
+ } else if (type == PRJQUOTA && (option = hasmntoptarg(mnt->me_opts, MNTOPT_PRJJQUOTA))) {
+ pathname = option;
+ has_quota_file_definition = 1;
+ sstrncpy(qfullname, mnt->me_dir, sizeof(qfullname));
+ sstrncat(qfullname, "/", sizeof(qfullname));
}
else
return -1;
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists