Add support for zerofree command.
[libguestfs.git] / daemon / file.c
1 /* libguestfs - the guestfsd daemon
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 <sys/stat.h>
27
28 #include "../src/guestfs_protocol.h"
29 #include "daemon.h"
30 #include "actions.h"
31
32 int
33 do_touch (const char *path)
34 {
35   int fd;
36   int r;
37
38   NEED_ROOT (-1);
39   ABS_PATH (path, -1);
40
41   CHROOT_IN;
42   fd = open (path, O_WRONLY | O_CREAT | O_NOCTTY, 0666);
43   CHROOT_OUT;
44
45   if (fd == -1) {
46     reply_with_perror ("open: %s", path);
47     return -1;
48   }
49
50 #ifdef HAVE_FUTIMENS
51   r = futimens (fd, NULL);
52 #else
53   r = futimes (fd, NULL);
54 #endif
55   if (r == -1) {
56     reply_with_perror ("futimens: %s", path);
57     close (fd);
58     return -1;
59   }
60
61   close (fd);
62   return 0;
63 }
64
65 char *
66 do_cat (const char *path)
67 {
68   int fd;
69   int alloc, size, r, max;
70   char *buf, *buf2;
71
72   NEED_ROOT (NULL);
73   ABS_PATH (path,NULL);
74
75   CHROOT_IN;
76   fd = open (path, O_RDONLY);
77   CHROOT_OUT;
78
79   if (fd == -1) {
80     reply_with_perror ("open: %s", path);
81     return NULL;
82   }
83
84   /* Read up to GUESTFS_MESSAGE_MAX - <overhead> bytes.  If it's
85    * larger than that, we need to return an error instead (for
86    * correctness).
87    */
88   max = GUESTFS_MESSAGE_MAX - 1000;
89   buf = NULL;
90   size = alloc = 0;
91
92   for (;;) {
93     if (size >= alloc) {
94       alloc += 8192;
95       if (alloc > max) {
96         reply_with_error ("cat: %s: file is too large for message buffer",
97                           path);
98         free (buf);
99         close (fd);
100         return NULL;
101       }
102       buf2 = realloc (buf, alloc);
103       if (buf2 == NULL) {
104         reply_with_perror ("realloc");
105         free (buf);
106         close (fd);
107         return NULL;
108       }
109       buf = buf2;
110     }
111
112     r = read (fd, buf + size, alloc - size);
113     if (r == -1) {
114       reply_with_perror ("read: %s", path);
115       free (buf);
116       close (fd);
117       return NULL;
118     }
119     if (r == 0) {
120       buf[size] = '\0';
121       break;
122     }
123     if (r > 0)
124       size += r;
125   }
126
127   if (close (fd) == -1) {
128     reply_with_perror ("close: %s", path);
129     free (buf);
130     return NULL;
131   }
132
133   return buf;                   /* caller will free */
134 }
135
136 char **
137 do_read_lines (const char *path)
138 {
139   char **r = NULL;
140   int size = 0, alloc = 0;
141   FILE *fp;
142   char *line = NULL;
143   size_t len = 0;
144   ssize_t n;
145
146   NEED_ROOT (NULL);
147   ABS_PATH (path, NULL);
148
149   CHROOT_IN;
150   fp = fopen (path, "r");
151   CHROOT_OUT;
152
153   if (!fp) {
154     reply_with_perror ("fopen: %s", path);
155     return NULL;
156   }
157
158   while ((n = getline (&line, &len, fp)) != -1) {
159     /* Remove either LF or CRLF. */
160     if (n >= 2 && line[n-2] == '\r' && line[n-1] == '\n')
161       line[n-2] = '\0';
162     else if (n >= 1 && line[n-1] == '\n')
163       line[n-1] = '\0';
164
165     if (add_string (&r, &size, &alloc, line) == -1) {
166       free (line);
167       fclose (fp);
168       return NULL;
169     }
170   }
171
172   free (line);
173
174   if (add_string (&r, &size, &alloc, NULL) == -1) {
175     fclose (fp);
176     return NULL;
177   }
178
179   if (fclose (fp) == EOF) {
180     reply_with_perror ("fclose: %s", path);
181     free_strings (r);
182     return NULL;
183   }
184
185   return r;
186 }
187
188 int
189 do_rm (const char *path)
190 {
191   int r;
192
193   NEED_ROOT (-1);
194   ABS_PATH (path, -1);
195
196   CHROOT_IN;
197   r = unlink (path);
198   CHROOT_OUT;
199
200   if (r == -1) {
201     reply_with_perror ("unlink: %s", path);
202     return -1;
203   }
204
205   return 0;
206 }
207
208 int
209 do_chmod (int mode, const char *path)
210 {
211   int r;
212
213   NEED_ROOT (-1);
214   ABS_PATH (path, -1);
215
216   CHROOT_IN;
217   r = chmod (path, mode);
218   CHROOT_OUT;
219
220   if (r == -1) {
221     reply_with_perror ("chmod: %s: 0%o", path, mode);
222     return -1;
223   }
224
225   return 0;
226 }
227
228 int
229 do_chown (int owner, int group, const char *path)
230 {
231   int r;
232
233   NEED_ROOT (-1);
234   ABS_PATH (path, -1);
235
236   CHROOT_IN;
237   r = chown (path, owner, group);
238   CHROOT_OUT;
239
240   if (r == -1) {
241     reply_with_perror ("chown: %s: %d.%d", path, owner, group);
242     return -1;
243   }
244
245   return 0;
246 }
247
248 int
249 do_exists (const char *path)
250 {
251   int r;
252
253   NEED_ROOT (-1);
254   ABS_PATH (path, -1);
255
256   CHROOT_IN;
257   r = access (path, F_OK);
258   CHROOT_OUT;
259
260   return r == 0;
261 }
262
263 int
264 do_is_file (const char *path)
265 {
266   int r;
267   struct stat buf;
268
269   NEED_ROOT (-1);
270   ABS_PATH (path, -1);
271
272   CHROOT_IN;
273   r = lstat (path, &buf);
274   CHROOT_OUT;
275
276   if (r == -1) {
277     if (errno != ENOENT && errno != ENOTDIR) {
278       reply_with_perror ("stat: %s", path);
279       return -1;
280     }
281     else
282       return 0;                 /* Not a file. */
283   }
284
285   return S_ISREG (buf.st_mode);
286 }
287
288 int
289 do_write_file (const char *path, const char *content, int size)
290 {
291   int fd;
292
293   NEED_ROOT (-1);
294   ABS_PATH (path, -1);
295
296   if (size == 0)
297     size = strlen (content);
298
299   CHROOT_IN;
300   fd = open (path, O_WRONLY | O_TRUNC | O_CREAT | O_NOCTTY, 0666);
301   CHROOT_OUT;
302
303   if (fd == -1) {
304     reply_with_perror ("open: %s", path);
305     return -1;
306   }
307
308   if (xwrite (fd, content, size) == -1) {
309     reply_with_perror ("write");
310     close (fd);
311     return -1;
312   }
313
314   if (close (fd) == -1) {
315     reply_with_perror ("close: %s", path);
316     return -1;
317   }
318
319   return 0;
320 }
321
322 /* This runs the 'file' command. */
323 char *
324 do_file (const char *path)
325 {
326   char *out, *err;
327   int r, len, freeit = 0;
328   char *buf;
329
330   NEED_ROOT_OR_IS_DEVICE (path, NULL);
331   ABS_PATH (path, NULL);
332
333   if (strncmp (path, "/dev/", 5) == 0)
334     buf = (char *) path;
335   else {
336     len = strlen (path) + 9;
337     buf = malloc (len);
338     if (!buf) {
339       reply_with_perror ("malloc");
340       return NULL;
341     }
342     snprintf (buf, len, "/sysroot%s", path);
343     freeit = 1;
344   }
345
346   /* file(1) manpage claims "file returns 0 on success, and non-zero on
347    * error", but this is evidently not true.  It always returns 0, in
348    * every scenario I can think up.  So check the target is readable
349    * first.
350    */
351   if (access (buf, R_OK) == -1) {
352     if (freeit) free (buf);
353     reply_with_perror ("access: %s", path);
354     return NULL;
355   }
356
357   r = command (&out, &err, "file", "-bsL", buf, NULL);
358   if (freeit) free (buf);
359
360   if (r == -1) {
361     free (out);
362     reply_with_error ("file: %s: %s", path, err);
363     free (err);
364     return NULL;
365   }
366   free (err);
367
368   /* We need to remove the trailing \n from output of file(1). */
369   len = strlen (out);
370   if (out[len-1] == '\n')
371     out[len-1] = '\0';
372
373   return out;                   /* caller frees */
374 }