We can't remove dev/* if we're not in the guest's root.
[util-vserver.git] / src / fstool.c
1 // $Id$    --*- c -*--
2
3 // Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 //  
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; version 2 of the License.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include "fstool.h"
24 #include "util.h"
25
26 #include <lib/vserver.h>
27
28 #include <getopt.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <dirent.h>
33 #include <errno.h>
34 #include <fcntl.h>
35
36 #define ENSC_WRAPPERS_DIRENT    1
37 #define ENSC_WRAPPERS_FCNTL     1
38 #define ENSC_WRAPPERS_UNISTD    1
39 #include <wrappers.h>
40
41 struct Arguments const *                global_args = 0;
42
43 int wrapper_exit_code = 1;
44
45 inline static bool
46 isSpecialDir(char const *d)
47 {
48   return ( (d[0]=='.' && !global_args->do_display_dot) ||
49            (d[0]=='.' && (d[1]=='\0' || (d[1]=='.' && d[2]=='\0'))) );
50 }
51
52 #define CONCAT_PATHS(LHS, LHS_LEN, RHS)                                 \
53   size_t                l_rhs = strlen(RHS);                            \
54   char                  new_path[(LHS_LEN) + l_rhs + sizeof("/")];      \
55   memcpy(new_path, LHS, (LHS_LEN));                                     \
56   memcpy(new_path+(LHS_LEN), "/", 1);                                   \
57   memcpy(new_path+(LHS_LEN)+1, RHS, l_rhs);                             \
58   new_path[(LHS_LEN)+1+l_rhs] = '\0';
59
60 static uint64_t
61 iterateFilesystem(char const *path)
62 {
63   bool                  do_again = false;
64   size_t                path_len = strlen(path);
65   uint64_t              err = 0;
66   struct stat           cur_st;
67   DIR *                 dir = opendir(".");
68
69   if (dir==0) {
70     perror("opendir()");
71     return 1;
72   }
73
74   // show current directory entry first
75   if (lstat(".", &cur_st)==-1) perror("lstat()");
76   else err += handleFile(".", path) ? 0 : 1;
77
78   // strip trailing '/'
79   while (path_len>0 && path[path_len-1]=='/') --path_len;
80
81   // process regular files before directories
82   for (;;) {
83     struct dirent       *ent = Ereaddir(dir);
84     struct stat         st;
85     
86     if (ent==0) break;
87     if (isSpecialDir(ent->d_name)) continue;
88
89     if (lstat(ent->d_name, &st)==-1) {
90       perror("lstat()");
91       ++err;
92       continue;
93     }
94
95     if (S_ISDIR(st.st_mode) && global_args->do_recurse) {
96       do_again = true;
97       continue;
98     }
99     
100     {
101       CONCAT_PATHS(path, path_len, ent->d_name);
102       err += handleFile(ent->d_name, new_path) ? 0 : 1;
103     }
104   }
105
106   if (do_again) {
107     int         cur_dir = Eopen(".", O_RDONLY, 0);
108     rewinddir(dir);
109
110     for (;;) {
111       struct dirent     *ent = Ereaddir(dir);
112       struct stat       st;
113     
114       if (ent==0) break;
115       if (isSpecialDir(ent->d_name)) continue;
116       
117       if (lstat(ent->d_name, &st)==-1) {
118         perror("lstat()");
119         ++err;
120         continue;
121       }
122
123       if (!S_ISDIR(st.st_mode) ||
124           (global_args->local_fs && st.st_dev!=cur_st.st_dev))
125         continue;
126
127       if (safeChdir(ent->d_name, &st)==-1) {
128         perror("chdir()");
129         ++err;
130         continue;
131       }
132       
133       {
134         CONCAT_PATHS(path, path_len, ent->d_name);
135         err += iterateFilesystem(new_path);
136       }
137       Efchdir(cur_dir);
138     }
139     Eclose(cur_dir);
140   }
141
142   Eclosedir(dir);
143
144   return err;
145 }
146 #undef CONCAT_PATHS
147
148 static uint64_t
149 processFile(char const *path)
150 {
151   struct stat           st;
152
153   if (lstat(path, &st)==-1) {
154     perror("lstat()");
155     return 1;
156   }
157
158   if (S_ISDIR(st.st_mode) && !global_args->do_display_dir) {
159     int         cur_dir = Eopen(".", O_RDONLY, 0);
160     uint64_t    ret;
161     Echdir(path);
162     ret = iterateFilesystem(path);
163     Efchdir(cur_dir);
164     Eclose(cur_dir);
165     return ret;
166   }
167   else
168     return handleFile(path, path) ? 0 : 1;
169 }
170
171 int main(int argc, char *argv[])
172 {
173   uint64_t              err_cnt = 0;
174   int                   i;
175   struct Arguments      args = {
176     .do_recurse         =  false,
177     .do_display_dot     =  false,
178     .do_display_dir     =  false,
179     .do_mapping         =  true,
180     .ctx                =  VC_NOCTX,
181     .is_legacy          =  false,
182     .do_set             =  false,
183     .do_unset           =  false,
184     .local_fs           =  false,
185     .set_mask           = 0,
186     .del_mask           = 0
187   };
188
189   global_args = &args;
190   while (1) {
191     int         c = getopt_long(argc, argv, CMDLINE_OPTIONS_SHORT,
192                                 CMDLINE_OPTIONS, 0);
193     if (c==-1) break;
194
195     switch (c) {
196       case CMD_HELP             :  showHelp(1, argv[0], 0);
197       case CMD_VERSION          :  showVersion();
198       case CMD_IMMU             :  args.set_mask |= VC_IATTR_IMMUTABLE; /*@fallthrough@*/
199       case CMD_IMMUX            :  args.set_mask |= VC_IATTR_IUNLINK; break;
200       case CMD_ADMIN            :  args.set_mask |= VC_IATTR_ADMIN;   break;
201       case CMD_WATCH            :  args.set_mask |= VC_IATTR_WATCH;   break;
202       case CMD_HIDE             :  args.set_mask |= VC_IATTR_HIDE;    break;
203       case CMD_BARRIER          :  args.set_mask |= VC_IATTR_BARRIER; break;
204       case CMD_UNSET_IMMU       :  args.del_mask |= VC_IATTR_IMMUTABLE; /*@fallthrough@*/
205       case CMD_UNSET_IMMUX      :  args.del_mask |= VC_IATTR_IUNLINK; break;
206       case CMD_UNSET_ADMIN      :  args.del_mask |= VC_IATTR_ADMIN;   break;
207       case CMD_UNSET_WATCH      :  args.del_mask |= VC_IATTR_WATCH;   break;
208       case CMD_UNSET_HIDE       :  args.del_mask |= VC_IATTR_HIDE;    break;
209       case CMD_UNSET_BARRIER    :  args.del_mask |= VC_IATTR_BARRIER; break;
210       case 'R'                  :  args.do_recurse     = true;  break;
211       case 'a'                  :  args.do_display_dot = true;  break;
212       case 'd'                  :  args.do_display_dir = true;  break;
213       case 'n'                  :  args.do_mapping     = false; break;
214       case 's'                  :  args.do_set         = true;  break;
215       case 'u'                  :  args.do_unset       = true;  break;
216       case 'c'                  :  args.ctx_str        = optarg; break;
217       case 'x'                  :  args.local_fs       = true;   break;
218       default           :
219         WRITE_MSG(2, "Try '");
220         WRITE_STR(2, argv[0]);
221         WRITE_MSG(2, " --help\" for more information.\n");
222         return EXIT_FAILURE;
223         break;
224     }
225   }
226
227   fixupParams(&args, argc);
228
229   if (optind==argc)
230     err_cnt  = processFile(".");
231   else for (i=optind; i<argc; ++i)
232     err_cnt += processFile(argv[i]);
233
234   return err_cnt>0 ? EXIT_FAILURE : EXIT_SUCCESS;
235 }