Update Red Hat Copyright Notices
[nbdkit.git] / plugins / linuxdisk / filesystem.c
blob3e4e2f3a5abc1d8c9e9d1f55d02be44d3f52b911
1 /* nbdkit
2 * Copyright Red Hat
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
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
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdbool.h>
38 #include <stdint.h>
39 #include <inttypes.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <errno.h>
45 #include <nbdkit-plugin.h>
47 #include "cleanup.h"
48 #include "minmax.h"
49 #include "rounding.h"
50 #include "utils.h"
52 #include "virtual-disk.h"
54 static int64_t estimate_size (void);
55 static int mke2fs (const char *filename);
57 int
58 create_filesystem (struct virtual_disk *disk)
60 const char *tmpdir;
61 CLEANUP_FREE char *filename = NULL;
62 int fd = -1;
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) {
69 int64_t estimate;
71 estimate = estimate_size ();
72 if (estimate == -1)
73 goto error;
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)
90 size += estimate;
91 else
92 size = 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");
102 if (tmpdir == NULL)
103 tmpdir = LARGE_TMPDIR;
104 if (asprintf (&filename, "%s/linuxdiskXXXXXX", tmpdir) == -1) {
105 nbdkit_error ("asprintf: %m");
106 goto error;
109 fd = mkstemp (filename);
110 if (fd == -1) {
111 nbdkit_error ("mkstemp: %s: %m", filename);
112 goto error;
114 if (ftruncate (fd, size) == -1) {
115 nbdkit_error ("ftruncate: %s: %m", filename);
116 goto error;
119 /* Create the filesystem. */
120 if (mke2fs (filename) == -1)
121 goto error;
123 unlink (filename);
124 disk->filesystem_size = size;
125 disk->fd = fd;
126 return 0;
128 error:
129 if (fd >= 0)
130 close (fd);
131 if (filename)
132 unlink (filename);
133 return -1;
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:
142 * 12345 dir1
143 * 34567 dir2
144 * 46912 total
146 * We ignore everything except the first number on the last line.
148 static int64_t
149 estimate_size (void)
151 CLEANUP_FREE char *command = NULL, *line = NULL;
152 size_t len = 0;
153 FILE *fp;
154 int64_t ret;
155 int r;
157 /* Create the du command. */
158 fp = open_memstream (&command, &len);
159 if (fp == NULL) {
160 nbdkit_error ("open_memstream: %m");
161 return -1;
163 fprintf (fp, "du -c -k -s ");
164 shell_quote (dir, fp);
165 if (fclose (fp) == EOF) {
166 nbdkit_error ("memstream failed: %m");
167 return -1;
170 /* Run the command. */
171 nbdkit_debug ("%s", command);
172 fp = popen (command, "r");
173 if (fp == NULL) {
174 nbdkit_error ("du command failed: %m");
175 return -1;
178 /* Ignore everything up to the last line. */
179 len = 0;
180 while (getline (&line, &len, fp) != -1)
181 /* empty */;
182 if (ferror (fp)) {
183 nbdkit_error ("getline failed: %m");
184 pclose (fp);
185 return -1;
188 r = pclose (fp);
189 if (r == -1) {
190 nbdkit_error ("pclose: %m");
191 return -1;
193 if (exit_status_to_nbd_error (r, "pclose: du") == -1)
194 return -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);
199 return -1;
202 /* Result is in 1K blocks, convert it to bytes. */
203 ret *= 1024;
204 return ret;
207 static int
208 mke2fs (const char *filename)
210 CLEANUP_FREE char *command = NULL;
211 size_t len = 0;
212 FILE *fp;
213 int r;
215 /* Create the mke2fs command. */
216 fp = open_memstream (&command, &len);
217 if (fp == NULL) {
218 nbdkit_error ("open_memstream: %m");
219 return -1;
222 fprintf (fp, "mke2fs -q -F -t %s ", type);
223 if (label) {
224 fprintf (fp, "-L ");
225 shell_quote (label, fp);
226 fprintf (fp, " ");
228 fprintf (fp, "-d ");
229 shell_quote (dir, fp);
230 fprintf (fp, " ");
231 shell_quote (filename, fp);
233 if (fclose (fp) == EOF) {
234 nbdkit_error ("memstream failed: %m");
235 return -1;
238 /* Run the command. */
239 nbdkit_debug ("%s", command);
240 r = system (command);
241 if (exit_status_to_nbd_error (r, "mke2fs") == -1)
242 return -1;
244 return 0;