]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - repair/attr_repair.c
fix various typos
[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"
2814f3d6 26#include "protos.h"
2bd0ea18 27
5e656dbb 28static int xfs_acl_valid(xfs_acl_disk_t *daclp);
14290264 29static 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
84int
85valuecheck(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 */
149int
150process_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 *)&currententry->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 *)&currententry->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 */
329static int
330rmtval_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 370size_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
380static int
381process_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
430static int
431process_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);
481out:
482 return XFS_ATTR_LEAF_ENTSIZE_REMOTE(remotep->namelen);
483
484bad_free_out:
485 free(value);
486 return -1;
487}
488
2bd0ea18
NS
489int
490process_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 */
643int
644process_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
756error_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 */
773int
774process_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
824int
825process_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
925static xfs_acl_t *
926xfs_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 */
952int
953process_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 */
987static int
5e656dbb 988xfs_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 1047acl_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 */
1057static 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 */
1072static int
14290264 1073xfs_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}