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