2 * Copyright (C) 2018-2019 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 #include <nbdkit-plugin.h>
52 #include "virtual-disk.h"
54 static int64_t estimate_size (void);
55 static int mke2fs (const char *filename
);
58 create_filesystem (struct virtual_disk
*disk
)
61 CLEANUP_FREE
char *filename
= NULL
;
64 /* Estimate the filesystem size and compute the final virtual size
65 * of the disk. We only need to do this if the user didn't specify
66 * the exact size on the command line.
68 if (size
== 0 || size_add_estimate
) {
71 estimate
= estimate_size ();
75 nbdkit_debug ("filesystem size estimate: %" PRIi64
, estimate
);
77 /* Add 20% to the estimate to account for the overhead of
78 * filesystem metadata. Also set a minimum size. Note we are
79 * only wasting virtual space (since this will be stored sparsely
80 * under $TMPDIR) so we can be generous here.
82 estimate
= estimate
* 6 / 5;
83 estimate
= MAX (estimate
, 1024*1024);
85 /* For ext3 and ext4, add something for the journal. */
86 if (strncmp (type
, "ext", 3) == 0 && type
[3] > '2')
87 estimate
+= 32*1024*1024;
89 if (size_add_estimate
)
95 /* Round the final size up to a whole number of sectors. */
96 size
= ROUND_UP (size
, SECTOR_SIZE
);
98 nbdkit_debug ("filesystem virtual size: %" PRIi64
, size
);
100 /* Create the filesystem file. */
101 tmpdir
= getenv ("TMPDIR");
103 tmpdir
= LARGE_TMPDIR
;
104 if (asprintf (&filename
, "%s/linuxdiskXXXXXX", tmpdir
) == -1) {
105 nbdkit_error ("asprintf: %m");
109 fd
= mkstemp (filename
);
111 nbdkit_error ("mkstemp: %s: %m", filename
);
114 if (ftruncate (fd
, size
) == -1) {
115 nbdkit_error ("ftruncate: %s: %m", filename
);
119 /* Create the filesystem. */
120 if (mke2fs (filename
) == -1)
124 disk
->filesystem_size
= size
;
136 /* Use ‘du’ to estimate the size of the filesystem quickly. We use
137 * the -c option to allow the possibility of supporting multiple
138 * directories in future.
140 * Typical output from ‘du -cs dir1 dir2’ is:
146 * We ignore everything except the first number on the last line.
151 CLEANUP_FREE
char *command
= NULL
, *line
= NULL
;
157 /* Create the du command. */
158 fp
= open_memstream (&command
, &len
);
160 nbdkit_error ("open_memstream: %m");
163 fprintf (fp
, "du -c -k -s ");
164 shell_quote (dir
, fp
);
165 if (fclose (fp
) == EOF
) {
166 nbdkit_error ("memstream failed: %m");
170 /* Run the command. */
171 nbdkit_debug ("%s", command
);
172 fp
= popen (command
, "r");
174 nbdkit_error ("du command failed: %m");
178 /* Ignore everything up to the last line. */
180 while (getline (&line
, &len
, fp
) != -1)
183 nbdkit_error ("getline failed: %m");
190 nbdkit_error ("pclose: %m");
193 if (exit_status_to_nbd_error (r
, "pclose: du") == -1)
196 /* Parse the last line. */
197 if (sscanf (line
, "%" SCNi64
, &ret
) != 1 || ret
< 0) {
198 nbdkit_error ("could not parse last line of output: %s", line
);
202 /* Result is in 1K blocks, convert it to bytes. */
208 mke2fs (const char *filename
)
210 CLEANUP_FREE
char *command
= NULL
;
215 /* Create the mke2fs command. */
216 fp
= open_memstream (&command
, &len
);
218 nbdkit_error ("open_memstream: %m");
222 fprintf (fp
, "mke2fs -q -F -t %s ", type
);
225 shell_quote (label
, fp
);
229 shell_quote (dir
, fp
);
231 shell_quote (filename
, fp
);
233 if (fclose (fp
) == EOF
) {
234 nbdkit_error ("memstream failed: %m");
238 /* Run the command. */
239 nbdkit_debug ("%s", command
);
240 r
= system (command
);
241 if (exit_status_to_nbd_error (r
, "mke2fs") == -1)