]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - repair/attr_repair.c
Update copyright/license notices to match SGI legal prefered boilerplate.
[thirdparty/xfsprogs-dev.git] / repair / attr_repair.c
CommitLineData
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 27static int xfs_acl_valid(xfs_acl_t *aclp);
14290264 28static 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
83int
84valuecheck(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 */
148int
149process_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 *)&currententry->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 *)&currententry->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 */
332static int
333rmtval_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 */
373static 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
383int
384process_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 */
655int
656process_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
772error_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 */
789int
790process_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
840int
841process_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 944static void
43533211 945xfs_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 */
962int
963process_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 */
999static int
43533211 1000xfs_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;
1053acl_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 */
1062static 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 */
1077static int
14290264 1078xfs_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}