New commands: rm rmdir rm-rf mkdir mkdir-p chmod chown
[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 | O_NONBLOCK, 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 }