tests: Fix read_file test.
[libguestfs.git] / daemon / dir.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 <sys/stat.h>
26 #include <sys/types.h>
27
28 #include "../src/guestfs_protocol.h"
29 #include "daemon.h"
30 #include "actions.h"
31
32 int
33 do_rmdir (char *path)
34 {
35   int r;
36
37   NEED_ROOT (-1);
38   ABS_PATH (path, -1);
39
40   CHROOT_IN;
41   r = rmdir (path);
42   CHROOT_OUT;
43
44   if (r == -1) {
45     reply_with_perror ("rmdir: %s", path);
46     return -1;
47   }
48
49   return 0;
50 }
51
52 /* This implementation is quick and dirty, and allows people to try
53  * to remove parts of the initramfs (eg. "rm -r /..") but if people
54  * do stupid stuff, who are we to try to stop them?
55  */
56 int
57 do_rm_rf (char *path)
58 {
59   int r;
60   char *buf, *err;
61
62   NEED_ROOT (-1);
63   ABS_PATH (path, -1);
64
65   if (strcmp (path, "/") == 0) {
66     reply_with_error ("rm -rf: cannot remove root directory");
67     return -1;
68   }
69
70   buf = sysroot_path (path);
71   if (buf == NULL) {
72     reply_with_perror ("malloc");
73     return -1;
74   }
75
76   r = command (NULL, &err, "rm", "-rf", buf, NULL);
77   free (buf);
78
79   /* rm -rf is never supposed to fail.  I/O errors perhaps? */
80   if (r == -1) {
81     reply_with_error ("rm -rf: %s: %s", path, err);
82     free (err);
83     return -1;
84   }
85
86   free (err);
87
88   return 0;
89 }
90
91 int
92 do_mkdir (char *path)
93 {
94   int r;
95
96   NEED_ROOT (-1);
97   ABS_PATH (path, -1);
98
99   CHROOT_IN;
100   r = mkdir (path, 0777);
101   CHROOT_OUT;
102
103   if (r == -1) {
104     reply_with_perror ("mkdir: %s", path);
105     return -1;
106   }
107
108   return 0;
109 }
110
111 static int
112 recursive_mkdir (const char *path)
113 {
114   int loop = 0;
115   int r;
116   char *ppath, *p;
117   struct stat buf;
118
119  again:
120   r = mkdir (path, 0777);
121   if (r == -1) {
122     if (errno == EEXIST) {      /* Something exists here, might not be a dir. */
123       r = lstat (path, &buf);
124       if (r == -1) return -1;
125       if (!S_ISDIR (buf.st_mode)) {
126         errno = ENOTDIR;
127         return -1;
128       }
129       return 0;                 /* OK - directory exists here already. */
130     }
131
132     if (!loop && errno == ENOENT) {
133       loop = 1;                 /* Stops it looping forever. */
134
135       /* If we're at the root, and we failed, just give up. */
136       if (path[0] == '/' && path[1] == '\0') return -1;
137
138       /* Try to make the parent directory first. */
139       ppath = strdup (path);
140       if (ppath == NULL) return -1;
141
142       p = strrchr (ppath, '/');
143       if (p) *p = '\0';
144
145       r = recursive_mkdir (ppath);
146       free (ppath);
147
148       if (r == -1) return -1;
149
150       goto again;
151     } else        /* Failed for some other reason, so return error. */
152       return -1;
153   }
154   return 0;
155 }
156
157 int
158 do_mkdir_p (char *path)
159 {
160   int r;
161
162   NEED_ROOT (-1);
163   ABS_PATH (path, -1);
164
165   CHROOT_IN;
166   r = recursive_mkdir (path);
167   CHROOT_OUT;
168
169   if (r == -1) {
170     reply_with_perror ("mkdir -p: %s", path);
171     return -1;
172   }
173
174   return 0;
175 }
176
177 int
178 do_is_dir (char *path)
179 {
180   int r;
181   struct stat buf;
182
183   NEED_ROOT (-1);
184   ABS_PATH (path, -1);
185
186   CHROOT_IN;
187   r = lstat (path, &buf);
188   CHROOT_OUT;
189
190   if (r == -1) {
191     if (errno != ENOENT && errno != ENOTDIR) {
192       reply_with_perror ("stat: %s", path);
193       return -1;
194     }
195     else
196       return 0;                 /* Not a directory. */
197   }
198
199   return S_ISDIR (buf.st_mode);
200 }
201
202 char *
203 do_mkdtemp (char *template)
204 {
205   char *r;
206
207   NEED_ROOT (NULL);
208   ABS_PATH (template, NULL);
209
210   CHROOT_IN;
211   r = mkdtemp (template);
212   CHROOT_OUT;
213
214   if (r == NULL) {
215     reply_with_perror ("mkdtemp: %s", template);
216     return NULL;
217   }
218
219   /* The caller will free template AND try to free the return value,
220    * so we must make a copy here.
221    */
222   if (r == template) {
223     r = strdup (template);
224     if (r == NULL) {
225       reply_with_perror ("strdup");
226       return NULL;
227     }
228   }
229   return r;
230 }