diff --git a/SOURCES/xfsprogs-5.10.0-xfs_quota-document-how-the-default-quota-is-stored.patch b/SOURCES/xfsprogs-5.10.0-xfs_quota-document-how-the-default-quota-is-stored.patch new file mode 100644 index 0000000..bfb71e3 --- /dev/null +++ b/SOURCES/xfsprogs-5.10.0-xfs_quota-document-how-the-default-quota-is-stored.patch @@ -0,0 +1,35 @@ +From 0d7b09ac95e4cde766a534fdb7ea8dd46451ad53 Mon Sep 17 00:00:00 2001 +From: Eric Sandeen +Date: Fri, 4 Dec 2020 12:17:12 -0500 +Subject: [PATCH] xfs_quota: document how the default quota is stored + +Nowhere in the man page is the default quota described; what it +does or where it is stored. Add some brief information about this. + +Signed-off-by: Eric Sandeen +Reviewed-by: Darrick J. Wong +Signed-off-by: Eric Sandeen +--- + man/man8/xfs_quota.8 | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/man/man8/xfs_quota.8 b/man/man8/xfs_quota.8 +index dd0479cd..6ead7ee9 100644 +--- a/man/man8/xfs_quota.8 ++++ b/man/man8/xfs_quota.8 +@@ -178,6 +178,12 @@ to a file on + where the user's quota has not been exceeded. + Then after rectifying the quota situation, the file can be moved back to the + filesystem it belongs on. ++.SS Default Quotas ++The XFS quota subsystem allows a default quota to be enforced ++for any user, group or project which does not have a quota limit ++explicitly set. ++These limits are stored in and displayed as ID 0's limits, although they ++do not actually limit ID 0. + .SH USER COMMANDS + .TP + .B print +-- +2.29.2 + diff --git a/SOURCES/xfsprogs-5.10.0-xfs_repair-Use-proper-min-max-values-in-compute_level_geometry.patch b/SOURCES/xfsprogs-5.10.0-xfs_repair-Use-proper-min-max-values-in-compute_level_geometry.patch new file mode 100644 index 0000000..5a07f04 --- /dev/null +++ b/SOURCES/xfsprogs-5.10.0-xfs_repair-Use-proper-min-max-values-in-compute_level_geometry.patch @@ -0,0 +1,92 @@ +xfs_repair: Use proper min/max values in compute_level_geometry + +When compute_level_geometry was added it exclusively uses +m_alloc_mnr/m_alloc_mxr but the rmap btree should be using +m_rmap_mnr/m_rmap_mxr. Pass those in directly to fix the +problem. + +Signed-off-by: Eric Sandeen +--- + +diff --git a/repair/phase5.c b/repair/phase5.c +index 0b8a55ff..dff342c8 100644 +--- a/repair/phase5.c ++++ b/repair/phase5.c +@@ -355,12 +355,12 @@ compute_level_geometry( + struct bt_stat_level *lptr, + uint64_t nr_this_level, + int slack, +- bool leaf) ++ uint maxrecs, ++ uint minrecs) + { +- unsigned int maxrecs = mp->m_alloc_mxr[!leaf]; + unsigned int desired_npb; + +- desired_npb = max(mp->m_alloc_mnr[!leaf], maxrecs - slack); ++ desired_npb = max(minrecs, maxrecs - slack); + lptr->num_recs_tot = nr_this_level; + lptr->num_blocks = max(1ULL, nr_this_level / desired_npb); + +@@ -410,7 +410,8 @@ calculate_freespace_cursor(xfs_mount_t *mp, xfs_agnumber_t agno, + * of the tree and set up the cursor for the leaf level + * (note that the same code is duplicated further down) + */ +- compute_level_geometry(mp, lptr, num_extents, 2, true); ++ compute_level_geometry(mp, lptr, num_extents, 2, ++ mp->m_alloc_mxr[0], mp->m_alloc_mnr[0]); + level = 1; + + #ifdef XR_BLD_FREE_TRACE +@@ -429,7 +430,8 @@ calculate_freespace_cursor(xfs_mount_t *mp, xfs_agnumber_t agno, + lptr = &btree_curs->level[level]; + + compute_level_geometry(mp, lptr, +- p_lptr->num_blocks, 0, false); ++ p_lptr->num_blocks, 0, ++ mp->m_alloc_mxr[1], mp->m_alloc_mnr[1]); + #ifdef XR_BLD_FREE_TRACE + fprintf(stderr, "%s %d %d %d %d %d\n", __func__, + level, +@@ -509,7 +511,8 @@ calculate_freespace_cursor(xfs_mount_t *mp, xfs_agnumber_t agno, + * of the number of extents changing + */ + old_blocks = btree_curs->level[0].num_blocks; +- compute_level_geometry(mp, &btree_curs->level[0], num_extents, 2, true); ++ compute_level_geometry(mp, &btree_curs->level[0], num_extents, 2, ++ mp->m_alloc_mxr[0], mp->m_alloc_mnr[0]); + extra_blocks = 0; + + if (old_blocks != btree_curs->level[0].num_blocks) { +@@ -578,7 +581,8 @@ calculate_freespace_cursor(xfs_mount_t *mp, xfs_agnumber_t agno, + lptr = &btree_curs->level[level++]; + + compute_level_geometry(mp, lptr, +- p_lptr->num_blocks, 0, false); ++ p_lptr->num_blocks, 0, ++ mp->m_alloc_mxr[1], mp->m_alloc_mnr[1]); + } + ASSERT(level < XFS_BTREE_MAXLEVELS); + ASSERT(lptr->num_blocks == 1); +@@ -1399,7 +1403,8 @@ init_rmapbt_cursor( + * metadata AG entries without too many splits. + */ + compute_level_geometry(mp, lptr, num_recs, +- num_recs > mp->m_rmap_mxr[0] ? 10 : 0, true); ++ num_recs > mp->m_rmap_mxr[0] ? 10 : 0, ++ mp->m_rmap_mxr[0], mp->m_rmap_mnr[0]); + blocks_allocated = lptr->num_blocks; + level = 1; + +@@ -1408,7 +1413,8 @@ init_rmapbt_cursor( + lptr = &btree_curs->level[level++]; + + compute_level_geometry(mp, lptr, +- p_lptr->num_blocks, 0, false); ++ p_lptr->num_blocks, 0, ++ mp->m_rmap_mxr[1], mp->m_rmap_mnr[1]); + blocks_allocated += lptr->num_blocks; + } + ASSERT(level < XFS_BTREE_MAXLEVELS); + + diff --git a/SOURCES/xfsprogs-5.7.0-xfs_quota-allow-individual-timer-extension.patch b/SOURCES/xfsprogs-5.7.0-xfs_quota-allow-individual-timer-extension.patch new file mode 100644 index 0000000..92a2149 --- /dev/null +++ b/SOURCES/xfsprogs-5.7.0-xfs_quota-allow-individual-timer-extension.patch @@ -0,0 +1,227 @@ +From 36dc471cc9bb17868b79cf8dea8151b207387539 Mon Sep 17 00:00:00 2001 +From: Eric Sandeen +Date: Tue, 26 May 2020 14:36:26 -0400 +Subject: [PATCH] xfs_quota: allow individual timer extension + +The only grace period which can be set via xfs_quota today is for id 0, +i.e. the default grace period for all users. However, setting an +individual grace period is useful; for example: + + Alice has a soft quota of 100 inodes, and a hard quota of 200 inodes + Alice uses 150 inodes, and enters a short grace period + Alice really needs to use those 150 inodes past the grace period + The administrator extends Alice's grace period until next Monday + +vfs quota users such as ext4 can do this today, with setquota -T + +xfs_quota can now accept an optional user id or name (symmetric with +how warn limits are specified), in which case that user's grace period +is extended to expire the given amount of time from now(). + +To maintain compatibility with old command lines, if none of +[-d|id|name] are specified, default limits are set as before. + +(kernelspace requires updates to enable all this as well.) + +Signed-off-by: Eric Sandeen +Reviewed-by: Darrick J. Wong +Signed-off-by: Eric Sandeen +--- + man/man8/xfs_quota.8 | 36 +++++++++++++++++-- + quota/edit.c | 83 +++++++++++++++++++++++++++++++++++++------- + 2 files changed, 104 insertions(+), 15 deletions(-) + +Index: xfsprogs-5.0.0/man/man8/xfs_quota.8 +=================================================================== +--- xfsprogs-5.0.0.orig/man/man8/xfs_quota.8 ++++ xfsprogs-5.0.0/man/man8/xfs_quota.8 +@@ -460,14 +460,46 @@ must be specified. + .B \-bir + ] + .I value ++[ ++.B -d ++| ++.I id ++| ++.I name ++] + .br + Allows the quota enforcement timeout (i.e. the amount of time allowed + to pass before the soft limits are enforced as the hard limits) to + be modified. The current timeout setting can be displayed using the + .B state +-command. The value argument is a number of seconds, but units of +-\&'minutes', 'hours', 'days', and 'weeks' are also understood ++command. ++.br ++When setting the default timer via the ++.B \-d ++option, or for ++.B id ++0, or if no argument is given after ++.I value ++the ++.I value ++argument is a number of seconds indicating the relative amount of time after ++soft limits are exceeded, before hard limits are enforced. ++.br ++When setting any other individual timer by ++.I id ++or ++.I name, ++the ++.I value ++is the number of seconds from now, at which time the hard limits will be enforced. ++This allows extending the grace time of an individual user who has exceeded soft ++limits. ++.br ++For ++.I value, ++units of \&'minutes', 'hours', 'days', and 'weeks' are also understood + (as are their abbreviations 'm', 'h', 'd', and 'w'). ++.br + .HP + .B warn + [ +Index: xfsprogs-5.0.0/quota/edit.c +=================================================================== +--- xfsprogs-5.0.0.orig/quota/edit.c ++++ xfsprogs-5.0.0/quota/edit.c +@@ -419,6 +419,7 @@ restore_f( + + static void + set_timer( ++ uint32_t id, + uint type, + uint mask, + char *dev, +@@ -427,14 +428,43 @@ set_timer( + fs_disk_quota_t d; + + memset(&d, 0, sizeof(d)); ++ ++ /* ++ * If id is specified we are extending grace time by value ++ * Otherwise we are setting the default grace time ++ */ ++ if (id) { ++ time_t now; ++ ++ /* Get quota to find out whether user is past soft limits */ ++ if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0) { ++ exitcode = 1; ++ fprintf(stderr, _("%s: cannot get quota: %s\n"), ++ progname, strerror(errno)); ++ return; ++ } ++ ++ time(&now); ++ ++ /* Only set grace time if user is already past soft limit */ ++ if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit) ++ d.d_btimer = now + value; ++ if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit) ++ d.d_itimer = now + value; ++ if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit) ++ d.d_rtbtimer = now + value; ++ } else { ++ d.d_btimer = value; ++ d.d_itimer = value; ++ d.d_rtbtimer = value; ++ } ++ + d.d_version = FS_DQUOT_VERSION; + d.d_flags = type; + d.d_fieldmask = mask; +- d.d_itimer = value; +- d.d_btimer = value; +- d.d_rtbtimer = value; ++ d.d_id = id; + +- if (xfsquotactl(XFS_SETQLIM, dev, type, 0, (void *)&d) < 0) { ++ if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) { + exitcode = 1; + fprintf(stderr, _("%s: cannot set timer: %s\n"), + progname, strerror(errno)); +@@ -447,10 +477,15 @@ timer_f( + char **argv) + { + uint value; +- int c, type = 0, mask = 0; ++ char *name = NULL; ++ uint32_t id = 0; ++ int c, flags = 0, type = 0, mask = 0; + +- while ((c = getopt(argc, argv, "bgipru")) != EOF) { ++ while ((c = getopt(argc, argv, "bdgipru")) != EOF) { + switch (c) { ++ case 'd': ++ flags |= DEFAULTS_FLAG; ++ break; + case 'b': + mask |= FS_DQ_BTIMER; + break; +@@ -474,23 +509,45 @@ timer_f( + } + } + +- if (argc != optind + 1) ++ /* ++ * Older versions of the command did not accept -d|id|name, ++ * so in that case we assume we're setting default timer, ++ * and the last arg is the timer value. ++ * ++ * Otherwise, if the defaults flag is set, we expect 1 more arg for ++ * timer value ; if not, 2 more args: 1 for value, one for id/name. ++ */ ++ if (!(flags & DEFAULTS_FLAG) && (argc == optind + 1)) { ++ value = cvttime(argv[optind++]); ++ } else if (flags & DEFAULTS_FLAG) { ++ if (argc != optind + 1) ++ return command_usage(&timer_cmd); ++ value = cvttime(argv[optind++]); ++ } else if (argc == optind + 2) { ++ value = cvttime(argv[optind++]); ++ name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++]; ++ } else + return command_usage(&timer_cmd); + +- value = cvttime(argv[optind++]); + ++ /* if none of -bir specified, set them all */ + if (!mask) + mask = FS_DQ_TIMER_MASK; + + if (!type) { + type = XFS_USER_QUOTA; + } else if (type != XFS_GROUP_QUOTA && +- type != XFS_PROJ_QUOTA && +- type != XFS_USER_QUOTA) { ++ type != XFS_PROJ_QUOTA && ++ type != XFS_USER_QUOTA) { + return command_usage(&timer_cmd); + } + +- set_timer(type, mask, fs_path->fs_name, value); ++ if (name) ++ id = id_from_string(name, type); ++ ++ if (id >= 0) ++ set_timer(id, type, mask, fs_path->fs_name, value); ++ + return 0; + } + +@@ -616,9 +673,9 @@ edit_init(void) + + timer_cmd.name = "timer"; + timer_cmd.cfunc = timer_f; +- timer_cmd.argmin = 2; ++ timer_cmd.argmin = 1; + timer_cmd.argmax = -1; +- timer_cmd.args = _("[-bir] [-g|-p|-u] value"); ++ timer_cmd.args = _("[-bir] [-g|-p|-u] value [-d|id|name]"); + timer_cmd.oneline = _("set quota enforcement timeouts"); + timer_cmd.help = timer_help; + timer_cmd.flags = CMD_FLAG_FOREIGN_OK; diff --git a/SOURCES/xfsprogs-5.7.0-xfs_quota-fix-unsigned-int-id-comparisons.patch b/SOURCES/xfsprogs-5.7.0-xfs_quota-fix-unsigned-int-id-comparisons.patch new file mode 100644 index 0000000..33a8860 --- /dev/null +++ b/SOURCES/xfsprogs-5.7.0-xfs_quota-fix-unsigned-int-id-comparisons.patch @@ -0,0 +1,69 @@ +From eaa5b0b79bcf2eb36f7a5e1a5b7171ad5ced7bac Mon Sep 17 00:00:00 2001 +From: "Darrick J. Wong" +Date: Fri, 10 Jul 2020 15:33:36 -0400 +Subject: [PATCH] xfs_quota: fix unsigned int id comparisons + +Fix compiler warnings about unsigned int comparisons by replacing them +with an explicit check for the one possible invalid value (-1U). +id_from_string sets exitcode to nonzero when it sees this value, so the +call sites don't have to do that. + +Coverity-id: 1463855, 1463856, 1463857 +Fixes: 67a73d6139d0 ("xfs_quota: refactor code to generate id from name") +Fixes: 36dc471cc9bb ("xfs_quota: allow individual timer extension") +Signed-off-by: Darrick J. Wong +Reviewed-by: Christoph Hellwig +Signed-off-by: Eric Sandeen +--- + quota/edit.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +Index: xfsprogs-5.0.0/quota/edit.c +=================================================================== +--- xfsprogs-5.0.0.orig/quota/edit.c ++++ xfsprogs-5.0.0/quota/edit.c +@@ -307,11 +307,11 @@ limit_f( + + + id = id_from_string(name, type); +- if (id >= 0) +- set_limits(id, type, mask, fs_path->fs_name, +- &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); +- else +- exitcode = -1; ++ if (id == -1) ++ return 0; ++ ++ set_limits(id, type, mask, fs_path->fs_name, ++ &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); + return 0; + } + +@@ -545,9 +545,10 @@ timer_f( + if (name) + id = id_from_string(name, type); + +- if (id >= 0) +- set_timer(id, type, mask, fs_path->fs_name, value); ++ if (id == -1) ++ return 0; + ++ set_timer(id, type, mask, fs_path->fs_name, value); + return 0; + } + +@@ -642,11 +643,10 @@ warn_f( + } + + id = id_from_string(name, type); +- if (id >= 0) +- set_warnings(id, type, mask, fs_path->fs_name, value); +- else +- exitcode = -1; ++ if (id == -1) ++ return 0; + ++ set_warnings(id, type, mask, fs_path->fs_name, value); + return 0; + } + diff --git a/SOURCES/xfsprogs-5.7.0-xfs_quota-refactor-code-to-generate-id-from-name.patch b/SOURCES/xfsprogs-5.7.0-xfs_quota-refactor-code-to-generate-id-from-name.patch new file mode 100644 index 0000000..0223f44 --- /dev/null +++ b/SOURCES/xfsprogs-5.7.0-xfs_quota-refactor-code-to-generate-id-from-name.patch @@ -0,0 +1,271 @@ +From 67a73d6139d0336eb7ced05bd78a26b57f408187 Mon Sep 17 00:00:00 2001 +From: Eric Sandeen +Date: Tue, 26 May 2020 14:36:04 -0400 +Subject: [PATCH] xfs_quota: refactor code to generate id from name + +There's boilerplate for setting limits and warnings, where we have +a case statement for each of the 3 quota types, and from there call +3 different functions to configure each of the 3 types, each of which +calls its own version of id to string function... + +Refactor this so that the main function can call a generic id to string +conversion routine, and then call a common action. This save a lot of +LOC. + +I was looking at allowing xfs to bump out individual grace periods like +setquota can do, and this refactoring allows us to add new actions like +that without copying all the boilerplate again. + +Signed-off-by: Eric Sandeen +Reviewed-by: Christoph Hellwig +Signed-off-by: Eric Sandeen +--- + quota/edit.c | 194 +++++++++++++-------------------------------------- + 1 file changed, 49 insertions(+), 145 deletions(-) + +Index: xfsprogs-5.0.0/quota/edit.c +=================================================================== +--- xfsprogs-5.0.0.orig/quota/edit.c ++++ xfsprogs-5.0.0/quota/edit.c +@@ -101,6 +101,40 @@ warn_help(void) + "\n")); + } + ++static uint32_t ++id_from_string( ++ char *name, ++ int type) ++{ ++ uint32_t id = -1; ++ const char *type_name = "unknown type"; ++ ++ switch (type) { ++ case XFS_USER_QUOTA: ++ type_name = "user"; ++ id = uid_from_string(name); ++ break; ++ case XFS_GROUP_QUOTA: ++ type_name = "group"; ++ id = gid_from_string(name); ++ break; ++ case XFS_PROJ_QUOTA: ++ type_name = "project"; ++ id = prid_from_string(name); ++ break; ++ default: ++ ASSERT(0); ++ break; ++ } ++ ++ if (id == -1) { ++ fprintf(stderr, _("%s: invalid %s name: %s\n"), ++ type_name, progname, name); ++ exitcode = 1; ++ } ++ return id; ++} ++ + static void + set_limits( + uint32_t id, +@@ -135,75 +169,6 @@ set_limits( + } + } + +-static void +-set_user_limits( +- char *name, +- uint type, +- uint mask, +- uint64_t *bsoft, +- uint64_t *bhard, +- uint64_t *isoft, +- uint64_t *ihard, +- uint64_t *rtbsoft, +- uint64_t *rtbhard) +-{ +- uid_t uid = uid_from_string(name); +- +- if (uid == -1) { +- exitcode = 1; +- fprintf(stderr, _("%s: invalid user name: %s\n"), +- progname, name); +- } else +- set_limits(uid, type, mask, fs_path->fs_name, +- bsoft, bhard, isoft, ihard, rtbsoft, rtbhard); +-} +- +-static void +-set_group_limits( +- char *name, +- uint type, +- uint mask, +- uint64_t *bsoft, +- uint64_t *bhard, +- uint64_t *isoft, +- uint64_t *ihard, +- uint64_t *rtbsoft, +- uint64_t *rtbhard) +-{ +- gid_t gid = gid_from_string(name); +- +- if (gid == -1) { +- exitcode = 1; +- fprintf(stderr, _("%s: invalid group name: %s\n"), +- progname, name); +- } else +- set_limits(gid, type, mask, fs_path->fs_name, +- bsoft, bhard, isoft, ihard, rtbsoft, rtbhard); +-} +- +-static void +-set_project_limits( +- char *name, +- uint type, +- uint mask, +- uint64_t *bsoft, +- uint64_t *bhard, +- uint64_t *isoft, +- uint64_t *ihard, +- uint64_t *rtbsoft, +- uint64_t *rtbhard) +-{ +- prid_t prid = prid_from_string(name); +- +- if (prid == -1) { +- exitcode = 1; +- fprintf(stderr, _("%s: invalid project name: %s\n"), +- progname, name); +- } else +- set_limits(prid, type, mask, fs_path->fs_name, +- bsoft, bhard, isoft, ihard, rtbsoft, rtbhard); +-} +- + /* extract number of blocks from an ascii string */ + static int + extractb( +@@ -258,6 +223,7 @@ limit_f( + char **argv) + { + char *name; ++ uint32_t id; + uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard; + int c, type = 0, mask = 0, flags = 0; + uint bsize, ssize, endoptions; +@@ -339,20 +305,13 @@ limit_f( + return command_usage(&limit_cmd); + } + +- switch (type) { +- case XFS_USER_QUOTA: +- set_user_limits(name, type, mask, +- &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); +- break; +- case XFS_GROUP_QUOTA: +- set_group_limits(name, type, mask, +- &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); +- break; +- case XFS_PROJ_QUOTA: +- set_project_limits(name, type, mask, +- &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); +- break; +- } ++ ++ id = id_from_string(name, type); ++ if (id >= 0) ++ set_limits(id, type, mask, fs_path->fs_name, ++ &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); ++ else ++ exitcode = -1; + return 0; + } + +@@ -561,63 +520,13 @@ set_warnings( + } + } + +-static void +-set_user_warnings( +- char *name, +- uint type, +- uint mask, +- uint value) +-{ +- uid_t uid = uid_from_string(name); +- +- if (uid == -1) { +- exitcode = 1; +- fprintf(stderr, _("%s: invalid user name: %s\n"), +- progname, name); +- } else +- set_warnings(uid, type, mask, fs_path->fs_name, value); +-} +- +-static void +-set_group_warnings( +- char *name, +- uint type, +- uint mask, +- uint value) +-{ +- gid_t gid = gid_from_string(name); +- +- if (gid == -1) { +- exitcode = 1; +- fprintf(stderr, _("%s: invalid group name: %s\n"), +- progname, name); +- } else +- set_warnings(gid, type, mask, fs_path->fs_name, value); +-} +- +-static void +-set_project_warnings( +- char *name, +- uint type, +- uint mask, +- uint value) +-{ +- prid_t prid = prid_from_string(name); +- +- if (prid == -1) { +- exitcode = 1; +- fprintf(stderr, _("%s: invalid project name: %s\n"), +- progname, name); +- } else +- set_warnings(prid, type, mask, fs_path->fs_name, value); +-} +- + static int + warn_f( + int argc, + char **argv) + { + char *name; ++ uint32_t id; + uint value; + int c, flags = 0, type = 0, mask = 0; + +@@ -675,17 +584,12 @@ warn_f( + return command_usage(&warn_cmd); + } + +- switch (type) { +- case XFS_USER_QUOTA: +- set_user_warnings(name, type, mask, value); +- break; +- case XFS_GROUP_QUOTA: +- set_group_warnings(name, type, mask, value); +- break; +- case XFS_PROJ_QUOTA: +- set_project_warnings(name, type, mask, value); +- break; +- } ++ id = id_from_string(name, type); ++ if (id >= 0) ++ set_warnings(id, type, mask, fs_path->fs_name, value); ++ else ++ exitcode = -1; ++ + return 0; + } + diff --git a/SOURCES/xfsprogs-5.7.0-xfs_repair-check-for-AG-btree-records-that-would-wra.patch b/SOURCES/xfsprogs-5.7.0-xfs_repair-check-for-AG-btree-records-that-would-wra.patch new file mode 100644 index 0000000..a0afd5c --- /dev/null +++ b/SOURCES/xfsprogs-5.7.0-xfs_repair-check-for-AG-btree-records-that-would-wra.patch @@ -0,0 +1,54 @@ +From cae4fd291266c32441c6a7fcca49929fe11c391c Mon Sep 17 00:00:00 2001 +From: "Darrick J. Wong" +Date: Fri, 10 Jul 2020 15:35:44 -0400 +Subject: [PATCH] xfs_repair: check for AG btree records that would wrap around + +For AG btree types, make sure that each record's length is not so huge +that integer wraparound would happen. + +Found via xfs/358 fuzzing recs[1].blockcount = ones. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Christoph Hellwig +Signed-off-by: Eric Sandeen +--- + repair/scan.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/repair/scan.c b/repair/scan.c +index 5c8d8b23..1ddb5763 100644 +--- a/repair/scan.c ++++ b/repair/scan.c +@@ -684,7 +684,8 @@ _("%s freespace btree block claimed (state %d), agno %d, bno %d, suspect %d\n"), + b, i, name, agno, bno); + continue; + } +- if (len == 0 || !verify_agbno(mp, agno, end - 1)) { ++ if (len == 0 || end <= b || ++ !verify_agbno(mp, agno, end - 1)) { + do_warn( + _("invalid length %u in record %u of %s btree block %u/%u\n"), + len, i, name, agno, bno); +@@ -1066,7 +1067,8 @@ _("%s rmap btree block claimed (state %d), agno %d, bno %d, suspect %d\n"), + b, i, name, agno, bno); + continue; + } +- if (len == 0 || !verify_agbno(mp, agno, end - 1)) { ++ if (len == 0 || end <= b || ++ !verify_agbno(mp, agno, end - 1)) { + do_warn( + _("invalid length %u in record %u of %s btree block %u/%u\n"), + len, i, name, agno, bno); +@@ -1353,7 +1355,8 @@ _("leftover CoW extent has invalid startblock in record %u of %s btree block %u/ + b, i, name, agno, bno); + continue; + } +- if (len == 0 || !verify_agbno(mp, agno, end - 1)) { ++ if (len == 0 || end <= agb || ++ !verify_agbno(mp, agno, end - 1)) { + do_warn( + _("invalid length %u in record %u of %s btree block %u/%u\n"), + len, i, name, agno, bno); +-- +2.29.2 + diff --git a/SOURCES/xfsprogs-5.7.0-xfs_repair-complain-about-bad-interior-btree-pointer.patch b/SOURCES/xfsprogs-5.7.0-xfs_repair-complain-about-bad-interior-btree-pointer.patch new file mode 100644 index 0000000..62255ce --- /dev/null +++ b/SOURCES/xfsprogs-5.7.0-xfs_repair-complain-about-bad-interior-btree-pointer.patch @@ -0,0 +1,124 @@ +From 320cc3b263542e692c4978fb327efa591892ab37 Mon Sep 17 00:00:00 2001 +From: "Darrick J. Wong" +Date: Fri, 10 Jul 2020 15:35:45 -0400 +Subject: [PATCH] xfs_repair: complain about bad interior btree pointers + +Actually complain about garbage btree node pointers, don't just silently +ignore them. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Christoph Hellwig +Signed-off-by: Eric Sandeen +--- + libxfs/libxfs_api_defs.h | 1 + + repair/scan.c | 55 +++++++++++++++++++++++++++++----------- + 2 files changed, 41 insertions(+), 15 deletions(-) + +Index: xfsprogs-5.0.0/libxfs/libxfs_api_defs.h +=================================================================== +--- xfsprogs-5.0.0.orig/libxfs/libxfs_api_defs.h ++++ xfsprogs-5.0.0/libxfs/libxfs_api_defs.h +@@ -118,6 +118,7 @@ + #define xfs_symlink_blocks libxfs_symlink_blocks + #define xfs_symlink_hdr_ok libxfs_symlink_hdr_ok + ++#define xfs_verify_agbno libxfs_verify_agbno + #define xfs_verify_cksum libxfs_verify_cksum + #define xfs_dinode_verify libxfs_dinode_verify + +Index: xfsprogs-5.0.0/repair/scan.c +=================================================================== +--- xfsprogs-5.0.0.orig/repair/scan.c ++++ xfsprogs-5.0.0/repair/scan.c +@@ -743,6 +743,14 @@ _("%s freespace btree block claimed (sta + for (i = 0; i < numrecs; i++) { + xfs_agblock_t agbno = be32_to_cpu(pp[i]); + ++ if (!libxfs_verify_agbno(mp, agno, agbno)) { ++ do_warn( ++ _("bad btree pointer (%u) in %sbt block %u/%u\n"), ++ agbno, name, agno, bno); ++ suspect++; ++ return; ++ } ++ + /* + * XXX - put sibling detection right here. + * we know our sibling chain is good. So as we go, +@@ -752,10 +760,8 @@ _("%s freespace btree block claimed (sta + * pointer mismatch, try and extract as much data + * as possible. + */ +- if (agbno != 0 && verify_agbno(mp, agno, agbno)) { +- scan_sbtree(agbno, level, agno, suspect, scan_allocbt, +- 0, magic, priv, ops); +- } ++ scan_sbtree(agbno, level, agno, suspect, scan_allocbt, 0, ++ magic, priv, ops); + } + } + +@@ -1196,10 +1202,16 @@ advance: + continue; + } + +- if (agbno != 0 && verify_agbno(mp, agno, agbno)) { +- scan_sbtree(agbno, level, agno, suspect, scan_rmapbt, 0, +- magic, priv, ops); ++ if (!libxfs_verify_agbno(mp, agno, agbno)) { ++ do_warn( ++ _("bad btree pointer (%u) in %sbt block %u/%u\n"), ++ agbno, name, agno, bno); ++ suspect++; ++ return; + } ++ ++ scan_sbtree(agbno, level, agno, suspect, scan_rmapbt, 0, magic, ++ priv, ops); + } + + out: +@@ -1416,10 +1428,16 @@ _("extent (%u/%u) len %u claimed, state + for (i = 0; i < numrecs; i++) { + xfs_agblock_t agbno = be32_to_cpu(pp[i]); + +- if (agbno != 0 && verify_agbno(mp, agno, agbno)) { +- scan_sbtree(agbno, level, agno, suspect, scan_refcbt, 0, +- magic, priv, ops); ++ if (!libxfs_verify_agbno(mp, agno, agbno)) { ++ do_warn( ++ _("bad btree pointer (%u) in %sbt block %u/%u\n"), ++ agbno, name, agno, bno); ++ suspect++; ++ return; + } ++ ++ scan_sbtree(agbno, level, agno, suspect, scan_refcbt, 0, magic, ++ priv, ops); + } + out: + if (suspect) +@@ -2083,11 +2101,18 @@ _("inode btree block claimed (state %d), + } + + for (i = 0; i < numrecs; i++) { +- if (be32_to_cpu(pp[i]) != 0 && verify_agbno(mp, agno, +- be32_to_cpu(pp[i]))) +- scan_sbtree(be32_to_cpu(pp[i]), level, agno, +- suspect, scan_inobt, 0, magic, priv, +- ops); ++ xfs_agblock_t agbno = be32_to_cpu(pp[i]); ++ ++ if (!libxfs_verify_agbno(mp, agno, agbno)) { ++ do_warn( ++ _("bad btree pointer (%u) in %sbt block %u/%u\n"), ++ agbno, name, agno, bno); ++ suspect++; ++ return; ++ } ++ ++ scan_sbtree(be32_to_cpu(pp[i]), level, agno, suspect, ++ scan_inobt, 0, magic, priv, ops); + } + } + diff --git a/SOURCES/xfsprogs-5.7.0-xfs_repair-convert-to-libxfs_verify_agbno.patch b/SOURCES/xfsprogs-5.7.0-xfs_repair-convert-to-libxfs_verify_agbno.patch new file mode 100644 index 0000000..1787328 --- /dev/null +++ b/SOURCES/xfsprogs-5.7.0-xfs_repair-convert-to-libxfs_verify_agbno.patch @@ -0,0 +1,209 @@ +From dcd6c2e1490ba5c59c14ca8ea843ca36048888b8 Mon Sep 17 00:00:00 2001 +From: "Darrick J. Wong" +Date: Fri, 10 Jul 2020 15:35:45 -0400 +Subject: [PATCH] xfs_repair: convert to libxfs_verify_agbno + +Convert the homegrown verify_agbno callers to use the libxfs function, +as needed. In some places we drop the "bno != 0" checks because those +conditionals are checking btree roots; btree roots should never be +zero if the corresponding feature bit is set; and repair skips the if +clause entirely if the feature bit is disabled. + +In effect, this strengthens repair to validate that AG btree pointers +neither point to the AG headers nor past the end of the AG. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Christoph Hellwig +Signed-off-by: Eric Sandeen +--- + libxfs/libxfs_api_defs.h | 1 + + repair/dinode.c | 11 ----------- + repair/dinode.h | 5 ----- + repair/scan.c | 36 +++++++++++++++++++++++------------- + 4 files changed, 24 insertions(+), 29 deletions(-) + +Index: xfsprogs-5.0.0/libxfs/libxfs_api_defs.h +=================================================================== +--- xfsprogs-5.0.0.orig/libxfs/libxfs_api_defs.h ++++ xfsprogs-5.0.0/libxfs/libxfs_api_defs.h +@@ -121,6 +121,7 @@ + #define xfs_verify_agbno libxfs_verify_agbno + #define xfs_verify_cksum libxfs_verify_cksum + #define xfs_dinode_verify libxfs_dinode_verify ++#define xfs_ag_block_count libxfs_ag_block_count + + #define xfs_alloc_ag_max_usable libxfs_alloc_ag_max_usable + #define xfs_allocbt_maxrecs libxfs_allocbt_maxrecs +Index: xfsprogs-5.0.0/repair/dinode.c +=================================================================== +--- xfsprogs-5.0.0.orig/repair/dinode.c ++++ xfsprogs-5.0.0/repair/dinode.c +@@ -284,17 +284,6 @@ verify_dfsbno_range(xfs_mount_t *mp, + return (XR_DFSBNORANGE_VALID); + } + +-int +-verify_agbno(xfs_mount_t *mp, +- xfs_agnumber_t agno, +- xfs_agblock_t agbno) +-{ +- xfs_sb_t *sbp = &mp->m_sb;; +- +- /* range check ag #, ag block. range-checking offset is pointless */ +- return verify_ag_bno(sbp, agno, agbno) == 0; +-} +- + static int + process_rt_rec( + xfs_mount_t *mp, +Index: xfsprogs-5.0.0/repair/dinode.h +=================================================================== +--- xfsprogs-5.0.0.orig/repair/dinode.h ++++ xfsprogs-5.0.0/repair/dinode.h +@@ -10,11 +10,6 @@ struct blkmap; + struct prefetch_args; + + int +-verify_agbno(xfs_mount_t *mp, +- xfs_agnumber_t agno, +- xfs_agblock_t agbno); +- +-int + verify_dfsbno(xfs_mount_t *mp, + xfs_fsblock_t fsbno); + +Index: xfsprogs-5.0.0/repair/scan.c +=================================================================== +--- xfsprogs-5.0.0.orig/repair/scan.c ++++ xfsprogs-5.0.0/repair/scan.c +@@ -642,14 +642,14 @@ _("%s freespace btree block claimed (sta + len = be32_to_cpu(rp[i].ar_blockcount); + end = b + len; + +- if (b == 0 || !verify_agbno(mp, agno, b)) { ++ if (!libxfs_verify_agbno(mp, agno, b)) { + do_warn( + _("invalid start block %u in record %u of %s btree block %u/%u\n"), + b, i, name, agno, bno); + continue; + } + if (len == 0 || end <= b || +- !verify_agbno(mp, agno, end - 1)) { ++ !libxfs_verify_agbno(mp, agno, end - 1)) { + do_warn( + _("invalid length %u in record %u of %s btree block %u/%u\n"), + len, i, name, agno, bno); +@@ -914,6 +914,16 @@ rmap_in_order( + return offset > lastoffset; + } + ++static inline bool ++verify_rmap_agbno( ++ struct xfs_mount *mp, ++ xfs_agnumber_t agno, ++ xfs_agblock_t agbno) ++{ ++ return agbno < libxfs_ag_block_count(mp, agno); ++} ++ ++ + static void + scan_rmapbt( + struct xfs_btree_block *block, +@@ -1031,14 +1041,14 @@ _("%s rmap btree block claimed (state %d + end = key.rm_startblock + key.rm_blockcount; + + /* Make sure agbno & len make sense. */ +- if (!verify_agbno(mp, agno, b)) { ++ if (!verify_rmap_agbno(mp, agno, b)) { + do_warn( + _("invalid start block %u in record %u of %s btree block %u/%u\n"), + b, i, name, agno, bno); + continue; + } + if (len == 0 || end <= b || +- !verify_agbno(mp, agno, end - 1)) { ++ !verify_rmap_agbno(mp, agno, end - 1)) { + do_warn( + _("invalid length %u in record %u of %s btree block %u/%u\n"), + len, i, name, agno, bno); +@@ -1325,14 +1335,14 @@ _("leftover CoW extent has invalid start + } + end = agb + len; + +- if (!verify_agbno(mp, agno, agb)) { ++ if (!libxfs_verify_agbno(mp, agno, agb)) { + do_warn( + _("invalid start block %u in record %u of %s btree block %u/%u\n"), + b, i, name, agno, bno); + continue; + } + if (len == 0 || end <= agb || +- !verify_agbno(mp, agno, end - 1)) { ++ !libxfs_verify_agbno(mp, agno, end - 1)) { + do_warn( + _("invalid length %u in record %u of %s btree block %u/%u\n"), + len, i, name, agno, bno); +@@ -2145,7 +2155,7 @@ scan_agfl( + { + struct agfl_state *as = priv; + +- if (verify_agbno(mp, as->agno, bno)) ++ if (libxfs_verify_agbno(mp, as->agno, bno)) + set_bmap(as->agno, bno, XR_E_FREE); + else + do_warn(_("bad agbno %u in agfl, agno %d\n"), +@@ -2217,7 +2227,7 @@ validate_agf( + uint32_t magic; + + bno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]); +- if (bno != 0 && verify_agbno(mp, agno, bno)) { ++ if (libxfs_verify_agbno(mp, agno, bno)) { + magic = xfs_sb_version_hascrc(&mp->m_sb) ? XFS_ABTB_CRC_MAGIC + : XFS_ABTB_MAGIC; + scan_sbtree(bno, be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]), +@@ -2229,7 +2239,7 @@ validate_agf( + } + + bno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]); +- if (bno != 0 && verify_agbno(mp, agno, bno)) { ++ if (libxfs_verify_agbno(mp, agno, bno)) { + magic = xfs_sb_version_hascrc(&mp->m_sb) ? XFS_ABTC_CRC_MAGIC + : XFS_ABTC_MAGIC; + scan_sbtree(bno, be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]), +@@ -2249,7 +2259,7 @@ validate_agf( + priv.last_rec.rm_owner = XFS_RMAP_OWN_UNKNOWN; + priv.nr_blocks = 0; + bno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_RMAP]); +- if (bno != 0 && verify_agbno(mp, agno, bno)) { ++ if (libxfs_verify_agbno(mp, agno, bno)) { + scan_sbtree(bno, + be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]), + agno, 0, scan_rmapbt, 1, XFS_RMAP_CRC_MAGIC, +@@ -2267,7 +2277,7 @@ validate_agf( + + if (xfs_sb_version_hasreflink(&mp->m_sb)) { + bno = be32_to_cpu(agf->agf_refcount_root); +- if (bno != 0 && verify_agbno(mp, agno, bno)) { ++ if (libxfs_verify_agbno(mp, agno, bno)) { + struct refc_priv priv; + + memset(&priv, 0, sizeof(priv)); +@@ -2315,7 +2325,7 @@ validate_agi( + uint32_t magic; + + bno = be32_to_cpu(agi->agi_root); +- if (bno != 0 && verify_agbno(mp, agno, bno)) { ++ if (libxfs_verify_agbno(mp, agno, bno)) { + magic = xfs_sb_version_hascrc(&mp->m_sb) ? XFS_IBT_CRC_MAGIC + : XFS_IBT_MAGIC; + scan_sbtree(bno, be32_to_cpu(agi->agi_level), +@@ -2328,7 +2338,7 @@ validate_agi( + + if (xfs_sb_version_hasfinobt(&mp->m_sb)) { + bno = be32_to_cpu(agi->agi_free_root); +- if (bno != 0 && verify_agbno(mp, agno, bno)) { ++ if (libxfs_verify_agbno(mp, agno, bno)) { + magic = xfs_sb_version_hascrc(&mp->m_sb) ? + XFS_FIBT_CRC_MAGIC : XFS_FIBT_MAGIC; + scan_sbtree(bno, be32_to_cpu(agi->agi_free_level), diff --git a/SOURCES/xfsprogs-5.7.0-xfs_repair-fix-rebuilding-btree-block-less-than-minr.patch b/SOURCES/xfsprogs-5.7.0-xfs_repair-fix-rebuilding-btree-block-less-than-minr.patch new file mode 100644 index 0000000..9192518 --- /dev/null +++ b/SOURCES/xfsprogs-5.7.0-xfs_repair-fix-rebuilding-btree-block-less-than-minr.patch @@ -0,0 +1,290 @@ +From 6df28d12d7760701c9d11e659e374665c5ffd0b9 Mon Sep 17 00:00:00 2001 +From: Gao Xiang +Date: Fri, 10 Jul 2020 15:32:36 -0400 +Subject: [PATCH] xfs_repair: fix rebuilding btree block less than minrecs + +In production, we found that sometimes xfs_repair phase 5 +rebuilds freespace node block with pointers less than minrecs +and if we trigger xfs_repair again it would report such +the following message: + +bad btree nrecs (39, min=40, max=80) in btbno block 0/7882 + +The background is that xfs_repair starts to rebuild AGFL +after the freespace btree is settled in phase 5 so we may +need to leave necessary room in advance for each btree +leaves in order to avoid freespace btree split and then +result in AGFL rebuild fails. The old mathematics uses +ceil(num_extents / maxrecs) to decide the number of node +blocks. That would be fine without leaving extra space +since minrecs = maxrecs / 2 but if some slack was decreased +from maxrecs, the result would be larger than what is +expected and cause num_recs_pb less than minrecs, i.e: + +num_extents = 79, adj_maxrecs = 80 - 2 (slack) = 78 + +so we'd get + +num_blocks = ceil(79 / 78) = 2, +num_recs_pb = 79 / 2 = 39, which is less than +minrecs = 80 / 2 = 40 + +OTOH, btree bulk loading code behaves in a different way. +As in xfs_btree_bload_level_geometry it wrote + +num_blocks = floor(num_extents / maxrecs) + +which will never go below minrecs. And when it goes above +maxrecs, just increment num_blocks and recalculate so we +can get the reasonable results. + +Later, btree bulk loader will replace the current repair code. +But we may still want to look for a backportable solution +for stable versions. Hence, keep the same logic to avoid +the freespace as well as rmap btree minrecs underflow for now. + +Cc: "Darrick J. Wong" +Cc: Dave Chinner +Cc: Eric Sandeen +Fixes: 9851fd79bfb1 ("repair: AGFL rebuild fails if btree split required") +Signed-off-by: Gao Xiang +Reviewed-by: Darrick J. Wong +Signed-off-by: Eric Sandeen +--- + repair/phase5.c | 152 ++++++++++++++++++++---------------------------- + 1 file changed, 63 insertions(+), 89 deletions(-) + +Index: xfsprogs-5.0.0/repair/phase5.c +=================================================================== +--- xfsprogs-5.0.0.orig/repair/phase5.c ++++ xfsprogs-5.0.0/repair/phase5.c +@@ -346,11 +346,32 @@ finish_cursor(bt_status_t *curs) + * failure at runtime. Hence leave a couple of records slack space in + * each block to allow immediate modification of the tree without + * requiring splits to be done. +- * +- * XXX(hch): any reason we don't just look at mp->m_alloc_mxr? + */ +-#define XR_ALLOC_BLOCK_MAXRECS(mp, level) \ +- (libxfs_allocbt_maxrecs((mp), (mp)->m_sb.sb_blocksize, (level) == 0) - 2) ++static void ++compute_level_geometry( ++ struct xfs_mount *mp, ++ struct bt_stat_level *lptr, ++ uint64_t nr_this_level, ++ int slack, ++ bool leaf) ++{ ++ unsigned int maxrecs = mp->m_alloc_mxr[!leaf]; ++ unsigned int desired_npb; ++ ++ desired_npb = max(mp->m_alloc_mnr[!leaf], maxrecs - slack); ++ lptr->num_recs_tot = nr_this_level; ++ lptr->num_blocks = max(1ULL, nr_this_level / desired_npb); ++ ++ lptr->num_recs_pb = nr_this_level / lptr->num_blocks; ++ lptr->modulo = nr_this_level % lptr->num_blocks; ++ if (lptr->num_recs_pb > maxrecs || ++ (lptr->num_recs_pb == maxrecs && lptr->modulo)) { ++ lptr->num_blocks++; ++ ++ lptr->num_recs_pb = nr_this_level / lptr->num_blocks; ++ lptr->modulo = nr_this_level % lptr->num_blocks; ++ } ++} + + /* + * this calculates a freespace cursor for an ag. +@@ -368,6 +389,7 @@ calculate_freespace_cursor(xfs_mount_t * + int i; + int extents_used; + int extra_blocks; ++ uint64_t old_blocks; + bt_stat_level_t *lptr; + bt_stat_level_t *p_lptr; + extent_tree_node_t *ext_ptr; +@@ -386,10 +408,7 @@ calculate_freespace_cursor(xfs_mount_t * + * of the tree and set up the cursor for the leaf level + * (note that the same code is duplicated further down) + */ +- lptr->num_blocks = howmany(num_extents, XR_ALLOC_BLOCK_MAXRECS(mp, 0)); +- lptr->num_recs_pb = num_extents / lptr->num_blocks; +- lptr->modulo = num_extents % lptr->num_blocks; +- lptr->num_recs_tot = num_extents; ++ compute_level_geometry(mp, lptr, num_extents, 2, true); + level = 1; + + #ifdef XR_BLD_FREE_TRACE +@@ -403,30 +422,23 @@ calculate_freespace_cursor(xfs_mount_t * + * if we need more levels, set them up. # of records + * per level is the # of blocks in the level below it + */ +- if (lptr->num_blocks > 1) { +- for (; btree_curs->level[level - 1].num_blocks > 1 +- && level < XFS_BTREE_MAXLEVELS; +- level++) { +- lptr = &btree_curs->level[level]; +- p_lptr = &btree_curs->level[level - 1]; +- lptr->num_blocks = howmany(p_lptr->num_blocks, +- XR_ALLOC_BLOCK_MAXRECS(mp, level)); +- lptr->modulo = p_lptr->num_blocks +- % lptr->num_blocks; +- lptr->num_recs_pb = p_lptr->num_blocks +- / lptr->num_blocks; +- lptr->num_recs_tot = p_lptr->num_blocks; ++ while (lptr->num_blocks > 1) { ++ p_lptr = lptr; ++ lptr = &btree_curs->level[level]; ++ ++ compute_level_geometry(mp, lptr, ++ p_lptr->num_blocks, 0, false); + #ifdef XR_BLD_FREE_TRACE +- fprintf(stderr, "%s %d %d %d %d %d\n", __func__, +- level, +- lptr->num_blocks, +- lptr->num_recs_pb, +- lptr->modulo, +- lptr->num_recs_tot); ++ fprintf(stderr, "%s %d %d %d %d %d\n", __func__, ++ level, ++ lptr->num_blocks, ++ lptr->num_recs_pb, ++ lptr->modulo, ++ lptr->num_recs_tot); + #endif +- } ++ level++; + } +- ++ ASSERT(level < XFS_BTREE_MAXLEVELS); + ASSERT(lptr->num_blocks == 1); + btree_curs->num_levels = level; + +@@ -494,8 +506,11 @@ calculate_freespace_cursor(xfs_mount_t * + * see if the number of leaf blocks will change as a result + * of the number of extents changing + */ +- if (howmany(num_extents, XR_ALLOC_BLOCK_MAXRECS(mp, 0)) +- != btree_curs->level[0].num_blocks) { ++ old_blocks = btree_curs->level[0].num_blocks; ++ compute_level_geometry(mp, &btree_curs->level[0], num_extents, 2, true); ++ extra_blocks = 0; ++ ++ if (old_blocks != btree_curs->level[0].num_blocks) { + /* + * yes -- recalculate the cursor. If the number of + * excess (overallocated) blocks is < xfs_agfl_size/2, we're ok. +@@ -551,31 +566,19 @@ calculate_freespace_cursor(xfs_mount_t * + } + + lptr = &btree_curs->level[0]; +- lptr->num_blocks = howmany(num_extents, +- XR_ALLOC_BLOCK_MAXRECS(mp, 0)); +- lptr->num_recs_pb = num_extents / lptr->num_blocks; +- lptr->modulo = num_extents % lptr->num_blocks; +- lptr->num_recs_tot = num_extents; + level = 1; + + /* + * if we need more levels, set them up + */ +- if (lptr->num_blocks > 1) { +- for (level = 1; btree_curs->level[level-1].num_blocks +- > 1 && level < XFS_BTREE_MAXLEVELS; +- level++) { +- lptr = &btree_curs->level[level]; +- p_lptr = &btree_curs->level[level-1]; +- lptr->num_blocks = howmany(p_lptr->num_blocks, +- XR_ALLOC_BLOCK_MAXRECS(mp, level)); +- lptr->modulo = p_lptr->num_blocks +- % lptr->num_blocks; +- lptr->num_recs_pb = p_lptr->num_blocks +- / lptr->num_blocks; +- lptr->num_recs_tot = p_lptr->num_blocks; +- } ++ while (lptr->num_blocks > 1) { ++ p_lptr = lptr; ++ lptr = &btree_curs->level[level++]; ++ ++ compute_level_geometry(mp, lptr, ++ p_lptr->num_blocks, 0, false); + } ++ ASSERT(level < XFS_BTREE_MAXLEVELS); + ASSERT(lptr->num_blocks == 1); + btree_curs->num_levels = level; + +@@ -589,22 +592,6 @@ calculate_freespace_cursor(xfs_mount_t * + + ASSERT(blocks_allocated_total >= blocks_needed); + extra_blocks = blocks_allocated_total - blocks_needed; +- } else { +- if (extents_used > 0) { +- /* +- * reset the leaf level geometry to account +- * for consumed extents. we can leave the +- * rest of the cursor alone since the number +- * of leaf blocks hasn't changed. +- */ +- lptr = &btree_curs->level[0]; +- +- lptr->num_recs_pb = num_extents / lptr->num_blocks; +- lptr->modulo = num_extents % lptr->num_blocks; +- lptr->num_recs_tot = num_extents; +- } +- +- extra_blocks = 0; + } + + btree_curs->num_tot_blocks = blocks_allocated_pt; +@@ -1335,7 +1322,6 @@ init_rmapbt_cursor( + struct bt_stat_level *lptr; + struct bt_stat_level *p_lptr; + xfs_extlen_t blocks_allocated; +- int maxrecs; + + if (!xfs_sb_version_hasrmapbt(&mp->m_sb)) { + memset(btree_curs, 0, sizeof(struct bt_status)); +@@ -1371,32 +1357,20 @@ init_rmapbt_cursor( + * Leave enough slack in the rmapbt that we can insert the + * metadata AG entries without too many splits. + */ +- maxrecs = mp->m_rmap_mxr[0]; +- if (num_recs > maxrecs) +- maxrecs -= 10; +- blocks_allocated = lptr->num_blocks = howmany(num_recs, maxrecs); +- +- lptr->modulo = num_recs % lptr->num_blocks; +- lptr->num_recs_pb = num_recs / lptr->num_blocks; +- lptr->num_recs_tot = num_recs; ++ compute_level_geometry(mp, lptr, num_recs, ++ num_recs > mp->m_rmap_mxr[0] ? 10 : 0, true); ++ blocks_allocated = lptr->num_blocks; + level = 1; + +- if (lptr->num_blocks > 1) { +- for (; btree_curs->level[level-1].num_blocks > 1 +- && level < XFS_BTREE_MAXLEVELS; +- level++) { +- lptr = &btree_curs->level[level]; +- p_lptr = &btree_curs->level[level - 1]; +- lptr->num_blocks = howmany(p_lptr->num_blocks, +- mp->m_rmap_mxr[1]); +- lptr->modulo = p_lptr->num_blocks % lptr->num_blocks; +- lptr->num_recs_pb = p_lptr->num_blocks +- / lptr->num_blocks; +- lptr->num_recs_tot = p_lptr->num_blocks; +- +- blocks_allocated += lptr->num_blocks; +- } ++ while (lptr->num_blocks > 1) { ++ p_lptr = lptr; ++ lptr = &btree_curs->level[level++]; ++ ++ compute_level_geometry(mp, lptr, ++ p_lptr->num_blocks, 0, false); ++ blocks_allocated += lptr->num_blocks; + } ++ ASSERT(level < XFS_BTREE_MAXLEVELS); + ASSERT(lptr->num_blocks == 1); + btree_curs->num_levels = level; + diff --git a/SOURCES/xfsprogs-5.7.0-xfs_repair-tag-inobt-vs-finobt-errors-properly.patch b/SOURCES/xfsprogs-5.7.0-xfs_repair-tag-inobt-vs-finobt-errors-properly.patch new file mode 100644 index 0000000..82a1e3b --- /dev/null +++ b/SOURCES/xfsprogs-5.7.0-xfs_repair-tag-inobt-vs-finobt-errors-properly.patch @@ -0,0 +1,88 @@ +From 08280b4b6efd317c673c6718a27d77e702d0480d Mon Sep 17 00:00:00 2001 +From: "Darrick J. Wong" +Date: Fri, 10 Jul 2020 15:35:45 -0400 +Subject: [PATCH] xfs_repair: tag inobt vs finobt errors properly + +Amend the generic inode btree block scanner function to tag correctly +which tree it's complaining about. Previously, dubious finobt headers +would be attributed to the "inode btree", which is at best ambiguous +and misleading at worst. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Christoph Hellwig +Signed-off-by: Eric Sandeen +--- + repair/scan.c | 36 ++++++++++++++++++++++++++---------- + 1 file changed, 26 insertions(+), 10 deletions(-) + +Index: xfsprogs-5.0.0/repair/scan.c +=================================================================== +--- xfsprogs-5.0.0.orig/repair/scan.c ++++ xfsprogs-5.0.0/repair/scan.c +@@ -1934,6 +1934,7 @@ scan_inobt( + const struct xfs_buf_ops *ops) + { + struct aghdr_cnts *agcnts = priv; ++ char *name; + int i; + int numrecs; + int state; +@@ -1944,17 +1945,32 @@ scan_inobt( + + hdr_errors = 0; + ++ switch (magic) { ++ case XFS_FIBT_MAGIC: ++ case XFS_FIBT_CRC_MAGIC: ++ name = "fino"; ++ break; ++ case XFS_IBT_MAGIC: ++ case XFS_IBT_CRC_MAGIC: ++ name = "ino"; ++ break; ++ default: ++ name = "(unknown)"; ++ assert(0); ++ break; ++ } ++ + if (be32_to_cpu(block->bb_magic) != magic) { +- do_warn(_("bad magic # %#x in inobt block %d/%d\n"), +- be32_to_cpu(block->bb_magic), agno, bno); ++ do_warn(_("bad magic # %#x in %sbt block %d/%d\n"), ++ be32_to_cpu(block->bb_magic), name, agno, bno); + hdr_errors++; + bad_ino_btree = 1; + if (suspect) + return; + } + if (be16_to_cpu(block->bb_level) != level) { +- do_warn(_("expected level %d got %d in inobt block %d/%d\n"), +- level, be16_to_cpu(block->bb_level), agno, bno); ++ do_warn(_("expected level %d got %d in %sbt block %d/%d\n"), ++ level, be16_to_cpu(block->bb_level), name, agno, bno); + hdr_errors++; + bad_ino_btree = 1; + if (suspect) +@@ -1976,8 +1992,8 @@ scan_inobt( + default: + set_bmap(agno, bno, XR_E_MULT); + do_warn( +-_("inode btree block claimed (state %d), agno %d, bno %d, suspect %d\n"), +- state, agno, bno, suspect); ++_("%sbt btree block claimed (state %d), agno %d, bno %d, suspect %d\n"), ++ name, state, agno, bno, suspect); + } + + numrecs = be16_to_cpu(block->bb_numrecs); +@@ -1999,8 +2015,8 @@ _("inode btree block claimed (state %d), + + if (hdr_errors) { + bad_ino_btree = 1; +- do_warn(_("dubious inode btree block header %d/%d\n"), +- agno, bno); ++ do_warn(_("dubious %sbt btree block header %d/%d\n"), ++ name, agno, bno); + suspect++; + } + diff --git a/SOURCES/xfsprogs-5.8.0-xfs_db-short-circuit-type_f-if-type-is-unchanged.patch b/SOURCES/xfsprogs-5.8.0-xfs_db-short-circuit-type_f-if-type-is-unchanged.patch new file mode 100644 index 0000000..784ad1e --- /dev/null +++ b/SOURCES/xfsprogs-5.8.0-xfs_db-short-circuit-type_f-if-type-is-unchanged.patch @@ -0,0 +1,31 @@ +From 7b4a7b3f6bce91be45b54fc68e169cb756dc8c74 Mon Sep 17 00:00:00 2001 +From: Eric Sandeen +Date: Mon, 24 Aug 2020 12:47:47 -0400 +Subject: [PATCH] xfs_db: short circuit type_f if type is unchanged + +There's no reason to go through the type change code if the +type has not been changed. + +Signed-off-by: Eric Sandeen +Reviewed-by: Darrick J. Wong +Signed-off-by: Eric Sandeen +--- + db/type.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/db/type.c b/db/type.c +index 3cb1e868..572ac6d6 100644 +--- a/db/type.c ++++ b/db/type.c +@@ -216,6 +216,8 @@ type_f( + tt = findtyp(argv[1]); + if (tt == NULL) { + dbprintf(_("no such type %s\n"), argv[1]); ++ } else if (iocur_top->typ == tt) { ++ return 0; + } else { + if (iocur_top->typ == NULL) + dbprintf(_("no current object\n")); +-- +2.29.2 + diff --git a/SOURCES/xfsprogs-5.9.0-mkfs.xfs-fix-ASSERT-on-too-small-device-with-stripe.patch b/SOURCES/xfsprogs-5.9.0-mkfs.xfs-fix-ASSERT-on-too-small-device-with-stripe.patch new file mode 100644 index 0000000..86323d0 --- /dev/null +++ b/SOURCES/xfsprogs-5.9.0-mkfs.xfs-fix-ASSERT-on-too-small-device-with-stripe.patch @@ -0,0 +1,76 @@ +From 97a4059660b27a9b0e3d8cdde5dbef8712685865 Mon Sep 17 00:00:00 2001 +From: Pavel Reichl +Date: Mon, 28 Sep 2020 17:31:18 -0400 +Subject: [PATCH] mkfs.xfs: fix ASSERT on too-small device with stripe geometry + +When a too-small device is created with stripe geometry, we hit an +assert in align_ag_geometry(): + +mkfs.xfs: xfs_mkfs.c:2834: align_ag_geometry: Assertion `cfg->agcount != 0' failed. + +This is because align_ag_geometry() finds that the size of the last +(only) AG is too small, and attempts to trim it off. Obviously 0 +AGs is invalid, and we hit the ASSERT. + +Reported-by: Zdenek Kabelac +Suggested-by: Dave Chinner +Signed-off-by: Pavel Reichl +Reviewed-by: Christoph Hellwig +Reviewed-by: Carlos Maiolino +Signed-off-by: Eric Sandeen +--- + include/xfs_multidisk.h | 14 +++++++------- + mkfs/xfs_mkfs.c | 6 +++--- + 2 files changed, 10 insertions(+), 10 deletions(-) + +Index: xfsprogs-5.0.0/include/xfs_multidisk.h +=================================================================== +--- xfsprogs-5.0.0.orig/include/xfs_multidisk.h ++++ xfsprogs-5.0.0/include/xfs_multidisk.h +@@ -14,7 +14,6 @@ + #define XFS_DFL_BLOCKSIZE_LOG 12 /* 4096 byte blocks */ + #define XFS_DINODE_DFL_LOG 8 /* 256 byte inodes */ + #define XFS_DINODE_DFL_CRC_LOG 9 /* 512 byte inodes for CRCs */ +-#define XFS_MIN_DATA_BLOCKS 100 + #define XFS_MIN_INODE_PERBLOCK 2 /* min inodes per block */ + #define XFS_DFL_IMAXIMUM_PCT 25 /* max % of space for inodes */ + #define XFS_MIN_REC_DIRSIZE 12 /* 4096 byte dirblocks (V2) */ +@@ -25,13 +24,14 @@ + * accept w/o warnings + */ + +-#define XFS_AG_BYTES(bblog) ((long long)BBSIZE << (bblog)) +-#define XFS_AG_MIN_BYTES ((XFS_AG_BYTES(15))) /* 16 MB */ +-#define XFS_AG_MAX_BYTES ((XFS_AG_BYTES(31))) /* 1 TB */ +-#define XFS_AG_MIN_BLOCKS(blog) (XFS_AG_MIN_BYTES >> (blog)) +-#define XFS_AG_MAX_BLOCKS(blog) ((XFS_AG_MAX_BYTES - 1) >> (blog)) ++#define XFS_AG_BYTES(bblog) ((long long)BBSIZE << (bblog)) ++#define XFS_MIN_DATA_BLOCKS(cfg) (XFS_AG_MIN_BLOCKS((cfg)->blocklog)) ++#define XFS_AG_MIN_BYTES ((XFS_AG_BYTES(15))) /* 16 MB */ ++#define XFS_AG_MAX_BYTES ((XFS_AG_BYTES(31))) /* 1 TB */ ++#define XFS_AG_MIN_BLOCKS(blog) (XFS_AG_MIN_BYTES >> (blog)) ++#define XFS_AG_MAX_BLOCKS(blog) ((XFS_AG_MAX_BYTES - 1) >> (blog)) + +-#define XFS_MAX_AGNUMBER ((xfs_agnumber_t)(NULLAGNUMBER - 1)) ++#define XFS_MAX_AGNUMBER ((xfs_agnumber_t)(NULLAGNUMBER - 1)) + + /* + * These values define what we consider a "multi-disk" filesystem. That is, a +Index: xfsprogs-5.0.0/mkfs/xfs_mkfs.c +=================================================================== +--- xfsprogs-5.0.0.orig/mkfs/xfs_mkfs.c ++++ xfsprogs-5.0.0/mkfs/xfs_mkfs.c +@@ -2581,10 +2581,10 @@ _("size %s specified for data subvolume + cfg->dblocks = DTOBT(xi->dsize, cfg->blocklog); + } + +- if (cfg->dblocks < XFS_MIN_DATA_BLOCKS) { ++ if (cfg->dblocks < XFS_MIN_DATA_BLOCKS(cfg)) { + fprintf(stderr, +-_("size %lld of data subvolume is too small, minimum %d blocks\n"), +- (long long)cfg->dblocks, XFS_MIN_DATA_BLOCKS); ++_("size %lld of data subvolume is too small, minimum %lld blocks\n"), ++ (long long)cfg->dblocks, XFS_MIN_DATA_BLOCKS(cfg)); + usage(); + } + diff --git a/SPECS/xfsprogs.spec b/SPECS/xfsprogs.spec index 1af6b15..b07b2c6 100644 --- a/SPECS/xfsprogs.spec +++ b/SPECS/xfsprogs.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing the XFS filesystem Name: xfsprogs Version: 5.0.0 -Release: 4%{?dist} +Release: 8%{?dist} License: GPL+ and LGPLv2+ Group: System Environment/Base URL: https://xfs.wiki.kernel.org @@ -25,6 +25,18 @@ Patch4: xfsprogs-5.3.0-xfs_growfs-allow-mounted-device-node-as-argument.patch Patch5: xfsprogs-5.5.0-libxfs-use-FALLOC_FL_ZERO_RANGE-in-libxfs_device_zer.patch Patch6: xfsprogs-5.4.0-mkfs-Break-block-discard-into-chunks-of-2-GB.patch Patch7: xfsprogs-5.4.0-mkfs-tidy-up-discard-notifications.patch +Patch8: xfsprogs-5.7.0-xfs_quota-refactor-code-to-generate-id-from-name.patch +Patch9: xfsprogs-5.7.0-xfs_quota-allow-individual-timer-extension.patch +Patch10: xfsprogs-5.7.0-xfs_quota-fix-unsigned-int-id-comparisons.patch +Patch11: xfsprogs-5.7.0-xfs_repair-check-for-AG-btree-records-that-would-wra.patch +Patch12: xfsprogs-5.7.0-xfs_repair-tag-inobt-vs-finobt-errors-properly.patch +Patch13: xfsprogs-5.7.0-xfs_repair-complain-about-bad-interior-btree-pointer.patch +Patch14: xfsprogs-5.7.0-xfs_repair-convert-to-libxfs_verify_agbno.patch +Patch15: xfsprogs-5.9.0-mkfs.xfs-fix-ASSERT-on-too-small-device-with-stripe.patch +Patch16: xfsprogs-5.7.0-xfs_repair-fix-rebuilding-btree-block-less-than-minr.patch +Patch17: xfsprogs-5.10.0-xfs_quota-document-how-the-default-quota-is-stored.patch +Patch18: xfsprogs-5.8.0-xfs_db-short-circuit-type_f-if-type-is-unchanged.patch +Patch19: xfsprogs-5.10.0-xfs_repair-Use-proper-min-max-values-in-compute_level_geometry.patch %description A set of commands to use the XFS filesystem, including mkfs.xfs. @@ -63,6 +75,18 @@ also want to install xfsprogs. %patch5 -p1 %patch6 -p1 %patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 %build export tagname=CC @@ -122,6 +146,21 @@ rm -rf $RPM_BUILD_ROOT/%{_mandir}/man8/xfs_scrub* %{_libdir}/*.so %changelog +* Thu Jan 07 2021 Bill O'Donnell 5.0.0-8 +- xfs_repair: Use proper min/max values in compute_level_geometry (#1910384) + +* Mon Dec 14 2020 Bill O'Donnell 5.0.0-7 +- xfs_quota: document how the default quota is stored (#1850188) +- xfs_db: skip type change if type_f unchanged (#1867474) + +* Wed Dec 09 2020 Bill O'Donnell 5.0.0-6 +- xfs_repair: improve AG btree ptr validation (libxfs_verify_agbno) (#1887288) +- mkfs.xfs: fix ASSERT on too-small device with stripe geometry (#1887401) +- xfs_repair: fix rebuilding btree block less than minrecs (#1759452) + +* Wed Dec 02 2020 Bill O'Donnell 5.0.0-5 +- xfs_quota: allow individual timer extension (#1899204) + * Wed Jun 03 2020 Eric Sandeen 5.0.0-4 - mkfs.xfs: inform user about discard, and make interruptable (#1836414)