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