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