1 /*
2 * fs/partitions/msdos.c
3 *
4 * Code extracted from drivers/block/genhd.c
5 * Copyright (C) 1991-1998 Linus Torvalds
6 *
7 * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
8 * in the early extended-partition checks and added DM partitions
9 *
10 * Support for DiskManager v6.0x added by Mark Lord,
11 * with information provided by OnTrack. This now works for linux fdisk
12 * and LILO, as well as loadlin and bootln. Note that disks other than
13 * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
14 *
15 * More flexible handling of extended partitions - aeb, 950831
16 *
17 * Check partition table on IDE disks for common CHS translations
18 *
19 * Re-organised Feb 1998 Russell King
20 */
21
22 #include <linux/config.h>
23 #include <linux/fs.h>
24 #include <linux/genhd.h>
25 #include <linux/kernel.h>
26 #include <linux/major.h>
27 #include <linux/string.h>
28 #include <linux/blk.h>
29
30 #ifdef CONFIG_BLK_DEV_IDE
31 #include <linux/ide.h> /* IDE xlate */
32 #elif defined(CONFIG_BLK_DEV_IDE_MODULE)
33 #include <linux/module.h>
34
35 int (*ide_xlate_1024_hook)(kdev_t, int, int, const char *);
36 EXPORT_SYMBOL(ide_xlate_1024_hook);
37 #define ide_xlate_1024 ide_xlate_1024_hook
38 #endif
39
40 #include <asm/system.h>
41
42 #include "check.h"
43 #include "msdos.h"
44
45 #if CONFIG_BLK_DEV_MD
46 extern void md_autodetect_dev(kdev_t dev);
47 #endif
48
49 /*
50 * Many architectures don't like unaligned accesses, which is
51 * frequently the case with the nr_sects and start_sect partition
52 * table entries.
53 */
54 #include <asm/unaligned.h>
55
56 #define SYS_IND(p) (get_unaligned(&p->sys_ind))
57 #define NR_SECTS(p) ({ __typeof__(p->nr_sects) __a = \
58 get_unaligned(&p->nr_sects); \
59 le32_to_cpu(__a); \
60 })
61
62 #define START_SECT(p) ({ __typeof__(p->start_sect) __a = \
63 get_unaligned(&p->start_sect); \
64 le32_to_cpu(__a); \
65 })
66
is_extended_partition(struct partition * p)67 static inline int is_extended_partition(struct partition *p)
68 {
69 return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
70 SYS_IND(p) == WIN98_EXTENDED_PARTITION ||
71 SYS_IND(p) == LINUX_EXTENDED_PARTITION);
72 }
73
74 /*
75 * msdos_partition_name() formats the short partition name into the supplied
76 * buffer, and returns a pointer to that buffer.
77 * Used by several partition types which makes conditional inclusion messy,
78 * use __attribute__ ((unused)) instead.
79 */
80 static char __attribute__ ((unused))
msdos_partition_name(struct gendisk * hd,int minor,char * buf)81 *msdos_partition_name (struct gendisk *hd, int minor, char *buf)
82 {
83 #ifdef CONFIG_DEVFS_FS
84 sprintf(buf, "p%d", (minor & ((1 << hd->minor_shift) - 1)));
85 return buf;
86 #else
87 return disk_name(hd, minor, buf);
88 #endif
89 }
90
91 #define MSDOS_LABEL_MAGIC1 0x55
92 #define MSDOS_LABEL_MAGIC2 0xAA
93
94 static inline int
msdos_magic_present(unsigned char * p)95 msdos_magic_present(unsigned char *p)
96 {
97 return (p[0] == MSDOS_LABEL_MAGIC1 && p[1] == MSDOS_LABEL_MAGIC2);
98 }
99
100 /*
101 * Create devices for each logical partition in an extended partition.
102 * The logical partitions form a linked list, with each entry being
103 * a partition table with two entries. The first entry
104 * is the real data partition (with a start relative to the partition
105 * table start). The second is a pointer to the next logical partition
106 * (with a start relative to the entire extended partition).
107 * We do not create a Linux partition for the partition tables, but
108 * only for the actual data partitions.
109 */
110
extended_partition(struct gendisk * hd,struct block_device * bdev,int minor,unsigned long first_size,int * current_minor)111 static void extended_partition(struct gendisk *hd, struct block_device *bdev,
112 int minor, unsigned long first_size, int *current_minor)
113 {
114 struct partition *p;
115 Sector sect;
116 unsigned char *data;
117 unsigned long first_sector, this_sector, this_size;
118 int mask = (1 << hd->minor_shift) - 1;
119 int sector_size = get_hardsect_size(to_kdev_t(bdev->bd_dev)) / 512;
120 int loopct = 0; /* number of links followed
121 without finding a data partition */
122 int i;
123
124 this_sector = first_sector = hd->part[minor].start_sect;
125 this_size = first_size;
126
127 while (1) {
128 if (++loopct > 100)
129 return;
130 if ((*current_minor & mask) == 0)
131 return;
132 data = read_dev_sector(bdev, this_sector, §);
133 if (!data)
134 return;
135
136 if (!msdos_magic_present(data + 510))
137 goto done;
138
139 p = (struct partition *) (data + 0x1be);
140
141 /*
142 * Usually, the first entry is the real data partition,
143 * the 2nd entry is the next extended partition, or empty,
144 * and the 3rd and 4th entries are unused.
145 * However, DRDOS sometimes has the extended partition as
146 * the first entry (when the data partition is empty),
147 * and OS/2 seems to use all four entries.
148 */
149
150 /*
151 * First process the data partition(s)
152 */
153 for (i=0; i<4; i++, p++) {
154 unsigned long offs, size, next;
155 if (!NR_SECTS(p) || is_extended_partition(p))
156 continue;
157
158 /* Check the 3rd and 4th entries -
159 these sometimes contain random garbage */
160 offs = START_SECT(p)*sector_size;
161 size = NR_SECTS(p)*sector_size;
162 next = this_sector + offs;
163 if (i >= 2) {
164 if (offs + size > this_size)
165 continue;
166 if (next < first_sector)
167 continue;
168 if (next + size > first_sector + first_size)
169 continue;
170 }
171
172 add_gd_partition(hd, *current_minor, next, size);
173 #if CONFIG_BLK_DEV_MD
174 if (SYS_IND(p) == LINUX_RAID_PARTITION) {
175 md_autodetect_dev(MKDEV(hd->major,*current_minor));
176 }
177 #endif
178
179 (*current_minor)++;
180 loopct = 0;
181 if ((*current_minor & mask) == 0)
182 goto done;
183 }
184 /*
185 * Next, process the (first) extended partition, if present.
186 * (So far, there seems to be no reason to make
187 * extended_partition() recursive and allow a tree
188 * of extended partitions.)
189 * It should be a link to the next logical partition.
190 * Create a minor for this just long enough to get the next
191 * partition table. The minor will be reused for the next
192 * data partition.
193 */
194 p -= 4;
195 for (i=0; i<4; i++, p++)
196 if (NR_SECTS(p) && is_extended_partition(p))
197 break;
198 if (i == 4)
199 goto done; /* nothing left to do */
200
201 this_sector = first_sector + START_SECT(p) * sector_size;
202 this_size = NR_SECTS(p) * sector_size;
203 minor = *current_minor;
204 put_dev_sector(sect);
205 }
206 done:
207 put_dev_sector(sect);
208 }
209
210 /* james@bpgc.com: Solaris has a nasty indicator: 0x82 which also
211 indicates linux swap. Be careful before believing this is Solaris. */
212
213 static void
solaris_x86_partition(struct gendisk * hd,struct block_device * bdev,int minor,int * current_minor)214 solaris_x86_partition(struct gendisk *hd, struct block_device *bdev,
215 int minor, int *current_minor)
216 {
217
218 #ifdef CONFIG_SOLARIS_X86_PARTITION
219 long offset = hd->part[minor].start_sect;
220 Sector sect;
221 struct solaris_x86_vtoc *v;
222 struct solaris_x86_slice *s;
223 int mask = (1 << hd->minor_shift) - 1;
224 int i;
225 char buf[40];
226
227 v = (struct solaris_x86_vtoc *)read_dev_sector(bdev, offset+1, §);
228 if (!v)
229 return;
230 if (le32_to_cpu(v->v_sanity) != SOLARIS_X86_VTOC_SANE) {
231 put_dev_sector(sect);
232 return;
233 }
234 printk(" %s: <solaris:", msdos_partition_name(hd, minor, buf));
235 if (le32_to_cpu(v->v_version) != 1) {
236 printk(" cannot handle version %d vtoc>\n",
237 le32_to_cpu(v->v_version));
238 put_dev_sector(sect);
239 return;
240 }
241 for (i=0; i<SOLARIS_X86_NUMSLICE; i++) {
242 if ((*current_minor & mask) == 0)
243 break;
244 s = &v->v_slice[i];
245
246 if (s->s_size == 0)
247 continue;
248 printk(" [s%d]", i);
249 /* solaris partitions are relative to current MS-DOS
250 * one but add_gd_partition starts relative to sector
251 * zero of the disk. Therefore, must add the offset
252 * of the current partition */
253 add_gd_partition(hd, *current_minor,
254 le32_to_cpu(s->s_start)+offset,
255 le32_to_cpu(s->s_size));
256 (*current_minor)++;
257 }
258 put_dev_sector(sect);
259 printk(" >\n");
260 #endif
261 }
262
263 #ifdef CONFIG_BSD_DISKLABEL
264 static void
check_and_add_bsd_partition(struct gendisk * hd,struct bsd_partition * bsd_p,int baseminor,int * current_minor)265 check_and_add_bsd_partition(struct gendisk *hd, struct bsd_partition *bsd_p,
266 int baseminor, int *current_minor)
267 {
268 int i, bsd_start, bsd_size;
269
270 bsd_start = le32_to_cpu(bsd_p->p_offset);
271 bsd_size = le32_to_cpu(bsd_p->p_size);
272
273 /* check relative position of already allocated partitions */
274 for (i = baseminor+1; i < *current_minor; i++) {
275 int start = hd->part[i].start_sect;
276 int size = hd->part[i].nr_sects;
277
278 if (start+size <= bsd_start || start >= bsd_start+bsd_size)
279 continue; /* no overlap */
280
281 if (start == bsd_start && size == bsd_size)
282 return; /* equal -> no need to add */
283
284 if (start <= bsd_start && start+size >= bsd_start+bsd_size) {
285 /* bsd living within dos partition */
286 #ifdef DEBUG_BSD_DISKLABEL
287 printk("w: %d %ld+%ld,%d+%d",
288 i, start, size, bsd_start, bsd_size);
289 #endif
290 break; /* ok */
291 }
292
293 /* ouch: bsd and linux overlap */
294 #ifdef DEBUG_BSD_DISKLABEL
295 printk("???: %d %ld+%ld,%d+%d",
296 i, start, size, bsd_start, bsd_size);
297 #endif
298 printk("???");
299 return;
300 }
301
302 add_gd_partition(hd, *current_minor, bsd_start, bsd_size);
303 (*current_minor)++;
304 }
305
306 /*
307 * Create devices for BSD partitions listed in a disklabel, under a
308 * dos-like partition. See extended_partition() for more information.
309 */
do_bsd_partition(struct gendisk * hd,struct block_device * bdev,int minor,int * current_minor,char * name,int max_partitions)310 static void do_bsd_partition(struct gendisk *hd, struct block_device *bdev,
311 int minor, int *current_minor, char *name, int max_partitions)
312 {
313 long offset = hd->part[minor].start_sect;
314 Sector sect;
315 struct bsd_disklabel *l;
316 struct bsd_partition *p;
317 int mask = (1 << hd->minor_shift) - 1;
318 int baseminor = (minor & ~mask);
319 char buf[40];
320
321 l = (struct bsd_disklabel *)read_dev_sector(bdev, offset+1, §);
322 if (!l)
323 return;
324 if (le32_to_cpu(l->d_magic) != BSD_DISKMAGIC) {
325 put_dev_sector(sect);
326 return;
327 }
328 printk(" %s: <%s:", msdos_partition_name(hd, minor, buf), name);
329
330 if (le16_to_cpu(l->d_npartitions) < max_partitions)
331 max_partitions = le16_to_cpu(l->d_npartitions);
332 for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) {
333 if ((*current_minor & mask) == 0)
334 break;
335 if (p->p_fstype == BSD_FS_UNUSED)
336 continue;
337 check_and_add_bsd_partition(hd, p, baseminor, current_minor);
338 }
339 put_dev_sector(sect);
340 printk(" >\n");
341 }
342 #endif
343
bsd_partition(struct gendisk * hd,struct block_device * bdev,int minor,int * current_minor)344 static void bsd_partition(struct gendisk *hd, struct block_device *bdev,
345 int minor, int *current_minor)
346 {
347 #ifdef CONFIG_BSD_DISKLABEL
348 do_bsd_partition(hd, bdev, minor, current_minor, "bsd",
349 BSD_MAXPARTITIONS);
350 #endif
351 }
352
netbsd_partition(struct gendisk * hd,struct block_device * bdev,int minor,int * current_minor)353 static void netbsd_partition(struct gendisk *hd, struct block_device *bdev,
354 int minor, int *current_minor)
355 {
356 #ifdef CONFIG_BSD_DISKLABEL
357 do_bsd_partition(hd, bdev, minor, current_minor, "netbsd",
358 BSD_MAXPARTITIONS);
359 #endif
360 }
361
openbsd_partition(struct gendisk * hd,struct block_device * bdev,int minor,int * current_minor)362 static void openbsd_partition(struct gendisk *hd, struct block_device *bdev,
363 int minor, int *current_minor)
364 {
365 #ifdef CONFIG_BSD_DISKLABEL
366 do_bsd_partition(hd, bdev, minor, current_minor,
367 "openbsd", OPENBSD_MAXPARTITIONS);
368 #endif
369 }
370
371 /*
372 * Create devices for Unixware partitions listed in a disklabel, under a
373 * dos-like partition. See extended_partition() for more information.
374 */
unixware_partition(struct gendisk * hd,struct block_device * bdev,int minor,int * current_minor)375 static void unixware_partition(struct gendisk *hd, struct block_device *bdev,
376 int minor, int *current_minor)
377 {
378 #ifdef CONFIG_UNIXWARE_DISKLABEL
379 long offset = hd->part[minor].start_sect;
380 Sector sect;
381 struct unixware_disklabel *l;
382 struct unixware_slice *p;
383 int mask = (1 << hd->minor_shift) - 1;
384 char buf[40];
385
386 l = (struct unixware_disklabel *)read_dev_sector(bdev, offset+29, §);
387 if (!l)
388 return;
389 if (le32_to_cpu(l->d_magic) != UNIXWARE_DISKMAGIC ||
390 le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2) {
391 put_dev_sector(sect);
392 return;
393 }
394 printk(" %s: <unixware:", msdos_partition_name(hd, minor, buf));
395 p = &l->vtoc.v_slice[1];
396 /* I omit the 0th slice as it is the same as whole disk. */
397 while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) {
398 if ((*current_minor & mask) == 0)
399 break;
400
401 if (p->s_label != UNIXWARE_FS_UNUSED) {
402 add_gd_partition(hd, *current_minor, START_SECT(p),
403 NR_SECTS(p));
404 (*current_minor)++;
405 }
406 p++;
407 }
408 put_dev_sector(sect);
409 printk(" >\n");
410 #endif
411 }
412
413 /*
414 * Minix 2.0.0/2.0.2 subpartition support.
415 * Anand Krishnamurthy <anandk@wiproge.med.ge.com>
416 * Rajeev V. Pillai <rajeevvp@yahoo.com>
417 */
minix_partition(struct gendisk * hd,struct block_device * bdev,int minor,int * current_minor)418 static void minix_partition(struct gendisk *hd, struct block_device *bdev,
419 int minor, int *current_minor)
420 {
421 #ifdef CONFIG_MINIX_SUBPARTITION
422 long offset = hd->part[minor].start_sect;
423 Sector sect;
424 unsigned char *data;
425 struct partition *p;
426 int mask = (1 << hd->minor_shift) - 1;
427 int i;
428 char buf[40];
429
430 data = read_dev_sector(bdev, offset, §);
431 if (!data)
432 return;
433
434 p = (struct partition *)(data + 0x1be);
435
436 /* The first sector of a Minix partition can have either
437 * a secondary MBR describing its subpartitions, or
438 * the normal boot sector. */
439 if (msdos_magic_present (data + 510) &&
440 SYS_IND(p) == MINIX_PARTITION) { /* subpartition table present */
441
442 printk(" %s: <minix:", msdos_partition_name(hd, minor, buf));
443 for (i = 0; i < MINIX_NR_SUBPARTITIONS; i++, p++) {
444 if ((*current_minor & mask) == 0)
445 break;
446 /* add each partition in use */
447 if (SYS_IND(p) == MINIX_PARTITION) {
448 add_gd_partition(hd, *current_minor,
449 START_SECT(p), NR_SECTS(p));
450 (*current_minor)++;
451 }
452 }
453 printk(" >\n");
454 }
455 put_dev_sector(sect);
456 #endif /* CONFIG_MINIX_SUBPARTITION */
457 }
458
459 static struct {
460 unsigned char id;
461 void (*parse)(struct gendisk *, struct block_device *, int, int *);
462 } subtypes[] = {
463 {BSD_PARTITION, bsd_partition},
464 {NETBSD_PARTITION, netbsd_partition},
465 {OPENBSD_PARTITION, openbsd_partition},
466 {MINIX_PARTITION, minix_partition},
467 {UNIXWARE_PARTITION, unixware_partition},
468 {SOLARIS_X86_PARTITION, solaris_x86_partition},
469 {0, NULL},
470 };
471 /*
472 * Look for various forms of IDE disk geometry translation
473 */
handle_ide_mess(struct block_device * bdev)474 static int handle_ide_mess(struct block_device *bdev)
475 {
476 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
477 Sector sect;
478 unsigned char *data;
479 kdev_t dev = to_kdev_t(bdev->bd_dev);
480 unsigned int sig;
481 int heads = 0;
482 struct partition *p;
483 int i;
484 #ifdef CONFIG_BLK_DEV_IDE_MODULE
485 if (!ide_xlate_1024)
486 return 1;
487 #endif
488 /*
489 * The i386 partition handling programs very often
490 * make partitions end on cylinder boundaries.
491 * There is no need to do so, and Linux fdisk doesn't always
492 * do this, and Windows NT on Alpha doesn't do this either,
493 * but still, this helps to guess #heads.
494 */
495 data = read_dev_sector(bdev, 0, §);
496 if (!data)
497 return -1;
498 if (!msdos_magic_present(data + 510)) {
499 put_dev_sector(sect);
500 return 0;
501 }
502 sig = le16_to_cpu(*(unsigned short *)(data + 2));
503 p = (struct partition *) (data + 0x1be);
504 for (i = 0; i < 4; i++) {
505 struct partition *q = &p[i];
506 if (NR_SECTS(q)) {
507 if ((q->sector & 63) == 1 &&
508 (q->end_sector & 63) == 63)
509 heads = q->end_head + 1;
510 break;
511 }
512 }
513 if (SYS_IND(p) == EZD_PARTITION) {
514 /*
515 * Accesses to sector 0 must go to sector 1 instead.
516 */
517 if (ide_xlate_1024(dev, -1, heads, " [EZD]"))
518 goto reread;
519 } else if (SYS_IND(p) == DM6_PARTITION) {
520
521 /*
522 * Everything on the disk is offset by 63 sectors,
523 * including a "new" MBR with its own partition table.
524 */
525 if (ide_xlate_1024(dev, 1, heads, " [DM6:DDO]"))
526 goto reread;
527 } else if (sig <= 0x1ae &&
528 data[sig] == 0xAA && data[sig+1] == 0x55 &&
529 (data[sig+2] & 1)) {
530 /* DM6 signature in MBR, courtesy of OnTrack */
531 (void) ide_xlate_1024 (dev, 0, heads, " [DM6:MBR]");
532 } else if (SYS_IND(p) == DM6_AUX1PARTITION ||
533 SYS_IND(p) == DM6_AUX3PARTITION) {
534 /*
535 * DM6 on other than the first (boot) drive
536 */
537 (void) ide_xlate_1024(dev, 0, heads, " [DM6:AUX]");
538 } else {
539 (void) ide_xlate_1024(dev, 2, heads, " [PTBL]");
540 }
541 put_dev_sector(sect);
542 return 1;
543
544 reread:
545 put_dev_sector(sect);
546 /* Flush the cache */
547 invalidate_bdev(bdev, 1);
548 truncate_inode_pages(bdev->bd_inode->i_mapping, 0);
549 #endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
550 return 1;
551 }
552
msdos_partition(struct gendisk * hd,struct block_device * bdev,unsigned long first_sector,int first_part_minor)553 int msdos_partition(struct gendisk *hd, struct block_device *bdev,
554 unsigned long first_sector, int first_part_minor)
555 {
556 int i, minor = first_part_minor;
557 Sector sect;
558 struct partition *p;
559 unsigned char *data;
560 int mask = (1 << hd->minor_shift) - 1;
561 int sector_size = get_hardsect_size(to_kdev_t(bdev->bd_dev)) / 512;
562 int current_minor = first_part_minor;
563 int err;
564
565 err = handle_ide_mess(bdev);
566 if (err <= 0)
567 return err;
568 data = read_dev_sector(bdev, 0, §);
569 if (!data)
570 return -1;
571 if (!msdos_magic_present(data + 510)) {
572 put_dev_sector(sect);
573 return 0;
574 }
575 /*
576 * Now that the 55aa signature is present, this is probably
577 * either the boot sector of a FAT filesystem or a DOS-type
578 * partition table. Reject this in case the boot indicator
579 * is not 0 or 0x80.
580 */
581 p = (struct partition *) (data + 0x1be);
582 for (i = 1; i <= 4; i++, p++) {
583 if (p->boot_ind != 0 && p->boot_ind != 0x80) {
584 put_dev_sector(sect);
585 return 0;
586 }
587 }
588 p = (struct partition *) (data + 0x1be);
589
590 /*
591 * Look for partitions in two passes:
592 * First find the primary and DOS-type extended partitions.
593 * On the second pass look inside *BSD, Unixware and Solaris partitions.
594 */
595
596 current_minor += 4;
597 for (i=1 ; i<=4 ; minor++,i++,p++) {
598 if (!NR_SECTS(p))
599 continue;
600 add_gd_partition(hd, minor,
601 first_sector+START_SECT(p)*sector_size,
602 NR_SECTS(p)*sector_size);
603 #if CONFIG_BLK_DEV_MD
604 if (SYS_IND(p) == LINUX_RAID_PARTITION) {
605 md_autodetect_dev(MKDEV(hd->major,minor));
606 }
607 #endif
608 if (is_extended_partition(p)) {
609 unsigned long size = hd->part[minor].nr_sects;
610 printk(" <");
611 /* prevent someone doing mkfs or mkswap on an
612 extended partition, but leave room for LILO */
613 if (size > 2)
614 hd->part[minor].nr_sects = 2;
615 extended_partition(hd, bdev, minor, size, ¤t_minor);
616 printk(" >");
617 }
618 }
619
620 /*
621 * Check for old-style Disk Manager partition table
622 */
623 if (msdos_magic_present(data + 0xfc)) {
624 p = (struct partition *) (0x1be + data);
625 for (i = 4 ; i < 16 ; i++, current_minor++) {
626 p--;
627 if ((current_minor & mask) == 0)
628 break;
629 if (!(START_SECT(p) && NR_SECTS(p)))
630 continue;
631 add_gd_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
632 }
633 }
634 printk("\n");
635
636 /* second pass - output for each on a separate line */
637 minor -= 4;
638 p = (struct partition *) (0x1be + data);
639 for (i=1 ; i<=4 ; minor++,i++,p++) {
640 unsigned char id = SYS_IND(p);
641 int n;
642
643 if (!NR_SECTS(p))
644 continue;
645
646 for (n = 0; subtypes[n].parse && id != subtypes[n].id; n++)
647 ;
648
649 if (subtypes[n].parse)
650 subtypes[n].parse(hd, bdev, minor, ¤t_minor);
651 }
652 put_dev_sector(sect);
653 return 1;
654 }
655