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