1 /* 2 3 /usr/src/ext2ed/init.c 4 5 A part of the extended file system 2 disk editor. 6 7 -------------------------------- 8 Various initialization routines. 9 -------------------------------- 10 11 First written on: April 9 1995 12 13 Copyright (C) 1995 Gadi Oxman 14 15 */ 16 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #ifdef HAVE_READLINE 21 #include <readline.h> 22 #endif 23 #include <signal.h> 24 #include <unistd.h> 25 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <fcntl.h> 29 30 #include "ext2ed.h" 31 32 char lines_s [80],cols_s [80]; 33 34 void signal_handler (void); 35 36 void prepare_to_close (void) 37 38 { 39 close_windows (); 40 if (device_handle!=NULL) 41 fclose (device_handle); 42 free_user_commands (&general_commands); 43 free_user_commands (&ext2_commands); 44 free_struct_descriptors (); 45 } 46 47 int init (void) 48 49 { 50 printf ("Initializing ...\n"); 51 52 if (!process_configuration_file ()) { 53 fprintf (stderr,"Error - Unable to complete configuration. Quitting.\n"); 54 return (0); 55 }; 56 57 general_commands.last_command=-1; /* No commands whatsoever meanwhile */ 58 ext2_commands.last_command=-1; 59 add_general_commands (); /* Add the general commands, aviable always */ 60 device_handle=NULL; /* Notice that our device is still not set up */ 61 device_offset=-1; 62 current_type=NULL; /* No filesystem specific types yet */ 63 64 remember_lifo.entries_count=0; /* Object memory is empty */ 65 66 init_windows (); /* Initialize the NCURSES interface */ 67 init_readline (); /* Initialize the READLINE interface */ 68 init_signals (); /* Initialize the signal handlers */ 69 write_access=0; /* Write access disabled */ 70 71 strcpy (last_command_line,"help"); /* Show the help screen to the user */ 72 dispatch ("help"); 73 return (1); /* Success */ 74 } 75 76 void add_general_commands (void) 77 78 { 79 add_user_command (&general_commands,"help","EXT2ED help system",help); 80 add_user_command (&general_commands,"set","Changes a variable in the current object",set); 81 add_user_command (&general_commands,"setdevice","Selects the filesystem block device (e.g. /dev/hda1)",set_device); 82 add_user_command (&general_commands,"setoffset","Moves asynchronicly in the filesystem",set_offset); 83 add_user_command (&general_commands,"settype","Tells EXT2ED how to interpert the current object",set_type); 84 add_user_command (&general_commands,"show","Displays the current object",show); 85 add_user_command (&general_commands,"pgup","Scrolls data one page up",pgup); 86 add_user_command (&general_commands,"pgdn","Scrolls data one page down",pgdn); 87 add_user_command (&general_commands,"redraw","Redisplay the screen",redraw); 88 add_user_command (&general_commands,"remember","Saves the current position and data information",remember); 89 add_user_command (&general_commands,"recall","Gets back to the saved object position",recall); 90 add_user_command (&general_commands,"enablewrite","Enters Read/Write mode - Allows changing the filesystem",enable_write); 91 add_user_command (&general_commands,"disablewrite","Enters read only mode",disable_write); 92 add_user_command (&general_commands,"writedata","Write data back to disk",write_data); 93 add_user_command (&general_commands,"next","Moves to the next byte in hex mode",next); 94 add_user_command (&general_commands,"prev","Moves to the previous byte in hex mode",prev); 95 } 96 97 void add_ext2_general_commands (void) 98 99 { 100 add_user_command (&ext2_commands,"super","Moves to the superblock of the filesystem",type_ext2___super); 101 add_user_command (&ext2_commands,"group","Moves to the first group descriptor",type_ext2___group); 102 add_user_command (&ext2_commands,"cd","Moves to the directory specified",type_ext2___cd); 103 } 104 105 int set_struct_descriptors (char *file_name) 106 107 { 108 FILE *fp; 109 char current_line [500],current_word [50],*ch; 110 char variable_name [50],variable_type [20]; 111 struct struct_descriptor *current_descriptor; 112 113 if ( (fp=fopen (file_name,"rt"))==NULL) { 114 wprintw (command_win,"Error - Failed to open descriptors file %s\n",file_name); 115 refresh_command_win (); return (0); 116 }; 117 118 while (!feof (fp)) { 119 fgets (current_line,500,fp); 120 if (feof (fp)) break; 121 ch=parse_word (current_line,current_word); 122 if (strcmp (current_word,"struct")==0) { 123 ch=parse_word (ch,current_word); 124 current_descriptor=add_new_descriptor (current_word); 125 126 while (strchr (current_line,'{')==NULL) { 127 fgets (current_line,500,fp); 128 if (feof (fp)) break; 129 }; 130 if (feof (fp)) break; 131 132 fgets (current_line,500,fp); 133 134 while (strchr (current_line,'}')==NULL) { 135 while (strchr (current_line,';')==NULL) { 136 fgets (current_line,500,fp); 137 if (strchr (current_line,'}')!=NULL) break; 138 }; 139 if (strchr (current_line,'}') !=NULL) break; 140 ch=parse_word (current_line,variable_type); 141 ch=parse_word (ch,variable_name); 142 while (variable_name [strlen (variable_name)-1]!=';') { 143 strcpy (variable_type,variable_name); 144 ch=parse_word (ch,variable_name); 145 }; 146 variable_name [strlen (variable_name)-1]=0; 147 add_new_variable (current_descriptor,variable_type,variable_name); 148 fgets (current_line,500,fp); 149 }; 150 }; 151 }; 152 153 fclose (fp); 154 return (1); 155 } 156 157 void free_struct_descriptors (void) 158 159 { 160 struct struct_descriptor *ptr,*next; 161 162 ptr=first_type; 163 while (ptr!=NULL) { 164 next=ptr->next; 165 free_user_commands (&ptr->type_commands); 166 free (ptr); 167 ptr=next; 168 } 169 first_type=last_type=current_type=NULL; 170 } 171 172 void free_user_commands (struct struct_commands *ptr) 173 174 { 175 int i; 176 177 for (i=0;i<=ptr->last_command;i++) { 178 free (ptr->names [i]); 179 free (ptr->descriptions [i]); 180 } 181 182 ptr->last_command=-1; 183 } 184 185 struct struct_descriptor *add_new_descriptor (char *name) 186 187 { 188 struct struct_descriptor *ptr; 189 190 ptr = malloc (sizeof (struct struct_descriptor)); 191 if (ptr == NULL) { 192 printf ("Error - Can not allocate memory - Quitting\n"); 193 exit (1); 194 } 195 memset(ptr, 0, sizeof(struct struct_descriptor)); 196 ptr->prev = ptr->next = NULL; 197 strcpy (ptr->name,name); 198 ptr->length=0; 199 ptr->fields_num=0; 200 if (first_type==NULL) { 201 first_type = last_type = ptr; 202 } else { 203 ptr->prev = last_type; last_type->next = ptr; last_type=ptr; 204 } 205 ptr->type_commands.last_command=-1; 206 fill_type_commands (ptr); 207 return (ptr); 208 } 209 210 struct type_table { 211 char *name; 212 int field_type; 213 int len; 214 }; 215 216 struct type_table type_table[] = { 217 { "long", FIELD_TYPE_INT, 4 }, 218 { "short", FIELD_TYPE_INT, 2 }, 219 { "char", FIELD_TYPE_CHAR, 1 }, 220 { "__u32", FIELD_TYPE_UINT, 4 }, 221 { "__s32", FIELD_TYPE_INT, 4 }, 222 { "__u16", FIELD_TYPE_UINT, 2 }, 223 { "__s16", FIELD_TYPE_INT, 2 }, 224 { "__u8", FIELD_TYPE_UINT, 1 }, 225 { "__s8", FIELD_TYPE_INT, 1 }, 226 { 0, 0, 0 } 227 }; 228 229 void add_new_variable (struct struct_descriptor *ptr,char *v_type,char *v_name) 230 231 { 232 short len=1; 233 char field_type=FIELD_TYPE_INT; 234 struct type_table *p; 235 236 strcpy (ptr->field_names [ptr->fields_num],v_name); 237 ptr->field_positions [ptr->fields_num]=ptr->length; 238 239 for (p = type_table; p->name; p++) { 240 if (strcmp(v_type, p->name) == 0) { 241 len = p->len; 242 field_type = p->field_type; 243 break; 244 } 245 } 246 if (p->name == 0) { 247 if (strncmp(v_type, "char[", 5) == 0) { 248 len = atoi(v_type+5); 249 field_type = FIELD_TYPE_CHAR; 250 } else { 251 printf("Unknown type %s for field %s\n", v_type, v_name); 252 exit(1); 253 } 254 } 255 256 ptr->field_lengths [ptr->fields_num] = len; 257 ptr->field_types [ptr->fields_num] = field_type; 258 259 ptr->length+=len; 260 ptr->fields_num++; 261 } 262 263 void fill_type_commands (struct struct_descriptor *ptr) 264 265 /* 266 267 Set specific type user commands. 268 269 */ 270 271 { 272 273 if (strcmp ((ptr->name),"file")==0) { 274 add_user_command (&ptr->type_commands,"show","Shows file data",type_file___show); 275 add_user_command (&ptr->type_commands,"inode","Returns to the inode of the current file",type_file___inode); 276 add_user_command (&ptr->type_commands,"display","Specifies data format - text or hex",type_file___display); 277 add_user_command (&ptr->type_commands,"next","Pass to next byte",type_file___next); 278 add_user_command (&ptr->type_commands,"prev","Pass to the previous byte",type_file___prev); 279 add_user_command (&ptr->type_commands,"offset","Pass to a specified byte in the current block",type_file___offset); 280 add_user_command (&ptr->type_commands,"nextblock","Pass to next file block",type_file___nextblock); 281 add_user_command (&ptr->type_commands,"prevblock","Pass to the previous file block",type_file___prevblock); 282 add_user_command (&ptr->type_commands,"block","Specify which file block to edit",type_file___block); 283 add_user_command (&ptr->type_commands,"remember","Saves the file\'s inode position for later reference",type_file___remember); 284 add_user_command (&ptr->type_commands,"set","Sets the current byte",type_file___set); 285 add_user_command (&ptr->type_commands,"writedata","Writes the current block to the disk",type_file___writedata); 286 } 287 288 if (strcmp ((ptr->name),"ext2_inode")==0) { 289 add_user_command (&ptr->type_commands,"show","Shows inode data",type_ext2_inode___show); 290 add_user_command (&ptr->type_commands,"next","Move to next inode in current block group",type_ext2_inode___next); 291 add_user_command (&ptr->type_commands,"prev","Move to next inode in current block group",type_ext2_inode___prev); 292 add_user_command (&ptr->type_commands,"group","Move to the group descriptors of the current inode table",type_ext2_inode___group); 293 add_user_command (&ptr->type_commands,"entry","Move to a specified entry in the current inode table",type_ext2_inode___entry); 294 add_user_command (&ptr->type_commands,"file","Display file data of the current inode",type_ext2_inode___file); 295 add_user_command (&ptr->type_commands,"dir","Display directory data of the current inode",type_ext2_inode___dir); 296 } 297 298 if (strcmp ((ptr->name),"dir")==0) { 299 add_user_command (&ptr->type_commands,"show","Shows current directory data",type_dir___show); 300 add_user_command (&ptr->type_commands,"inode","Returns to the inode of the current directory",type_dir___inode); 301 add_user_command (&ptr->type_commands,"next","Pass to the next directory entry",type_dir___next); 302 add_user_command (&ptr->type_commands,"prev","Pass to the previous directory entry",type_dir___prev); 303 add_user_command (&ptr->type_commands,"followinode","Follows the inode specified in this directory entry",type_dir___followinode); 304 add_user_command (&ptr->type_commands,"remember","Remember the inode of the current directory entry",type_dir___remember); 305 add_user_command (&ptr->type_commands,"cd","Changes directory relative to the current directory",type_dir___cd); 306 add_user_command (&ptr->type_commands,"entry","Moves to a specified entry in the current directory",type_dir___entry); 307 add_user_command (&ptr->type_commands,"writedata","Writes the current entry to the disk",type_dir___writedata); 308 add_user_command (&ptr->type_commands,"set","Changes a variable in the current directory entry",type_dir___set); 309 } 310 311 if (strcmp ((ptr->name),"ext2_super_block")==0) { 312 add_user_command (&ptr->type_commands,"show","Displays the super block data",type_ext2_super_block___show); 313 add_user_command (&ptr->type_commands,"gocopy","Move to another backup copy of the superblock",type_ext2_super_block___gocopy); 314 add_user_command (&ptr->type_commands,"setactivecopy","Copies the current superblock to the main superblock",type_ext2_super_block___setactivecopy); 315 } 316 317 if (strcmp ((ptr->name),"ext2_group_desc")==0) { 318 add_user_command (&ptr->type_commands,"next","Pass to the next block group decriptor",type_ext2_group_desc___next); 319 add_user_command (&ptr->type_commands,"prev","Pass to the previous group descriptor",type_ext2_group_desc___prev); 320 add_user_command (&ptr->type_commands,"entry","Pass to a specific group descriptor",type_ext2_group_desc___entry); 321 add_user_command (&ptr->type_commands,"show","Shows the current group descriptor",type_ext2_group_desc___show); 322 add_user_command (&ptr->type_commands,"inode","Pass to the inode table of the current group block",type_ext2_group_desc___inode); 323 add_user_command (&ptr->type_commands,"gocopy","Move to another backup copy of the group descriptor",type_ext2_group_desc___gocopy); 324 add_user_command (&ptr->type_commands,"blockbitmap","Show the block allocation bitmap of the current group block",type_ext2_group_desc___blockbitmap); 325 add_user_command (&ptr->type_commands,"inodebitmap","Show the inode allocation bitmap of the current group block",type_ext2_group_desc___inodebitmap); 326 add_user_command (&ptr->type_commands,"setactivecopy","Copies the current group descriptor to the main table",type_ext2_super_block___setactivecopy); 327 } 328 329 if (strcmp ((ptr->name),"block_bitmap")==0) { 330 add_user_command (&ptr->type_commands,"show","Displays the block allocation bitmap",type_ext2_block_bitmap___show); 331 add_user_command (&ptr->type_commands,"entry","Moves to a specific bit",type_ext2_block_bitmap___entry); 332 add_user_command (&ptr->type_commands,"next","Moves to the next bit",type_ext2_block_bitmap___next); 333 add_user_command (&ptr->type_commands,"prev","Moves to the previous bit",type_ext2_block_bitmap___prev); 334 add_user_command (&ptr->type_commands,"allocate","Allocates the current block",type_ext2_block_bitmap___allocate); 335 add_user_command (&ptr->type_commands,"deallocate","Deallocates the current block",type_ext2_block_bitmap___deallocate); 336 } 337 338 if (strcmp ((ptr->name),"inode_bitmap")==0) { 339 add_user_command (&ptr->type_commands,"show","Displays the inode allocation bitmap",type_ext2_inode_bitmap___show); 340 add_user_command (&ptr->type_commands,"entry","Moves to a specific bit",type_ext2_inode_bitmap___entry); 341 add_user_command (&ptr->type_commands,"next","Moves to the next bit",type_ext2_inode_bitmap___next); 342 add_user_command (&ptr->type_commands,"prev","Moves to the previous bit",type_ext2_inode_bitmap___prev); 343 add_user_command (&ptr->type_commands,"allocate","Allocates the current inode",type_ext2_inode_bitmap___allocate); 344 add_user_command (&ptr->type_commands,"deallocate","Deallocates the current inode",type_ext2_inode_bitmap___deallocate); 345 } 346 347 } 348 349 void add_user_command (struct struct_commands *ptr,char *name,char *description,PF callback) 350 351 { 352 int num; 353 354 num=ptr->last_command; 355 if (num+1==MAX_COMMANDS_NUM) { 356 printf ("Internal Error - Can't add command %s\n",name); 357 return; 358 } 359 360 ptr->last_command=++num; 361 362 ptr->names [num]=(char *) malloc (strlen (name)+1); 363 strcpy (ptr->names [num],name); 364 365 if (*description!=0) { 366 ptr->descriptions [num]=(char *) malloc (strlen (description)+1); 367 strcpy (ptr->descriptions [num],description); 368 } 369 370 ptr->callback [num]=callback; 371 } 372 373 int set_file_system_info (void) 374 375 { 376 int ext2_detected=0; 377 struct ext2_super_block *sb; 378 379 file_system_info.super_block_offset=1024; 380 file_system_info.file_system_size=DefaultTotalBlocks*DefaultBlockSize; 381 382 low_read ((char *) &file_system_info.super_block,sizeof (struct ext2_super_block),file_system_info.super_block_offset); 383 384 sb=&file_system_info.super_block; 385 386 if (sb->s_magic == EXT2_SUPER_MAGIC) 387 ext2_detected=1; 388 389 if (ext2_detected) 390 wprintw (command_win,"Detected extended 2 file system on device %s\n",device_name); 391 else 392 wprintw (command_win,"Warning - Extended 2 filesystem not detected on device %s\n",device_name); 393 394 if (!ext2_detected && !ForceExt2) 395 wprintw (command_win,"You may wish to use the configuration option ForceExt2 on\n"); 396 397 if (ForceExt2 && !ext2_detected) 398 wprintw (command_win,"Forcing extended 2 filesystem\n"); 399 400 if (ForceDefault || !ext2_detected) 401 wprintw (command_win,"Forcing default parameters\n"); 402 403 refresh_command_win (); 404 405 if (ext2_detected || ForceExt2) { 406 add_ext2_general_commands (); 407 if (!set_struct_descriptors (Ext2Descriptors)) 408 return (0); 409 } 410 411 if (!ForceDefault && ext2_detected) { 412 413 file_system_info.block_size=EXT2_MIN_BLOCK_SIZE << sb->s_log_block_size; 414 if (file_system_info.block_size == EXT2_MIN_BLOCK_SIZE) 415 file_system_info.first_group_desc_offset=2*EXT2_MIN_BLOCK_SIZE; 416 else 417 file_system_info.first_group_desc_offset=file_system_info.block_size; 418 file_system_info.groups_count = ext2fs_div64_ceil(ext2fs_blocks_count(sb), 419 sb->s_blocks_per_group); 420 421 file_system_info.inodes_per_block=file_system_info.block_size/sizeof (struct ext2_inode); 422 file_system_info.blocks_per_group=sb->s_inodes_per_group/file_system_info.inodes_per_block; 423 file_system_info.no_blocks_in_group=sb->s_blocks_per_group; 424 file_system_info.file_system_size=(ext2fs_blocks_count(sb)-1)*file_system_info.block_size; 425 } 426 427 else { 428 file_system_info.file_system_size=DefaultTotalBlocks*DefaultBlockSize; 429 file_system_info.block_size=DefaultBlockSize; 430 file_system_info.no_blocks_in_group=DefaultBlocksInGroup; 431 } 432 433 if (file_system_info.file_system_size > 2147483647) { 434 wprintw (command_win,"Sorry, filesystems bigger than 2 GB are currently not supported\n"); 435 return (0); 436 } 437 return (1); 438 } 439 440 void init_readline (void) 441 442 { 443 #ifdef HAVE_READLINE 444 rl_completion_entry_function=(Function *) complete_command; 445 #endif 446 } 447 448 void init_signals (void) 449 450 { 451 signal (SIGWINCH, signal_SIGWINCH_handler); /* Catch SIGWINCH */ 452 signal (SIGTERM, signal_SIGTERM_handler); 453 signal (SIGSEGV, signal_SIGSEGV_handler); 454 455 } 456 457 void signal_SIGWINCH_handler (int sig_num) 458 459 { 460 redraw_request=1; /* We will handle it in main.c */ 461 462 /* Reset signal handler */ 463 signal (SIGWINCH, signal_SIGWINCH_handler); 464 465 } 466 467 void signal_SIGTERM_handler (int sig_num) 468 469 { 470 prepare_to_close (); 471 printf ("Terminated due to signal %d\n",sig_num); 472 exit (1); 473 } 474 475 void signal_SIGSEGV_handler (int sig_num) 476 477 { 478 prepare_to_close (); 479 printf ("Killed by signal %d!\n",sig_num); 480 exit (1); 481 } 482 483 int process_configuration_file (void) 484 485 { 486 char buffer [300]; 487 char option [80],value [80]; 488 FILE *fp; 489 490 strcpy (buffer, ROOT_SYSCONFDIR); 491 strcat (buffer,"/ext2ed.conf"); 492 493 if ((fp=fopen (buffer,"rt"))==NULL) { 494 fprintf (stderr,"Error - Unable to open configuration file %s\n",buffer); 495 return (0); 496 } 497 498 while (get_next_option (fp,option,value)) { 499 if (strcasecmp (option,"Ext2Descriptors")==0) { 500 strcpy (Ext2Descriptors,value); 501 } 502 503 else if (strcasecmp (option,"AlternateDescriptors")==0) { 504 strcpy (AlternateDescriptors,value); 505 } 506 507 else if (strcasecmp (option,"LogFile")==0) { 508 strcpy (LogFile,value); 509 } 510 511 else if (strcasecmp (option,"LogChanges")==0) { 512 if (strcasecmp (value,"on")==0) 513 LogChanges = 1; 514 else if (strcasecmp (value,"off")==0) 515 LogChanges = 0; 516 else { 517 fprintf (stderr,"Error - Illegal value: %s %s\n",option,value); 518 fclose (fp);return (0); 519 } 520 } 521 522 else if (strcasecmp (option,"AllowChanges")==0) { 523 if (strcasecmp (value,"on")==0) 524 AllowChanges = 1; 525 else if (strcasecmp (value,"off")==0) 526 AllowChanges = 0; 527 else { 528 fprintf (stderr,"Error - Illegal value: %s %s\n",option,value); 529 fclose (fp);return (0); 530 } 531 } 532 533 else if (strcasecmp (option,"AllowMountedRead")==0) { 534 if (strcasecmp (value,"on")==0) 535 AllowMountedRead = 1; 536 else if (strcasecmp (value,"off")==0) 537 AllowMountedRead = 0; 538 else { 539 fprintf (stderr,"Error - Illegal value: %s %s\n",option,value); 540 fclose (fp);return (0); 541 } 542 } 543 544 else if (strcasecmp (option,"ForceExt2")==0) { 545 if (strcasecmp (value,"on")==0) 546 ForceExt2 = 1; 547 else if (strcasecmp (value,"off")==0) 548 ForceExt2 = 0; 549 else { 550 fprintf (stderr,"Error - Illegal value: %s %s\n",option,value); 551 fclose (fp);return (0); 552 } 553 } 554 555 else if (strcasecmp (option,"DefaultBlockSize")==0) { 556 DefaultBlockSize = atoi (value); 557 } 558 559 else if (strcasecmp (option,"DefaultTotalBlocks")==0) { 560 DefaultTotalBlocks = strtoul (value,NULL,10); 561 } 562 563 else if (strcasecmp (option,"DefaultBlocksInGroup")==0) { 564 DefaultBlocksInGroup = strtoul (value,NULL,10); 565 } 566 567 else if (strcasecmp (option,"ForceDefault")==0) { 568 if (strcasecmp (value,"on")==0) 569 ForceDefault = 1; 570 else if (strcasecmp (value,"off")==0) 571 ForceDefault = 0; 572 else { 573 fprintf (stderr,"Error - Illegal value: %s %s\n",option,value); 574 fclose (fp);return (0); 575 } 576 } 577 578 else { 579 fprintf (stderr,"Error - Unknown option: %s\n",option); 580 fclose (fp);return (0); 581 } 582 } 583 584 printf ("Configuration completed\n"); 585 fclose (fp); 586 return (1); 587 } 588 589 int get_next_option (FILE *fp,char *option,char *value) 590 591 { 592 char *ptr; 593 char buffer [600]; 594 595 if (feof (fp)) return (0); 596 do{ 597 if (feof (fp)) return (0); 598 fgets (buffer,500,fp); 599 } while (buffer [0]=='#' || buffer [0]=='\n'); 600 601 ptr=parse_word (buffer,option); 602 ptr=parse_word (ptr,value); 603 return (1); 604 } 605 606 void check_mounted (char *name) 607 608 { 609 FILE *fp; 610 char *ptr; 611 char current_line [500],current_word [200]; 612 613 mounted=0; 614 615 if ( (fp=fopen ("/etc/mtab","rt"))==NULL) { 616 wprintw (command_win,"Error - Failed to open /etc/mtab. Assuming filesystem is mounted.\n"); 617 refresh_command_win ();mounted=1;return; 618 }; 619 620 while (!feof (fp)) { 621 fgets (current_line,500,fp); 622 if (feof (fp)) break; 623 ptr=parse_word (current_line,current_word); 624 if (strcasecmp (current_word,name)==0) { 625 mounted=1;fclose (fp);return; 626 } 627 }; 628 629 fclose (fp); 630 631 return; 632 } 633