diff options
Diffstat (limited to 'e2fsprogs/ext2fs/mkjournal.c')
-rw-r--r-- | e2fsprogs/ext2fs/mkjournal.c | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/e2fsprogs/ext2fs/mkjournal.c b/e2fsprogs/ext2fs/mkjournal.c new file mode 100644 index 000000000..427a08e72 --- /dev/null +++ b/e2fsprogs/ext2fs/mkjournal.c | |||
@@ -0,0 +1,425 @@ | |||
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 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" | ||
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 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 | */ | ||
92 | static 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; | ||
132 | errout: | ||
133 | ext2fs_free_mem(&buf); | ||
134 | return retval; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * Helper function for creating the journal using direct I/O routines | ||
139 | */ | ||
140 | struct mkjournal_struct { | ||
141 | int num_blocks; | ||
142 | int newblocks; | ||
143 | char *buf; | ||
144 | errcode_t err; | ||
145 | }; | ||
146 | |||
147 | static int mkjournal_proc(ext2_filsys fs, | ||
148 | blk_t *blocknr, | ||
149 | e2_blkcnt_t blockcnt, | ||
150 | blk_t ref_block EXT2FS_ATTR((unused)), | ||
151 | int ref_offset EXT2FS_ATTR((unused)), | ||
152 | void *priv_data) | ||
153 | { | ||
154 | struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; | ||
155 | blk_t new_blk; | ||
156 | static blk_t last_blk = 0; | ||
157 | errcode_t retval; | ||
158 | |||
159 | if (*blocknr) { | ||
160 | last_blk = *blocknr; | ||
161 | return 0; | ||
162 | } | ||
163 | retval = ext2fs_new_block(fs, last_blk, 0, &new_blk); | ||
164 | if (retval) { | ||
165 | es->err = retval; | ||
166 | return BLOCK_ABORT; | ||
167 | } | ||
168 | if (blockcnt > 0) | ||
169 | es->num_blocks--; | ||
170 | |||
171 | es->newblocks++; | ||
172 | retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf); | ||
173 | |||
174 | if (blockcnt == 0) | ||
175 | memset(es->buf, 0, fs->blocksize); | ||
176 | |||
177 | if (retval) { | ||
178 | es->err = retval; | ||
179 | return BLOCK_ABORT; | ||
180 | } | ||
181 | *blocknr = new_blk; | ||
182 | last_blk = new_blk; | ||
183 | ext2fs_block_alloc_stats(fs, new_blk, +1); | ||
184 | |||
185 | if (es->num_blocks == 0) | ||
186 | return (BLOCK_CHANGED | BLOCK_ABORT); | ||
187 | else | ||
188 | return BLOCK_CHANGED; | ||
189 | |||
190 | } | ||
191 | |||
192 | /* | ||
193 | * This function creates a journal using direct I/O routines. | ||
194 | */ | ||
195 | static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, | ||
196 | blk_t size, int flags) | ||
197 | { | ||
198 | char *buf; | ||
199 | errcode_t retval; | ||
200 | struct ext2_inode inode; | ||
201 | struct mkjournal_struct es; | ||
202 | |||
203 | if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) | ||
204 | return retval; | ||
205 | |||
206 | if ((retval = ext2fs_read_bitmaps(fs))) | ||
207 | return retval; | ||
208 | |||
209 | if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) | ||
210 | return retval; | ||
211 | |||
212 | if (inode.i_blocks > 0) | ||
213 | return EEXIST; | ||
214 | |||
215 | es.num_blocks = size; | ||
216 | es.newblocks = 0; | ||
217 | es.buf = buf; | ||
218 | es.err = 0; | ||
219 | |||
220 | retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND, | ||
221 | 0, mkjournal_proc, &es); | ||
222 | if (es.err) { | ||
223 | retval = es.err; | ||
224 | goto errout; | ||
225 | } | ||
226 | |||
227 | if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) | ||
228 | goto errout; | ||
229 | |||
230 | inode.i_size += fs->blocksize * size; | ||
231 | inode.i_blocks += (fs->blocksize / 512) * es.newblocks; | ||
232 | inode.i_mtime = inode.i_ctime = time(0); | ||
233 | inode.i_links_count = 1; | ||
234 | inode.i_mode = LINUX_S_IFREG | 0600; | ||
235 | |||
236 | if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) | ||
237 | goto errout; | ||
238 | retval = 0; | ||
239 | |||
240 | memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); | ||
241 | fs->super->s_jnl_blocks[16] = inode.i_size; | ||
242 | fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; | ||
243 | ext2fs_mark_super_dirty(fs); | ||
244 | |||
245 | errout: | ||
246 | ext2fs_free_mem(&buf); | ||
247 | return retval; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * This function adds a journal device to a filesystem | ||
252 | */ | ||
253 | errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) | ||
254 | { | ||
255 | struct stat st; | ||
256 | errcode_t retval; | ||
257 | char buf[1024]; | ||
258 | journal_superblock_t *jsb; | ||
259 | int start; | ||
260 | __u32 i, nr_users; | ||
261 | |||
262 | /* Make sure the device exists and is a block device */ | ||
263 | if (stat(journal_dev->device_name, &st) < 0) | ||
264 | return errno; | ||
265 | |||
266 | if (!S_ISBLK(st.st_mode)) | ||
267 | return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ | ||
268 | |||
269 | /* Get the journal superblock */ | ||
270 | start = 1; | ||
271 | if (journal_dev->blocksize == 1024) | ||
272 | start++; | ||
273 | if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf))) | ||
274 | return retval; | ||
275 | |||
276 | jsb = (journal_superblock_t *) buf; | ||
277 | if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || | ||
278 | (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) | ||
279 | return EXT2_ET_NO_JOURNAL_SB; | ||
280 | |||
281 | if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) | ||
282 | return EXT2_ET_UNEXPECTED_BLOCK_SIZE; | ||
283 | |||
284 | /* Check and see if this filesystem has already been added */ | ||
285 | nr_users = ntohl(jsb->s_nr_users); | ||
286 | for (i=0; i < nr_users; i++) { | ||
287 | if (memcmp(fs->super->s_uuid, | ||
288 | &jsb->s_users[i*16], 16) == 0) | ||
289 | break; | ||
290 | } | ||
291 | if (i >= nr_users) { | ||
292 | memcpy(&jsb->s_users[nr_users*16], | ||
293 | fs->super->s_uuid, 16); | ||
294 | jsb->s_nr_users = htonl(nr_users+1); | ||
295 | } | ||
296 | |||
297 | /* Writeback the journal superblock */ | ||
298 | if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf))) | ||
299 | return retval; | ||
300 | |||
301 | fs->super->s_journal_inum = 0; | ||
302 | fs->super->s_journal_dev = st.st_rdev; | ||
303 | memcpy(fs->super->s_journal_uuid, jsb->s_uuid, | ||
304 | sizeof(fs->super->s_journal_uuid)); | ||
305 | fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; | ||
306 | ext2fs_mark_super_dirty(fs); | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * This function adds a journal inode to a filesystem, using either | ||
312 | * POSIX routines if the filesystem is mounted, or using direct I/O | ||
313 | * functions if it is not. | ||
314 | */ | ||
315 | errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) | ||
316 | { | ||
317 | errcode_t retval; | ||
318 | ext2_ino_t journal_ino; | ||
319 | struct stat st; | ||
320 | char jfile[1024]; | ||
321 | int fd, mount_flags, f; | ||
322 | |||
323 | if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, | ||
324 | jfile, sizeof(jfile)-10))) | ||
325 | return retval; | ||
326 | |||
327 | if (mount_flags & EXT2_MF_MOUNTED) { | ||
328 | strcat(jfile, "/.journal"); | ||
329 | |||
330 | /* | ||
331 | * If .../.journal already exists, make sure any | ||
332 | * immutable or append-only flags are cleared. | ||
333 | */ | ||
334 | #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) | ||
335 | (void) chflags (jfile, 0); | ||
336 | #else | ||
337 | #if HAVE_EXT2_IOCTLS | ||
338 | fd = open(jfile, O_RDONLY); | ||
339 | if (fd >= 0) { | ||
340 | f = 0; | ||
341 | ioctl(fd, EXT2_IOC_SETFLAGS, &f); | ||
342 | close(fd); | ||
343 | } | ||
344 | #endif | ||
345 | #endif | ||
346 | |||
347 | /* Create the journal file */ | ||
348 | if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) | ||
349 | return errno; | ||
350 | |||
351 | if ((retval = write_journal_file(fs, jfile, size, flags))) | ||
352 | goto errout; | ||
353 | |||
354 | /* Get inode number of the journal file */ | ||
355 | if (fstat(fd, &st) < 0) | ||
356 | goto errout; | ||
357 | |||
358 | #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) | ||
359 | retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); | ||
360 | #else | ||
361 | #if HAVE_EXT2_IOCTLS | ||
362 | f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; | ||
363 | retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); | ||
364 | #endif | ||
365 | #endif | ||
366 | if (retval) | ||
367 | goto errout; | ||
368 | |||
369 | close(fd); | ||
370 | journal_ino = st.st_ino; | ||
371 | } else { | ||
372 | journal_ino = EXT2_JOURNAL_INO; | ||
373 | if ((retval = write_journal_inode(fs, journal_ino, | ||
374 | size, flags))) | ||
375 | return retval; | ||
376 | } | ||
377 | |||
378 | fs->super->s_journal_inum = journal_ino; | ||
379 | fs->super->s_journal_dev = 0; | ||
380 | memset(fs->super->s_journal_uuid, 0, | ||
381 | sizeof(fs->super->s_journal_uuid)); | ||
382 | fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; | ||
383 | |||
384 | ext2fs_mark_super_dirty(fs); | ||
385 | return 0; | ||
386 | errout: | ||
387 | close(fd); | ||
388 | return retval; | ||
389 | } | ||
390 | |||
391 | #ifdef DEBUG | ||
392 | main(int argc, char **argv) | ||
393 | { | ||
394 | errcode_t retval; | ||
395 | char *device_name; | ||
396 | ext2_filsys fs; | ||
397 | |||
398 | if (argc < 2) { | ||
399 | fprintf(stderr, "Usage: %s filesystem\n", argv[0]); | ||
400 | exit(1); | ||
401 | } | ||
402 | device_name = argv[1]; | ||
403 | |||
404 | retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, | ||
405 | unix_io_manager, &fs); | ||
406 | if (retval) { | ||
407 | com_err(argv[0], retval, "while opening %s", device_name); | ||
408 | exit(1); | ||
409 | } | ||
410 | |||
411 | retval = ext2fs_add_journal_inode(fs, 1024); | ||
412 | if (retval) { | ||
413 | com_err(argv[0], retval, "while adding journal to %s", | ||
414 | device_name); | ||
415 | exit(1); | ||
416 | } | ||
417 | retval = ext2fs_flush(fs); | ||
418 | if (retval) { | ||
419 | printf("Warning, had trouble writing out superblocks.\n"); | ||
420 | } | ||
421 | ext2fs_close(fs); | ||
422 | exit(0); | ||
423 | |||
424 | } | ||
425 | #endif | ||