1/* vi: set sw=4 ts=4: */ 2/* 3 * mkjournal.c --- make a journal for a filesystem 4 * 5 * Copyright (C) 2000 Theodore Ts'o. 6 * 7 * %Begin-Header% 8 * This file may be redistributed under the terms of the GNU Public 9 * License. 10 * %End-Header% 11 */ 12 13#include <stdio.h> 14#include <string.h> 15#if HAVE_UNISTD_H 16#include <unistd.h> 17#endif 18#if HAVE_ERRNO_H 19#include <errno.h> 20#endif 21#include <fcntl.h> 22#include <time.h> 23#if HAVE_SYS_STAT_H 24#include <sys/stat.h> 25#endif 26#if HAVE_SYS_TYPES_H 27#include <sys/types.h> 28#endif 29#if HAVE_SYS_IOCTL_H 30#include <sys/ioctl.h> 31#endif 32#if HAVE_NETINET_IN_H 33#include <netinet/in.h> 34#endif 35 36#include "ext2_fs.h" 37#include "../e2p/e2p.h" 38#include "../e2fsck.h" 39#include "ext2fs.h" 40#include "kernel-jbd.h" 41 42/* 43 * This function automatically sets up the journal superblock and 44 * returns it as an allocated block. 45 */ 46errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, 47 __u32 size, int flags, 48 char **ret_jsb) 49{ 50 errcode_t retval; 51 journal_superblock_t *jsb; 52 53 if (size < 1024) 54 return EXT2_ET_JOURNAL_TOO_SMALL; 55 56 if ((retval = ext2fs_get_mem(fs->blocksize, &jsb))) 57 return retval; 58 59 memset (jsb, 0, fs->blocksize); 60 61 jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); 62 if (flags & EXT2_MKJOURNAL_V1_SUPER) 63 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1); 64 else 65 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); 66 jsb->s_blocksize = htonl(fs->blocksize); 67 jsb->s_maxlen = htonl(size); 68 jsb->s_nr_users = htonl(1); 69 jsb->s_first = htonl(1); 70 jsb->s_sequence = htonl(1); 71 memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid)); 72 /* 73 * If we're creating an external journal device, we need to 74 * adjust these fields. 75 */ 76 if (fs->super->s_feature_incompat & 77 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { 78 jsb->s_nr_users = 0; 79 if (fs->blocksize == 1024) 80 jsb->s_first = htonl(3); 81 else 82 jsb->s_first = htonl(2); 83 } 84 85 *ret_jsb = (char *) jsb; 86 return 0; 87} 88 89/* 90 * This function writes a journal using POSIX routines. It is used 91 * for creating external journals and creating journals on live 92 * filesystems. 93 */ 94static errcode_t write_journal_file(ext2_filsys fs, char *filename, 95 blk_t size, int flags) 96{ 97 errcode_t retval; 98 char *buf = 0; 99 int fd, ret_size; 100 blk_t i; 101 102 if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) 103 return retval; 104 105 /* Open the device or journal file */ 106 if ((fd = open(filename, O_WRONLY)) < 0) { 107 retval = errno; 108 goto errout; 109 } 110 111 /* Write the superblock out */ 112 retval = EXT2_ET_SHORT_WRITE; 113 ret_size = write(fd, buf, fs->blocksize); 114 if (ret_size < 0) { 115 retval = errno; 116 goto errout; 117 } 118 if (ret_size != (int) fs->blocksize) 119 goto errout; 120 memset(buf, 0, fs->blocksize); 121 122 for (i = 1; i < size; i++) { 123 ret_size = write(fd, buf, fs->blocksize); 124 if (ret_size < 0) { 125 retval = errno; 126 goto errout; 127 } 128 if (ret_size != (int) fs->blocksize) 129 goto errout; 130 } 131 close(fd); 132 133 retval = 0; 134errout: 135 ext2fs_free_mem(&buf); 136 return retval; 137} 138 139/* 140 * Helper function for creating the journal using direct I/O routines 141 */ 142struct mkjournal_struct { 143 int num_blocks; 144 int newblocks; 145 char *buf; 146 errcode_t err; 147}; 148 149static int mkjournal_proc(ext2_filsys fs, 150 blk_t *blocknr, 151 e2_blkcnt_t blockcnt, 152 blk_t ref_block EXT2FS_ATTR((unused)), 153 int ref_offset EXT2FS_ATTR((unused)), 154 void *priv_data) 155{ 156 struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; 157 blk_t new_blk; 158 static blk_t last_blk = 0; 159 errcode_t retval; 160 161 if (*blocknr) { 162 last_blk = *blocknr; 163 return 0; 164 } 165 retval = ext2fs_new_block(fs, last_blk, 0, &new_blk); 166 if (retval) { 167 es->err = retval; 168 return BLOCK_ABORT; 169 } 170 if (blockcnt > 0) 171 es->num_blocks--; 172 173 es->newblocks++; 174 retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf); 175 176 if (blockcnt == 0) 177 memset(es->buf, 0, fs->blocksize); 178 179 if (retval) { 180 es->err = retval; 181 return BLOCK_ABORT; 182 } 183 *blocknr = new_blk; 184 last_blk = new_blk; 185 ext2fs_block_alloc_stats(fs, new_blk, +1); 186 187 if (es->num_blocks == 0) 188 return (BLOCK_CHANGED | BLOCK_ABORT); 189 else 190 return BLOCK_CHANGED; 191 192} 193 194/* 195 * This function creates a journal using direct I/O routines. 196 */ 197static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, 198 blk_t size, int flags) 199{ 200 char *buf; 201 errcode_t retval; 202 struct ext2_inode inode; 203 struct mkjournal_struct es; 204 205 if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) 206 return retval; 207 208 if ((retval = ext2fs_read_bitmaps(fs))) 209 return retval; 210 211 if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) 212 return retval; 213 214 if (inode.i_blocks > 0) 215 return EEXIST; 216 217 es.num_blocks = size; 218 es.newblocks = 0; 219 es.buf = buf; 220 es.err = 0; 221 222 retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND, 223 0, mkjournal_proc, &es); 224 if (es.err) { 225 retval = es.err; 226 goto errout; 227 } 228 229 if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) 230 goto errout; 231 232 inode.i_size += fs->blocksize * size; 233 inode.i_blocks += (fs->blocksize / 512) * es.newblocks; 234 inode.i_mtime = inode.i_ctime = time(0); 235 inode.i_links_count = 1; 236 inode.i_mode = LINUX_S_IFREG | 0600; 237 238 if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) 239 goto errout; 240 retval = 0; 241 242 memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); 243 fs->super->s_jnl_blocks[16] = inode.i_size; 244 fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; 245 ext2fs_mark_super_dirty(fs); 246 247errout: 248 ext2fs_free_mem(&buf); 249 return retval; 250} 251 252/* 253 * This function adds a journal device to a filesystem 254 */ 255errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) 256{ 257 struct stat st; 258 errcode_t retval; 259 char buf[1024]; 260 journal_superblock_t *jsb; 261 int start; 262 __u32 i, nr_users; 263 264 /* Make sure the device exists and is a block device */ 265 if (stat(journal_dev->device_name, &st) < 0) 266 return errno; 267 268 if (!S_ISBLK(st.st_mode)) 269 return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ 270 271 /* Get the journal superblock */ 272 start = 1; 273 if (journal_dev->blocksize == 1024) 274 start++; 275 if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf))) 276 return retval; 277 278 jsb = (journal_superblock_t *) buf; 279 if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || 280 (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) 281 return EXT2_ET_NO_JOURNAL_SB; 282 283 if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) 284 return EXT2_ET_UNEXPECTED_BLOCK_SIZE; 285 286 /* Check and see if this filesystem has already been added */ 287 nr_users = ntohl(jsb->s_nr_users); 288 for (i=0; i < nr_users; i++) { 289 if (memcmp(fs->super->s_uuid, 290 &jsb->s_users[i*16], 16) == 0) 291 break; 292 } 293 if (i >= nr_users) { 294 memcpy(&jsb->s_users[nr_users*16], 295 fs->super->s_uuid, 16); 296 jsb->s_nr_users = htonl(nr_users+1); 297 } 298 299 /* Writeback the journal superblock */ 300 if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf))) 301 return retval; 302 303 fs->super->s_journal_inum = 0; 304 fs->super->s_journal_dev = st.st_rdev; 305 memcpy(fs->super->s_journal_uuid, jsb->s_uuid, 306 sizeof(fs->super->s_journal_uuid)); 307 fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; 308 ext2fs_mark_super_dirty(fs); 309 return 0; 310} 311 312/* 313 * This function adds a journal inode to a filesystem, using either 314 * POSIX routines if the filesystem is mounted, or using direct I/O 315 * functions if it is not. 316 */ 317errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) 318{ 319 errcode_t retval; 320 ext2_ino_t journal_ino; 321 struct stat st; 322 char jfile[1024]; 323 int fd, mount_flags, f; 324 325 retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, 326 jfile, sizeof(jfile)-10); 327 if (retval) 328 return retval; 329 330 if (mount_flags & EXT2_MF_MOUNTED) { 331 strcat(jfile, "/.journal"); 332 333 /* 334 * If .../.journal already exists, make sure any 335 * immutable or append-only flags are cleared. 336 */ 337#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) 338 (void) chflags (jfile, 0); 339#else 340#if HAVE_EXT2_IOCTLS 341 fd = open(jfile, O_RDONLY); 342 if (fd >= 0) { 343 f = 0; 344 ioctl(fd, EXT2_IOC_SETFLAGS, &f); 345 close(fd); 346 } 347#endif 348#endif 349 350 /* Create the journal file */ 351 if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) 352 return errno; 353 354 if ((retval = write_journal_file(fs, jfile, size, flags))) 355 goto errout; 356 357 /* Get inode number of the journal file */ 358 if (fstat(fd, &st) < 0) 359 goto errout; 360 361#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) 362 retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); 363#else 364#if HAVE_EXT2_IOCTLS 365 f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; 366 retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); 367#endif 368#endif 369 if (retval) 370 goto errout; 371 372 close(fd); 373 journal_ino = st.st_ino; 374 } else { 375 journal_ino = EXT2_JOURNAL_INO; 376 if ((retval = write_journal_inode(fs, journal_ino, 377 size, flags))) 378 return retval; 379 } 380 381 fs->super->s_journal_inum = journal_ino; 382 fs->super->s_journal_dev = 0; 383 memset(fs->super->s_journal_uuid, 0, 384 sizeof(fs->super->s_journal_uuid)); 385 fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; 386 387 ext2fs_mark_super_dirty(fs); 388 return 0; 389errout: 390 close(fd); 391 return retval; 392} 393 394#ifdef DEBUG 395main(int argc, char **argv) 396{ 397 errcode_t retval; 398 char *device_name; 399 ext2_filsys fs; 400 401 if (argc < 2) { 402 fprintf(stderr, "Usage: %s filesystem\n", argv[0]); 403 exit(1); 404 } 405 device_name = argv[1]; 406 407 retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, 408 unix_io_manager, &fs); 409 if (retval) { 410 com_err(argv[0], retval, "while opening %s", device_name); 411 exit(1); 412 } 413 414 retval = ext2fs_add_journal_inode(fs, 1024); 415 if (retval) { 416 com_err(argv[0], retval, "while adding journal to %s", 417 device_name); 418 exit(1); 419 } 420 retval = ext2fs_flush(fs); 421 if (retval) { 422 printf("Warning, had trouble writing out superblocks.\n"); 423 } 424 ext2fs_close(fs); 425 exit(0); 426 427} 428#endif 429