'guestfish edit' commands and several bugfixes.
[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   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 (const char *path)
58 {
59   int r, len;
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   len = strlen (path) + 9;
71   buf = malloc (len);
72   if (buf == NULL) {
73     reply_with_perror ("malloc");
74     return -1;
75   }
76
77   snprintf (buf, len, "/sysroot%s", path);
78
79   r = command (NULL, &err, "rm", "-rf", buf, NULL);
80   free (buf);
81
82   /* rm -rf is never supposed to fail.  I/O errors perhaps? */
83   if (r == -1) {
84     reply_with_error ("rm -rf: %s: %s", path, err);
85     free (err);
86     return -1;
87   }
88
89   free (err);
90
91   return 0;
92 }
93
94 int
95 do_mkdir (const char *path)
96 {
97   int r;
98
99   NEED_ROOT (-1);
100   ABS_PATH (path, -1);
101
102   CHROOT_IN;
103   r = mkdir (path, 0777);
104   CHROOT_OUT;
105
106   if (r == -1) {
107     reply_with_perror ("mkdir: %s", path);
108     return -1;
109   }
110
111   return 0;
112 }
113
114 static int
115 recursive_mkdir (const char *path)
116 {
117   int loop = 0;
118   int r;
119   char *ppath, *p;
120
121  again:
122   r = mkdir (path, 0777);
123   if (r == -1) {
124     if (!loop && errno == ENOENT) {
125       loop = 1;                 /* Stops it looping forever. */
126
127       /* If we're at the root, and we failed, just give up. */
128       if (path[0] == '/' && path[1] == '\0') return -1;
129
130       /* Try to make the parent directory first. */
131       ppath = strdup (path);
132       if (ppath == NULL) return -1;
133
134       p = strrchr (ppath, '/');
135       if (p) *p = '\0';
136
137       r = recursive_mkdir (ppath);
138       free (ppath);
139
140       if (r == -1) return -1;
141
142       goto again;
143     } else        /* Failed for some other reason, so return error. */
144       return -1;
145   }
146   return 0;
147 }
148
149 int
150 do_mkdir_p (const char *path)
151 {
152   int r;
153
154   NEED_ROOT (-1);
155   ABS_PATH (path, -1);
156
157   CHROOT_IN;
158   r = recursive_mkdir (path);
159   CHROOT_OUT;
160
161   if (r == -1) {
162     reply_with_perror ("mkdir -p: %s", path);
163     return -1;
164   }
165
166   return 0;
167 }
168
169 int
170 do_is_dir (const char *path)
171 {
172   int r;
173   struct stat buf;
174
175   NEED_ROOT (-1);
176   ABS_PATH (path, -1);
177
178   CHROOT_IN;
179   r = lstat (path, &buf);
180   CHROOT_OUT;
181
182   if (r == -1) {
183     if (errno != ENOENT && errno != ENOTDIR) {
184       reply_with_perror ("stat: %s", path);
185       return -1;
186     }
187     else
188       return 0;                 /* Not a directory. */
189   }
190
191   return S_ISDIR (buf.st_mode);
192 }