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