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

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

Version: ~ [ linux-5.2-rc1 ] ~ [ linux-5.1.2 ] ~ [ linux-5.0.16 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.43 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.119 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.176 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.179 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.139 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.67 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0+
  2 /*
  3  * Copyright (C) 2017 Oracle.  All Rights Reserved.
  4  * Author: Darrick J. Wong <darrick.wong@oracle.com>
  5  */
  6 #include "xfs.h"
  7 #include "xfs_fs.h"
  8 #include "xfs_shared.h"
  9 #include "xfs_format.h"
 10 #include "xfs_trans_resv.h"
 11 #include "xfs_mount.h"
 12 #include "xfs_defer.h"
 13 #include "xfs_btree.h"
 14 #include "xfs_bit.h"
 15 #include "xfs_log_format.h"
 16 #include "xfs_trans.h"
 17 #include "xfs_sb.h"
 18 #include "xfs_inode.h"
 19 #include "xfs_da_format.h"
 20 #include "xfs_da_btree.h"
 21 #include "xfs_dir2.h"
 22 #include "xfs_attr.h"
 23 #include "xfs_attr_leaf.h"
 24 #include "scrub/xfs_scrub.h"
 25 #include "scrub/scrub.h"
 26 #include "scrub/common.h"
 27 #include "scrub/dabtree.h"
 28 #include "scrub/trace.h"
 29 
 30 #include <linux/posix_acl_xattr.h>
 31 #include <linux/xattr.h>
 32 
 33 /* Set us up to scrub an inode's extended attributes. */
 34 int
 35 xchk_setup_xattr(
 36         struct xfs_scrub        *sc,
 37         struct xfs_inode        *ip)
 38 {
 39         size_t                  sz;
 40 
 41         /*
 42          * Allocate the buffer without the inode lock held.  We need enough
 43          * space to read every xattr value in the file or enough space to
 44          * hold three copies of the xattr free space bitmap.  (Not both at
 45          * the same time.)
 46          */
 47         sz = max_t(size_t, XATTR_SIZE_MAX, 3 * sizeof(long) *
 48                         BITS_TO_LONGS(sc->mp->m_attr_geo->blksize));
 49         sc->buf = kmem_zalloc_large(sz, KM_SLEEP);
 50         if (!sc->buf)
 51                 return -ENOMEM;
 52 
 53         return xchk_setup_inode_contents(sc, ip, 0);
 54 }
 55 
 56 /* Extended Attributes */
 57 
 58 struct xchk_xattr {
 59         struct xfs_attr_list_context    context;
 60         struct xfs_scrub                *sc;
 61 };
 62 
 63 /*
 64  * Check that an extended attribute key can be looked up by hash.
 65  *
 66  * We use the XFS attribute list iterator (i.e. xfs_attr_list_int_ilocked)
 67  * to call this function for every attribute key in an inode.  Once
 68  * we're here, we load the attribute value to see if any errors happen,
 69  * or if we get more or less data than we expected.
 70  */
 71 static void
 72 xchk_xattr_listent(
 73         struct xfs_attr_list_context    *context,
 74         int                             flags,
 75         unsigned char                   *name,
 76         int                             namelen,
 77         int                             valuelen)
 78 {
 79         struct xchk_xattr               *sx;
 80         struct xfs_da_args              args = { NULL };
 81         int                             error = 0;
 82 
 83         sx = container_of(context, struct xchk_xattr, context);
 84 
 85         if (xchk_should_terminate(sx->sc, &error)) {
 86                 context->seen_enough = 1;
 87                 return;
 88         }
 89 
 90         if (flags & XFS_ATTR_INCOMPLETE) {
 91                 /* Incomplete attr key, just mark the inode for preening. */
 92                 xchk_ino_set_preen(sx->sc, context->dp->i_ino);
 93                 return;
 94         }
 95 
 96         /* Does this name make sense? */
 97         if (!xfs_attr_namecheck(name, namelen)) {
 98                 xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno);
 99                 return;
100         }
101 
102         args.flags = ATTR_KERNOTIME;
103         if (flags & XFS_ATTR_ROOT)
104                 args.flags |= ATTR_ROOT;
105         else if (flags & XFS_ATTR_SECURE)
106                 args.flags |= ATTR_SECURE;
107         args.geo = context->dp->i_mount->m_attr_geo;
108         args.whichfork = XFS_ATTR_FORK;
109         args.dp = context->dp;
110         args.name = name;
111         args.namelen = namelen;
112         args.hashval = xfs_da_hashname(args.name, args.namelen);
113         args.trans = context->tp;
114         args.value = sx->sc->buf;
115         args.valuelen = XATTR_SIZE_MAX;
116 
117         error = xfs_attr_get_ilocked(context->dp, &args);
118         if (error == -EEXIST)
119                 error = 0;
120         if (!xchk_fblock_process_error(sx->sc, XFS_ATTR_FORK, args.blkno,
121                         &error))
122                 goto fail_xref;
123         if (args.valuelen != valuelen)
124                 xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK,
125                                              args.blkno);
126 fail_xref:
127         if (sx->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
128                 context->seen_enough = 1;
129         return;
130 }
131 
132 /*
133  * Mark a range [start, start+len) in this map.  Returns true if the
134  * region was free, and false if there's a conflict or a problem.
135  *
136  * Within a char, the lowest bit of the char represents the byte with
137  * the smallest address
138  */
139 STATIC bool
140 xchk_xattr_set_map(
141         struct xfs_scrub        *sc,
142         unsigned long           *map,
143         unsigned int            start,
144         unsigned int            len)
145 {
146         unsigned int            mapsize = sc->mp->m_attr_geo->blksize;
147         bool                    ret = true;
148 
149         if (start >= mapsize)
150                 return false;
151         if (start + len > mapsize) {
152                 len = mapsize - start;
153                 ret = false;
154         }
155 
156         if (find_next_bit(map, mapsize, start) < start + len)
157                 ret = false;
158         bitmap_set(map, start, len);
159 
160         return ret;
161 }
162 
163 /*
164  * Check the leaf freemap from the usage bitmap.  Returns false if the
165  * attr freemap has problems or points to used space.
166  */
167 STATIC bool
168 xchk_xattr_check_freemap(
169         struct xfs_scrub                *sc,
170         unsigned long                   *map,
171         struct xfs_attr3_icleaf_hdr     *leafhdr)
172 {
173         unsigned long                   *freemap;
174         unsigned long                   *dstmap;
175         unsigned int                    mapsize = sc->mp->m_attr_geo->blksize;
176         int                             i;
177 
178         /* Construct bitmap of freemap contents. */
179         freemap = (unsigned long *)sc->buf + BITS_TO_LONGS(mapsize);
180         bitmap_zero(freemap, mapsize);
181         for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
182                 if (!xchk_xattr_set_map(sc, freemap,
183                                 leafhdr->freemap[i].base,
184                                 leafhdr->freemap[i].size))
185                         return false;
186         }
187 
188         /* Look for bits that are set in freemap and are marked in use. */
189         dstmap = freemap + BITS_TO_LONGS(mapsize);
190         return bitmap_and(dstmap, freemap, map, mapsize) == 0;
191 }
192 
193 /*
194  * Check this leaf entry's relations to everything else.
195  * Returns the number of bytes used for the name/value data.
196  */
197 STATIC void
198 xchk_xattr_entry(
199         struct xchk_da_btree            *ds,
200         int                             level,
201         char                            *buf_end,
202         struct xfs_attr_leafblock       *leaf,
203         struct xfs_attr3_icleaf_hdr     *leafhdr,
204         unsigned long                   *usedmap,
205         struct xfs_attr_leaf_entry      *ent,
206         int                             idx,
207         unsigned int                    *usedbytes,
208         __u32                           *last_hashval)
209 {
210         struct xfs_mount                *mp = ds->state->mp;
211         char                            *name_end;
212         struct xfs_attr_leaf_name_local *lentry;
213         struct xfs_attr_leaf_name_remote *rentry;
214         unsigned int                    nameidx;
215         unsigned int                    namesize;
216 
217         if (ent->pad2 != 0)
218                 xchk_da_set_corrupt(ds, level);
219 
220         /* Hash values in order? */
221         if (be32_to_cpu(ent->hashval) < *last_hashval)
222                 xchk_da_set_corrupt(ds, level);
223         *last_hashval = be32_to_cpu(ent->hashval);
224 
225         nameidx = be16_to_cpu(ent->nameidx);
226         if (nameidx < leafhdr->firstused ||
227             nameidx >= mp->m_attr_geo->blksize) {
228                 xchk_da_set_corrupt(ds, level);
229                 return;
230         }
231 
232         /* Check the name information. */
233         if (ent->flags & XFS_ATTR_LOCAL) {
234                 lentry = xfs_attr3_leaf_name_local(leaf, idx);
235                 namesize = xfs_attr_leaf_entsize_local(lentry->namelen,
236                                 be16_to_cpu(lentry->valuelen));
237                 name_end = (char *)lentry + namesize;
238                 if (lentry->namelen == 0)
239                         xchk_da_set_corrupt(ds, level);
240         } else {
241                 rentry = xfs_attr3_leaf_name_remote(leaf, idx);
242                 namesize = xfs_attr_leaf_entsize_remote(rentry->namelen);
243                 name_end = (char *)rentry + namesize;
244                 if (rentry->namelen == 0 || rentry->valueblk == 0)
245                         xchk_da_set_corrupt(ds, level);
246         }
247         if (name_end > buf_end)
248                 xchk_da_set_corrupt(ds, level);
249 
250         if (!xchk_xattr_set_map(ds->sc, usedmap, nameidx, namesize))
251                 xchk_da_set_corrupt(ds, level);
252         if (!(ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
253                 *usedbytes += namesize;
254 }
255 
256 /* Scrub an attribute leaf. */
257 STATIC int
258 xchk_xattr_block(
259         struct xchk_da_btree            *ds,
260         int                             level)
261 {
262         struct xfs_attr3_icleaf_hdr     leafhdr;
263         struct xfs_mount                *mp = ds->state->mp;
264         struct xfs_da_state_blk         *blk = &ds->state->path.blk[level];
265         struct xfs_buf                  *bp = blk->bp;
266         xfs_dablk_t                     *last_checked = ds->private;
267         struct xfs_attr_leafblock       *leaf = bp->b_addr;
268         struct xfs_attr_leaf_entry      *ent;
269         struct xfs_attr_leaf_entry      *entries;
270         unsigned long                   *usedmap = ds->sc->buf;
271         char                            *buf_end;
272         size_t                          off;
273         __u32                           last_hashval = 0;
274         unsigned int                    usedbytes = 0;
275         unsigned int                    hdrsize;
276         int                             i;
277 
278         if (*last_checked == blk->blkno)
279                 return 0;
280         *last_checked = blk->blkno;
281         bitmap_zero(usedmap, mp->m_attr_geo->blksize);
282 
283         /* Check all the padding. */
284         if (xfs_sb_version_hascrc(&ds->sc->mp->m_sb)) {
285                 struct xfs_attr3_leafblock      *leaf = bp->b_addr;
286 
287                 if (leaf->hdr.pad1 != 0 || leaf->hdr.pad2 != 0 ||
288                     leaf->hdr.info.hdr.pad != 0)
289                         xchk_da_set_corrupt(ds, level);
290         } else {
291                 if (leaf->hdr.pad1 != 0 || leaf->hdr.info.pad != 0)
292                         xchk_da_set_corrupt(ds, level);
293         }
294 
295         /* Check the leaf header */
296         xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
297         hdrsize = xfs_attr3_leaf_hdr_size(leaf);
298 
299         if (leafhdr.usedbytes > mp->m_attr_geo->blksize)
300                 xchk_da_set_corrupt(ds, level);
301         if (leafhdr.firstused > mp->m_attr_geo->blksize)
302                 xchk_da_set_corrupt(ds, level);
303         if (leafhdr.firstused < hdrsize)
304                 xchk_da_set_corrupt(ds, level);
305         if (!xchk_xattr_set_map(ds->sc, usedmap, 0, hdrsize))
306                 xchk_da_set_corrupt(ds, level);
307 
308         if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
309                 goto out;
310 
311         entries = xfs_attr3_leaf_entryp(leaf);
312         if ((char *)&entries[leafhdr.count] > (char *)leaf + leafhdr.firstused)
313                 xchk_da_set_corrupt(ds, level);
314 
315         buf_end = (char *)bp->b_addr + mp->m_attr_geo->blksize;
316         for (i = 0, ent = entries; i < leafhdr.count; ent++, i++) {
317                 /* Mark the leaf entry itself. */
318                 off = (char *)ent - (char *)leaf;
319                 if (!xchk_xattr_set_map(ds->sc, usedmap, off,
320                                 sizeof(xfs_attr_leaf_entry_t))) {
321                         xchk_da_set_corrupt(ds, level);
322                         goto out;
323                 }
324 
325                 /* Check the entry and nameval. */
326                 xchk_xattr_entry(ds, level, buf_end, leaf, &leafhdr,
327                                 usedmap, ent, i, &usedbytes, &last_hashval);
328 
329                 if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
330                         goto out;
331         }
332 
333         if (!xchk_xattr_check_freemap(ds->sc, usedmap, &leafhdr))
334                 xchk_da_set_corrupt(ds, level);
335 
336         if (leafhdr.usedbytes != usedbytes)
337                 xchk_da_set_corrupt(ds, level);
338 
339 out:
340         return 0;
341 }
342 
343 /* Scrub a attribute btree record. */
344 STATIC int
345 xchk_xattr_rec(
346         struct xchk_da_btree            *ds,
347         int                             level,
348         void                            *rec)
349 {
350         struct xfs_mount                *mp = ds->state->mp;
351         struct xfs_attr_leaf_entry      *ent = rec;
352         struct xfs_da_state_blk         *blk;
353         struct xfs_attr_leaf_name_local *lentry;
354         struct xfs_attr_leaf_name_remote        *rentry;
355         struct xfs_buf                  *bp;
356         xfs_dahash_t                    calc_hash;
357         xfs_dahash_t                    hash;
358         int                             nameidx;
359         int                             hdrsize;
360         unsigned int                    badflags;
361         int                             error;
362 
363         blk = &ds->state->path.blk[level];
364 
365         /* Check the whole block, if necessary. */
366         error = xchk_xattr_block(ds, level);
367         if (error)
368                 goto out;
369         if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
370                 goto out;
371 
372         /* Check the hash of the entry. */
373         error = xchk_da_btree_hash(ds, level, &ent->hashval);
374         if (error)
375                 goto out;
376 
377         /* Find the attr entry's location. */
378         bp = blk->bp;
379         hdrsize = xfs_attr3_leaf_hdr_size(bp->b_addr);
380         nameidx = be16_to_cpu(ent->nameidx);
381         if (nameidx < hdrsize || nameidx >= mp->m_attr_geo->blksize) {
382                 xchk_da_set_corrupt(ds, level);
383                 goto out;
384         }
385 
386         /* Retrieve the entry and check it. */
387         hash = be32_to_cpu(ent->hashval);
388         badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE |
389                         XFS_ATTR_INCOMPLETE);
390         if ((ent->flags & badflags) != 0)
391                 xchk_da_set_corrupt(ds, level);
392         if (ent->flags & XFS_ATTR_LOCAL) {
393                 lentry = (struct xfs_attr_leaf_name_local *)
394                                 (((char *)bp->b_addr) + nameidx);
395                 if (lentry->namelen <= 0) {
396                         xchk_da_set_corrupt(ds, level);
397                         goto out;
398                 }
399                 calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen);
400         } else {
401                 rentry = (struct xfs_attr_leaf_name_remote *)
402                                 (((char *)bp->b_addr) + nameidx);
403                 if (rentry->namelen <= 0) {
404                         xchk_da_set_corrupt(ds, level);
405                         goto out;
406                 }
407                 calc_hash = xfs_da_hashname(rentry->name, rentry->namelen);
408         }
409         if (calc_hash != hash)
410                 xchk_da_set_corrupt(ds, level);
411 
412 out:
413         return error;
414 }
415 
416 /* Scrub the extended attribute metadata. */
417 int
418 xchk_xattr(
419         struct xfs_scrub                *sc)
420 {
421         struct xchk_xattr               sx;
422         struct attrlist_cursor_kern     cursor = { 0 };
423         xfs_dablk_t                     last_checked = -1U;
424         int                             error = 0;
425 
426         if (!xfs_inode_hasattr(sc->ip))
427                 return -ENOENT;
428 
429         memset(&sx, 0, sizeof(sx));
430         /* Check attribute tree structure */
431         error = xchk_da_btree(sc, XFS_ATTR_FORK, xchk_xattr_rec,
432                         &last_checked);
433         if (error)
434                 goto out;
435 
436         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
437                 goto out;
438 
439         /* Check that every attr key can also be looked up by hash. */
440         sx.context.dp = sc->ip;
441         sx.context.cursor = &cursor;
442         sx.context.resynch = 1;
443         sx.context.put_listent = xchk_xattr_listent;
444         sx.context.tp = sc->tp;
445         sx.context.flags = ATTR_INCOMPLETE;
446         sx.sc = sc;
447 
448         /*
449          * Look up every xattr in this file by name.
450          *
451          * Use the backend implementation of xfs_attr_list to call
452          * xchk_xattr_listent on every attribute key in this inode.
453          * In other words, we use the same iterator/callback mechanism
454          * that listattr uses to scrub extended attributes, though in our
455          * _listent function, we check the value of the attribute.
456          *
457          * The VFS only locks i_rwsem when modifying attrs, so keep all
458          * three locks held because that's the only way to ensure we're
459          * the only thread poking into the da btree.  We traverse the da
460          * btree while holding a leaf buffer locked for the xattr name
461          * iteration, which doesn't really follow the usual buffer
462          * locking order.
463          */
464         error = xfs_attr_list_int_ilocked(&sx.context);
465         if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error))
466                 goto out;
467 out:
468         return error;
469 }
470 

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

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | 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.

osdn.jp