Whamcloud - gitweb
Remove the a.out DLL support, since it's been obsolete and unmaintained
[tools/e2fsprogs.git] / lib / evms / fsimext2.c
1 /*
2  *
3  *   Copyright (c) International Business Machines  Corp., 2000
4  *
5  *   This program is free software;  you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13  *   the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program;  if not, write to the Free Software
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  *   Module: fsimext2.c
20  *
21  */
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <sys/ioctl.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <plugin.h>
33 #include "fsimext2.h"
34
35 int fsim_rw_diskblocks( int, int64_t, int32_t, void *, int );
36 void set_mkfs_options( option_array_t *, char **, logical_volume_t *, char * );
37 void set_fsck_options( option_array_t *, char **, logical_volume_t * );
38
39 /* Vector of plugin record ptrs that we export for the EVMS Engine.  */
40 plugin_record_t *evms_plugin_records[] = {
41         &ext2_plugrec,
42         NULL
43 };
44
45 static plugin_record_t  * pMyPluginRecord = &ext2_plugrec;
46
47 /*-------------------------------------------------------------------------------------+
48 +                                                                                      +
49 +                                   Common Routines                                    +
50 +                                                                                      +
51 +--------------------------------------------------------------------------------------*/
52
53
54 /*
55  * Get the size limits for this volume.
56  */
57 int fsim_get_volume_limits( struct ext2_super_block * sb,
58                          sector_count_t   * fs_min_size,
59                          sector_count_t   * fs_max_size,
60                          sector_count_t   * vol_max_size)
61 {
62         int rc = 0;
63         int             blk_to_sect;
64
65         blk_to_sect = (1 + sb->s_log_block_size);
66         *fs_min_size = (sb->s_blocks_count - sb->s_free_blocks_count) << blk_to_sect;
67         *fs_max_size = (sector_count_t) 1 << (32+blk_to_sect);
68         *vol_max_size = 0xffffffffff;
69
70         LOGEXITRC();
71         return rc;
72 }
73
74
75 /*
76  * Un-Format the volume.
77  */
78 int fsim_unmkfs( logical_volume_t * volume )
79 {
80     int  fd;
81     int  rc = 0;
82
83     LOGENTRY();
84  
85     fd = open(EVMS_GET_DEVNAME(volume), O_RDWR|O_EXCL, 0);
86     if (fd < 0) return -1;
87
88     if ( volume->private_data ) {
89         /* clear private data */
90         memset( (void *) volume->private_data, 0, SIZE_OF_SUPER );
91         /* zero primary superblock */
92         rc =  fsim_rw_diskblocks( fd, EXT2_SUPER_LOC, SIZE_OF_SUPER,
93                                  volume->private_data, PUT );
94     } else {
95         rc = ERROR;
96     }
97
98     fd = close(fd);
99
100     LOGEXITRC();
101     return rc;
102 }
103
104
105 /*
106  * Format the volume.
107  */
108 int fsim_mkfs(logical_volume_t * volume, option_array_t * options )
109 {
110         int     rc = FSIM_ERROR;
111         char   *argv[MKFS_EXT2_OPTIONS_COUNT + 6];
112         char    logsize[sizeof(unsigned int) + 1];
113         pid_t   pidm;
114         int     status;
115
116         LOGENTRY();
117
118         /* Fork and execute the correct program. */
119     switch (pidm = fork()) {
120         
121         /* error */
122         case -1:
123             rc = EIO;
124
125         /* child */
126         case 0:  
127             set_mkfs_options( options, argv, volume, logsize );
128
129             /* close stderr, stdout to suppress mke2fs output */
130             close(1);
131             close(2);
132             open("/dev/null", O_WRONLY);
133             open("/dev/null", O_WRONLY);
134
135             (void) execvp(argv[0], argv);
136             /* using exit() can hang GUI, use _exit */
137             _exit(errno);
138
139         /* parent */
140         default:
141             /* wait for child to complete */
142             while (1) {
143                     rc = waitpid( pidm, &status, 0 );
144                     if (rc == -1) {
145                             if (errno == EINTR)
146                                     continue;
147                             rc = errno;
148                             goto reterr;
149                     } else
150                             break;
151             } 
152             if ( WIFEXITED(status) ) {
153                 /* get mke2fs exit code */
154                 rc = WEXITSTATUS(status);
155                 if (rc)
156                         LOG("mke2fs exited with status %d", rc);
157             } else {
158                     if (WIFSIGNALED(status))
159                             LOG("mke2fs died with signal %d",
160                                 WTERMSIG(status));
161                     rc = EINTR;
162             }
163     }
164
165 reterr:
166     LOGEXITRC();
167     return rc;
168 }
169
170
171 /*
172  * NAME: set_mkfs_options
173  *
174  * FUNCTION: Build options array (argv) for mkfs.ext2
175  *
176  * PARAMETERS:
177  *      options   - options array passed from EVMS engine
178  *      argv      - mkfs options array
179  *      vol_name  - volume name on which program will be executed
180  *
181  */                        
182 void set_mkfs_options( option_array_t * options, 
183                        char ** argv, 
184                        logical_volume_t * volume, 
185                        char * logsize )
186 {
187     int i, bufsize, opt_count = 2;
188     char *buf;
189
190     LOGENTRY();
191
192     argv[0] = "mke2fs";
193
194     /* 'quiet' option */
195     argv[1] = "-q";
196
197     /* the following is a big hack to make sure that we don't use a block */
198     /* size smaller than hardsector size since this does not work. */
199     /* would be nice if the ext2/3 utilities (mkfs) handled this themselves */
200     /* also, eventually we will implement this as a user option to manually */
201     /* set block size */
202     if (volume->object->geometry.bytes_per_sector != EVMS_VSECTOR_SIZE) {
203             switch (volume->object->geometry.bytes_per_sector) {
204             case 2048:
205                     argv[2] = "-b2048";
206                     opt_count++;
207                     break;
208             case 4096:
209                     argv[2] = "-b4096";
210                     opt_count++;
211                     break;
212             default:
213                     /* not one we expect, just skip it */
214                     break;
215             }
216     }
217
218     for ( i=0; i<options->count; i++ ) {
219
220         if ( options->option[i].is_number_based ) {
221
222             switch (options->option[i].number) {
223                 
224             case MKFS_CHECKBB_INDEX:
225                 /* 'check for bad blocks' option */
226                 if ( options->option[i].value.bool == TRUE ) {
227                     argv[opt_count++] = "-c";
228                 }
229                 break;
230
231             case MKFS_CHECKRW_INDEX:
232                 /* 'check for r/w bad blocks' option */
233                 if ( options->option[i].value.bool == TRUE ) {
234                     argv[opt_count++] = "-cc";
235                 }
236                 break;
237
238             case MKFS_JOURNAL_INDEX:
239                 /* 'create ext3 journal' option */
240                 if ( options->option[i].value.bool == TRUE ) {
241                     argv[opt_count++] = "-j";
242                 }
243                 break;
244
245             case MKFS_SETVOL_INDEX:
246                 /* 'set volume name' option */
247                 if ( options->option[i].value.s ) {
248                     argv[opt_count++] = "-L";
249                     argv[opt_count++] = options->option[i].value.s;
250                 }
251                 break;
252
253             default:
254                 break;
255             }
256
257         } else {
258
259             if ( !strcmp(options->option[i].name, "badblocks") ) {
260                 /* 'check for bad blocks' option */
261                 if ( options->option[i].value.bool == TRUE ) {
262                     argv[opt_count++] = "-c";
263                 }
264             }
265
266             if ( !strcmp(options->option[i].name, "badblocks_rw") ) {
267                 /* 'check for r/w bad blocks' option */
268                 if ( options->option[i].value.bool == TRUE ) {
269                     argv[opt_count++] = "-cc";
270                 }
271             }
272
273             if ( !strcmp(options->option[i].name, "journal") ) {
274                 /* 'create ext3 journal' option */
275                 if ( options->option[i].value.bool == TRUE ) {
276                     argv[opt_count++] = "-j";
277                 }
278             }
279
280             if ( !strcmp(options->option[i].name, "vollabel") ) {
281                 /* 'check for bad blocks' option */
282                 if ( options->option[i].value.s ) {
283                     argv[opt_count++] = "-L";
284                     argv[opt_count++] = options->option[i].value.s;
285                 }
286             }
287         }
288     }
289
290     argv[opt_count++] = EVMS_GET_DEVNAME(volume);
291     argv[opt_count] = NULL;
292      
293     bufsize = 0;
294     for (i=0; argv[i]; i++)
295             bufsize += strlen(argv[i]) + 5;
296     buf = malloc(bufsize+1);
297     if (!buf)
298             return;
299     buf[0] = 0;
300     for (i=0; argv[i]; i++) {
301             strcat(buf, argv[i]);
302             strcat(buf, " ");
303     }
304     EngFncs->write_log_entry(DEBUG, pMyPluginRecord,
305                              "mke2fs command: %s\n", buf);
306     free(buf);
307     
308     LOGEXIT();
309     return;
310 }
311
312
313 /*
314  * Run fsck on the volume.
315  */
316 int fsim_fsck(logical_volume_t * volume, option_array_t * options,
317               int *ret_status)
318 {
319         int     rc = FSIM_ERROR;
320         char   *argv[FSCK_EXT2_OPTIONS_COUNT + 3];
321         pid_t   pidf;
322         int     status, bytes_read;
323         char    *buffer = NULL;
324         int     fds2[2];
325         int     banner = 0;
326
327         LOGENTRY();
328
329         /* open pipe, alloc buffer for collecting fsck.jfs output */
330         rc = pipe(fds2);
331         if (rc) {
332             return(errno);
333         }
334         if (!(buffer = EngFncs->engine_alloc(MAX_USER_MESSAGE_LEN))) {
335             return(ENOMEM);
336         }
337
338         /* Fork and execute the correct program. */
339         switch (pidf = fork()) {
340         
341         /* error */
342         case -1:
343                 rc = EIO;
344
345         /* child */
346         case 0:  
347                 set_fsck_options( options, argv, volume );
348
349             /* pipe stderr, stdout */
350                 dup2(fds2[1],1);        /* fds2[1] replaces stdout */
351                 dup2(fds2[1],2);        /* fds2[1] replaces stderr */
352                 close(fds2[0]); /* don't need this here */
353
354                 execvp( argv[0], argv );
355                 /* should never get here */
356                 _exit(8);       /* FSCK_ERROR -- operational error */
357                 
358         /* parent */
359         default:
360                 close(fds2[1]);
361
362                 /* wait for child to complete */
363                 fcntl(fds2[0], F_SETFL, fcntl(fds2[0], F_GETFL,0) | O_NONBLOCK);
364                 while (!(waitpid( pidf, &status, WNOHANG ))) {
365                         /* read e2fsck output */
366                         bytes_read = read(fds2[0],buffer,MAX_USER_MESSAGE_LEN);
367                         if (bytes_read > 0) {
368                                 /* display e2fsck output */
369                                 if (!banner)
370                                         MESSAGE("e2fsck output:");
371                                 banner = 1;
372                                 MESSAGE("%s",buffer);
373                                 memset(buffer,0,bytes_read); /* clear out message  */
374                         }
375                         usleep(10000); /* don't hog all the cpu */
376                 }
377
378                 /* do final read, just in case we missed some */
379                 bytes_read = read(fds2[0],buffer,MAX_USER_MESSAGE_LEN);
380                 if (bytes_read > 0) {
381                         if (!banner)
382                                 MESSAGE("e2fsck output:");
383                         MESSAGE("%s",buffer);
384                 }
385                 if ( WIFEXITED(status) ) {
386                         /* get e2fsck exit code */
387                         *ret_status = WEXITSTATUS(status);
388                         LOG("e2fsck completed with exit code %d\n",
389                             *ret_status);
390                         rc = 0;
391                 } else {
392                         if (WIFSIGNALED(status))
393                                 LOG("e2fsck died with signal %d",
394                                     WTERMSIG(status));
395                         rc = EINTR;
396                 }
397         }
398
399         if (buffer) {
400                 EngFncs->engine_free(buffer);
401         }
402
403         close(fds2[0]);
404
405         LOGEXITRC();
406         return rc;
407 }
408
409
410 /*
411  * NAME: set_fsck_options
412  *
413  * FUNCTION: Build options array (argv) for e2fsck
414  *
415  * PARAMETERS:
416  *      options   - options array passed from EVMS engine
417  *      argv      - fsck options array
418  *      volume    - volume on which program will be executed
419  *
420  */                        
421 void set_fsck_options( option_array_t * options, char ** argv, logical_volume_t * volume )
422 {
423     int i, bufsize, num_opts, opt_count = 1;
424     int do_preen = 1;
425     char *buf;
426
427     LOGENTRY();
428
429     argv[0] = "e2fsck";
430
431     if (options) 
432             num_opts = options->count;
433     else {
434             /* No options, assume force (for resizing) */
435             argv[opt_count++] = "-f";
436             num_opts = 0;
437     }
438     
439     for ( i=0; i < num_opts; i++) {
440
441         if ( options->option[i].is_number_based ) {
442
443             /* 'force check' option */
444             if ( (options->option[i].number == FSCK_FORCE_INDEX) && 
445                  (options->option[i].value.bool == TRUE) ) {
446                 argv[opt_count++] = "-f";
447             }
448
449             /* 'check read only' option or mounted */
450             if ((options->option[i].number == FSCK_READONLY_INDEX) &&
451                 ((options->option[i].value.bool == TRUE) ||
452                  EVMS_IS_MOUNTED(volume))) {
453                 argv[opt_count++] = "-n";
454                 do_preen = 0;
455             }
456
457             /* 'bad blocks check' option and NOT mounted */
458             if ( (options->option[i].number == FSCK_CHECKBB_INDEX) && 
459                  (options->option[i].value.bool == TRUE)         &&
460                  !EVMS_IS_MOUNTED(volume) ) {
461                 argv[opt_count++] = "-c";
462                 do_preen = 0;
463             }
464
465             /* 'bad blocks check' option and NOT mounted */
466             if ( (options->option[i].number == FSCK_CHECKRW_INDEX) && 
467                  (options->option[i].value.bool == TRUE)         &&
468                  !EVMS_IS_MOUNTED(volume) ) {
469                 argv[opt_count++] = "-cc";
470                 do_preen = 0;
471             }
472             
473             /* timing option */
474             if ( (options->option[i].number == FSCK_TIMING_INDEX) && 
475                  (options->option[i].value.bool == TRUE) ) {
476                 argv[opt_count++] = "-tt";
477             }
478             
479     } else {
480
481             /* 'force check' option selected and NOT mounted */
482             if ( !strcmp(options->option[i].name, "force") &&
483                  (options->option[i].value.bool == TRUE) &&
484                  !EVMS_IS_MOUNTED(volume) ) {
485                 argv[opt_count++] = "-f";
486             }
487
488             /* 'check read only' option selected or mounted */
489             if ((!strcmp(options->option[i].name, "readonly")) &&
490                 ((options->option[i].value.bool == TRUE) ||
491                  EVMS_IS_MOUNTED(volume))) {
492                 argv[opt_count++] = "-n";
493                 do_preen = 0;
494             }
495
496             /* 'check badblocks' option selected and NOT mounted */
497             if (!strcmp(options->option[i].name, "badblocks") &&
498                 (options->option[i].value.bool == TRUE) &&
499                 !EVMS_IS_MOUNTED(volume)) {
500                 argv[opt_count++] = "-c";
501                 do_preen = 0;
502             }
503
504             /* 'check r/w badblocks' option selected and NOT mounted */
505             if (!strcmp(options->option[i].name, "badblocks_rw") &&
506                 (options->option[i].value.bool == TRUE) &&
507                 !EVMS_IS_MOUNTED(volume)) {
508                 argv[opt_count++] = "-cc";
509                 do_preen = 0;
510             }
511
512             /* 'timing' option selected */
513             if (!strcmp(options->option[i].name, "badblocks") &&
514                 (options->option[i].value.bool == TRUE)) {
515                 argv[opt_count++] = "-tt";
516             }
517         }
518     }
519
520     if (do_preen)
521             argv[opt_count++] = "-p";
522     argv[opt_count++] = EVMS_GET_DEVNAME(volume);
523     argv[opt_count]   = NULL;
524
525     bufsize = 0;
526     for (i=0; argv[i]; i++)
527             bufsize += strlen(argv[i]) + 5;
528     buf = malloc(bufsize+1);
529     if (!buf)
530             return;
531     buf[0] = 0;
532     for (i=0; argv[i]; i++) {
533             strcat(buf, argv[i]);
534             strcat(buf, " ");
535     }
536     EngFncs->write_log_entry(DEBUG, pMyPluginRecord,
537                              "fsck command: %s\n", buf);
538     free(buf);
539     
540     LOGEXIT();
541     return;
542 }
543 /*
544  * NAME:ext2fs_swap_super
545  *
546  * FUNCTION: Swap all fields in the super block to CPU format.
547  *
548  * PARAMETERS:
549  *      sb   - pointer to superblock
550  *
551  * RETURNS:
552  *        void
553  */                        
554 static void ext2fs_swap_super(struct ext2_super_block * sb)
555 {
556         LOGENTRY();
557         sb->s_inodes_count = DISK_TO_CPU32(sb->s_inodes_count);
558         sb->s_blocks_count = DISK_TO_CPU32(sb->s_blocks_count);
559         sb->s_r_blocks_count = DISK_TO_CPU32(sb->s_r_blocks_count);
560         sb->s_free_blocks_count = DISK_TO_CPU32(sb->s_free_blocks_count);
561         sb->s_free_inodes_count = DISK_TO_CPU32(sb->s_free_inodes_count);
562         sb->s_first_data_block = DISK_TO_CPU32(sb->s_first_data_block);
563         sb->s_log_block_size = DISK_TO_CPU32(sb->s_log_block_size);
564         sb->s_log_frag_size = DISK_TO_CPU32(sb->s_log_frag_size);
565         sb->s_blocks_per_group = DISK_TO_CPU32(sb->s_blocks_per_group);
566         sb->s_frags_per_group = DISK_TO_CPU32(sb->s_frags_per_group);
567         sb->s_inodes_per_group = DISK_TO_CPU32(sb->s_inodes_per_group);
568         sb->s_mtime = DISK_TO_CPU32(sb->s_mtime);
569         sb->s_wtime = DISK_TO_CPU32(sb->s_wtime);
570         sb->s_mnt_count = DISK_TO_CPU16(sb->s_mnt_count);
571         sb->s_max_mnt_count = DISK_TO_CPU16(sb->s_max_mnt_count);
572         sb->s_magic = DISK_TO_CPU16(sb->s_magic);
573         sb->s_state = DISK_TO_CPU16(sb->s_state);
574         sb->s_errors = DISK_TO_CPU16(sb->s_errors);
575         sb->s_minor_rev_level = DISK_TO_CPU16(sb->s_minor_rev_level);
576         sb->s_lastcheck = DISK_TO_CPU32(sb->s_lastcheck);
577         sb->s_checkinterval = DISK_TO_CPU32(sb->s_checkinterval);
578         sb->s_creator_os = DISK_TO_CPU32(sb->s_creator_os);
579         sb->s_rev_level = DISK_TO_CPU32(sb->s_rev_level);
580         sb->s_def_resuid = DISK_TO_CPU16(sb->s_def_resuid);
581         sb->s_def_resgid = DISK_TO_CPU16(sb->s_def_resgid);
582         sb->s_first_ino = DISK_TO_CPU32(sb->s_first_ino);
583         sb->s_inode_size = DISK_TO_CPU16(sb->s_inode_size);
584         sb->s_block_group_nr = DISK_TO_CPU16(sb->s_block_group_nr);
585         sb->s_feature_compat = DISK_TO_CPU32(sb->s_feature_compat);
586         sb->s_feature_incompat = DISK_TO_CPU32(sb->s_feature_incompat);
587         sb->s_feature_ro_compat = DISK_TO_CPU32(sb->s_feature_ro_compat);
588         sb->s_algorithm_usage_bitmap = DISK_TO_CPU32(sb->s_algorithm_usage_bitmap);
589         sb->s_journal_inum = DISK_TO_CPU32(sb->s_journal_inum);
590         sb->s_journal_dev = DISK_TO_CPU32(sb->s_journal_dev);
591         sb->s_last_orphan = DISK_TO_CPU32(sb->s_last_orphan);
592         LOGEXIT();
593 }
594
595
596 /*
597  * NAME: fsim_get_ext2_superblock
598  *
599  * FUNCTION: Get and validate a ext2/3 superblock
600  *
601  * PARAMETERS:
602  *      volume   - pointer to volume from which to get the superblock
603  *      sb_ptr   - pointer to superblock
604  *
605  * RETURNS:
606  *      (0) for success
607  *      != 0 otherwise
608  *        
609  */                        
610 int fsim_get_ext2_superblock( logical_volume_t *volume, struct ext2_super_block *sb_ptr )
611 {
612     int  fd;
613     int  rc = 0;
614
615     LOGENTRY();
616
617     fd = open(EVMS_GET_DEVNAME(volume), O_RDONLY, 0);
618     if (fd < 0) {
619             rc = EIO;
620             LOGEXITRC();
621             return rc;
622     }
623
624     /* get primary superblock */
625     rc = fsim_rw_diskblocks( fd, EXT2_SUPER_LOC, SIZE_OF_SUPER, sb_ptr, GET );
626
627     if( rc == 0 ) {
628         ext2fs_swap_super(sb_ptr);
629         /* see if superblock is ext2/3 */
630         if (( sb_ptr->s_magic != EXT2_SUPER_MAGIC ) ||
631             ( sb_ptr->s_rev_level > 1 ))
632                 rc = FSIM_ERROR;
633     }
634
635     close(fd);
636
637     LOGEXITRC();
638     return rc;
639 }
640
641
642 /*
643  * NAME: fsim_rw_diskblocks
644  *
645  * FUNCTION: Read or write specific number of bytes for an opened device.
646  *
647  * PARAMETERS:
648  *      dev_ptr         - file handle of an opened device to read/write
649  *      disk_offset     - byte offset from beginning of device for start of disk
650  *                        block read/write
651  *      disk_count      - number of bytes to read/write
652  *      data_buffer     - On read this will be filled in with data read from
653  *                        disk; on write this contains data to be written
654  *      mode            - GET (read) or PUT (write)
655  *
656  * RETURNS:
657  *      FSIM_SUCCESS (0) for success
658  *      ERROR       (-1) can't lseek
659  *      EINVAL           
660  *      EIO
661  *        
662  */                        
663 int fsim_rw_diskblocks( int      dev_ptr,
664                         int64_t  disk_offset,
665                         int32_t  disk_count,
666                         void     *data_buffer,
667                         int      mode )
668 {
669     off_t   Actual_Location;
670     size_t  Bytes_Transferred;
671     int     rc = 0;
672
673     LOGENTRY();
674     
675     Actual_Location = lseek(dev_ptr,disk_offset, SEEK_SET);
676     if ( ( Actual_Location < 0 ) || ( Actual_Location != disk_offset ) )
677         return ERROR;
678
679     switch ( mode ) {
680         case GET:
681             Bytes_Transferred = read(dev_ptr,data_buffer,disk_count);
682             break;
683         case PUT:
684             Bytes_Transferred = write(dev_ptr,data_buffer,disk_count);
685             break;
686         default:
687             rc = EINVAL;
688             LOGEXITRC();
689             return rc;
690             break;
691     }
692
693     if ( Bytes_Transferred != disk_count ) {
694         rc = EIO;
695         LOGEXITRC();
696         return rc;
697     }
698
699     LOGEXIT();
700     return FSIM_SUCCESS;
701 }
702
703
704 /*
705  * Test e2fsprogs version.
706  *
707  * We don't bother since we don't need any special functionality that
708  * hasn't been around for *years*
709  */     
710 int fsim_test_version( )
711 {
712         return 0;
713 }
714
715
716
717
718