]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2bd0ea18 | 2 | /* |
da23017d NS |
3 | * Copyright (c) 2000-2002,2004-2005 Silicon Graphics, Inc. |
4 | * All Rights Reserved. | |
2bd0ea18 NS |
5 | */ |
6 | ||
6b803e5a | 7 | #include "libxfs.h" |
2bd0ea18 NS |
8 | #include "globals.h" |
9 | #include "err_protos.h" | |
fc49813f | 10 | #include "attr_repair.h" |
2bd0ea18 NS |
11 | #include "dinode.h" |
12 | #include "bmap.h" | |
2814f3d6 | 13 | #include "protos.h" |
9a048535 | 14 | #include "dir2.h" |
360f4a2e | 15 | #include "da_util.h" |
2bd0ea18 | 16 | |
b5f5c2d1 | 17 | static int xfs_acl_valid(struct xfs_mount *mp, struct xfs_acl *daclp); |
14290264 | 18 | static int xfs_mac_valid(xfs_mac_label_t *lp); |
2bd0ea18 | 19 | |
9a048535 DC |
20 | /* |
21 | * da node check/verify functions that the attribute tree relies on are first in | |
22 | * the file before the actual attribute code. This used to be shared with the | |
23 | * dir v1 code, but that format is no longer supported yb the userspace | |
24 | * utilities and hence is now specific to the attribute tree implementation. | |
25 | */ | |
9a048535 DC |
26 | |
27 | typedef unsigned char da_freemap_t; | |
28 | ||
9a048535 DC |
29 | /* |
30 | * Allocate a freespace map for directory or attr leaf blocks (1 bit per byte) | |
31 | * 1 == used, 0 == free. | |
32 | */ | |
33 | static da_freemap_t * | |
34 | alloc_da_freemap(struct xfs_mount *mp) | |
35 | { | |
36 | return calloc(1, mp->m_sb.sb_blocksize / NBBY); | |
37 | } | |
38 | ||
39 | /* | |
40 | * Set the he range [start, stop) in the directory freemap. | |
41 | * | |
42 | * Returns 1 if there is a conflict or 0 if everything's good. | |
43 | * | |
44 | * Within a char, the lowest bit of the char represents the byte with | |
45 | * the smallest address | |
46 | */ | |
47 | static int | |
48 | set_da_freemap(xfs_mount_t *mp, da_freemap_t *map, int start, int stop) | |
49 | { | |
50 | const da_freemap_t mask = 0x1; | |
51 | int i; | |
52 | ||
53 | if (start > stop) { | |
54 | /* | |
55 | * allow == relation since [x, x) claims 1 byte | |
56 | */ | |
57 | do_warn(_("bad range claimed [%d, %d) in da block\n"), | |
58 | start, stop); | |
59 | return(1); | |
60 | } | |
61 | ||
62 | if (stop > mp->m_sb.sb_blocksize) { | |
63 | do_warn( | |
64 | _("byte range end [%d %d) in da block larger than blocksize %d\n"), | |
65 | start, stop, mp->m_sb.sb_blocksize); | |
66 | return(1); | |
67 | } | |
68 | ||
69 | for (i = start; i < stop; i ++) { | |
70 | if (map[i / NBBY] & (mask << i % NBBY)) { | |
71 | do_warn(_("multiply claimed byte %d in da block\n"), i); | |
72 | return(1); | |
73 | } | |
74 | map[i / NBBY] |= (mask << i % NBBY); | |
75 | } | |
76 | ||
77 | return(0); | |
78 | } | |
79 | ||
2bd0ea18 | 80 | /* |
dfc130f3 | 81 | * For attribute repair, there are 3 formats to worry about. First, is |
2bd0ea18 NS |
82 | * shortform attributes which reside in the inode. Second is the leaf |
83 | * form, and lastly the btree. Much of this models after the directory | |
dfc130f3 | 84 | * structure so code resembles the directory repair cases. |
2bd0ea18 | 85 | * For shortform case, if an attribute looks corrupt, it is removed. |
dfc130f3 | 86 | * If that leaves the shortform down to 0 attributes, it's okay and |
2bd0ea18 NS |
87 | * will appear to just have a null attribute fork. Some checks are done |
88 | * for validity of the value field based on what the security needs are. | |
43533211 NS |
89 | * Calls will be made to xfs_mac_valid or xfs_acl_valid routines if the |
90 | * security attributes exist. They will be cleared if invalid. | |
91 | * No other values will be checked. The DMF folks do not have current | |
2bd0ea18 NS |
92 | * requirements, but may in the future. |
93 | * | |
94 | * For leaf block attributes, it requires more processing. One sticky | |
dfc130f3 | 95 | * point is that the attributes can be local (within the leaf) or |
2bd0ea18 | 96 | * remote (outside the leaf in other blocks). Thinking of local only |
ff1f79a7 | 97 | * if you get a bad attribute, and want to delete just one, it's a-okay |
2bd0ea18 NS |
98 | * if it remains large enough to still be a leaf block attribute. Otherwise, |
99 | * it may have to be converted to shortform. How to convert this and when | |
100 | * is an issue. This call is happening in Phase3. Phase5 will capture empty | |
5e656dbb | 101 | * blocks, but Phase6 allows you to use the libxfs library which knows |
2bd0ea18 NS |
102 | * how to handle attributes in the kernel for converting formats. What we |
103 | * could do is mark an attribute to be cleared now, but in phase6 somehow | |
104 | * have it cleared for real and then the format changed to shortform if | |
105 | * applicable. Since this requires more work than I anticipate can be | |
106 | * accomplished for the next release, we will instead just say any bad | |
107 | * attribute in the leaf block will make the entire attribute fork be | |
108 | * cleared. The simplest way to do that is to ignore the leaf format, and | |
109 | * call clear_dinode_attr to just make a shortform attribute fork with | |
dfc130f3 | 110 | * zero entries. |
2bd0ea18 NS |
111 | * |
112 | * Another issue with handling repair on leaf attributes is the remote | |
113 | * blocks. To make sure that they look good and are not used multiple times | |
114 | * by the attribute fork, some mechanism to keep track of all them is necessary. | |
115 | * Do this in the future, time permitting. For now, note that there is no | |
116 | * check for remote blocks and their allocations. | |
117 | * | |
118 | * For btree formatted attributes, the model can follow directories. That | |
119 | * would mean go down the tree to the leftmost leaf. From there moving down | |
120 | * the links and processing each. They would call back up the tree, to verify | |
121 | * that the tree structure is okay. Any problems will result in the attribute | |
122 | * fork being emptied and put in shortform format. | |
123 | */ | |
124 | ||
45571fd5 ES |
125 | static int |
126 | attr_namecheck( | |
127 | uint8_t *name, | |
128 | int length) | |
129 | { | |
130 | return namecheck((char *)name, length, false); | |
131 | } | |
132 | ||
2bd0ea18 NS |
133 | /* |
134 | * This routine just checks what security needs are for attribute values | |
135 | * only called when root flag is set, otherwise these names could exist in | |
136 | * in user attribute land without a conflict. | |
137 | * If value is non-zero, then a remote attribute is being passed in | |
138 | */ | |
8b8a6b02 | 139 | static int |
b5f5c2d1 DC |
140 | valuecheck( |
141 | struct xfs_mount *mp, | |
142 | char *namevalue, | |
143 | char *value, | |
144 | int namelen, | |
145 | int valuelen) | |
2bd0ea18 | 146 | { |
dab9b8d6 | 147 | /* for proper alignment issues, get the structs and memmove the values */ |
14290264 | 148 | xfs_mac_label_t macl; |
2bd0ea18 NS |
149 | void *valuep; |
150 | int clearit = 0; | |
151 | ||
d6cfd5ae ES |
152 | if ((namelen == SGI_ACL_FILE_SIZE && |
153 | strncmp(namevalue, SGI_ACL_FILE, SGI_ACL_FILE_SIZE) == 0) || | |
154 | (namelen == SGI_ACL_DEFAULT_SIZE && | |
155 | strncmp(namevalue, SGI_ACL_DEFAULT, SGI_ACL_DEFAULT_SIZE) == 0)) { | |
dfc130f3 | 156 | if (value == NULL) { |
b5f5c2d1 DC |
157 | valuep = malloc(valuelen); |
158 | if (!valuep) | |
159 | do_error(_("No memory for ACL check!\n")); | |
160 | memcpy(valuep, namevalue + namelen, valuelen); | |
2bd0ea18 NS |
161 | } else |
162 | valuep = value; | |
163 | ||
b5f5c2d1 | 164 | if (xfs_acl_valid(mp, valuep) != 0) { |
2bd0ea18 | 165 | clearit = 1; |
507f4e33 NS |
166 | do_warn( |
167 | _("entry contains illegal value in attribute named SGI_ACL_FILE " | |
168 | "or SGI_ACL_DEFAULT\n")); | |
2bd0ea18 | 169 | } |
b5f5c2d1 DC |
170 | |
171 | if (valuep != value) | |
172 | free(valuep); | |
173 | ||
2bd0ea18 NS |
174 | } else if (strncmp(namevalue, SGI_MAC_FILE, SGI_MAC_FILE_SIZE) == 0) { |
175 | if (value == NULL) { | |
dab9b8d6 BN |
176 | memset(&macl, 0, sizeof(xfs_mac_label_t)); |
177 | memmove(&macl, namevalue+namelen, valuelen); | |
2bd0ea18 | 178 | valuep = &macl; |
dfc130f3 | 179 | } else |
2bd0ea18 NS |
180 | valuep = value; |
181 | ||
14290264 | 182 | if (xfs_mac_valid((xfs_mac_label_t *)valuep) != 1) { /* 1 is valid */ |
2bd0ea18 | 183 | /* |
dfc130f3 | 184 | * if sysconf says MAC enabled, |
2bd0ea18 NS |
185 | * temp = mac_from_text("msenhigh/mintlow", NULL) |
186 | * copy it to value, update valuelen, totsize | |
187 | * This causes pushing up or down of all following | |
188 | * attributes, forcing a attribute format change!! | |
189 | * else clearit = 1; | |
190 | */ | |
191 | clearit = 1; | |
507f4e33 NS |
192 | do_warn( |
193 | _("entry contains illegal value in attribute named SGI_MAC_LABEL\n")); | |
2bd0ea18 NS |
194 | } |
195 | } else if (strncmp(namevalue, SGI_CAP_FILE, SGI_CAP_FILE_SIZE) == 0) { | |
14290264 | 196 | if ( valuelen != sizeof(xfs_cap_set_t)) { |
2bd0ea18 | 197 | clearit = 1; |
507f4e33 NS |
198 | do_warn( |
199 | _("entry contains illegal value in attribute named SGI_CAP_FILE\n")); | |
2bd0ea18 NS |
200 | } |
201 | } | |
202 | ||
203 | return(clearit); | |
204 | } | |
205 | ||
206 | ||
207 | /* | |
208 | * this routine validates the attributes in shortform format. | |
209 | * a non-zero return repair value means certain attributes are bogus | |
210 | * and were cleared if possible. Warnings do not generate error conditions | |
211 | * if you cannot modify the structures. repair is set to 1, if anything | |
212 | * was fixed. | |
213 | */ | |
8b8a6b02 | 214 | static int |
2bd0ea18 | 215 | process_shortform_attr( |
b5f5c2d1 | 216 | struct xfs_mount *mp, |
2bd0ea18 NS |
217 | xfs_ino_t ino, |
218 | xfs_dinode_t *dip, | |
dfc130f3 | 219 | int *repair) |
2bd0ea18 NS |
220 | { |
221 | xfs_attr_shortform_t *asf; | |
222 | xfs_attr_sf_entry_t *currententry, *nextentry, *tempentry; | |
223 | int i, junkit; | |
224 | int currentsize, remainingspace; | |
dfc130f3 | 225 | |
2bd0ea18 NS |
226 | *repair = 0; |
227 | ||
46eca962 | 228 | asf = (xfs_attr_shortform_t *) XFS_DFORK_APTR(dip); |
2bd0ea18 NS |
229 | |
230 | /* Assumption: hdr.totsize is less than a leaf block and was checked | |
dfc130f3 | 231 | * by lclinode for valid sizes. Check the count though. |
2bd0ea18 | 232 | */ |
5e656dbb | 233 | if (asf->hdr.count == 0) |
2bd0ea18 | 234 | /* then the total size should just be the header length */ |
5e656dbb | 235 | if (be16_to_cpu(asf->hdr.totsize) != sizeof(xfs_attr_sf_hdr_t)) { |
2bd0ea18 NS |
236 | /* whoops there's a discrepancy. Clear the hdr */ |
237 | if (!no_modify) { | |
507f4e33 | 238 | do_warn( |
5d1b7f0f | 239 | _("there are no attributes in the fork for inode %" PRIu64 "\n"), |
507f4e33 | 240 | ino); |
f8149110 | 241 | asf->hdr.totsize = |
5e656dbb | 242 | cpu_to_be16(sizeof(xfs_attr_sf_hdr_t)); |
2bd0ea18 | 243 | *repair = 1; |
dfc130f3 | 244 | return(1); |
2bd0ea18 | 245 | } else { |
507f4e33 | 246 | do_warn( |
5d1b7f0f | 247 | _("would junk the attribute fork since count is 0 for inode %" PRIu64 "\n"), |
507f4e33 | 248 | ino); |
2bd0ea18 NS |
249 | return(1); |
250 | } | |
dfc130f3 RC |
251 | } |
252 | ||
253 | currentsize = sizeof(xfs_attr_sf_hdr_t); | |
5e656dbb | 254 | remainingspace = be16_to_cpu(asf->hdr.totsize) - currentsize; |
2bd0ea18 | 255 | nextentry = &asf->list[0]; |
5e656dbb | 256 | for (i = 0; i < asf->hdr.count; i++) { |
2bd0ea18 NS |
257 | currententry = nextentry; |
258 | junkit = 0; | |
259 | ||
260 | /* don't go off the end if the hdr.count was off */ | |
dfc130f3 | 261 | if ((currentsize + (sizeof(xfs_attr_sf_entry_t) - 1)) > |
5e656dbb | 262 | be16_to_cpu(asf->hdr.totsize)) |
2bd0ea18 NS |
263 | break; /* get out and reset count and totSize */ |
264 | ||
265 | /* if the namelen is 0, can't get to the rest of the entries */ | |
5e656dbb | 266 | if (currententry->namelen == 0) { |
507f4e33 | 267 | do_warn(_("zero length name entry in attribute fork,")); |
2bd0ea18 | 268 | if (!no_modify) { |
507f4e33 | 269 | do_warn( |
5d1b7f0f | 270 | _(" truncating attributes for inode %" PRIu64 " to %d\n"), ino, i); |
2bd0ea18 | 271 | *repair = 1; |
dfc130f3 | 272 | break; /* and then update hdr fields */ |
2bd0ea18 | 273 | } else { |
507f4e33 | 274 | do_warn( |
5d1b7f0f | 275 | _(" would truncate attributes for inode %" PRIu64 " to %d\n"), ino, i); |
2bd0ea18 NS |
276 | break; |
277 | } | |
278 | } else { | |
279 | /* It's okay to have a 0 length valuelen, but do a | |
280 | * rough check to make sure we haven't gone outside of | |
281 | * totsize. | |
282 | */ | |
5e656dbb BN |
283 | if (remainingspace < currententry->namelen || |
284 | ((remainingspace - currententry-> | |
285 | namelen) < currententry->valuelen)) { | |
507f4e33 | 286 | do_warn( |
5d1b7f0f | 287 | _("name or value attribute lengths are too large,\n")); |
2bd0ea18 | 288 | if (!no_modify) { |
507f4e33 | 289 | do_warn( |
5d1b7f0f | 290 | _(" truncating attributes for inode %" PRIu64 " to %d\n"), |
507f4e33 | 291 | ino, i); |
dfc130f3 | 292 | *repair = 1; |
2bd0ea18 NS |
293 | break; /* and then update hdr fields */ |
294 | } else { | |
507f4e33 | 295 | do_warn( |
5d1b7f0f | 296 | _(" would truncate attributes for inode %" PRIu64 " to %d\n"), |
dfc130f3 | 297 | ino, i); |
2bd0ea18 | 298 | break; |
dfc130f3 | 299 | } |
2bd0ea18 NS |
300 | } |
301 | } | |
dfc130f3 | 302 | |
45571fd5 ES |
303 | /* namecheck checks for null chars in attr names. */ |
304 | if (attr_namecheck(currententry->nameval, | |
305 | currententry->namelen)) { | |
507f4e33 NS |
306 | do_warn( |
307 | _("entry contains illegal character in shortform attribute name\n")); | |
2bd0ea18 NS |
308 | junkit = 1; |
309 | } | |
310 | ||
5e656dbb | 311 | if (currententry->flags & XFS_ATTR_INCOMPLETE) { |
507f4e33 NS |
312 | do_warn( |
313 | _("entry has INCOMPLETE flag on in shortform attribute\n")); | |
2bd0ea18 NS |
314 | junkit = 1; |
315 | } | |
316 | ||
317 | /* Only check values for root security attributes */ | |
5e656dbb | 318 | if (currententry->flags & XFS_ATTR_ROOT) |
50aeb09d ES |
319 | junkit |= valuecheck(mp, |
320 | (char *)¤tentry->nameval[0], | |
f8149110 | 321 | NULL, currententry->namelen, |
5e656dbb | 322 | currententry->valuelen); |
2bd0ea18 | 323 | |
dfc130f3 | 324 | remainingspace = remainingspace - |
5e656dbb | 325 | XFS_ATTR_SF_ENTSIZE(currententry); |
2bd0ea18 NS |
326 | |
327 | if (junkit) { | |
328 | if (!no_modify) { | |
329 | /* get rid of only this entry */ | |
507f4e33 | 330 | do_warn( |
5d1b7f0f | 331 | _("removing attribute entry %d for inode %" PRIu64 "\n"), |
507f4e33 | 332 | i, ino); |
2bd0ea18 | 333 | tempentry = (xfs_attr_sf_entry_t *) |
ee6cd73e | 334 | ((intptr_t) currententry + |
2bd0ea18 NS |
335 | XFS_ATTR_SF_ENTSIZE(currententry)); |
336 | memmove(currententry,tempentry,remainingspace); | |
5e656dbb | 337 | asf->hdr.count -= 1; |
2bd0ea18 NS |
338 | i--; /* no worries, it will wrap back to 0 */ |
339 | *repair = 1; | |
340 | continue; /* go back up now */ | |
dfc130f3 | 341 | } else { |
507f4e33 | 342 | do_warn( |
5d1b7f0f | 343 | _("would remove attribute entry %d for inode %" PRIu64 "\n"), |
507f4e33 | 344 | i, ino); |
dfc130f3 RC |
345 | } |
346 | } | |
2bd0ea18 NS |
347 | |
348 | /* Let's get ready for the next entry... */ | |
ee6cd73e | 349 | nextentry = (xfs_attr_sf_entry_t *)((intptr_t) nextentry + |
5e656dbb | 350 | XFS_ATTR_SF_ENTSIZE(currententry)); |
2bd0ea18 | 351 | currentsize = currentsize + XFS_ATTR_SF_ENTSIZE(currententry); |
dfc130f3 | 352 | |
507f4e33 | 353 | } /* end the loop */ |
2bd0ea18 | 354 | |
5e656dbb | 355 | if (asf->hdr.count != i) { |
2bd0ea18 | 356 | if (no_modify) { |
5d1b7f0f CH |
357 | do_warn( |
358 | _("would have corrected attribute entry count in inode %" PRIu64 " from %d to %d\n"), | |
5e656dbb | 359 | ino, asf->hdr.count, i); |
2bd0ea18 | 360 | } else { |
5d1b7f0f CH |
361 | do_warn( |
362 | _("corrected attribute entry count in inode %" PRIu64 ", was %d, now %d\n"), | |
5e656dbb BN |
363 | ino, asf->hdr.count, i); |
364 | asf->hdr.count = i; | |
2bd0ea18 NS |
365 | *repair = 1; |
366 | } | |
367 | } | |
dfc130f3 | 368 | |
2bd0ea18 | 369 | /* ASSUMPTION: currentsize <= totsize */ |
5e656dbb | 370 | if (be16_to_cpu(asf->hdr.totsize) != currentsize) { |
2bd0ea18 | 371 | if (no_modify) { |
5d1b7f0f CH |
372 | do_warn( |
373 | _("would have corrected attribute totsize in inode %" PRIu64 " from %d to %d\n"), | |
5e656dbb | 374 | ino, be16_to_cpu(asf->hdr.totsize), |
507f4e33 | 375 | currentsize); |
2bd0ea18 | 376 | } else { |
5d1b7f0f CH |
377 | do_warn( |
378 | _("corrected attribute entry totsize in inode %" PRIu64 ", was %d, now %d\n"), | |
5e656dbb | 379 | ino, be16_to_cpu(asf->hdr.totsize), |
507f4e33 | 380 | currentsize); |
5e656dbb | 381 | asf->hdr.totsize = cpu_to_be16(currentsize); |
2bd0ea18 NS |
382 | *repair = 1; |
383 | } | |
384 | } | |
385 | ||
386 | return(*repair); | |
387 | } | |
388 | ||
389 | /* This routine brings in blocks from disk one by one and assembles them | |
390 | * in the value buffer. If get_bmapi gets smarter later to return an extent | |
391 | * or list of extents, that would be great. For now, we don't expect too | |
392 | * many blocks per remote value, so one by one is sufficient. | |
393 | */ | |
394 | static int | |
395 | rmtval_get(xfs_mount_t *mp, xfs_ino_t ino, blkmap_t *blkmap, | |
396 | xfs_dablk_t blocknum, int valuelen, char* value) | |
397 | { | |
5a35bf2c | 398 | xfs_fsblock_t bno; |
2bd0ea18 NS |
399 | xfs_buf_t *bp; |
400 | int clearit = 0, i = 0, length = 0, amountdone = 0; | |
3a1d34e2 DC |
401 | int hdrsize = 0; |
402 | ||
403 | if (xfs_sb_version_hascrc(&mp->m_sb)) | |
404 | hdrsize = sizeof(struct xfs_attr3_rmt_hdr); | |
dfc130f3 | 405 | |
2bd0ea18 | 406 | /* ASSUMPTION: valuelen is a valid number, so use it for looping */ |
dfc130f3 | 407 | /* Note that valuelen is not a multiple of blocksize */ |
2bd0ea18 NS |
408 | while (amountdone < valuelen) { |
409 | bno = blkmap_get(blkmap, blocknum + i); | |
5a35bf2c | 410 | if (bno == NULLFSBLOCK) { |
5d1b7f0f CH |
411 | do_warn( |
412 | _("remote block for attributes of inode %" PRIu64 " is missing\n"), ino); | |
2bd0ea18 NS |
413 | clearit = 1; |
414 | break; | |
415 | } | |
416 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, bno), | |
3a1d34e2 DC |
417 | XFS_FSB_TO_BB(mp, 1), 0, |
418 | &xfs_attr3_rmt_buf_ops); | |
2bd0ea18 | 419 | if (!bp) { |
5d1b7f0f CH |
420 | do_warn( |
421 | _("can't read remote block for attributes of inode %" PRIu64 "\n"), ino); | |
2bd0ea18 NS |
422 | clearit = 1; |
423 | break; | |
424 | } | |
3a1d34e2 | 425 | |
12b53197 | 426 | if (bp->b_error == -EFSBADCRC || bp->b_error == -EFSCORRUPTED) { |
3a1d34e2 DC |
427 | do_warn( |
428 | _("Corrupt remote block for attributes of inode %" PRIu64 "\n"), ino); | |
41b465fa | 429 | libxfs_putbuf(bp); |
3a1d34e2 DC |
430 | clearit = 1; |
431 | break; | |
432 | } | |
433 | ||
135e4bfe | 434 | ASSERT(mp->m_sb.sb_blocksize == bp->b_bcount); |
3a1d34e2 | 435 | |
68d16907 | 436 | length = min(bp->b_bcount - hdrsize, valuelen - amountdone); |
259d52fc | 437 | memmove(value, bp->b_addr + hdrsize, length); |
2bd0ea18 NS |
438 | amountdone += length; |
439 | value += length; | |
440 | i++; | |
441 | libxfs_putbuf(bp); | |
442 | } | |
443 | return (clearit); | |
444 | } | |
445 | ||
2bd0ea18 NS |
446 | /* The block is read in. The magic number and forward / backward |
447 | * links are checked by the caller process_leaf_attr. | |
448 | * If any problems occur the routine returns with non-zero. In | |
449 | * this case the next step is to clear the attribute fork, by | |
450 | * changing it to shortform and zeroing it out. Forkoff need not | |
dfc130f3 | 451 | * be changed. |
2bd0ea18 NS |
452 | */ |
453 | ||
5e656dbb BN |
454 | static int |
455 | process_leaf_attr_local( | |
b5f5c2d1 | 456 | struct xfs_mount *mp, |
5e656dbb BN |
457 | xfs_attr_leafblock_t *leaf, |
458 | int i, | |
459 | xfs_attr_leaf_entry_t *entry, | |
460 | xfs_dahash_t last_hashval, | |
461 | xfs_dablk_t da_bno, | |
462 | xfs_ino_t ino) | |
463 | { | |
464 | xfs_attr_leaf_name_local_t *local; | |
465 | ||
a24374f4 | 466 | local = xfs_attr3_leaf_name_local(leaf, i); |
45571fd5 | 467 | if (local->namelen == 0 || attr_namecheck(local->nameval, |
5e656dbb | 468 | local->namelen)) { |
5d1b7f0f CH |
469 | do_warn( |
470 | _("attribute entry %d in attr block %u, inode %" PRIu64 " has bad name (namelen = %d)\n"), | |
5e656dbb BN |
471 | i, da_bno, ino, local->namelen); |
472 | return -1; | |
473 | } | |
474 | ||
475 | /* Check on the hash value. Checking order of values | |
476 | * is not necessary, since one wrong clears the whole | |
477 | * fork. If the ordering's wrong, it's caught here or | |
478 | * the kernel code has a bug with transaction logging | |
479 | * or attributes itself. Being paranoid, let's check | |
480 | * ordering anyway in case both the name value and the | |
481 | * hashvalue were wrong but matched. Unlikely, however. | |
482 | */ | |
483 | if (be32_to_cpu(entry->hashval) != libxfs_da_hashname( | |
484 | &local->nameval[0], local->namelen) || | |
485 | be32_to_cpu(entry->hashval) < last_hashval) { | |
5d1b7f0f CH |
486 | do_warn( |
487 | _("bad hashvalue for attribute entry %d in attr block %u, inode %" PRIu64 "\n"), | |
488 | i, da_bno, ino); | |
5e656dbb BN |
489 | return -1; |
490 | } | |
491 | ||
492 | /* Only check values for root security attributes */ | |
493 | if (entry->flags & XFS_ATTR_ROOT) { | |
f8149110 | 494 | if (valuecheck(mp, (char *)&local->nameval[0], NULL, |
5e656dbb | 495 | local->namelen, be16_to_cpu(local->valuelen))) { |
5d1b7f0f CH |
496 | do_warn( |
497 | _("bad security value for attribute entry %d in attr block %u, inode %" PRIu64 "\n"), | |
5e656dbb BN |
498 | i, da_bno, ino); |
499 | return -1; | |
500 | } | |
501 | } | |
f8149110 | 502 | return xfs_attr_leaf_entsize_local(local->namelen, |
5e656dbb BN |
503 | be16_to_cpu(local->valuelen)); |
504 | } | |
505 | ||
506 | static int | |
507 | process_leaf_attr_remote( | |
508 | xfs_attr_leafblock_t *leaf, | |
509 | int i, | |
510 | xfs_attr_leaf_entry_t *entry, | |
511 | xfs_dahash_t last_hashval, | |
512 | xfs_dablk_t da_bno, | |
513 | xfs_ino_t ino, | |
514 | xfs_mount_t *mp, | |
515 | blkmap_t *blkmap) | |
516 | { | |
517 | xfs_attr_leaf_name_remote_t *remotep; | |
518 | char* value; | |
519 | ||
a24374f4 | 520 | remotep = xfs_attr3_leaf_name_remote(leaf, i); |
5e656dbb | 521 | |
45571fd5 | 522 | if (remotep->namelen == 0 || attr_namecheck(remotep->name, |
f8149110 JT |
523 | remotep->namelen) || |
524 | be32_to_cpu(entry->hashval) != | |
525 | libxfs_da_hashname((unsigned char *)&remotep->name[0], | |
5e656dbb BN |
526 | remotep->namelen) || |
527 | be32_to_cpu(entry->hashval) < last_hashval || | |
528 | be32_to_cpu(remotep->valueblk) == 0) { | |
5d1b7f0f CH |
529 | do_warn( |
530 | _("inconsistent remote attribute entry %d in attr block %u, ino %" PRIu64 "\n"), i, da_bno, ino); | |
5e656dbb BN |
531 | return -1; |
532 | } | |
533 | ||
5e656dbb BN |
534 | value = malloc(be32_to_cpu(remotep->valuelen)); |
535 | if (value == NULL) { | |
5d1b7f0f CH |
536 | do_warn( |
537 | _("cannot malloc enough for remotevalue attribute for inode %" PRIu64 "\n"), | |
538 | ino); | |
5e656dbb BN |
539 | do_warn(_("SKIPPING this remote attribute\n")); |
540 | goto out; | |
541 | } | |
542 | if (rmtval_get(mp, ino, blkmap, be32_to_cpu(remotep->valueblk), | |
543 | be32_to_cpu(remotep->valuelen), value)) { | |
5d1b7f0f CH |
544 | do_warn( |
545 | _("remote attribute get failed for entry %d, inode %" PRIu64 "\n"), | |
546 | i, ino); | |
5e656dbb BN |
547 | goto bad_free_out; |
548 | } | |
930af869 DW |
549 | if ((entry->flags & XFS_ATTR_ROOT) && |
550 | valuecheck(mp, (char *)&remotep->name[0], value, remotep->namelen, | |
5e656dbb | 551 | be32_to_cpu(remotep->valuelen))) { |
5d1b7f0f CH |
552 | do_warn( |
553 | _("remote attribute value check failed for entry %d, inode %" PRIu64 "\n"), | |
554 | i, ino); | |
5e656dbb BN |
555 | goto bad_free_out; |
556 | } | |
557 | free(value); | |
558 | out: | |
56b2de80 | 559 | return xfs_attr_leaf_entsize_remote(remotep->namelen); |
5e656dbb BN |
560 | |
561 | bad_free_out: | |
562 | free(value); | |
563 | return -1; | |
564 | } | |
565 | ||
8b8a6b02 | 566 | static int |
2bd0ea18 NS |
567 | process_leaf_attr_block( |
568 | xfs_mount_t *mp, | |
569 | xfs_attr_leafblock_t *leaf, | |
570 | xfs_dablk_t da_bno, | |
571 | xfs_ino_t ino, | |
572 | blkmap_t *blkmap, | |
573 | xfs_dahash_t last_hashval, | |
574 | xfs_dahash_t *current_hashval, | |
dfc130f3 | 575 | int *repair) |
2bd0ea18 NS |
576 | { |
577 | xfs_attr_leaf_entry_t *entry; | |
2bd0ea18 | 578 | int i, start, stop, clearit, usedbs, firstb, thissize; |
356357a4 | 579 | da_freemap_t *attr_freemap; |
658ac3e3 | 580 | struct xfs_attr3_icleaf_hdr leafhdr; |
2bd0ea18 | 581 | |
19ebedcf | 582 | xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); |
2bd0ea18 | 583 | clearit = usedbs = 0; |
dfc130f3 | 584 | firstb = mp->m_sb.sb_blocksize; |
658ac3e3 | 585 | stop = xfs_attr3_leaf_hdr_size(leaf); |
2bd0ea18 NS |
586 | |
587 | /* does the count look sorta valid? */ | |
f7140161 ES |
588 | if (!leafhdr.count || |
589 | leafhdr.count * sizeof(xfs_attr_leaf_entry_t) + stop > | |
ff105f75 | 590 | mp->m_sb.sb_blocksize) { |
507f4e33 | 591 | do_warn( |
5d1b7f0f | 592 | _("bad attribute count %d in attr block %u, inode %" PRIu64 "\n"), |
658ac3e3 DC |
593 | leafhdr.count, da_bno, ino); |
594 | return 1; | |
2bd0ea18 | 595 | } |
dfc130f3 | 596 | |
356357a4 | 597 | attr_freemap = alloc_da_freemap(mp); |
2bd0ea18 | 598 | (void) set_da_freemap(mp, attr_freemap, 0, stop); |
dfc130f3 | 599 | |
2bd0ea18 | 600 | /* go thru each entry checking for problems */ |
658ac3e3 DC |
601 | for (i = 0, entry = xfs_attr3_leaf_entryp(leaf); |
602 | i < leafhdr.count; i++, entry++) { | |
dfc130f3 | 603 | |
2bd0ea18 | 604 | /* check if index is within some boundary. */ |
ff105f75 | 605 | if (be16_to_cpu(entry->nameidx) > mp->m_sb.sb_blocksize) { |
507f4e33 | 606 | do_warn( |
5d1b7f0f | 607 | _("bad attribute nameidx %d in attr block %u, inode %" PRIu64 "\n"), |
5e656dbb | 608 | be16_to_cpu(entry->nameidx), da_bno, ino); |
2bd0ea18 NS |
609 | clearit = 1; |
610 | break; | |
507f4e33 | 611 | } |
2bd0ea18 | 612 | |
5e656dbb | 613 | if (entry->flags & XFS_ATTR_INCOMPLETE) { |
2bd0ea18 | 614 | /* we are inconsistent state. get rid of us */ |
507f4e33 | 615 | do_warn( |
5d1b7f0f | 616 | _("attribute entry #%d in attr block %u, inode %" PRIu64 " is INCOMPLETE\n"), |
2bd0ea18 NS |
617 | i, da_bno, ino); |
618 | clearit = 1; | |
619 | break; | |
507f4e33 | 620 | } |
2bd0ea18 NS |
621 | |
622 | /* mark the entry used */ | |
ee6cd73e | 623 | start = (intptr_t)entry - (intptr_t)leaf; |
2bd0ea18 NS |
624 | stop = start + sizeof(xfs_attr_leaf_entry_t); |
625 | if (set_da_freemap(mp, attr_freemap, start, stop)) { | |
507f4e33 | 626 | do_warn( |
5d1b7f0f | 627 | _("attribute entry %d in attr block %u, inode %" PRIu64 " claims already used space\n"), |
507f4e33 | 628 | i, da_bno, ino); |
2bd0ea18 NS |
629 | clearit = 1; |
630 | break; /* got an overlap */ | |
507f4e33 | 631 | } |
2bd0ea18 | 632 | |
b5f5c2d1 DC |
633 | if (entry->flags & XFS_ATTR_LOCAL) |
634 | thissize = process_leaf_attr_local(mp, leaf, i, entry, | |
5e656dbb BN |
635 | last_hashval, da_bno, ino); |
636 | else | |
637 | thissize = process_leaf_attr_remote(leaf, i, entry, | |
638 | last_hashval, da_bno, ino, | |
639 | mp, blkmap); | |
640 | if (thissize < 0) { | |
b5f5c2d1 | 641 | clearit = 1; |
5e656dbb | 642 | break; |
2bd0ea18 NS |
643 | } |
644 | ||
5e656dbb | 645 | *current_hashval = last_hashval = be32_to_cpu(entry->hashval); |
2bd0ea18 | 646 | |
5e656dbb BN |
647 | if (set_da_freemap(mp, attr_freemap, be16_to_cpu(entry->nameidx), |
648 | be16_to_cpu(entry->nameidx) + thissize)) { | |
5d1b7f0f CH |
649 | do_warn( |
650 | _("attribute entry %d in attr block %u, inode %" PRIu64 " claims used space\n"), | |
2bd0ea18 NS |
651 | i, da_bno, ino); |
652 | clearit = 1; | |
653 | break; /* got an overlap */ | |
dfc130f3 | 654 | } |
2bd0ea18 | 655 | usedbs += thissize; |
5e656dbb BN |
656 | if (be16_to_cpu(entry->nameidx) < firstb) |
657 | firstb = be16_to_cpu(entry->nameidx); | |
2bd0ea18 NS |
658 | |
659 | } /* end the loop */ | |
660 | ||
661 | if (!clearit) { | |
662 | /* verify the header information is correct */ | |
663 | ||
664 | /* if the holes flag is set, don't reset first_used unless it's | |
665 | * pointing to used bytes. we're being conservative here | |
dfc130f3 | 666 | * since the block will get compacted anyhow by the kernel. |
2bd0ea18 NS |
667 | */ |
668 | ||
f8149110 | 669 | if ((leafhdr.holes == 0 && |
658ac3e3 DC |
670 | firstb != leafhdr.firstused) || |
671 | leafhdr.firstused > firstb) { | |
2bd0ea18 | 672 | if (!no_modify) { |
507f4e33 NS |
673 | do_warn( |
674 | _("- resetting first used heap value from %d to %d in " | |
5d1b7f0f | 675 | "block %u of attribute fork of inode %" PRIu64 "\n"), |
f8149110 | 676 | leafhdr.firstused, |
5e656dbb | 677 | firstb, da_bno, ino); |
658ac3e3 | 678 | leafhdr.firstused = firstb; |
2bd0ea18 NS |
679 | *repair = 1; |
680 | } else { | |
507f4e33 NS |
681 | do_warn( |
682 | _("- would reset first used value from %d to %d in " | |
5d1b7f0f | 683 | "block %u of attribute fork of inode %" PRIu64 "\n"), |
f8149110 | 684 | leafhdr.firstused, |
5e656dbb | 685 | firstb, da_bno, ino); |
2bd0ea18 NS |
686 | } |
687 | } | |
688 | ||
658ac3e3 | 689 | if (usedbs != leafhdr.usedbytes) { |
2bd0ea18 | 690 | if (!no_modify) { |
507f4e33 NS |
691 | do_warn( |
692 | _("- resetting usedbytes cnt from %d to %d in " | |
5d1b7f0f | 693 | "block %u of attribute fork of inode %" PRIu64 "\n"), |
f8149110 | 694 | leafhdr.usedbytes, |
5e656dbb | 695 | usedbs, da_bno, ino); |
658ac3e3 | 696 | leafhdr.usedbytes = usedbs; |
2bd0ea18 NS |
697 | *repair = 1; |
698 | } else { | |
507f4e33 NS |
699 | do_warn( |
700 | _("- would reset usedbytes cnt from %d to %d in " | |
5d1b7f0f | 701 | "block %u of attribute fork of %" PRIu64 "\n"), |
f8149110 | 702 | leafhdr.usedbytes, |
5e656dbb | 703 | usedbs, da_bno, ino); |
2bd0ea18 NS |
704 | } |
705 | } | |
706 | ||
707 | /* there's a lot of work in process_leaf_dir_block to go thru | |
708 | * checking for holes and compacting if appropiate. I don't think | |
709 | * attributes need all that, so let's just leave the holes. If | |
710 | * we discover later that this is a good place to do compaction | |
dfc130f3 | 711 | * we can add it then. |
2bd0ea18 NS |
712 | */ |
713 | } | |
aedb9c17 DW |
714 | /* |
715 | * If we're just going to zap the block, don't pretend like we | |
716 | * repaired it, because repairing the block stops the clear | |
717 | * operation. | |
718 | */ | |
719 | if (clearit) | |
720 | *repair = 0; | |
658ac3e3 | 721 | if (*repair) |
19ebedcf | 722 | xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, leaf, &leafhdr); |
356357a4 CH |
723 | |
724 | free(attr_freemap); | |
2bd0ea18 NS |
725 | return (clearit); /* and repair */ |
726 | } | |
727 | ||
728 | ||
729 | /* | |
730 | * returns 0 if the attribute fork is ok, 1 if it has to be junked. | |
731 | */ | |
8b8a6b02 | 732 | static int |
2bd0ea18 NS |
733 | process_leaf_attr_level(xfs_mount_t *mp, |
734 | da_bt_cursor_t *da_cursor) | |
735 | { | |
736 | int repair; | |
737 | xfs_attr_leafblock_t *leaf; | |
738 | xfs_buf_t *bp; | |
739 | xfs_ino_t ino; | |
5a35bf2c | 740 | xfs_fsblock_t dev_bno; |
2bd0ea18 NS |
741 | xfs_dablk_t da_bno; |
742 | xfs_dablk_t prev_bno; | |
743 | xfs_dahash_t current_hashval = 0; | |
744 | xfs_dahash_t greatest_hashval; | |
658ac3e3 | 745 | struct xfs_attr3_icleaf_hdr leafhdr; |
2bd0ea18 NS |
746 | |
747 | da_bno = da_cursor->level[0].bno; | |
3eafb268 | 748 | ino = da_cursor->ino; |
ce746a29 DW |
749 | /* |
750 | * 0 is the root block and no block | |
751 | * pointer can point to the root block of the btree | |
752 | */ | |
753 | if (da_bno == 0) { | |
754 | do_warn( | |
755 | _("btree cycle detected in attribute fork for inode %" PRIu64 "\n"), | |
756 | ino); | |
757 | goto error_out; | |
758 | } | |
759 | ||
2bd0ea18 NS |
760 | prev_bno = 0; |
761 | ||
762 | do { | |
763 | repair = 0; | |
764 | dev_bno = blkmap_get(da_cursor->blkmap, da_bno); | |
5a35bf2c | 765 | if (dev_bno == NULLFSBLOCK) { |
5d1b7f0f CH |
766 | do_warn( |
767 | _("can't map block %u for attribute fork for inode %" PRIu64 "\n"), | |
768 | da_bno, ino); | |
dfc130f3 | 769 | goto error_out; |
2bd0ea18 NS |
770 | } |
771 | ||
772 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, dev_bno), | |
3a1d34e2 DC |
773 | XFS_FSB_TO_BB(mp, 1), 0, |
774 | &xfs_attr3_leaf_buf_ops); | |
2bd0ea18 | 775 | if (!bp) { |
5d1b7f0f CH |
776 | do_warn( |
777 | _("can't read file block %u (fsbno %" PRIu64 ") for attribute fork of inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
778 | da_bno, dev_bno, ino); |
779 | goto error_out; | |
780 | } | |
781 | ||
658ac3e3 | 782 | leaf = bp->b_addr; |
19ebedcf | 783 | xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); |
2bd0ea18 NS |
784 | |
785 | /* check magic number for leaf directory btree block */ | |
658ac3e3 DC |
786 | if (!(leafhdr.magic == XFS_ATTR_LEAF_MAGIC || |
787 | leafhdr.magic == XFS_ATTR3_LEAF_MAGIC)) { | |
5d1b7f0f CH |
788 | do_warn( |
789 | _("bad attribute leaf magic %#x for inode %" PRIu64 "\n"), | |
658ac3e3 | 790 | leafhdr.magic, ino); |
2bd0ea18 NS |
791 | libxfs_putbuf(bp); |
792 | goto error_out; | |
793 | } | |
794 | ||
795 | /* | |
ff1f79a7 | 796 | * for each block, process the block, verify its path, |
2bd0ea18 NS |
797 | * then get next block. update cursor values along the way |
798 | */ | |
799 | if (process_leaf_attr_block(mp, leaf, da_bno, ino, | |
800 | da_cursor->blkmap, current_hashval, | |
801 | &greatest_hashval, &repair)) { | |
802 | libxfs_putbuf(bp); | |
803 | goto error_out; | |
804 | } | |
805 | ||
806 | /* | |
807 | * index can be set to hdr.count so match the | |
808 | * indexes of the interior blocks -- which at the | |
809 | * end of the block will point to 1 after the final | |
810 | * real entry in the block | |
811 | */ | |
812 | da_cursor->level[0].hashval = greatest_hashval; | |
813 | da_cursor->level[0].bp = bp; | |
814 | da_cursor->level[0].bno = da_bno; | |
658ac3e3 | 815 | da_cursor->level[0].index = leafhdr.count; |
dfc130f3 | 816 | da_cursor->level[0].dirty = repair; |
2bd0ea18 | 817 | |
658ac3e3 | 818 | if (leafhdr.back != prev_bno) { |
5d1b7f0f CH |
819 | do_warn( |
820 | _("bad sibling back pointer for block %u in attribute fork for inode %" PRIu64 "\n"), | |
507f4e33 | 821 | da_bno, ino); |
2bd0ea18 NS |
822 | libxfs_putbuf(bp); |
823 | goto error_out; | |
824 | } | |
825 | ||
826 | prev_bno = da_bno; | |
658ac3e3 | 827 | da_bno = leafhdr.forw; |
2bd0ea18 | 828 | |
360f4a2e ES |
829 | if (da_bno != 0) { |
830 | if (verify_da_path(mp, da_cursor, 0, XFS_ATTR_FORK)) { | |
831 | libxfs_putbuf(bp); | |
832 | goto error_out; | |
833 | } | |
2bd0ea18 NS |
834 | } |
835 | ||
836 | current_hashval = greatest_hashval; | |
e41dd632 ES |
837 | /* |
838 | * If block looks ok but CRC didn't match, make sure to | |
839 | * recompute it. | |
840 | */ | |
841 | if (!no_modify && bp->b_error == -EFSBADCRC) | |
842 | repair++; | |
2bd0ea18 | 843 | |
3a1d34e2 | 844 | if (repair && !no_modify) |
2bd0ea18 | 845 | libxfs_writebuf(bp, 0); |
3a1d34e2 | 846 | else |
2bd0ea18 | 847 | libxfs_putbuf(bp); |
2bd0ea18 NS |
848 | } while (da_bno != 0); |
849 | ||
5cd3b070 | 850 | if (verify_final_da_path(mp, da_cursor, 0, XFS_ATTR_FORK)) { |
2bd0ea18 NS |
851 | /* |
852 | * verify the final path up (right-hand-side) if still ok | |
853 | */ | |
5d1b7f0f CH |
854 | do_warn( |
855 | _("bad hash path in attribute fork for inode %" PRIu64 "\n"), | |
2bd0ea18 NS |
856 | da_cursor->ino); |
857 | goto error_out; | |
858 | } | |
859 | ||
860 | /* releases all buffers holding interior btree blocks */ | |
861 | release_da_cursor(mp, da_cursor, 0); | |
862 | return(0); | |
863 | ||
864 | error_out: | |
865 | /* release all buffers holding interior btree blocks */ | |
866 | err_release_da_cursor(mp, da_cursor, 0); | |
867 | return(1); | |
868 | } | |
869 | ||
870 | ||
871 | /* | |
872 | * a node directory is a true btree -- where the attribute fork | |
873 | * has gotten big enough that it is represented as a non-trivial (e.g. | |
874 | * has more than just a block) btree. | |
875 | * | |
876 | * Note that if we run into any problems, we will trash the attribute fork. | |
dfc130f3 | 877 | * |
2bd0ea18 | 878 | * returns 0 if things are ok, 1 if bad |
dfc130f3 | 879 | * Note this code has been based off process_node_dir. |
2bd0ea18 | 880 | */ |
8b8a6b02 | 881 | static int |
2bd0ea18 NS |
882 | process_node_attr( |
883 | xfs_mount_t *mp, | |
884 | xfs_ino_t ino, | |
885 | xfs_dinode_t *dip, | |
886 | blkmap_t *blkmap) | |
887 | { | |
888 | xfs_dablk_t bno; | |
889 | int error = 0; | |
890 | da_bt_cursor_t da_cursor; | |
891 | ||
892 | /* | |
893 | * try again -- traverse down left-side of tree until we hit | |
894 | * the left-most leaf block setting up the btree cursor along | |
895 | * the way. Then walk the leaf blocks left-to-right, calling | |
896 | * a parent-verification routine each time we traverse a block. | |
897 | */ | |
dab9b8d6 | 898 | memset(&da_cursor, 0, sizeof(da_bt_cursor_t)); |
2bd0ea18 | 899 | da_cursor.active = 0; |
2bd0ea18 NS |
900 | da_cursor.ino = ino; |
901 | da_cursor.dip = dip; | |
902 | da_cursor.greatest_bno = 0; | |
903 | da_cursor.blkmap = blkmap; | |
904 | ||
905 | /* | |
906 | * now process interior node. don't have any buffers held in this path. | |
907 | */ | |
908 | error = traverse_int_dablock(mp, &da_cursor, &bno, XFS_ATTR_FORK); | |
dfc130f3 | 909 | if (error == 0) |
2bd0ea18 NS |
910 | return(1); /* 0 means unsuccessful */ |
911 | ||
912 | /* | |
913 | * now pass cursor and bno into leaf-block processing routine | |
914 | * the leaf dir level routine checks the interior paths | |
915 | * up to the root including the final right-most path. | |
916 | */ | |
dfc130f3 | 917 | |
2bd0ea18 NS |
918 | return (process_leaf_attr_level(mp, &da_cursor)); |
919 | } | |
920 | ||
1c8e2f03 DW |
921 | /* check v5 metadata */ |
922 | static int | |
923 | __check_attr_header( | |
924 | struct xfs_mount *mp, | |
925 | struct xfs_buf *bp, | |
926 | xfs_ino_t ino) | |
927 | { | |
928 | struct xfs_da3_blkinfo *info = bp->b_addr; | |
929 | ||
930 | if (info->hdr.magic != cpu_to_be16(XFS_ATTR3_LEAF_MAGIC) && | |
931 | info->hdr.magic != cpu_to_be16(XFS_DA3_NODE_MAGIC)) | |
932 | return 0; | |
933 | ||
934 | /* verify owner */ | |
935 | if (be64_to_cpu(info->owner) != ino) { | |
936 | do_warn( | |
937 | _("expected owner inode %" PRIu64 ", got %llu, attr block %" PRIu64 "\n"), | |
51073f86 DW |
938 | ino, (unsigned long long)be64_to_cpu(info->owner), |
939 | bp->b_bn); | |
1c8e2f03 DW |
940 | return 1; |
941 | } | |
942 | /* verify block number */ | |
943 | if (be64_to_cpu(info->blkno) != bp->b_bn) { | |
944 | do_warn( | |
945 | _("expected block %" PRIu64 ", got %llu, inode %" PRIu64 "attr block\n"), | |
51073f86 DW |
946 | bp->b_bn, (unsigned long long)be64_to_cpu(info->blkno), |
947 | ino); | |
1c8e2f03 DW |
948 | return 1; |
949 | } | |
950 | /* verify uuid */ | |
951 | if (platform_uuid_compare(&info->uuid, &mp->m_sb.sb_meta_uuid) != 0) { | |
952 | do_warn( | |
953 | _("wrong FS UUID, inode %" PRIu64 " attr block %" PRIu64 "\n"), | |
954 | ino, bp->b_bn); | |
955 | return 1; | |
956 | } | |
957 | ||
958 | return 0; | |
959 | } | |
960 | ||
2bd0ea18 NS |
961 | /* |
962 | * Start processing for a leaf or fuller btree. | |
963 | * A leaf directory is one where the attribute fork is too big for | |
964 | * the inode but is small enough to fit into one btree block | |
965 | * outside the inode. This code is modelled after process_leaf_dir_block. | |
966 | * | |
967 | * returns 0 if things are ok, 1 if bad (attributes needs to be junked) | |
968 | * repair is set, if anything was changed, but attributes can live thru it | |
969 | */ | |
8b8a6b02 | 970 | static int |
2bd0ea18 NS |
971 | process_longform_attr( |
972 | xfs_mount_t *mp, | |
973 | xfs_ino_t ino, | |
974 | xfs_dinode_t *dip, | |
975 | blkmap_t *blkmap, | |
976 | int *repair) /* out - 1 if something was fixed */ | |
977 | { | |
978 | xfs_attr_leafblock_t *leaf; | |
5a35bf2c | 979 | xfs_fsblock_t bno; |
2bd0ea18 NS |
980 | xfs_buf_t *bp; |
981 | xfs_dahash_t next_hashval; | |
982 | int repairlinks = 0; | |
658ac3e3 | 983 | struct xfs_attr3_icleaf_hdr leafhdr; |
aedb9c17 | 984 | int error; |
2bd0ea18 NS |
985 | |
986 | *repair = 0; | |
987 | ||
988 | bno = blkmap_get(blkmap, 0); | |
989 | ||
5a35bf2c | 990 | if ( bno == NULLFSBLOCK ) { |
f8149110 | 991 | if (dip->di_aformat == XFS_DINODE_FMT_EXTENTS && |
56b2de80 | 992 | be16_to_cpu(dip->di_anextents) == 0) |
5e656dbb | 993 | return(0); /* the kernel can handle this state */ |
5d1b7f0f CH |
994 | do_warn( |
995 | _("block 0 of inode %" PRIu64 " attribute fork is missing\n"), | |
5e656dbb BN |
996 | ino); |
997 | return(1); | |
2bd0ea18 NS |
998 | } |
999 | /* FIX FOR bug 653709 -- EKN */ | |
1000 | if (mp->m_sb.sb_agcount < XFS_FSB_TO_AGNO(mp, bno)) { | |
5d1b7f0f CH |
1001 | do_warn( |
1002 | _("agno of attribute fork of inode %" PRIu64 " out of regular partition\n"), ino); | |
2bd0ea18 NS |
1003 | return(1); |
1004 | } | |
1005 | ||
1006 | bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, bno), | |
658ac3e3 | 1007 | XFS_FSB_TO_BB(mp, 1), 0, &xfs_da3_node_buf_ops); |
2bd0ea18 | 1008 | if (!bp) { |
5d1b7f0f CH |
1009 | do_warn( |
1010 | _("can't read block 0 of inode %" PRIu64 " attribute fork\n"), | |
2bd0ea18 NS |
1011 | ino); |
1012 | return(1); | |
1013 | } | |
12b53197 | 1014 | if (bp->b_error == -EFSBADCRC) |
3a1d34e2 | 1015 | (*repair)++; |
2bd0ea18 | 1016 | |
1c8e2f03 DW |
1017 | /* is this block sane? */ |
1018 | if (__check_attr_header(mp, bp, ino)) { | |
1019 | *repair = 0; | |
1020 | libxfs_putbuf(bp); | |
1021 | return 1; | |
1022 | } | |
1023 | ||
2bd0ea18 | 1024 | /* verify leaf block */ |
259d52fc | 1025 | leaf = bp->b_addr; |
19ebedcf | 1026 | xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf); |
2bd0ea18 NS |
1027 | |
1028 | /* check sibling pointers in leaf block or root block 0 before | |
1029 | * we have to release the btree block | |
1030 | */ | |
658ac3e3 | 1031 | if (leafhdr.forw != 0 || leafhdr.back != 0) { |
2bd0ea18 | 1032 | if (!no_modify) { |
5d1b7f0f CH |
1033 | do_warn( |
1034 | _("clearing forw/back pointers in block 0 for attributes in inode %" PRIu64 "\n"), | |
1035 | ino); | |
2bd0ea18 | 1036 | repairlinks = 1; |
658ac3e3 DC |
1037 | leafhdr.forw = 0; |
1038 | leafhdr.back = 0; | |
19ebedcf DC |
1039 | xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, |
1040 | leaf, &leafhdr); | |
2bd0ea18 | 1041 | } else { |
5d1b7f0f CH |
1042 | do_warn( |
1043 | _("would clear forw/back pointers in block 0 for attributes in inode %" PRIu64 "\n"), ino); | |
2bd0ea18 NS |
1044 | } |
1045 | } | |
1046 | ||
1047 | /* | |
1048 | * use magic number to tell us what type of attribute this is. | |
1049 | * it's possible to have a node or leaf attribute in either an | |
1050 | * extent format or btree format attribute fork. | |
1051 | */ | |
658ac3e3 | 1052 | switch (leafhdr.magic) { |
2bd0ea18 | 1053 | case XFS_ATTR_LEAF_MAGIC: /* leaf-form attribute */ |
658ac3e3 | 1054 | case XFS_ATTR3_LEAF_MAGIC: |
2bd0ea18 NS |
1055 | if (process_leaf_attr_block(mp, leaf, 0, ino, blkmap, |
1056 | 0, &next_hashval, repair)) { | |
aedb9c17 | 1057 | *repair = 0; |
2bd0ea18 NS |
1058 | /* the block is bad. lose the attribute fork. */ |
1059 | libxfs_putbuf(bp); | |
dfc130f3 | 1060 | return(1); |
2bd0ea18 | 1061 | } |
dfc130f3 | 1062 | *repair = *repair || repairlinks; |
2bd0ea18 NS |
1063 | break; |
1064 | ||
1065 | case XFS_DA_NODE_MAGIC: /* btree-form attribute */ | |
658ac3e3 | 1066 | case XFS_DA3_NODE_MAGIC: |
2bd0ea18 | 1067 | /* must do this now, to release block 0 before the traversal */ |
3a1d34e2 | 1068 | if ((*repair || repairlinks) && !no_modify) { |
2bd0ea18 NS |
1069 | *repair = 1; |
1070 | libxfs_writebuf(bp, 0); | |
dfc130f3 RC |
1071 | } else |
1072 | libxfs_putbuf(bp); | |
aedb9c17 DW |
1073 | error = process_node_attr(mp, ino, dip, blkmap); /* + repair */ |
1074 | if (error) | |
1075 | *repair = 0; | |
1076 | return error; | |
2bd0ea18 | 1077 | default: |
5d1b7f0f CH |
1078 | do_warn( |
1079 | _("bad attribute leaf magic # %#x for dir ino %" PRIu64 "\n"), | |
5e656dbb | 1080 | be16_to_cpu(leaf->hdr.info.magic), ino); |
2bd0ea18 | 1081 | libxfs_putbuf(bp); |
aedb9c17 | 1082 | *repair = 0; |
2bd0ea18 NS |
1083 | return(1); |
1084 | } | |
1085 | ||
dfc130f3 | 1086 | if (*repair && !no_modify) |
2bd0ea18 NS |
1087 | libxfs_writebuf(bp, 0); |
1088 | else | |
1089 | libxfs_putbuf(bp); | |
1090 | ||
1091 | return(0); /* repair may be set */ | |
1092 | } | |
1093 | ||
1094 | ||
7675c153 | 1095 | static int |
b5f5c2d1 DC |
1096 | xfs_acl_from_disk( |
1097 | struct xfs_mount *mp, | |
1098 | struct xfs_icacl **aclp, | |
1099 | struct xfs_acl *dacl) | |
061313d4 | 1100 | { |
b5f5c2d1 DC |
1101 | struct xfs_icacl *acl; |
1102 | struct xfs_icacl_entry *ace; | |
1103 | struct xfs_acl_entry *dace; | |
5e656dbb | 1104 | int count; |
b5f5c2d1 | 1105 | int i; |
5e656dbb BN |
1106 | |
1107 | count = be32_to_cpu(dacl->acl_cnt); | |
b5f5c2d1 | 1108 | if (count > XFS_ACL_MAX_ENTRIES(mp)) { |
7675c153 CH |
1109 | do_warn(_("Too many ACL entries, count %d\n"), count); |
1110 | *aclp = NULL; | |
1111 | return EINVAL; | |
1112 | } | |
1113 | ||
1114 | ||
b5f5c2d1 DC |
1115 | acl = malloc(sizeof(struct xfs_icacl) + |
1116 | count * sizeof(struct xfs_icacl_entry)); | |
7675c153 CH |
1117 | if (!acl) { |
1118 | do_warn(_("cannot malloc enough for ACL attribute\n")); | |
1119 | do_warn(_("SKIPPING this ACL\n")); | |
1120 | *aclp = NULL; | |
1121 | return ENOMEM; | |
1122 | } | |
1123 | ||
5e656dbb | 1124 | acl->acl_cnt = count; |
b5f5c2d1 DC |
1125 | for (i = 0; i < count; i++) { |
1126 | ace = &acl->acl_entry[i]; | |
1127 | dace = &dacl->acl_entry[i]; | |
1128 | ||
5e656dbb BN |
1129 | ace->ae_tag = be32_to_cpu(dace->ae_tag); |
1130 | ace->ae_id = be32_to_cpu(dace->ae_id); | |
1131 | ace->ae_perm = be16_to_cpu(dace->ae_perm); | |
43533211 | 1132 | } |
7675c153 CH |
1133 | |
1134 | *aclp = acl; | |
1135 | return 0; | |
061313d4 AG |
1136 | } |
1137 | ||
2bd0ea18 NS |
1138 | /* |
1139 | * returns 1 if attributes got cleared | |
dfc130f3 | 1140 | * and 0 if things are ok. |
2bd0ea18 NS |
1141 | */ |
1142 | int | |
1143 | process_attributes( | |
1144 | xfs_mount_t *mp, | |
1145 | xfs_ino_t ino, | |
1146 | xfs_dinode_t *dip, | |
1147 | blkmap_t *blkmap, | |
1148 | int *repair) /* returned if we did repair */ | |
1149 | { | |
5e656dbb | 1150 | int err; |
56b2de80 | 1151 | __u8 aformat = dip->di_aformat; |
6bddecbc | 1152 | #ifdef DEBUG |
2bd0ea18 NS |
1153 | xfs_attr_shortform_t *asf; |
1154 | ||
46eca962 | 1155 | asf = (xfs_attr_shortform_t *) XFS_DFORK_APTR(dip); |
6bddecbc | 1156 | #endif |
2bd0ea18 | 1157 | |
5e656dbb BN |
1158 | if (aformat == XFS_DINODE_FMT_LOCAL) { |
1159 | ASSERT(be16_to_cpu(asf->hdr.totsize) <= | |
46eca962 | 1160 | XFS_DFORK_ASIZE(dip, mp)); |
b5f5c2d1 | 1161 | err = process_shortform_attr(mp, ino, dip, repair); |
5e656dbb BN |
1162 | } else if (aformat == XFS_DINODE_FMT_EXTENTS || |
1163 | aformat == XFS_DINODE_FMT_BTREE) { | |
2bd0ea18 NS |
1164 | err = process_longform_attr(mp, ino, dip, blkmap, |
1165 | repair); | |
1166 | /* if err, convert this to shortform and clear it */ | |
1167 | /* if repair and no error, it's taken care of */ | |
1168 | } else { | |
5d1b7f0f | 1169 | do_warn(_("illegal attribute format %d, ino %" PRIu64 "\n"), |
5e656dbb | 1170 | aformat, ino); |
dfc130f3 | 1171 | err = 1; |
2bd0ea18 NS |
1172 | } |
1173 | return (err); /* and repair */ | |
1174 | } | |
1175 | ||
dfc130f3 | 1176 | /* |
2bd0ea18 NS |
1177 | * Validate an ACL |
1178 | */ | |
1179 | static int | |
b5f5c2d1 DC |
1180 | xfs_acl_valid( |
1181 | struct xfs_mount *mp, | |
1182 | struct xfs_acl *daclp) | |
2bd0ea18 | 1183 | { |
b5f5c2d1 DC |
1184 | struct xfs_icacl *aclp = NULL; |
1185 | struct xfs_icacl_entry *entry, *e; | |
2bd0ea18 NS |
1186 | int user = 0, group = 0, other = 0, mask = 0, mask_required = 0; |
1187 | int i, j; | |
1188 | ||
5e656dbb | 1189 | if (daclp == NULL) |
2bd0ea18 NS |
1190 | goto acl_invalid; |
1191 | ||
b5f5c2d1 | 1192 | switch (xfs_acl_from_disk(mp, &aclp, daclp)) { |
7675c153 | 1193 | case ENOMEM: |
5e656dbb | 1194 | return 0; |
7675c153 | 1195 | case EINVAL: |
2bd0ea18 | 1196 | goto acl_invalid; |
7675c153 CH |
1197 | default: |
1198 | break; | |
1199 | } | |
2bd0ea18 | 1200 | |
43533211 | 1201 | for (i = 0; i < aclp->acl_cnt; i++) { |
2bd0ea18 | 1202 | entry = &aclp->acl_entry[i]; |
e80ad1c7 NS |
1203 | if (entry->ae_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE)) |
1204 | goto acl_invalid; | |
43533211 | 1205 | switch (entry->ae_tag) { |
2bd0ea18 NS |
1206 | case ACL_USER_OBJ: |
1207 | if (user++) | |
1208 | goto acl_invalid; | |
1209 | break; | |
1210 | case ACL_GROUP_OBJ: | |
1211 | if (group++) | |
1212 | goto acl_invalid; | |
1213 | break; | |
43533211 | 1214 | case ACL_OTHER: |
2bd0ea18 NS |
1215 | if (other++) |
1216 | goto acl_invalid; | |
1217 | break; | |
1218 | case ACL_USER: | |
1219 | case ACL_GROUP: | |
43533211 | 1220 | for (j = i + 1; j < aclp->acl_cnt; j++) { |
2bd0ea18 | 1221 | e = &aclp->acl_entry[j]; |
43533211 NS |
1222 | if (e->ae_id == entry->ae_id && |
1223 | e->ae_tag == entry->ae_tag) | |
2bd0ea18 NS |
1224 | goto acl_invalid; |
1225 | } | |
1226 | mask_required++; | |
1227 | break; | |
1228 | case ACL_MASK: | |
1229 | if (mask++) | |
1230 | goto acl_invalid; | |
1231 | break; | |
1232 | default: | |
1233 | goto acl_invalid; | |
1234 | } | |
1235 | } | |
1236 | if (!user || !group || !other || (mask_required && !mask)) | |
1237 | goto acl_invalid; | |
5e656dbb BN |
1238 | free(aclp); |
1239 | return 0; | |
2bd0ea18 | 1240 | acl_invalid: |
5e656dbb | 1241 | free(aclp); |
2bd0ea18 NS |
1242 | errno = EINVAL; |
1243 | return (-1); | |
1244 | } | |
1245 | ||
1246 | /* | |
1247 | * Check a category or division set to ensure that all values are in | |
1248 | * ascending order and each division or category appears only once. | |
1249 | */ | |
1250 | static int | |
1251 | __check_setvalue(const unsigned short *list, unsigned short count) | |
1252 | { | |
43533211 | 1253 | unsigned short i; |
2bd0ea18 | 1254 | |
43533211 NS |
1255 | for (i = 1; i < count ; i++) |
1256 | if (list[i] <= list[i-1]) | |
1257 | return -1; | |
1258 | return 0; | |
2bd0ea18 NS |
1259 | } |
1260 | ||
2bd0ea18 | 1261 | /* |
43533211 | 1262 | * xfs_mac_valid(lp) |
14290264 | 1263 | * Check the validity of a MAC label. |
2bd0ea18 NS |
1264 | */ |
1265 | static int | |
14290264 | 1266 | xfs_mac_valid(xfs_mac_label_t *lp) |
2bd0ea18 NS |
1267 | { |
1268 | if (lp == NULL) | |
1269 | return (0); | |
1270 | ||
1271 | /* | |
1272 | * if the total category set and division set is greater than 250 | |
1273 | * report error | |
1274 | */ | |
14290264 | 1275 | if ((lp->ml_catcount + lp->ml_divcount) > XFS_MAC_MAX_SETS) |
2bd0ea18 NS |
1276 | return(0); |
1277 | ||
1278 | /* | |
1279 | * check whether the msentype value is valid, and do they have | |
dfc130f3 RC |
1280 | * appropriate level, category association. |
1281 | */ | |
2bd0ea18 | 1282 | switch (lp->ml_msen_type) { |
e49e365f NS |
1283 | case XFS_MSEN_ADMIN_LABEL: |
1284 | case XFS_MSEN_EQUAL_LABEL: | |
1285 | case XFS_MSEN_HIGH_LABEL: | |
1286 | case XFS_MSEN_MLD_HIGH_LABEL: | |
1287 | case XFS_MSEN_LOW_LABEL: | |
1288 | case XFS_MSEN_MLD_LOW_LABEL: | |
2bd0ea18 NS |
1289 | if (lp->ml_level != 0 || lp->ml_catcount > 0 ) |
1290 | return (0); | |
1291 | break; | |
e49e365f NS |
1292 | case XFS_MSEN_TCSEC_LABEL: |
1293 | case XFS_MSEN_MLD_LABEL: | |
2bd0ea18 NS |
1294 | if (lp->ml_catcount > 0 && |
1295 | __check_setvalue(lp->ml_list, | |
1296 | lp->ml_catcount) == -1) | |
1297 | return (0); | |
1298 | break; | |
e49e365f | 1299 | case XFS_MSEN_UNKNOWN_LABEL: |
2bd0ea18 NS |
1300 | default: |
1301 | return (0); | |
1302 | } | |
1303 | ||
1304 | /* | |
1305 | * check whether the minttype value is valid, and do they have | |
1306 | * appropriate grade, division association. | |
1307 | */ | |
1308 | switch (lp->ml_mint_type) { | |
e49e365f | 1309 | case XFS_MINT_BIBA_LABEL: |
2bd0ea18 NS |
1310 | if (lp->ml_divcount > 0 && |
1311 | __check_setvalue(lp->ml_list + lp->ml_catcount, | |
1312 | lp->ml_divcount) == -1) | |
1313 | return(0); | |
1314 | break; | |
e49e365f NS |
1315 | case XFS_MINT_EQUAL_LABEL: |
1316 | case XFS_MINT_HIGH_LABEL: | |
1317 | case XFS_MINT_LOW_LABEL: | |
2bd0ea18 NS |
1318 | if (lp->ml_grade != 0 || lp->ml_divcount > 0 ) |
1319 | return(0); | |
1320 | break; | |
1321 | default: | |
1322 | return(0); | |
1323 | } | |
1324 | ||
1325 | return (1); | |
1326 | } |