Whamcloud - gitweb
libext2fs: add support for the SOURCE_DATE_EPOCH environment variable
[tools/e2fsprogs.git] / lib / ext2fs / mkjournal.c
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 "config.h"
13 #include <stdio.h>
14 #include <string.h>
15 #include <assert.h>
16 #if HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #if HAVE_ERRNO_H
20 #include <errno.h>
21 #endif
22 #include <fcntl.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 "ext2fsP.h"
39
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  */
46 errcode_t ext2fs_create_journal_superblock2(ext2_filsys fs,
47                                            struct ext2fs_journal_params *jparams,
48                                            int flags, char **ret_jsb)
49 {
50         errcode_t               retval;
51         journal_superblock_t    *jsb;
52
53         if (jparams->num_journal_blocks < JBD2_MIN_JOURNAL_BLOCKS)
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(JBD2_MAGIC_NUMBER);
62         if (flags & EXT2_MKJOURNAL_V1_SUPER)
63                 jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V1);
64         else
65                 jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V2);
66         jsb->s_blocksize = htonl(fs->blocksize);
67         jsb->s_maxlen = htonl(jparams->num_journal_blocks + jparams->num_fc_blocks);
68         jsb->s_nr_users = htonl(1);
69         jsb->s_first = htonl(1);
70         jsb->s_sequence = htonl(1);
71         jsb->s_num_fc_blks = htonl(jparams->num_fc_blocks);
72         memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid));
73         /*
74          * If we're creating an external journal device, we need to
75          * adjust these fields.
76          */
77         if (ext2fs_has_feature_journal_dev(fs->super)) {
78                 jsb->s_nr_users = 0;
79                 jsb->s_first = htonl(ext2fs_journal_sb_start(fs->blocksize) + 1);
80         }
81
82         *ret_jsb = (char *) jsb;
83         return 0;
84 }
85
86 errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, __u32 num_blocks,
87                                         int flags, char **ret_sb)
88 {
89         struct ext2fs_journal_params jparams;
90
91         jparams.num_journal_blocks = num_blocks;
92         jparams.num_fc_blocks = 0;
93
94         return ext2fs_create_journal_superblock2(fs, &jparams, flags, ret_sb);
95 }
96
97 /*
98  * This function writes a journal using POSIX routines.  It is used
99  * for creating external journals and creating journals on live
100  * filesystems.
101  */
102 static errcode_t write_journal_file(ext2_filsys fs, char *filename,
103                                     struct ext2fs_journal_params *jparams,
104                                     int flags)
105 {
106         errcode_t       retval;
107         char            *buf = 0;
108         int             fd, ret_size;
109         blk_t           i;
110
111         if ((retval = ext2fs_create_journal_superblock2(fs, jparams, flags,
112                                                        &buf)))
113                 return retval;
114
115         /* Open the device or journal file */
116         if ((fd = open(filename, O_WRONLY)) < 0) {
117                 retval = errno;
118                 goto errfree;
119         }
120
121         /* Write the superblock out */
122         retval = EXT2_ET_SHORT_WRITE;
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         memset(buf, 0, fs->blocksize);
131
132         if (flags & EXT2_MKJOURNAL_LAZYINIT)
133                 goto success;
134
135         for (i = 1; i < jparams->num_journal_blocks + jparams->num_fc_blocks; i++) {
136                 ret_size = write(fd, buf, fs->blocksize);
137                 if (ret_size < 0) {
138                         retval = errno;
139                         goto errout;
140                 }
141                 if (ret_size != (int) fs->blocksize)
142                         goto errout;
143         }
144
145 success:
146         retval = 0;
147 errout:
148         close(fd);
149 errfree:
150         ext2fs_free_mem(&buf);
151         return retval;
152 }
153
154 /*
155  * Convenience function which zeros out _num_ blocks starting at
156  * _blk_.  In case of an error, the details of the error is returned
157  * via _ret_blk_ and _ret_count_ if they are non-NULL pointers.
158  * Returns 0 on success, and an error code on an error.
159  *
160  * As a special case, if the first argument is NULL, then it will
161  * attempt to free the static zeroizing buffer.  (This is to keep
162  * programs that check for memory leaks happy.)
163  */
164 #define MAX_STRIDE_LENGTH (4194304 / (int) fs->blocksize)
165 errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num,
166                               blk64_t *ret_blk, int *ret_count)
167 {
168         int             j, count;
169         static void     *buf;
170         static int      stride_length;
171         errcode_t       retval;
172
173         /* If fs is null, clean up the static buffer and return */
174         if (!fs) {
175                 if (buf) {
176                         free(buf);
177                         buf = 0;
178                         stride_length = 0;
179                 }
180                 return 0;
181         }
182
183         /* Deal with zeroing less than 1 block */
184         if (num <= 0)
185                 return 0;
186
187         /* Try a zero out command, if supported */
188         retval = io_channel_zeroout(fs->io, blk, num);
189         if (retval == 0)
190                 return 0;
191
192         /* Allocate the zeroizing buffer if necessary */
193         if (num > stride_length && stride_length < MAX_STRIDE_LENGTH) {
194                 void *p;
195                 int new_stride = num;
196
197                 if (new_stride > MAX_STRIDE_LENGTH)
198                         new_stride = MAX_STRIDE_LENGTH;
199                 p = realloc(buf, fs->blocksize * new_stride);
200                 if (!p)
201                         return EXT2_ET_NO_MEMORY;
202                 buf = p;
203                 stride_length = new_stride;
204                 memset(buf, 0, fs->blocksize * stride_length);
205         }
206         /* OK, do the write loop */
207         j=0;
208         while (j < num) {
209                 if (blk % stride_length) {
210                         count = stride_length - (blk % stride_length);
211                         if (count > (num - j))
212                                 count = num - j;
213                 } else {
214                         count = num - j;
215                         if (count > stride_length)
216                                 count = stride_length;
217                 }
218                 retval = io_channel_write_blk64(fs->io, blk, count, buf);
219                 if (retval) {
220                         if (ret_count)
221                                 *ret_count = count;
222                         if (ret_blk)
223                                 *ret_blk = blk;
224                         return retval;
225                 }
226                 j += count; blk += count;
227         }
228         return 0;
229 }
230
231 errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num,
232                              blk_t *ret_blk, int *ret_count)
233 {
234         blk64_t ret_blk2;
235         errcode_t retval;
236
237         retval = ext2fs_zero_blocks2(fs, blk, num, &ret_blk2, ret_count);
238         if (retval)
239                 *ret_blk = (blk_t) ret_blk2;
240         return retval;
241 }
242
243 /*
244  * Calculate the initial goal block to be roughly at the middle of the
245  * filesystem.  Pick a group that has the largest number of free
246  * blocks.
247  */
248 static blk64_t get_midpoint_journal_block(ext2_filsys fs)
249 {
250         dgrp_t  group, start, end, i, log_flex;
251
252         group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) -
253                                          fs->super->s_first_data_block) / 2);
254         log_flex = 1U << fs->super->s_log_groups_per_flex;
255         if (fs->super->s_log_groups_per_flex && (group > log_flex)) {
256                 group = group & ~(log_flex - 1);
257                 while ((group < fs->group_desc_count) &&
258                        ext2fs_bg_free_blocks_count(fs, group) == 0)
259                         group++;
260                 if (group == fs->group_desc_count)
261                         group = 0;
262                 start = group;
263         } else
264                 start = (group > 0) ? group-1 : group;
265         end = ((group+1) < fs->group_desc_count) ? group+1 : group;
266         group = start;
267         for (i = start + 1; i <= end; i++)
268                 if (ext2fs_bg_free_blocks_count(fs, i) >
269                     ext2fs_bg_free_blocks_count(fs, group))
270                         group = i;
271         return ext2fs_group_first_block2(fs, group);
272 }
273
274 /*
275  * This function creates a journal using direct I/O routines.
276  */
277 static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
278                                      struct ext2fs_journal_params *jparams,
279                                      blk64_t goal, int flags)
280 {
281         char                    *buf;
282         errcode_t               retval;
283         struct ext2_inode       inode;
284         unsigned long long      inode_size;
285         int                     falloc_flags = EXT2_FALLOCATE_FORCE_INIT;
286         blk64_t                 zblk;
287         time_t                  now;
288
289         if ((retval = ext2fs_create_journal_superblock2(fs, jparams, flags,
290                                                        &buf)))
291                 return retval;
292
293         if ((retval = ext2fs_read_bitmaps(fs)))
294                 goto out2;
295
296         if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
297                 goto out2;
298
299         if (inode.i_blocks > 0) {
300                 retval = EEXIST;
301                 goto out2;
302         }
303
304         if (goal == ~0ULL)
305                 goal = get_midpoint_journal_block(fs);
306
307         if (ext2fs_has_feature_extents(fs->super))
308                 inode.i_flags |= EXT4_EXTENTS_FL;
309
310         if (!(flags & EXT2_MKJOURNAL_LAZYINIT))
311                 falloc_flags |= EXT2_FALLOCATE_ZERO_BLOCKS;
312
313         inode_size = (unsigned long long)fs->blocksize *
314                         (jparams->num_journal_blocks + jparams->num_fc_blocks);
315         now = ext2fsP_get_time(fs);
316         ext2fs_inode_xtime_set(&inode, i_mtime, now);
317         ext2fs_inode_xtime_set(&inode, i_ctime, now);
318         inode.i_links_count = 1;
319         inode.i_mode = LINUX_S_IFREG | 0600;
320         retval = ext2fs_inode_size_set(fs, &inode, inode_size);
321         if (retval)
322                 goto out2;
323
324         retval = ext2fs_fallocate(fs, falloc_flags, journal_ino,
325                                   &inode, goal, 0,
326                                   jparams->num_journal_blocks + jparams->num_fc_blocks);
327         if (retval)
328                 goto out2;
329
330         if ((retval = ext2fs_write_new_inode(fs, journal_ino, &inode)))
331                 goto out2;
332
333         retval = ext2fs_bmap2(fs, journal_ino, &inode, NULL, 0, 0, NULL, &zblk);
334         if (retval)
335                 goto out2;
336
337         retval = io_channel_write_blk64(fs->io, zblk, 1, buf);
338         if (retval)
339                 goto out2;
340
341         memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4);
342         fs->super->s_jnl_blocks[15] = inode.i_size_high;
343         fs->super->s_jnl_blocks[16] = inode.i_size;
344         fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
345         ext2fs_mark_super_dirty(fs);
346
347 out2:
348         ext2fs_free_mem(&buf);
349         return retval;
350 }
351
352 /*
353  * Find a reasonable journal file size (in blocks) given the number of blocks
354  * in the filesystem.  For very small filesystems, it is not reasonable to
355  * have a journal that fills more than half of the filesystem.
356  *
357  * n.b. comments assume 4k blocks
358  */
359 int ext2fs_default_journal_size(__u64 num_blocks)
360 {
361         if (num_blocks < 2048)
362                 return -1;
363         if (num_blocks < 32768)         /* 128 MB */
364                 return (1024);                  /* 4 MB */
365         if (num_blocks < 256*1024)      /* 1 GB */
366                 return (4096);                  /* 16 MB */
367         if (num_blocks < 512*1024)      /* 2 GB */
368                 return (8192);                  /* 32 MB */
369         if (num_blocks < 4096*1024)     /* 16 GB */
370                 return (16384);                 /* 64 MB */
371         if (num_blocks < 8192*1024)     /* 32 GB */
372                 return (32768);                 /* 128 MB */
373         if (num_blocks < 16384*1024)    /* 64 GB */
374                 return (65536);                 /* 256 MB */
375         if (num_blocks < 32768*1024)    /* 128 GB */
376                 return (131072);                /* 512 MB */
377         return 262144;                          /* 1 GB */
378 }
379
380 errcode_t ext2fs_get_journal_params(struct ext2fs_journal_params *params,
381                 ext2_filsys fs)
382 {
383         blk_t total_blks;
384         int ret;
385
386         memset(params, 0, sizeof(*params));
387         if (ext2fs_has_feature_journal_dev(fs->super)) {
388                 total_blks = ext2fs_blocks_count(fs->super);
389                 if (total_blks < JBD2_MIN_JOURNAL_BLOCKS)
390                         return EXT2_ET_JOURNAL_TOO_SMALL;
391
392                 if (!ext2fs_has_feature_fast_commit(fs->super)) {
393                         params->num_journal_blocks = total_blks;
394                         params->num_fc_blocks = 0;
395                         return 0;
396                 }
397                 params->num_journal_blocks = ext2fs_blocks_count(fs->super) *
398                                 EXT2_JOURNAL_TO_FC_BLKS_RATIO /
399                                 (EXT2_JOURNAL_TO_FC_BLKS_RATIO + 1);
400                 if (JBD2_MIN_JOURNAL_BLOCKS > params->num_journal_blocks)
401                         params->num_journal_blocks = JBD2_MIN_JOURNAL_BLOCKS;
402                 params->num_fc_blocks = total_blks - params->num_journal_blocks;
403                 return 0;
404         }
405
406         ret = ext2fs_default_journal_size(ext2fs_blocks_count(fs->super));
407         if (ret < 0)
408                 return EXT2_ET_JOURNAL_TOO_SMALL;
409
410         params->num_journal_blocks = ret;
411         if (ext2fs_has_feature_fast_commit(fs->super))
412                 params->num_fc_blocks = params->num_journal_blocks /
413                         EXT2_JOURNAL_TO_FC_BLKS_RATIO;
414         return 0;
415 }
416
417 int ext2fs_journal_sb_start(int blocksize)
418 {
419         if (blocksize == EXT2_MIN_BLOCK_SIZE)
420                 return 2;
421         return 1;
422 }
423
424 /*
425  * This function adds a journal device to a filesystem
426  */
427 errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev)
428 {
429         struct stat     st;
430         errcode_t       retval;
431         char            buf[SUPERBLOCK_SIZE];
432         journal_superblock_t    *jsb;
433         int             start;
434         __u32           i, nr_users;
435
436         /* Make sure the device exists and is a block device */
437         if (stat(journal_dev->device_name, &st) < 0)
438                 return errno;
439
440         if (!S_ISBLK(st.st_mode))
441                 return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */
442
443         /* Get the journal superblock */
444         start = ext2fs_journal_sb_start(journal_dev->blocksize);
445         if ((retval = io_channel_read_blk64(journal_dev->io, start,
446                                             -SUPERBLOCK_SIZE,
447                                             buf)))
448                 return retval;
449
450         jsb = (journal_superblock_t *) buf;
451         if ((jsb->s_header.h_magic != (unsigned) ntohl(JBD2_MAGIC_NUMBER)) ||
452             (jsb->s_header.h_blocktype != (unsigned) ntohl(JBD2_SUPERBLOCK_V2)))
453                 return EXT2_ET_NO_JOURNAL_SB;
454
455         if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize)
456                 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
457
458         /* Check and see if this filesystem has already been added */
459         nr_users = ntohl(jsb->s_nr_users);
460         if (nr_users > JBD2_USERS_MAX)
461                 return EXT2_ET_CORRUPT_JOURNAL_SB;
462         for (i=0; i < nr_users; i++) {
463                 if (memcmp(fs->super->s_uuid,
464                            &jsb->s_users[i*16], 16) == 0)
465                         break;
466         }
467         if (i >= nr_users) {
468                 memcpy(&jsb->s_users[nr_users*16],
469                        fs->super->s_uuid, 16);
470                 jsb->s_nr_users = htonl(nr_users+1);
471         }
472
473         /* Writeback the journal superblock */
474         if ((retval = io_channel_write_blk64(journal_dev->io, start,
475                                             -SUPERBLOCK_SIZE, buf)))
476                 return retval;
477
478         fs->super->s_journal_inum = 0;
479         fs->super->s_journal_dev = st.st_rdev;
480         memcpy(fs->super->s_journal_uuid, jsb->s_uuid,
481                sizeof(fs->super->s_journal_uuid));
482         memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks));
483         ext2fs_set_feature_journal(fs->super);
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_inode3(ext2_filsys fs, struct ext2fs_journal_params *jparams,
494                                     blk64_t goal, int flags)
495 {
496         errcode_t               retval;
497         ext2_ino_t              journal_ino;
498         struct stat             st;
499         char                    jfile[1024];
500         int                     mount_flags;
501         int                     fd = -1;
502
503         if (flags & EXT2_MKJOURNAL_NO_MNT_CHECK)
504                 mount_flags = 0;
505         else if ((retval = ext2fs_check_mount_point(fs->device_name,
506                                                     &mount_flags,
507                                                     jfile, sizeof(jfile)-10)))
508                 return retval;
509
510         if (mount_flags & EXT2_MF_MOUNTED) {
511 #if HAVE_EXT2_IOCTLS
512                 int f = 0;
513 #endif
514                 strcat(jfile, "/.journal");
515
516                 /*
517                  * If .../.journal already exists, make sure any
518                  * immutable or append-only flags are cleared.
519                  */
520 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
521                 (void) chflags (jfile, 0);
522 #else
523 #if HAVE_EXT2_IOCTLS
524                 fd = open(jfile, O_RDONLY);
525                 if (fd >= 0) {
526                         retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
527                         close(fd);
528                         if (retval)
529                                 return errno;
530                 }
531 #endif
532 #endif
533
534                 /* Create the journal file */
535                 if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0)
536                         return errno;
537
538                 /* Note that we can't do lazy journal initialization for mounted
539                  * filesystems, since the zero writing is also allocating the
540                  * journal blocks.  We could use fallocate, but not all kernels
541                  * support that, and creating a journal on a mounted ext2
542                  * filesystems is extremely rare these days...  Ignore it. */
543                 flags &= ~EXT2_MKJOURNAL_LAZYINIT;
544
545                 if ((retval = write_journal_file(fs, jfile, jparams, flags)))
546                         goto errout;
547
548                 /* Get inode number of the journal file */
549                 if (fstat(fd, &st) < 0) {
550                         retval = errno;
551                         goto errout;
552                 }
553
554 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
555                 retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE);
556 #else
557 #if HAVE_EXT2_IOCTLS
558                 if (ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) {
559                         retval = errno;
560                         goto errout;
561                 }
562                 f |= EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL;
563                 retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
564 #endif
565 #endif
566                 if (retval) {
567                         retval = errno;
568                         goto errout;
569                 }
570
571                 if (close(fd) < 0) {
572                         retval = errno;
573                         fd = -1;
574                         goto errout;
575                 }
576                 journal_ino = st.st_ino;
577                 memset(fs->super->s_jnl_blocks, 0,
578                        sizeof(fs->super->s_jnl_blocks));
579         } else {
580                 if ((mount_flags & EXT2_MF_BUSY) &&
581                     !(fs->flags & EXT2_FLAG_EXCLUSIVE)) {
582                         retval = EBUSY;
583                         goto errout;
584                 }
585                 journal_ino = EXT2_JOURNAL_INO;
586                 if ((retval = write_journal_inode(fs, journal_ino,
587                                                   jparams, goal, flags)))
588                         return retval;
589         }
590
591         fs->super->s_journal_inum = journal_ino;
592         fs->super->s_journal_dev = 0;
593         memset(fs->super->s_journal_uuid, 0,
594                sizeof(fs->super->s_journal_uuid));
595         ext2fs_set_feature_journal(fs->super);
596
597         ext2fs_mark_super_dirty(fs);
598         return 0;
599 errout:
600         if (fd >= 0)
601                 close(fd);
602         return retval;
603 }
604
605 errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks,
606                                     blk64_t goal, int flags)
607 {
608         struct ext2fs_journal_params jparams;
609
610         jparams.num_journal_blocks = num_blocks;
611         jparams.num_fc_blocks = 0;
612
613         return ext2fs_add_journal_inode3(fs, &jparams, goal, flags);
614 }
615
616 errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags)
617 {
618         return ext2fs_add_journal_inode2(fs, num_blocks, ~0ULL, flags);
619 }
620
621
622 #ifdef DEBUG
623 main(int argc, char **argv)
624 {
625         errcode_t       retval;
626         char            *device_name;
627         ext2_filsys     fs;
628
629         if (argc < 2) {
630                 fprintf(stderr, "Usage: %s filesystem\n", argv[0]);
631                 exit(1);
632         }
633         device_name = argv[1];
634
635         retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0,
636                               unix_io_manager, &fs);
637         if (retval) {
638                 com_err(argv[0], retval, "while opening %s", device_name);
639                 exit(1);
640         }
641
642         retval = ext2fs_add_journal_inode(fs, JBD2_MIN_JOURNAL_BLOCKS, 0);
643         if (retval) {
644                 com_err(argv[0], retval, "while adding journal to %s",
645                         device_name);
646                 exit(1);
647         }
648         retval = ext2fs_flush(fs);
649         if (retval) {
650                 printf("Warning, had trouble writing out superblocks.\n");
651         }
652         ext2fs_close_free(&fs);
653         exit(0);
654
655 }
656 #endif