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