mkjournal.c revision 4a2a9b70c89891a146bcb9cee3f29572ca2e1370
1/* 2 * mkjournal.c --- make a journal for a filesystem 3 * 4 * Copyright (C) 2000 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Library 8 * General Public License, version 2. 9 * %End-Header% 10 */ 11 12#include <stdio.h> 13#include <string.h> 14#if HAVE_UNISTD_H 15#include <unistd.h> 16#endif 17#if HAVE_ERRNO_H 18#include <errno.h> 19#endif 20#include <fcntl.h> 21#include <time.h> 22#if HAVE_SYS_STAT_H 23#include <sys/stat.h> 24#endif 25#if HAVE_SYS_TYPES_H 26#include <sys/types.h> 27#endif 28#if HAVE_SYS_IOCTL_H 29#include <sys/ioctl.h> 30#endif 31#if HAVE_NETINET_IN_H 32#include <netinet/in.h> 33#endif 34 35#include "ext2_fs.h" 36#include "e2p/e2p.h" 37#include "ext2fs.h" 38#include "jfs_user.h" 39 40/* 41 * This function automatically sets up the journal superblock and 42 * returns it as an allocated block. 43 */ 44errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, 45 __u32 size, int flags, 46 char **ret_jsb) 47{ 48 errcode_t retval; 49 journal_superblock_t *jsb; 50 51 if (size < 1024) 52 return EXT2_ET_JOURNAL_TOO_SMALL; 53 54 if ((retval = ext2fs_get_mem(fs->blocksize, &jsb))) 55 return retval; 56 57 memset (jsb, 0, fs->blocksize); 58 59 jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); 60 if (flags & EXT2_MKJOURNAL_V1_SUPER) 61 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1); 62 else 63 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); 64 jsb->s_blocksize = htonl(fs->blocksize); 65 jsb->s_maxlen = htonl(size); 66 jsb->s_nr_users = htonl(1); 67 jsb->s_first = htonl(1); 68 jsb->s_sequence = htonl(1); 69 memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid)); 70 /* 71 * If we're creating an external journal device, we need to 72 * adjust these fields. 73 */ 74 if (fs->super->s_feature_incompat & 75 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { 76 jsb->s_nr_users = 0; 77 if (fs->blocksize == 1024) 78 jsb->s_first = htonl(3); 79 else 80 jsb->s_first = htonl(2); 81 } 82 83 *ret_jsb = (char *) jsb; 84 return 0; 85} 86 87/* 88 * This function writes a journal using POSIX routines. It is used 89 * for creating external journals and creating journals on live 90 * filesystems. 91 */ 92static errcode_t write_journal_file(ext2_filsys fs, char *filename, 93 blk_t size, int flags) 94{ 95 errcode_t retval; 96 char *buf = 0; 97 int fd, ret_size; 98 blk_t i; 99 100 if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) 101 return retval; 102 103 /* Open the device or journal file */ 104 if ((fd = open(filename, O_WRONLY)) < 0) { 105 retval = errno; 106 goto errout; 107 } 108 109 /* Write the superblock out */ 110 retval = EXT2_ET_SHORT_WRITE; 111 ret_size = write(fd, buf, fs->blocksize); 112 if (ret_size < 0) { 113 retval = errno; 114 goto errout; 115 } 116 if (ret_size != (int) fs->blocksize) 117 goto errout; 118 memset(buf, 0, fs->blocksize); 119 120 for (i = 1; i < size; i++) { 121 ret_size = write(fd, buf, fs->blocksize); 122 if (ret_size < 0) { 123 retval = errno; 124 goto errout; 125 } 126 if (ret_size != (int) fs->blocksize) 127 goto errout; 128 } 129 close(fd); 130 131 retval = 0; 132errout: 133 ext2fs_free_mem(&buf); 134 return retval; 135} 136 137/* 138 * Convenience function which zeros out _num_ blocks starting at 139 * _blk_. In case of an error, the details of the error is returned 140 * via _ret_blk_ and _ret_count_ if they are non-NULL pointers. 141 * Returns 0 on success, and an error code on an error. 142 * 143 * As a special case, if the first argument is NULL, then it will 144 * attempt to free the static zeroizing buffer. (This is to keep 145 * programs that check for memory leaks happy.) 146 */ 147#define STRIDE_LENGTH 8 148errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num, 149 blk64_t *ret_blk, int *ret_count) 150{ 151 int j, count; 152 static char *buf; 153 errcode_t retval; 154 155 /* If fs is null, clean up the static buffer and return */ 156 if (!fs) { 157 if (buf) { 158 free(buf); 159 buf = 0; 160 } 161 return 0; 162 } 163 /* Allocate the zeroizing buffer if necessary */ 164 if (!buf) { 165 buf = malloc(fs->blocksize * STRIDE_LENGTH); 166 if (!buf) 167 return ENOMEM; 168 memset(buf, 0, fs->blocksize * STRIDE_LENGTH); 169 } 170 /* OK, do the write loop */ 171 j=0; 172 while (j < num) { 173 if (blk % STRIDE_LENGTH) { 174 count = STRIDE_LENGTH - (blk % STRIDE_LENGTH); 175 if (count > (num - j)) 176 count = num - j; 177 } else { 178 count = num - j; 179 if (count > STRIDE_LENGTH) 180 count = STRIDE_LENGTH; 181 } 182 retval = io_channel_write_blk64(fs->io, blk, count, buf); 183 if (retval) { 184 if (ret_count) 185 *ret_count = count; 186 if (ret_blk) 187 *ret_blk = blk; 188 return retval; 189 } 190 j += count; blk += count; 191 } 192 return 0; 193} 194 195errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num, 196 blk_t *ret_blk, int *ret_count) 197{ 198 blk64_t ret_blk2; 199 errcode_t retval; 200 201 retval = ext2fs_zero_blocks2(fs, blk, num, &ret_blk2, ret_count); 202 if (retval) 203 *ret_blk = (blk_t) ret_blk2; 204 return retval; 205} 206 207/* 208 * Helper function for creating the journal using direct I/O routines 209 */ 210struct mkjournal_struct { 211 int num_blocks; 212 int newblocks; 213 blk64_t goal; 214 blk64_t blk_to_zero; 215 int zero_count; 216 char *buf; 217 errcode_t err; 218}; 219 220static int mkjournal_proc(ext2_filsys fs, 221 blk64_t *blocknr, 222 e2_blkcnt_t blockcnt, 223 blk64_t ref_block EXT2FS_ATTR((unused)), 224 int ref_offset EXT2FS_ATTR((unused)), 225 void *priv_data) 226{ 227 struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; 228 blk64_t new_blk; 229 errcode_t retval; 230 231 if (*blocknr) { 232 es->goal = *blocknr; 233 return 0; 234 } 235 if (blockcnt && 236 (EXT2FS_B2C(fs, es->goal) == EXT2FS_B2C(fs, es->goal+1))) 237 new_blk = es->goal+1; 238 else { 239 es->goal &= ~EXT2FS_CLUSTER_MASK(fs); 240 retval = ext2fs_new_block2(fs, es->goal, 0, &new_blk); 241 if (retval) { 242 es->err = retval; 243 return BLOCK_ABORT; 244 } 245 } 246 if (blockcnt >= 0) 247 es->num_blocks--; 248 249 es->newblocks++; 250 retval = 0; 251 if (blockcnt <= 0) 252 retval = io_channel_write_blk64(fs->io, new_blk, 1, es->buf); 253 else { 254 if (es->zero_count) { 255 if ((es->blk_to_zero + es->zero_count == new_blk) && 256 (es->zero_count < 1024)) 257 es->zero_count++; 258 else { 259 retval = ext2fs_zero_blocks2(fs, 260 es->blk_to_zero, 261 es->zero_count, 262 0, 0); 263 es->zero_count = 0; 264 } 265 } 266 if (es->zero_count == 0) { 267 es->blk_to_zero = new_blk; 268 es->zero_count = 1; 269 } 270 } 271 272 if (blockcnt == 0) 273 memset(es->buf, 0, fs->blocksize); 274 275 if (retval) { 276 es->err = retval; 277 return BLOCK_ABORT; 278 } 279 *blocknr = es->goal = new_blk; 280 ext2fs_block_alloc_stats2(fs, new_blk, +1); 281 282 if (es->num_blocks == 0) 283 return (BLOCK_CHANGED | BLOCK_ABORT); 284 else 285 return BLOCK_CHANGED; 286 287} 288 289/* 290 * This function creates a journal using direct I/O routines. 291 */ 292static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, 293 blk64_t size, int flags) 294{ 295 char *buf; 296 dgrp_t group, start, end, i, log_flex; 297 errcode_t retval; 298 struct ext2_inode inode; 299 struct mkjournal_struct es; 300 301 if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) 302 return retval; 303 304 if ((retval = ext2fs_read_bitmaps(fs))) 305 return retval; 306 307 if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) 308 return retval; 309 310 if (inode.i_blocks > 0) 311 return EEXIST; 312 313 es.num_blocks = size; 314 es.newblocks = 0; 315 es.buf = buf; 316 es.err = 0; 317 es.zero_count = 0; 318 319 if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { 320 inode.i_flags |= EXT4_EXTENTS_FL; 321 if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) 322 return retval; 323 } 324 325 /* 326 * Set the initial goal block to be roughly at the middle of 327 * the filesystem. Pick a group that has the largest number 328 * of free blocks. 329 */ 330 group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) - 331 fs->super->s_first_data_block) / 2); 332 log_flex = 1 << fs->super->s_log_groups_per_flex; 333 if (fs->super->s_log_groups_per_flex && (group > log_flex)) { 334 group = group & ~(log_flex - 1); 335 while ((group < fs->group_desc_count) && 336 ext2fs_bg_free_blocks_count(fs, group) == 0) 337 group++; 338 if (group == fs->group_desc_count) 339 group = 0; 340 start = group; 341 } else 342 start = (group > 0) ? group-1 : group; 343 end = ((group+1) < fs->group_desc_count) ? group+1 : group; 344 group = start; 345 for (i=start+1; i <= end; i++) 346 if (ext2fs_bg_free_blocks_count(fs, i) > 347 ext2fs_bg_free_blocks_count(fs, group)) 348 group = i; 349 350 es.goal = (fs->super->s_blocks_per_group * group) + 351 fs->super->s_first_data_block; 352 353 retval = ext2fs_block_iterate3(fs, journal_ino, BLOCK_FLAG_APPEND, 354 0, mkjournal_proc, &es); 355 if (es.err) { 356 retval = es.err; 357 goto errout; 358 } 359 if (es.zero_count) { 360 retval = ext2fs_zero_blocks2(fs, es.blk_to_zero, 361 es.zero_count, 0, 0); 362 if (retval) 363 goto errout; 364 } 365 366 if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) 367 goto errout; 368 369 inode.i_size += fs->blocksize * size; 370 ext2fs_iblk_add_blocks(fs, &inode, es.newblocks); 371 inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0); 372 inode.i_links_count = 1; 373 inode.i_mode = LINUX_S_IFREG | 0600; 374 375 if ((retval = ext2fs_write_new_inode(fs, journal_ino, &inode))) 376 goto errout; 377 retval = 0; 378 379 memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); 380 fs->super->s_jnl_blocks[16] = inode.i_size; 381 fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; 382 ext2fs_mark_super_dirty(fs); 383 384errout: 385 ext2fs_zero_blocks2(0, 0, 0, 0, 0); 386 ext2fs_free_mem(&buf); 387 return retval; 388} 389 390/* 391 * Find a reasonable journal file size (in blocks) given the number of blocks 392 * in the filesystem. For very small filesystems, it is not reasonable to 393 * have a journal that fills more than half of the filesystem. 394 */ 395int ext2fs_default_journal_size(__u64 blocks) 396{ 397 if (blocks < 2048) 398 return -1; 399 if (blocks < 32768) 400 return (1024); 401 if (blocks < 256*1024) 402 return (4096); 403 if (blocks < 512*1024) 404 return (8192); 405 if (blocks < 1024*1024) 406 return (16384); 407 return 32768; 408} 409 410/* 411 * This function adds a journal device to a filesystem 412 */ 413errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) 414{ 415 struct stat st; 416 errcode_t retval; 417 char buf[1024]; 418 journal_superblock_t *jsb; 419 int start; 420 __u32 i, nr_users; 421 422 /* Make sure the device exists and is a block device */ 423 if (stat(journal_dev->device_name, &st) < 0) 424 return errno; 425 426 if (!S_ISBLK(st.st_mode)) 427 return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ 428 429 /* Get the journal superblock */ 430 start = 1; 431 if (journal_dev->blocksize == 1024) 432 start++; 433 if ((retval = io_channel_read_blk64(journal_dev->io, start, -1024, 434 buf))) 435 return retval; 436 437 jsb = (journal_superblock_t *) buf; 438 if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || 439 (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) 440 return EXT2_ET_NO_JOURNAL_SB; 441 442 if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) 443 return EXT2_ET_UNEXPECTED_BLOCK_SIZE; 444 445 /* Check and see if this filesystem has already been added */ 446 nr_users = ntohl(jsb->s_nr_users); 447 for (i=0; i < nr_users; i++) { 448 if (memcmp(fs->super->s_uuid, 449 &jsb->s_users[i*16], 16) == 0) 450 break; 451 } 452 if (i >= nr_users) { 453 memcpy(&jsb->s_users[nr_users*16], 454 fs->super->s_uuid, 16); 455 jsb->s_nr_users = htonl(nr_users+1); 456 } 457 458 /* Writeback the journal superblock */ 459 if ((retval = io_channel_write_blk64(journal_dev->io, start, -1024, buf))) 460 return retval; 461 462 fs->super->s_journal_inum = 0; 463 fs->super->s_journal_dev = st.st_rdev; 464 memcpy(fs->super->s_journal_uuid, jsb->s_uuid, 465 sizeof(fs->super->s_journal_uuid)); 466 fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; 467 ext2fs_mark_super_dirty(fs); 468 return 0; 469} 470 471/* 472 * This function adds a journal inode to a filesystem, using either 473 * POSIX routines if the filesystem is mounted, or using direct I/O 474 * functions if it is not. 475 */ 476errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) 477{ 478 errcode_t retval; 479 ext2_ino_t journal_ino; 480 struct stat st; 481 char jfile[1024]; 482 int mount_flags, f; 483 int fd = -1; 484 485 if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, 486 jfile, sizeof(jfile)-10))) 487 return retval; 488 489 if (mount_flags & EXT2_MF_MOUNTED) { 490 strcat(jfile, "/.journal"); 491 492 /* 493 * If .../.journal already exists, make sure any 494 * immutable or append-only flags are cleared. 495 */ 496#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) 497 (void) chflags (jfile, 0); 498#else 499#if HAVE_EXT2_IOCTLS 500 fd = open(jfile, O_RDONLY); 501 if (fd >= 0) { 502 f = 0; 503 ioctl(fd, EXT2_IOC_SETFLAGS, &f); 504 close(fd); 505 } 506#endif 507#endif 508 509 /* Create the journal file */ 510 if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) 511 return errno; 512 513 if ((retval = write_journal_file(fs, jfile, size, flags))) 514 goto errout; 515 516 /* Get inode number of the journal file */ 517 if (fstat(fd, &st) < 0) { 518 retval = errno; 519 goto errout; 520 } 521 522#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) 523 retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); 524#else 525#if HAVE_EXT2_IOCTLS 526 if (ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) { 527 retval = errno; 528 goto errout; 529 } 530 f |= EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; 531 retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); 532#endif 533#endif 534 if (retval) { 535 retval = errno; 536 goto errout; 537 } 538 539 if (close(fd) < 0) { 540 retval = errno; 541 fd = -1; 542 goto errout; 543 } 544 journal_ino = st.st_ino; 545 } else { 546 if ((mount_flags & EXT2_MF_BUSY) && 547 !(fs->flags & EXT2_FLAG_EXCLUSIVE)) { 548 retval = EBUSY; 549 goto errout; 550 } 551 journal_ino = EXT2_JOURNAL_INO; 552 if ((retval = write_journal_inode(fs, journal_ino, 553 size, flags))) 554 return retval; 555 } 556 557 fs->super->s_journal_inum = journal_ino; 558 fs->super->s_journal_dev = 0; 559 memset(fs->super->s_journal_uuid, 0, 560 sizeof(fs->super->s_journal_uuid)); 561 fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; 562 563 ext2fs_mark_super_dirty(fs); 564 return 0; 565errout: 566 if (fd > 0) 567 close(fd); 568 return retval; 569} 570 571#ifdef DEBUG 572main(int argc, char **argv) 573{ 574 errcode_t retval; 575 char *device_name; 576 ext2_filsys fs; 577 578 if (argc < 2) { 579 fprintf(stderr, "Usage: %s filesystem\n", argv[0]); 580 exit(1); 581 } 582 device_name = argv[1]; 583 584 retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, 585 unix_io_manager, &fs); 586 if (retval) { 587 com_err(argv[0], retval, "while opening %s", device_name); 588 exit(1); 589 } 590 591 retval = ext2fs_add_journal_inode(fs, 1024); 592 if (retval) { 593 com_err(argv[0], retval, "while adding journal to %s", 594 device_name); 595 exit(1); 596 } 597 retval = ext2fs_flush(fs); 598 if (retval) { 599 printf("Warning, had trouble writing out superblocks.\n"); 600 } 601 ext2fs_close(fs); 602 exit(0); 603 604} 605#endif 606