~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/xfs/scrub/quota.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  * Copyright (C) 2017-2023 Oracle.  All Rights Reserved.
  4  * Author: Darrick J. Wong <djwong@kernel.org>
  5  */
  6 #include "xfs.h"
  7 #include "xfs_fs.h"
  8 #include "xfs_shared.h"
  9 #include "xfs_bit.h"
 10 #include "xfs_format.h"
 11 #include "xfs_trans_resv.h"
 12 #include "xfs_mount.h"
 13 #include "xfs_log_format.h"
 14 #include "xfs_trans.h"
 15 #include "xfs_inode.h"
 16 #include "xfs_quota.h"
 17 #include "xfs_qm.h"
 18 #include "xfs_bmap.h"
 19 #include "scrub/scrub.h"
 20 #include "scrub/common.h"
 21 #include "scrub/quota.h"
 22 
 23 /* Convert a scrub type code to a DQ flag, or return 0 if error. */
 24 xfs_dqtype_t
 25 xchk_quota_to_dqtype(
 26         struct xfs_scrub        *sc)
 27 {
 28         switch (sc->sm->sm_type) {
 29         case XFS_SCRUB_TYPE_UQUOTA:
 30                 return XFS_DQTYPE_USER;
 31         case XFS_SCRUB_TYPE_GQUOTA:
 32                 return XFS_DQTYPE_GROUP;
 33         case XFS_SCRUB_TYPE_PQUOTA:
 34                 return XFS_DQTYPE_PROJ;
 35         default:
 36                 return 0;
 37         }
 38 }
 39 
 40 /* Set us up to scrub a quota. */
 41 int
 42 xchk_setup_quota(
 43         struct xfs_scrub        *sc)
 44 {
 45         xfs_dqtype_t            dqtype;
 46         int                     error;
 47 
 48         if (!XFS_IS_QUOTA_ON(sc->mp))
 49                 return -ENOENT;
 50 
 51         dqtype = xchk_quota_to_dqtype(sc);
 52         if (dqtype == 0)
 53                 return -EINVAL;
 54 
 55         if (!xfs_this_quota_on(sc->mp, dqtype))
 56                 return -ENOENT;
 57 
 58         if (xchk_need_intent_drain(sc))
 59                 xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
 60 
 61         error = xchk_setup_fs(sc);
 62         if (error)
 63                 return error;
 64 
 65         error = xchk_install_live_inode(sc, xfs_quota_inode(sc->mp, dqtype));
 66         if (error)
 67                 return error;
 68 
 69         xchk_ilock(sc, XFS_ILOCK_EXCL);
 70         return 0;
 71 }
 72 
 73 /* Quotas. */
 74 
 75 struct xchk_quota_info {
 76         struct xfs_scrub        *sc;
 77         xfs_dqid_t              last_id;
 78 };
 79 
 80 /* There's a written block backing this dquot, right? */
 81 STATIC int
 82 xchk_quota_item_bmap(
 83         struct xfs_scrub        *sc,
 84         struct xfs_dquot        *dq,
 85         xfs_fileoff_t           offset)
 86 {
 87         struct xfs_bmbt_irec    irec;
 88         struct xfs_mount        *mp = sc->mp;
 89         int                     nmaps = 1;
 90         int                     error;
 91 
 92         if (!xfs_verify_fileoff(mp, offset)) {
 93                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 94                 return 0;
 95         }
 96 
 97         if (dq->q_fileoffset != offset) {
 98                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
 99                 return 0;
100         }
101 
102         error = xfs_bmapi_read(sc->ip, offset, 1, &irec, &nmaps, 0);
103         if (error)
104                 return error;
105 
106         if (nmaps != 1) {
107                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
108                 return 0;
109         }
110 
111         if (!xfs_verify_fsbno(mp, irec.br_startblock))
112                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
113         if (XFS_FSB_TO_DADDR(mp, irec.br_startblock) != dq->q_blkno)
114                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
115         if (!xfs_bmap_is_written_extent(&irec))
116                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
117 
118         return 0;
119 }
120 
121 /* Complain if a quota timer is incorrectly set. */
122 static inline void
123 xchk_quota_item_timer(
124         struct xfs_scrub                *sc,
125         xfs_fileoff_t                   offset,
126         const struct xfs_dquot_res      *res)
127 {
128         if ((res->softlimit && res->count > res->softlimit) ||
129             (res->hardlimit && res->count > res->hardlimit)) {
130                 if (!res->timer)
131                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
132         } else {
133                 if (res->timer)
134                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
135         }
136 }
137 
138 /* Scrub the fields in an individual quota item. */
139 STATIC int
140 xchk_quota_item(
141         struct xchk_quota_info  *sqi,
142         struct xfs_dquot        *dq)
143 {
144         struct xfs_scrub        *sc = sqi->sc;
145         struct xfs_mount        *mp = sc->mp;
146         struct xfs_quotainfo    *qi = mp->m_quotainfo;
147         xfs_fileoff_t           offset;
148         xfs_ino_t               fs_icount;
149         int                     error = 0;
150 
151         if (xchk_should_terminate(sc, &error))
152                 return error;
153 
154         /*
155          * We want to validate the bmap record for the storage backing this
156          * dquot, so we need to lock the dquot and the quota file.  For quota
157          * operations, the locking order is first the ILOCK and then the dquot.
158          * However, dqiterate gave us a locked dquot, so drop the dquot lock to
159          * get the ILOCK.
160          */
161         xfs_dqunlock(dq);
162         xchk_ilock(sc, XFS_ILOCK_SHARED);
163         xfs_dqlock(dq);
164 
165         /*
166          * Except for the root dquot, the actual dquot we got must either have
167          * the same or higher id as we saw before.
168          */
169         offset = dq->q_id / qi->qi_dqperchunk;
170         if (dq->q_id && dq->q_id <= sqi->last_id)
171                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
172 
173         sqi->last_id = dq->q_id;
174 
175         error = xchk_quota_item_bmap(sc, dq, offset);
176         xchk_iunlock(sc, XFS_ILOCK_SHARED);
177         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, offset, &error))
178                 return error;
179 
180         /*
181          * Warn if the hard limits are larger than the fs.
182          * Administrators can do this, though in production this seems
183          * suspect, which is why we flag it for review.
184          *
185          * Complain about corruption if the soft limit is greater than
186          * the hard limit.
187          */
188         if (dq->q_blk.hardlimit > mp->m_sb.sb_dblocks)
189                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
190         if (dq->q_blk.softlimit > dq->q_blk.hardlimit)
191                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
192 
193         if (dq->q_ino.hardlimit > M_IGEO(mp)->maxicount)
194                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
195         if (dq->q_ino.softlimit > dq->q_ino.hardlimit)
196                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
197 
198         if (dq->q_rtb.hardlimit > mp->m_sb.sb_rblocks)
199                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
200         if (dq->q_rtb.softlimit > dq->q_rtb.hardlimit)
201                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
202 
203         /* Check the resource counts. */
204         fs_icount = percpu_counter_sum(&mp->m_icount);
205 
206         /*
207          * Check that usage doesn't exceed physical limits.  However, on
208          * a reflink filesystem we're allowed to exceed physical space
209          * if there are no quota limits.
210          */
211         if (xfs_has_reflink(mp)) {
212                 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
213                         xchk_fblock_set_warning(sc, XFS_DATA_FORK,
214                                         offset);
215         } else {
216                 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
217                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
218                                         offset);
219         }
220         if (dq->q_ino.count > fs_icount || dq->q_rtb.count > mp->m_sb.sb_rblocks)
221                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
222 
223         /*
224          * We can violate the hard limits if the admin suddenly sets a
225          * lower limit than the actual usage.  However, we flag it for
226          * admin review.
227          */
228         if (dq->q_id == 0)
229                 goto out;
230 
231         if (dq->q_blk.hardlimit != 0 &&
232             dq->q_blk.count > dq->q_blk.hardlimit)
233                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
234 
235         if (dq->q_ino.hardlimit != 0 &&
236             dq->q_ino.count > dq->q_ino.hardlimit)
237                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
238 
239         if (dq->q_rtb.hardlimit != 0 &&
240             dq->q_rtb.count > dq->q_rtb.hardlimit)
241                 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
242 
243         xchk_quota_item_timer(sc, offset, &dq->q_blk);
244         xchk_quota_item_timer(sc, offset, &dq->q_ino);
245         xchk_quota_item_timer(sc, offset, &dq->q_rtb);
246 
247 out:
248         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
249                 return -ECANCELED;
250 
251         return 0;
252 }
253 
254 /* Check the quota's data fork. */
255 STATIC int
256 xchk_quota_data_fork(
257         struct xfs_scrub        *sc)
258 {
259         struct xfs_bmbt_irec    irec = { 0 };
260         struct xfs_iext_cursor  icur;
261         struct xfs_quotainfo    *qi = sc->mp->m_quotainfo;
262         struct xfs_ifork        *ifp;
263         xfs_fileoff_t           max_dqid_off;
264         int                     error = 0;
265 
266         /* Invoke the fork scrubber. */
267         error = xchk_metadata_inode_forks(sc);
268         if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
269                 return error;
270 
271         /* Check for data fork problems that apply only to quota files. */
272         max_dqid_off = XFS_DQ_ID_MAX / qi->qi_dqperchunk;
273         ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
274         for_each_xfs_iext(ifp, &icur, &irec) {
275                 if (xchk_should_terminate(sc, &error))
276                         break;
277 
278                 /*
279                  * delalloc/unwritten extents or blocks mapped above the highest
280                  * quota id shouldn't happen.
281                  */
282                 if (!xfs_bmap_is_written_extent(&irec) ||
283                     irec.br_startoff > max_dqid_off ||
284                     irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) {
285                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
286                                         irec.br_startoff);
287                         break;
288                 }
289         }
290 
291         return error;
292 }
293 
294 /* Scrub all of a quota type's items. */
295 int
296 xchk_quota(
297         struct xfs_scrub        *sc)
298 {
299         struct xchk_dqiter      cursor = { };
300         struct xchk_quota_info  sqi = { .sc = sc };
301         struct xfs_mount        *mp = sc->mp;
302         struct xfs_quotainfo    *qi = mp->m_quotainfo;
303         struct xfs_dquot        *dq;
304         xfs_dqtype_t            dqtype;
305         int                     error = 0;
306 
307         dqtype = xchk_quota_to_dqtype(sc);
308 
309         /* Look for problem extents. */
310         error = xchk_quota_data_fork(sc);
311         if (error)
312                 goto out;
313         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
314                 goto out;
315 
316         /*
317          * Check all the quota items.  Now that we've checked the quota inode
318          * data fork we have to drop ILOCK_EXCL to use the regular dquot
319          * functions.
320          */
321         xchk_iunlock(sc, sc->ilock_flags);
322 
323         /* Now look for things that the quota verifiers won't complain about. */
324         xchk_dqiter_init(&cursor, sc, dqtype);
325         while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) {
326                 error = xchk_quota_item(&sqi, dq);
327                 xfs_qm_dqput(dq);
328                 if (error)
329                         break;
330         }
331         if (error == -ECANCELED)
332                 error = 0;
333         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK,
334                         sqi.last_id * qi->qi_dqperchunk, &error))
335                 goto out;
336 
337 out:
338         return error;
339 }
340 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php