1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2000-2005 Silicon Graphics, Inc. |
4 | * Copyright (c) 2013 Red Hat, Inc. |
5 | * All Rights Reserved. |
6 | */ |
7 | #include "xfs.h" |
8 | #include "xfs_fs.h" |
9 | #include "xfs_shared.h" |
10 | #include "xfs_format.h" |
11 | #include "xfs_log_format.h" |
12 | #include "xfs_trans_resv.h" |
13 | #include "xfs_bit.h" |
14 | #include "xfs_mount.h" |
15 | #include "xfs_defer.h" |
16 | #include "xfs_da_format.h" |
17 | #include "xfs_da_btree.h" |
18 | #include "xfs_inode.h" |
19 | #include "xfs_trans.h" |
20 | #include "xfs_bmap.h" |
21 | #include "xfs_attr.h" |
22 | #include "xfs_attr_remote.h" |
23 | #include "xfs_trace.h" |
24 | #include "xfs_error.h" |
25 | #include "xfs_health.h" |
26 | |
27 | #define ATTR_RMTVALUE_MAPSIZE 1 /* # of map entries at once */ |
28 | |
29 | /* |
30 | * Remote Attribute Values |
31 | * ======================= |
32 | * |
33 | * Remote extended attribute values are conceptually simple -- they're written |
34 | * to data blocks mapped by an inode's attribute fork, and they have an upper |
35 | * size limit of 64k. Setting a value does not involve the XFS log. |
36 | * |
37 | * However, on a v5 filesystem, maximally sized remote attr values require one |
38 | * block more than 64k worth of space to hold both the remote attribute value |
39 | * header (64 bytes). On a 4k block filesystem this results in a 68k buffer; |
40 | * on a 64k block filesystem, this would be a 128k buffer. Note that the log |
41 | * format can only handle a dirty buffer of XFS_MAX_BLOCKSIZE length (64k). |
42 | * Therefore, we /must/ ensure that remote attribute value buffers never touch |
43 | * the logging system and therefore never have a log item. |
44 | */ |
45 | |
46 | /* |
47 | * Each contiguous block has a header, so it is not just a simple attribute |
48 | * length to FSB conversion. |
49 | */ |
50 | int |
51 | xfs_attr3_rmt_blocks( |
52 | struct xfs_mount *mp, |
53 | int attrlen) |
54 | { |
55 | if (xfs_has_crc(mp)) { |
56 | int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize); |
57 | return (attrlen + buflen - 1) / buflen; |
58 | } |
59 | return XFS_B_TO_FSB(mp, attrlen); |
60 | } |
61 | |
62 | /* |
63 | * Checking of the remote attribute header is split into two parts. The verifier |
64 | * does CRC, location and bounds checking, the unpacking function checks the |
65 | * attribute parameters and owner. |
66 | */ |
67 | static xfs_failaddr_t |
68 | xfs_attr3_rmt_hdr_ok( |
69 | void *ptr, |
70 | xfs_ino_t ino, |
71 | uint32_t offset, |
72 | uint32_t size, |
73 | xfs_daddr_t bno) |
74 | { |
75 | struct xfs_attr3_rmt_hdr *rmt = ptr; |
76 | |
77 | if (bno != be64_to_cpu(rmt->rm_blkno)) |
78 | return __this_address; |
79 | if (offset != be32_to_cpu(rmt->rm_offset)) |
80 | return __this_address; |
81 | if (size != be32_to_cpu(rmt->rm_bytes)) |
82 | return __this_address; |
83 | if (ino != be64_to_cpu(rmt->rm_owner)) |
84 | return __this_address; |
85 | |
86 | /* ok */ |
87 | return NULL; |
88 | } |
89 | |
90 | static xfs_failaddr_t |
91 | xfs_attr3_rmt_verify( |
92 | struct xfs_mount *mp, |
93 | struct xfs_buf *bp, |
94 | void *ptr, |
95 | int fsbsize, |
96 | xfs_daddr_t bno) |
97 | { |
98 | struct xfs_attr3_rmt_hdr *rmt = ptr; |
99 | |
100 | if (!xfs_verify_magic(bp, rmt->rm_magic)) |
101 | return __this_address; |
102 | if (!uuid_equal(&rmt->rm_uuid, &mp->m_sb.sb_meta_uuid)) |
103 | return __this_address; |
104 | if (be64_to_cpu(rmt->rm_blkno) != bno) |
105 | return __this_address; |
106 | if (be32_to_cpu(rmt->rm_bytes) > fsbsize - sizeof(*rmt)) |
107 | return __this_address; |
108 | if (be32_to_cpu(rmt->rm_offset) + |
109 | be32_to_cpu(rmt->rm_bytes) > XFS_XATTR_SIZE_MAX) |
110 | return __this_address; |
111 | if (rmt->rm_owner == 0) |
112 | return __this_address; |
113 | |
114 | return NULL; |
115 | } |
116 | |
117 | static int |
118 | __xfs_attr3_rmt_read_verify( |
119 | struct xfs_buf *bp, |
120 | bool check_crc, |
121 | xfs_failaddr_t *failaddr) |
122 | { |
123 | struct xfs_mount *mp = bp->b_mount; |
124 | char *ptr; |
125 | int len; |
126 | xfs_daddr_t bno; |
127 | int blksize = mp->m_attr_geo->blksize; |
128 | |
129 | /* no verification of non-crc buffers */ |
130 | if (!xfs_has_crc(mp)) |
131 | return 0; |
132 | |
133 | ptr = bp->b_addr; |
134 | bno = xfs_buf_daddr(bp); |
135 | len = BBTOB(bp->b_length); |
136 | ASSERT(len >= blksize); |
137 | |
138 | while (len > 0) { |
139 | if (check_crc && |
140 | !xfs_verify_cksum(ptr, blksize, XFS_ATTR3_RMT_CRC_OFF)) { |
141 | *failaddr = __this_address; |
142 | return -EFSBADCRC; |
143 | } |
144 | *failaddr = xfs_attr3_rmt_verify(mp, bp, ptr, blksize, bno); |
145 | if (*failaddr) |
146 | return -EFSCORRUPTED; |
147 | len -= blksize; |
148 | ptr += blksize; |
149 | bno += BTOBB(blksize); |
150 | } |
151 | |
152 | if (len != 0) { |
153 | *failaddr = __this_address; |
154 | return -EFSCORRUPTED; |
155 | } |
156 | |
157 | return 0; |
158 | } |
159 | |
160 | static void |
161 | xfs_attr3_rmt_read_verify( |
162 | struct xfs_buf *bp) |
163 | { |
164 | xfs_failaddr_t fa; |
165 | int error; |
166 | |
167 | error = __xfs_attr3_rmt_read_verify(bp, true, &fa); |
168 | if (error) |
169 | xfs_verifier_error(bp, error, fa); |
170 | } |
171 | |
172 | static xfs_failaddr_t |
173 | xfs_attr3_rmt_verify_struct( |
174 | struct xfs_buf *bp) |
175 | { |
176 | xfs_failaddr_t fa; |
177 | int error; |
178 | |
179 | error = __xfs_attr3_rmt_read_verify(bp, false, &fa); |
180 | return error ? fa : NULL; |
181 | } |
182 | |
183 | static void |
184 | xfs_attr3_rmt_write_verify( |
185 | struct xfs_buf *bp) |
186 | { |
187 | struct xfs_mount *mp = bp->b_mount; |
188 | xfs_failaddr_t fa; |
189 | int blksize = mp->m_attr_geo->blksize; |
190 | char *ptr; |
191 | int len; |
192 | xfs_daddr_t bno; |
193 | |
194 | /* no verification of non-crc buffers */ |
195 | if (!xfs_has_crc(mp)) |
196 | return; |
197 | |
198 | ptr = bp->b_addr; |
199 | bno = xfs_buf_daddr(bp); |
200 | len = BBTOB(bp->b_length); |
201 | ASSERT(len >= blksize); |
202 | |
203 | while (len > 0) { |
204 | struct xfs_attr3_rmt_hdr *rmt = (struct xfs_attr3_rmt_hdr *)ptr; |
205 | |
206 | fa = xfs_attr3_rmt_verify(mp, bp, ptr, blksize, bno); |
207 | if (fa) { |
208 | xfs_verifier_error(bp, -EFSCORRUPTED, fa); |
209 | return; |
210 | } |
211 | |
212 | /* |
213 | * Ensure we aren't writing bogus LSNs to disk. See |
214 | * xfs_attr3_rmt_hdr_set() for the explanation. |
215 | */ |
216 | if (rmt->rm_lsn != cpu_to_be64(NULLCOMMITLSN)) { |
217 | xfs_verifier_error(bp, -EFSCORRUPTED, __this_address); |
218 | return; |
219 | } |
220 | xfs_update_cksum(ptr, blksize, XFS_ATTR3_RMT_CRC_OFF); |
221 | |
222 | len -= blksize; |
223 | ptr += blksize; |
224 | bno += BTOBB(blksize); |
225 | } |
226 | |
227 | if (len != 0) |
228 | xfs_verifier_error(bp, -EFSCORRUPTED, __this_address); |
229 | } |
230 | |
231 | const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = { |
232 | .name = "xfs_attr3_rmt" , |
233 | .magic = { 0, cpu_to_be32(XFS_ATTR3_RMT_MAGIC) }, |
234 | .verify_read = xfs_attr3_rmt_read_verify, |
235 | .verify_write = xfs_attr3_rmt_write_verify, |
236 | .verify_struct = xfs_attr3_rmt_verify_struct, |
237 | }; |
238 | |
239 | STATIC int |
240 | xfs_attr3_rmt_hdr_set( |
241 | struct xfs_mount *mp, |
242 | void *ptr, |
243 | xfs_ino_t ino, |
244 | uint32_t offset, |
245 | uint32_t size, |
246 | xfs_daddr_t bno) |
247 | { |
248 | struct xfs_attr3_rmt_hdr *rmt = ptr; |
249 | |
250 | if (!xfs_has_crc(mp)) |
251 | return 0; |
252 | |
253 | rmt->rm_magic = cpu_to_be32(XFS_ATTR3_RMT_MAGIC); |
254 | rmt->rm_offset = cpu_to_be32(offset); |
255 | rmt->rm_bytes = cpu_to_be32(size); |
256 | uuid_copy(&rmt->rm_uuid, &mp->m_sb.sb_meta_uuid); |
257 | rmt->rm_owner = cpu_to_be64(ino); |
258 | rmt->rm_blkno = cpu_to_be64(bno); |
259 | |
260 | /* |
261 | * Remote attribute blocks are written synchronously, so we don't |
262 | * have an LSN that we can stamp in them that makes any sense to log |
263 | * recovery. To ensure that log recovery handles overwrites of these |
264 | * blocks sanely (i.e. once they've been freed and reallocated as some |
265 | * other type of metadata) we need to ensure that the LSN has a value |
266 | * that tells log recovery to ignore the LSN and overwrite the buffer |
267 | * with whatever is in it's log. To do this, we use the magic |
268 | * NULLCOMMITLSN to indicate that the LSN is invalid. |
269 | */ |
270 | rmt->rm_lsn = cpu_to_be64(NULLCOMMITLSN); |
271 | |
272 | return sizeof(struct xfs_attr3_rmt_hdr); |
273 | } |
274 | |
275 | /* |
276 | * Helper functions to copy attribute data in and out of the one disk extents |
277 | */ |
278 | STATIC int |
279 | xfs_attr_rmtval_copyout( |
280 | struct xfs_mount *mp, |
281 | struct xfs_buf *bp, |
282 | struct xfs_inode *dp, |
283 | int *offset, |
284 | int *valuelen, |
285 | uint8_t **dst) |
286 | { |
287 | char *src = bp->b_addr; |
288 | xfs_ino_t ino = dp->i_ino; |
289 | xfs_daddr_t bno = xfs_buf_daddr(bp); |
290 | int len = BBTOB(bp->b_length); |
291 | int blksize = mp->m_attr_geo->blksize; |
292 | |
293 | ASSERT(len >= blksize); |
294 | |
295 | while (len > 0 && *valuelen > 0) { |
296 | int hdr_size = 0; |
297 | int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize); |
298 | |
299 | byte_cnt = min(*valuelen, byte_cnt); |
300 | |
301 | if (xfs_has_crc(mp)) { |
302 | if (xfs_attr3_rmt_hdr_ok(src, ino, *offset, |
303 | byte_cnt, bno)) { |
304 | xfs_alert(mp, |
305 | "remote attribute header mismatch bno/off/len/owner (0x%llx/0x%x/Ox%x/0x%llx)" , |
306 | bno, *offset, byte_cnt, ino); |
307 | xfs_dirattr_mark_sick(dp, XFS_ATTR_FORK); |
308 | return -EFSCORRUPTED; |
309 | } |
310 | hdr_size = sizeof(struct xfs_attr3_rmt_hdr); |
311 | } |
312 | |
313 | memcpy(*dst, src + hdr_size, byte_cnt); |
314 | |
315 | /* roll buffer forwards */ |
316 | len -= blksize; |
317 | src += blksize; |
318 | bno += BTOBB(blksize); |
319 | |
320 | /* roll attribute data forwards */ |
321 | *valuelen -= byte_cnt; |
322 | *dst += byte_cnt; |
323 | *offset += byte_cnt; |
324 | } |
325 | return 0; |
326 | } |
327 | |
328 | STATIC void |
329 | xfs_attr_rmtval_copyin( |
330 | struct xfs_mount *mp, |
331 | struct xfs_buf *bp, |
332 | xfs_ino_t ino, |
333 | int *offset, |
334 | int *valuelen, |
335 | uint8_t **src) |
336 | { |
337 | char *dst = bp->b_addr; |
338 | xfs_daddr_t bno = xfs_buf_daddr(bp); |
339 | int len = BBTOB(bp->b_length); |
340 | int blksize = mp->m_attr_geo->blksize; |
341 | |
342 | ASSERT(len >= blksize); |
343 | |
344 | while (len > 0 && *valuelen > 0) { |
345 | int hdr_size; |
346 | int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize); |
347 | |
348 | byte_cnt = min(*valuelen, byte_cnt); |
349 | hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset, |
350 | byte_cnt, bno); |
351 | |
352 | memcpy(dst + hdr_size, *src, byte_cnt); |
353 | |
354 | /* |
355 | * If this is the last block, zero the remainder of it. |
356 | * Check that we are actually the last block, too. |
357 | */ |
358 | if (byte_cnt + hdr_size < blksize) { |
359 | ASSERT(*valuelen - byte_cnt == 0); |
360 | ASSERT(len == blksize); |
361 | memset(dst + hdr_size + byte_cnt, 0, |
362 | blksize - hdr_size - byte_cnt); |
363 | } |
364 | |
365 | /* roll buffer forwards */ |
366 | len -= blksize; |
367 | dst += blksize; |
368 | bno += BTOBB(blksize); |
369 | |
370 | /* roll attribute data forwards */ |
371 | *valuelen -= byte_cnt; |
372 | *src += byte_cnt; |
373 | *offset += byte_cnt; |
374 | } |
375 | } |
376 | |
377 | /* |
378 | * Read the value associated with an attribute from the out-of-line buffer |
379 | * that we stored it in. |
380 | * |
381 | * Returns 0 on successful retrieval, otherwise an error. |
382 | */ |
383 | int |
384 | xfs_attr_rmtval_get( |
385 | struct xfs_da_args *args) |
386 | { |
387 | struct xfs_bmbt_irec map[ATTR_RMTVALUE_MAPSIZE]; |
388 | struct xfs_mount *mp = args->dp->i_mount; |
389 | struct xfs_buf *bp; |
390 | xfs_dablk_t lblkno = args->rmtblkno; |
391 | uint8_t *dst = args->value; |
392 | int valuelen; |
393 | int nmap; |
394 | int error; |
395 | int blkcnt = args->rmtblkcnt; |
396 | int i; |
397 | int offset = 0; |
398 | |
399 | trace_xfs_attr_rmtval_get(args); |
400 | |
401 | ASSERT(args->valuelen != 0); |
402 | ASSERT(args->rmtvaluelen == args->valuelen); |
403 | |
404 | valuelen = args->rmtvaluelen; |
405 | while (valuelen > 0) { |
406 | nmap = ATTR_RMTVALUE_MAPSIZE; |
407 | error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno, |
408 | blkcnt, map, &nmap, |
409 | XFS_BMAPI_ATTRFORK); |
410 | if (error) |
411 | return error; |
412 | ASSERT(nmap >= 1); |
413 | |
414 | for (i = 0; (i < nmap) && (valuelen > 0); i++) { |
415 | xfs_daddr_t dblkno; |
416 | int dblkcnt; |
417 | |
418 | ASSERT((map[i].br_startblock != DELAYSTARTBLOCK) && |
419 | (map[i].br_startblock != HOLESTARTBLOCK)); |
420 | dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock); |
421 | dblkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount); |
422 | error = xfs_buf_read(mp->m_ddev_targp, dblkno, dblkcnt, |
423 | 0, &bp, &xfs_attr3_rmt_buf_ops); |
424 | if (xfs_metadata_is_sick(error)) |
425 | xfs_dirattr_mark_sick(args->dp, XFS_ATTR_FORK); |
426 | if (error) |
427 | return error; |
428 | |
429 | error = xfs_attr_rmtval_copyout(mp, bp, args->dp, |
430 | &offset, &valuelen, |
431 | &dst); |
432 | xfs_buf_relse(bp); |
433 | if (error) |
434 | return error; |
435 | |
436 | /* roll attribute extent map forwards */ |
437 | lblkno += map[i].br_blockcount; |
438 | blkcnt -= map[i].br_blockcount; |
439 | } |
440 | } |
441 | ASSERT(valuelen == 0); |
442 | return 0; |
443 | } |
444 | |
445 | /* |
446 | * Find a "hole" in the attribute address space large enough for us to drop the |
447 | * new attributes value into |
448 | */ |
449 | int |
450 | xfs_attr_rmt_find_hole( |
451 | struct xfs_da_args *args) |
452 | { |
453 | struct xfs_inode *dp = args->dp; |
454 | struct xfs_mount *mp = dp->i_mount; |
455 | int error; |
456 | int blkcnt; |
457 | xfs_fileoff_t lfileoff = 0; |
458 | |
459 | /* |
460 | * Because CRC enable attributes have headers, we can't just do a |
461 | * straight byte to FSB conversion and have to take the header space |
462 | * into account. |
463 | */ |
464 | blkcnt = xfs_attr3_rmt_blocks(mp, attrlen: args->rmtvaluelen); |
465 | error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff, |
466 | XFS_ATTR_FORK); |
467 | if (error) |
468 | return error; |
469 | |
470 | args->rmtblkno = (xfs_dablk_t)lfileoff; |
471 | args->rmtblkcnt = blkcnt; |
472 | |
473 | return 0; |
474 | } |
475 | |
476 | int |
477 | xfs_attr_rmtval_set_value( |
478 | struct xfs_da_args *args) |
479 | { |
480 | struct xfs_inode *dp = args->dp; |
481 | struct xfs_mount *mp = dp->i_mount; |
482 | struct xfs_bmbt_irec map; |
483 | xfs_dablk_t lblkno; |
484 | uint8_t *src = args->value; |
485 | int blkcnt; |
486 | int valuelen; |
487 | int nmap; |
488 | int error; |
489 | int offset = 0; |
490 | |
491 | /* |
492 | * Roll through the "value", copying the attribute value to the |
493 | * already-allocated blocks. Blocks are written synchronously |
494 | * so that we can know they are all on disk before we turn off |
495 | * the INCOMPLETE flag. |
496 | */ |
497 | lblkno = args->rmtblkno; |
498 | blkcnt = args->rmtblkcnt; |
499 | valuelen = args->rmtvaluelen; |
500 | while (valuelen > 0) { |
501 | struct xfs_buf *bp; |
502 | xfs_daddr_t dblkno; |
503 | int dblkcnt; |
504 | |
505 | ASSERT(blkcnt > 0); |
506 | |
507 | nmap = 1; |
508 | error = xfs_bmapi_read(dp, (xfs_fileoff_t)lblkno, |
509 | blkcnt, &map, &nmap, |
510 | XFS_BMAPI_ATTRFORK); |
511 | if (error) |
512 | return error; |
513 | ASSERT(nmap == 1); |
514 | ASSERT((map.br_startblock != DELAYSTARTBLOCK) && |
515 | (map.br_startblock != HOLESTARTBLOCK)); |
516 | |
517 | dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock), |
518 | dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount); |
519 | |
520 | error = xfs_buf_get(mp->m_ddev_targp, dblkno, dblkcnt, &bp); |
521 | if (error) |
522 | return error; |
523 | bp->b_ops = &xfs_attr3_rmt_buf_ops; |
524 | |
525 | xfs_attr_rmtval_copyin(mp, bp, args->dp->i_ino, &offset, |
526 | &valuelen, &src); |
527 | |
528 | error = xfs_bwrite(bp); /* GROT: NOTE: synchronous write */ |
529 | xfs_buf_relse(bp); |
530 | if (error) |
531 | return error; |
532 | |
533 | |
534 | /* roll attribute extent map forwards */ |
535 | lblkno += map.br_blockcount; |
536 | blkcnt -= map.br_blockcount; |
537 | } |
538 | ASSERT(valuelen == 0); |
539 | return 0; |
540 | } |
541 | |
542 | /* Mark stale any incore buffers for the remote value. */ |
543 | int |
544 | xfs_attr_rmtval_stale( |
545 | struct xfs_inode *ip, |
546 | struct xfs_bmbt_irec *map, |
547 | xfs_buf_flags_t incore_flags) |
548 | { |
549 | struct xfs_mount *mp = ip->i_mount; |
550 | struct xfs_buf *bp; |
551 | int error; |
552 | |
553 | xfs_assert_ilocked(ip, XFS_ILOCK_EXCL); |
554 | |
555 | if (XFS_IS_CORRUPT(mp, map->br_startblock == DELAYSTARTBLOCK) || |
556 | XFS_IS_CORRUPT(mp, map->br_startblock == HOLESTARTBLOCK)) { |
557 | xfs_bmap_mark_sick(ip, XFS_ATTR_FORK); |
558 | return -EFSCORRUPTED; |
559 | } |
560 | |
561 | error = xfs_buf_incore(mp->m_ddev_targp, |
562 | XFS_FSB_TO_DADDR(mp, map->br_startblock), |
563 | XFS_FSB_TO_BB(mp, map->br_blockcount), |
564 | incore_flags, &bp); |
565 | if (error) { |
566 | if (error == -ENOENT) |
567 | return 0; |
568 | return error; |
569 | } |
570 | |
571 | xfs_buf_stale(bp); |
572 | xfs_buf_relse(bp); |
573 | return 0; |
574 | } |
575 | |
576 | /* |
577 | * Find a hole for the attr and store it in the delayed attr context. This |
578 | * initializes the context to roll through allocating an attr extent for a |
579 | * delayed attr operation |
580 | */ |
581 | int |
582 | xfs_attr_rmtval_find_space( |
583 | struct xfs_attr_intent *attr) |
584 | { |
585 | struct xfs_da_args *args = attr->xattri_da_args; |
586 | struct xfs_bmbt_irec *map = &attr->xattri_map; |
587 | int error; |
588 | |
589 | attr->xattri_lblkno = 0; |
590 | attr->xattri_blkcnt = 0; |
591 | args->rmtblkcnt = 0; |
592 | args->rmtblkno = 0; |
593 | memset(map, 0, sizeof(struct xfs_bmbt_irec)); |
594 | |
595 | error = xfs_attr_rmt_find_hole(args); |
596 | if (error) |
597 | return error; |
598 | |
599 | attr->xattri_blkcnt = args->rmtblkcnt; |
600 | attr->xattri_lblkno = args->rmtblkno; |
601 | |
602 | return 0; |
603 | } |
604 | |
605 | /* |
606 | * Write one block of the value associated with an attribute into the |
607 | * out-of-line buffer that we have defined for it. This is similar to a subset |
608 | * of xfs_attr_rmtval_set, but records the current block to the delayed attr |
609 | * context, and leaves transaction handling to the caller. |
610 | */ |
611 | int |
612 | xfs_attr_rmtval_set_blk( |
613 | struct xfs_attr_intent *attr) |
614 | { |
615 | struct xfs_da_args *args = attr->xattri_da_args; |
616 | struct xfs_inode *dp = args->dp; |
617 | struct xfs_bmbt_irec *map = &attr->xattri_map; |
618 | int nmap; |
619 | int error; |
620 | |
621 | nmap = 1; |
622 | error = xfs_bmapi_write(args->trans, dp, |
623 | (xfs_fileoff_t)attr->xattri_lblkno, |
624 | attr->xattri_blkcnt, XFS_BMAPI_ATTRFORK, args->total, |
625 | map, &nmap); |
626 | if (error) |
627 | return error; |
628 | |
629 | ASSERT(nmap == 1); |
630 | ASSERT((map->br_startblock != DELAYSTARTBLOCK) && |
631 | (map->br_startblock != HOLESTARTBLOCK)); |
632 | |
633 | /* roll attribute extent map forwards */ |
634 | attr->xattri_lblkno += map->br_blockcount; |
635 | attr->xattri_blkcnt -= map->br_blockcount; |
636 | |
637 | return 0; |
638 | } |
639 | |
640 | /* |
641 | * Remove the value associated with an attribute by deleting the |
642 | * out-of-line buffer that it is stored on. |
643 | */ |
644 | int |
645 | xfs_attr_rmtval_invalidate( |
646 | struct xfs_da_args *args) |
647 | { |
648 | xfs_dablk_t lblkno; |
649 | int blkcnt; |
650 | int error; |
651 | |
652 | /* |
653 | * Roll through the "value", invalidating the attribute value's blocks. |
654 | */ |
655 | lblkno = args->rmtblkno; |
656 | blkcnt = args->rmtblkcnt; |
657 | while (blkcnt > 0) { |
658 | struct xfs_bmbt_irec map; |
659 | int nmap; |
660 | |
661 | /* |
662 | * Try to remember where we decided to put the value. |
663 | */ |
664 | nmap = 1; |
665 | error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno, |
666 | blkcnt, &map, &nmap, XFS_BMAPI_ATTRFORK); |
667 | if (error) |
668 | return error; |
669 | if (XFS_IS_CORRUPT(args->dp->i_mount, nmap != 1)) { |
670 | xfs_bmap_mark_sick(args->dp, XFS_ATTR_FORK); |
671 | return -EFSCORRUPTED; |
672 | } |
673 | error = xfs_attr_rmtval_stale(args->dp, &map, XBF_TRYLOCK); |
674 | if (error) |
675 | return error; |
676 | |
677 | lblkno += map.br_blockcount; |
678 | blkcnt -= map.br_blockcount; |
679 | } |
680 | return 0; |
681 | } |
682 | |
683 | /* |
684 | * Remove the value associated with an attribute by deleting the out-of-line |
685 | * buffer that it is stored on. Returns -EAGAIN for the caller to refresh the |
686 | * transaction and re-call the function. Callers should keep calling this |
687 | * routine until it returns something other than -EAGAIN. |
688 | */ |
689 | int |
690 | xfs_attr_rmtval_remove( |
691 | struct xfs_attr_intent *attr) |
692 | { |
693 | struct xfs_da_args *args = attr->xattri_da_args; |
694 | int error, done; |
695 | |
696 | /* |
697 | * Unmap value blocks for this attr. |
698 | */ |
699 | error = xfs_bunmapi(args->trans, args->dp, args->rmtblkno, |
700 | args->rmtblkcnt, XFS_BMAPI_ATTRFORK, 1, &done); |
701 | if (error) |
702 | return error; |
703 | |
704 | /* |
705 | * We don't need an explicit state here to pick up where we left off. We |
706 | * can figure it out using the !done return code. The actual value of |
707 | * attr->xattri_dela_state may be some value reminiscent of the calling |
708 | * function, but it's value is irrelevant with in the context of this |
709 | * function. Once we are done here, the next state is set as needed by |
710 | * the parent |
711 | */ |
712 | if (!done) { |
713 | trace_xfs_attr_rmtval_remove_return(attr->xattri_dela_state, |
714 | args->dp); |
715 | return -EAGAIN; |
716 | } |
717 | |
718 | args->rmtblkno = 0; |
719 | args->rmtblkcnt = 0; |
720 | return 0; |
721 | } |
722 | |