Fix FileIn cmds losing synch if both ends send cancel messages (RHBZ#576879).
[libguestfs.git] / fish / alloc.c
1 /* guestfish - the filesystem interactive shell
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <errno.h>
28
29 #include "fish.h"
30
31 int
32 do_alloc (const char *cmd, int argc, char *argv[])
33 {
34   if (argc != 2) {
35     fprintf (stderr, _("use 'alloc file size' to create an image\n"));
36     return -1;
37   }
38
39   if (alloc_disk (argv[0], argv[1], 1, 0) == -1)
40     return -1;
41
42   return 0;
43 }
44
45 int
46 do_sparse (const char *cmd, int argc, char *argv[])
47 {
48   if (argc != 2) {
49     fprintf (stderr, _("use 'sparse file size' to create a sparse image\n"));
50     return -1;
51   }
52
53   if (alloc_disk (argv[0], argv[1], 1, 1) == -1)
54     return -1;
55
56   return 0;
57 }
58
59 static int parse_size (const char *str, off_t *size_rtn);
60
61 /* This is the underlying allocation function.  It's called from
62  * a few other places in guestfish.
63  */
64 int
65 alloc_disk (const char *filename, const char *size_str, int add, int sparse)
66 {
67   off_t size;
68   int fd;
69   char c = 0;
70
71   if (parse_size (size_str, &size) == -1)
72     return -1;
73
74   if (!guestfs_is_config (g)) {
75     fprintf (stderr, _("can't allocate or add disks after launching\n"));
76     return -1;
77   }
78
79   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666);
80   if (fd == -1) {
81     perror (filename);
82     return -1;
83   }
84
85   if (!sparse) {                /* Not sparse */
86 #ifdef HAVE_POSIX_FALLOCATE
87     int err = posix_fallocate (fd, 0, size);
88     if (err != 0) {
89       errno = err;
90       perror ("fallocate");
91       close (fd);
92       unlink (filename);
93       return -1;
94     }
95 #else
96     /* Slow emulation of posix_fallocate on platforms which don't have it. */
97     char buffer[BUFSIZ];
98     memset (buffer, 0, sizeof buffer);
99
100     size_t remaining = size;
101     while (remaining > 0) {
102       size_t n = remaining > sizeof buffer ? sizeof buffer : remaining;
103       ssize_t r = write (fd, buffer, n);
104       if (r == -1) {
105         perror ("write");
106         close (fd);
107         unlink (filename);
108         return -1;
109       }
110       remaining -= r;
111     }
112 #endif
113   } else {                      /* Sparse */
114     if (lseek (fd, size-1, SEEK_SET) == (off_t) -1) {
115       perror ("lseek");
116       close (fd);
117       unlink (filename);
118       return -1;
119     }
120
121     if (write (fd, &c, 1) != 1) {
122       perror ("write");
123       close (fd);
124       unlink (filename);
125       return -1;
126     }
127   }
128
129   if (close (fd) == -1) {
130     perror (filename);
131     unlink (filename);
132     return -1;
133   }
134
135   if (add) {
136     if (guestfs_add_drive (g, filename) == -1) {
137       unlink (filename);
138       return -1;
139     }
140   }
141
142   return 0;
143 }
144
145 static int
146 parse_size (const char *str, off_t *size_rtn)
147 {
148   uint64_t size;
149   char type;
150
151   /* Note that the parsing here is looser than what is specified in the
152    * help, but we may tighten it up in future so beware.
153    */
154   if (sscanf (str, "%"SCNu64"%c", &size, &type) == 2) {
155     switch (type) {
156     case 'k': case 'K': size *= 1024ULL; break;
157     case 'm': case 'M': size *= 1024ULL * 1024ULL; break;
158     case 'g': case 'G': size *= 1024ULL * 1024ULL * 1024ULL; break;
159     case 't': case 'T': size *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; break;
160     case 'p': case 'P': size *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; break;
161     case 'e': case 'E': size *= 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; break;
162     case 's': size *= 512; break;
163     default:
164       fprintf (stderr, _("could not parse size specification '%s'\n"), str);
165       return -1;
166     }
167   }
168   else if (sscanf (str, "%"SCNu64, &size) == 1)
169     size *= 1024ULL;
170   else {
171     fprintf (stderr, _("could not parse size specification '%s'\n"), str);
172     return -1;
173   }
174
175   /* XXX 32 bit file offsets, if anyone uses them?  GCC should give
176    * a warning here anyhow.
177    */
178   *size_rtn = size;
179
180   return 0;
181 }