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