Whamcloud - gitweb
mke2fs: add make_hugefile feature
[tools/e2fsprogs.git] / misc / mk_hugefiles.c
1 /*
2  * mk_hugefiles.c -- create huge files
3  */
4
5 #define _XOPEN_SOURCE 600 /* for inclusion of PATH_MAX in Solaris */
6
7 #include "config.h"
8 #include <stdio.h>
9 #include <string.h>
10 #include <strings.h>
11 #include <fcntl.h>
12 #include <ctype.h>
13 #include <time.h>
14 #ifdef __linux__
15 #include <sys/utsname.h>
16 #endif
17 #ifdef HAVE_GETOPT_H
18 #include <getopt.h>
19 #else
20 extern char *optarg;
21 extern int optind;
22 #endif
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29 #ifdef HAVE_ERRNO_H
30 #include <errno.h>
31 #endif
32 #include <sys/ioctl.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <libgen.h>
36 #include <limits.h>
37 #include <blkid/blkid.h>
38
39 #include "ext2fs/ext2_fs.h"
40 #include "ext2fs/ext2fsP.h"
41 #include "et/com_err.h"
42 #include "uuid/uuid.h"
43 #include "e2p/e2p.h"
44 #include "ext2fs/ext2fs.h"
45 #include "util.h"
46 #include "profile.h"
47 #include "prof_err.h"
48 #include "nls-enable.h"
49 #include "mke2fs.h"
50
51 static int uid;
52 static int gid;
53 static blk64_t num_blocks;
54 static blk64_t num_slack;
55 static unsigned long num_files;
56 static blk64_t goal;
57 static char *fn_prefix;
58 static int idx_digits;
59 static char *fn_buf;
60 static char *fn_numbuf;
61 int zero_hugefile = 1;
62
63 static errcode_t create_directory(ext2_filsys fs, char *dir,
64                                   ext2_ino_t *ret_ino)
65
66 {
67         struct ext2_inode       inode;
68         ext2_ino_t              ino = EXT2_ROOT_INO;
69         ext2_ino_t              newdir;
70         errcode_t               retval = 0;
71         char                    *fn, *cp, *next;
72
73         fn = malloc(strlen(dir) + 1);
74         if (fn == NULL)
75                 return ENOMEM;
76
77         strcpy(fn, dir);
78         cp = fn;
79         while(1) {
80                 next = strchr(cp, '/');
81                 if (next)
82                         *next++ = 0;
83                 if (*cp) {
84                         retval = ext2fs_new_inode(fs, ino, LINUX_S_IFDIR,
85                                                   NULL, &newdir);
86                         if (retval)
87                                 goto errout;
88
89                         retval = ext2fs_mkdir(fs, ino, newdir, cp);
90                         if (retval)
91                                 goto errout;
92
93                         ino = newdir;
94                         retval = ext2fs_read_inode(fs, ino, &inode);
95                         if (retval)
96                                 goto errout;
97
98                         inode.i_uid = uid & 0xFFFF;
99                         ext2fs_set_i_uid_high(inode, (uid >> 16) & 0xffff);
100                         inode.i_gid = gid & 0xFFFF;
101                         ext2fs_set_i_gid_high(inode, (gid >> 16) & 0xffff);
102                         retval = ext2fs_write_inode(fs, ino, &inode);
103                         if (retval)
104                                 goto errout;
105                 }
106                 if (next == NULL || *next == '\0')
107                         break;
108                 cp = next;
109         }
110 errout:
111         free(fn);
112         if (retval == 0)
113                 *ret_ino = ino;
114         return retval;
115 }
116
117 static errcode_t mk_hugefile(ext2_filsys fs, blk64_t num,
118                              ext2_ino_t dir, unsigned long idx, ext2_ino_t *ino)
119
120 {
121         errcode_t               retval;
122         blk64_t                 lblk, bend;
123         __u64                   size;
124         blk64_t                 left;
125         blk64_t                 count = 0;
126         struct ext2_inode       inode;
127         ext2_extent_handle_t    handle;
128
129         retval = ext2fs_new_inode(fs, 0, LINUX_S_IFREG, NULL, ino);
130         if (retval)
131                 return retval;
132
133         memset(&inode, 0, sizeof(struct ext2_inode));
134         inode.i_mode = LINUX_S_IFREG | (0666 & ~fs->umask);
135         inode.i_links_count = 1;
136         inode.i_uid = uid & 0xFFFF;
137         ext2fs_set_i_uid_high(inode, (uid >> 16) & 0xffff);
138         inode.i_gid = gid & 0xFFFF;
139         ext2fs_set_i_gid_high(inode, (gid >> 16) & 0xffff);
140
141         retval = ext2fs_write_new_inode(fs, *ino, &inode);
142         if (retval)
143                 return retval;
144
145         ext2fs_inode_alloc_stats2(fs, *ino, +1, 0);
146
147         retval = ext2fs_extent_open2(fs, *ino, &inode, &handle);
148         if (retval)
149                 return retval;
150
151         lblk = 0;
152         left = num ? num : 1;
153         while (left) {
154                 blk64_t pblk, end;
155                 blk64_t n = left;
156
157                 retval =  ext2fs_find_first_zero_block_bitmap2(fs->block_map,
158                         goal, ext2fs_blocks_count(fs->super) - 1, &end);
159                 if (retval)
160                         goto errout;
161                 goal = end;
162
163                 retval =  ext2fs_find_first_set_block_bitmap2(fs->block_map, goal,
164                                ext2fs_blocks_count(fs->super) - 1, &bend);
165                 if (retval == ENOENT) {
166                         bend = ext2fs_blocks_count(fs->super);
167                         if (num == 0)
168                                 left = 0;
169                 }
170                 if (!num || bend - goal < left)
171                         n = bend - goal;
172                 pblk = goal;
173                 if (num)
174                         left -= n;
175                 goal += n;
176                 count += n;
177                 ext2fs_block_alloc_stats_range(fs, pblk, n, +1);
178
179                 if (zero_hugefile) {
180                         blk64_t ret_blk;
181                         retval = ext2fs_zero_blocks2(fs, pblk, n,
182                                                      &ret_blk, NULL);
183
184                         if (retval)
185                                 com_err(program_name, retval,
186                                         _("while zeroing block %llu "
187                                           "for hugefile"), ret_blk);
188                 }
189
190                 while (n) {
191                         blk64_t l = n;
192                         struct ext2fs_extent newextent;
193
194                         if (l > EXT_INIT_MAX_LEN)
195                                 l = EXT_INIT_MAX_LEN;
196
197                         newextent.e_len = l;
198                         newextent.e_pblk = pblk;
199                         newextent.e_lblk = lblk;
200                         newextent.e_flags = 0;
201
202                         retval = ext2fs_extent_insert(handle,
203                                         EXT2_EXTENT_INSERT_AFTER, &newextent);
204                         if (retval)
205                                 return retval;
206                         pblk += l;
207                         lblk += l;
208                         n -= l;
209                 }
210         }
211
212         retval = ext2fs_read_inode(fs, *ino, &inode);
213         if (retval)
214                 goto errout;
215
216         retval = ext2fs_iblk_add_blocks(fs, &inode,
217                                         count / EXT2FS_CLUSTER_RATIO(fs));
218         if (retval)
219                 goto errout;
220         size = (__u64) count * fs->blocksize;
221         inode.i_size = size & 0xffffffff;
222         inode.i_size_high = (size >> 32);
223
224         retval = ext2fs_write_new_inode(fs, *ino, &inode);
225         if (retval)
226                 goto errout;
227
228         if (idx_digits)
229                 sprintf(fn_numbuf, "%0*lu", idx_digits, idx);
230         else if (num_files > 1)
231                 sprintf(fn_numbuf, "%lu", idx);
232
233 retry:
234         retval = ext2fs_link(fs, dir, fn_buf, *ino, EXT2_FT_REG_FILE);
235         if (retval == EXT2_ET_DIR_NO_SPACE) {
236                 retval = ext2fs_expand_dir(fs, dir);
237                 if (retval)
238                         goto errout;
239                 goto retry;
240         }
241
242         if (retval)
243                 goto errout;
244
245 errout:
246         if (handle)
247                 ext2fs_extent_free(handle);
248
249         return retval;
250 }
251
252 static blk64_t calc_overhead(ext2_filsys fs, blk64_t num)
253 {
254         blk64_t e_blocks, e_blocks2, e_blocks3, e_blocks4;
255         int extents_per_block;
256         int extents = (num + EXT_INIT_MAX_LEN - 1) / EXT_INIT_MAX_LEN;
257
258         if (extents <= 4)
259                 return 0;
260
261         /*
262          * This calculation is due to the fact that we are inefficient
263          * in how handle extent splits when appending to the end of
264          * the extent tree.  Sigh.  We should fix this so that we can
265          * actually store 340 extents per 4k block, instead of only 170.
266          */
267         extents_per_block = ((fs->blocksize -
268                               sizeof(struct ext3_extent_header)) /
269                              sizeof(struct ext3_extent));
270         extents_per_block = (extents_per_block/ 2) - 1;
271
272         e_blocks = (extents + extents_per_block - 1) / extents_per_block;
273         e_blocks2 = (e_blocks + extents_per_block - 1) / extents_per_block;
274         e_blocks3 = (e_blocks2 + extents_per_block - 1) / extents_per_block;
275         e_blocks4 = (e_blocks3 + extents_per_block - 1) / extents_per_block;
276         return e_blocks + e_blocks2 + e_blocks3 + e_blocks4;
277 }
278
279 /*
280  * Find the place where we should start allocating blocks for the huge
281  * files.  Leave <slack> free blocks at the beginning of the file
282  * system for things like metadata blocks.
283  */
284 static blk64_t get_start_block(ext2_filsys fs, blk64_t slack)
285 {
286         errcode_t retval;
287         blk64_t blk = fs->super->s_first_data_block, next;
288         blk64_t last_blk = ext2fs_blocks_count(fs->super) - 1;
289
290         while (slack) {
291                 retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
292                                                 blk, last_blk, &blk);
293                 if (retval)
294                         break;
295
296                 retval = ext2fs_find_first_set_block_bitmap2(fs->block_map,
297                                                 blk, last_blk, &next);
298                 if (retval)
299                         next = last_blk;
300                 next--;
301
302                 if (next - blk > slack) {
303                         blk += slack;
304                         break;
305                 }
306
307                 slack -= (next - blk);
308                 blk = next;
309         }
310         return blk;
311 }
312
313 errcode_t mk_hugefiles(ext2_filsys fs)
314 {
315         unsigned long   i;
316         ext2_ino_t      dir;
317         errcode_t       retval;
318         blk64_t         fs_blocks;
319         int             d, dsize;
320         char            *t;
321
322         if (!get_bool_from_profile(fs_types, "make_hugefiles", 0))
323                 return 0;
324
325         uid = get_int_from_profile(fs_types, "hugefiles_uid", 0);
326         gid = get_int_from_profile(fs_types, "hugefiles_gid", 0);
327         fs->umask = get_int_from_profile(fs_types, "hugefiles_umask", 077);
328         num_files = get_int_from_profile(fs_types, "num_hugefiles", 0);
329         t = get_string_from_profile(fs_types, "hugefiles_slack", "1M");
330         num_slack = parse_num_blocks2(t, fs->super->s_log_block_size);
331         free(t);
332         t = get_string_from_profile(fs_types, "hugefiles_size", "0");
333         num_blocks = parse_num_blocks2(t, fs->super->s_log_block_size);
334         free(t);
335         zero_hugefile = get_int_from_profile(fs_types, "zero_hugefiles",
336                                              zero_hugefile);
337
338         t = get_string_from_profile(fs_types, "hugefiles_dir", "/");
339         retval = create_directory(fs, t, &dir);
340         free(t);
341         if (retval)
342                 return retval;
343
344         fn_prefix = get_string_from_profile(fs_types, "hugefiles_name",
345                                             "hugefile");
346         idx_digits = get_int_from_profile(fs_types, "hugefiles_digits", 5);
347         d = int_log10(num_files) + 1;
348         if (idx_digits > d)
349                 d = idx_digits;
350         dsize = strlen(fn_prefix) + d + 16;
351         fn_buf = malloc(dsize);
352         if (!fn_buf) {
353                 free(fn_prefix);
354                 return ENOMEM;
355         }
356         strcpy(fn_buf, fn_prefix);
357         fn_numbuf = fn_buf + strlen(fn_prefix);
358         free(fn_prefix);
359
360         fs_blocks = ext2fs_free_blocks_count(fs->super) -num_slack;
361         if (num_blocks == 0 && num_files == 0)
362                 num_files = 1;
363
364         if (num_files == 0 && num_blocks) {
365                 num_files = fs_blocks / num_blocks;
366                 fs_blocks -= (num_files / 16) + 1;
367                 fs_blocks -= calc_overhead(fs, num_blocks) * num_files;
368                 num_files = fs_blocks / num_blocks;
369         }
370
371         if (num_blocks == 0 && num_files > 1) {
372                 num_blocks = fs_blocks / num_files;
373                 fs_blocks -= (num_files / 16) + 1;
374                 fs_blocks -= calc_overhead(fs, num_blocks) * num_files;
375                 num_blocks = fs_blocks / num_files;
376         }
377
378         num_slack += calc_overhead(fs, num_blocks) * num_files;
379         num_slack += (num_files / 16) + 1; /* space for dir entries */
380         goal = get_start_block(fs, num_slack);
381
382         if (!quiet) {
383                 if (zero_hugefile && verbose)
384                         printf(_("Huge files will be zero'ed\n"));
385                 printf(_("Creating %lu huge file(s) "), num_files);
386                 if (num_blocks)
387                         printf(_("with %llu blocks each"), num_blocks);
388                 fputs(": ", stdout);
389         }
390         for (i=0; i < num_files; i++) {
391                 ext2_ino_t ino;
392
393                 retval = mk_hugefile(fs, num_blocks, dir, i, &ino);
394                 if (retval) {
395                         com_err(program_name, retval,
396                                 _("while creating huge file %lu"), i);
397                         goto errout;
398                 }
399         }
400         if (!quiet)
401                 fputs(_("done\n"), stdout);
402
403 errout:
404         free(fn_buf);
405         return retval;
406 }