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