added Vector_zeroEnd() function
[util-vserver.git] / util-vserver / 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 bool
46 checkForRace(int fd, char const * name, struct stat const *exp_st)
47 {
48   struct stat           st;
49   
50   if (fstat(fd, &st)==-1) {
51     perror("fstat()");
52     return false;
53  }
54
55   if (st.st_dev  != exp_st->st_dev ||
56       st.st_ino  != exp_st->st_ino ||
57       st.st_mode != exp_st->st_mode) {
58     close(fd);
59     WRITE_MSG(2, "race while visiting '");
60     WRITE_STR(2, name);
61     WRITE_MSG(2, "'\n");
62     exit(2);
63   }
64
65   return true;
66 }
67
68 inline static bool
69 isSpecialDir(char const *d)
70 {
71   return ( (d[0]=='.' && !global_args->do_display_dot) ||
72            (d[0]=='.' && (d[1]=='\0' || (d[1]=='.' && d[2]=='\0'))) );
73 }
74
75 #define CONCAT_PATHS(LHS, LHS_LEN, RHS)                                 \
76   size_t                l_rhs = strlen(RHS);                            \
77   char                  new_path[(LHS_LEN) + l_rhs + sizeof("/")];      \
78   memcpy(new_path, LHS, (LHS_LEN));                                     \
79   memcpy(new_path+(LHS_LEN), "/", 1);                                   \
80   memcpy(new_path+(LHS_LEN)+1, RHS, l_rhs);                             \
81   new_path[(LHS_LEN)+1+l_rhs] = '\0';
82
83 static uint64_t
84 iterateFilesystem(char const *path)
85 {
86   bool                  do_again = false;
87   size_t                path_len = strlen(path);
88   uint64_t              err = 0;
89   struct stat           cur_st;
90   DIR *                 dir = opendir(".");
91
92   if (dir==0) {
93     perror("opendir()");
94     return 1;
95   }
96
97   // show current directory entry first
98   if (lstat(".", &cur_st)==-1) perror("lstat()");
99   else err += handleFile(".", path) ? 0 : 1;
100
101   // strip trailing '/'
102   while (path_len>0 && path[path_len-1]=='/') --path_len;
103
104   // process regular files before directories
105   for (;;) {
106     struct dirent       *ent = Ereaddir(dir);
107     struct stat         st;
108     
109     if (ent==0) break;
110     if (isSpecialDir(ent->d_name)) continue;
111
112     if (lstat(ent->d_name, &st)==-1) {
113       perror("lstat()");
114       ++err;
115       continue;
116     }
117
118     if (S_ISDIR(st.st_mode) && global_args->do_recurse) {
119       do_again = true;
120       continue;
121     }
122     
123     {
124       CONCAT_PATHS(path, path_len, ent->d_name);
125       err += handleFile(ent->d_name, new_path) ? 0 : 1;
126     }
127   }
128
129   if (do_again) {
130     int         cur_dir = Eopen(".", O_RDONLY, 0);
131     rewinddir(dir);
132
133     for (;;) {
134       struct dirent     *ent = Ereaddir(dir);
135       struct stat       st;
136     
137       if (ent==0) break;
138       if (isSpecialDir(ent->d_name)) continue;
139       
140       if (lstat(ent->d_name, &st)==-1) {
141         perror("lstat()");
142         ++err;
143         continue;
144       }
145
146       if (!S_ISDIR(st.st_mode) ||
147           (global_args->local_fs && st.st_dev!=cur_st.st_dev))
148         continue;
149
150       if (safeChdir(ent->d_name, &st)==-1) {
151         perror("chdir()");
152         ++err;
153         continue;
154       }
155       
156       {
157         CONCAT_PATHS(path, path_len, ent->d_name);
158         err += iterateFilesystem(new_path);
159       }
160       Efchdir(cur_dir);
161     }
162     Eclose(cur_dir);
163   }
164
165   Eclosedir(dir);
166
167   return err;
168 }
169 #undef CONCAT_PATHS
170
171 static uint64_t
172 processFile(char const *path)
173 {
174   struct stat           st;
175
176   if (lstat(path, &st)==-1) {
177     perror("lstat()");
178     return 1;
179   }
180
181   if (S_ISDIR(st.st_mode) && !global_args->do_display_dir) {
182     Echdir(path);
183     return iterateFilesystem(path);
184   }
185   else
186     return handleFile(path, path) ? 0 : 1;
187 }
188
189 int main(int argc, char *argv[])
190 {
191   uint64_t              err_cnt = 0;
192   int                   i;
193   struct Arguments      args = {
194     .do_recurse         =  false,
195     .do_display_dot     =  false,
196     .do_display_dir     =  false,
197     .do_mapping         =  true,
198     .ctx                =  VC_NOCTX,
199     .is_legacy          =  false,
200     .do_set             =  false,
201     .do_unset           =  false,
202     .local_fs           =  false,
203     .set_mask           = 0,
204     .del_mask           = 0
205   };
206
207   global_args = &args;
208   while (1) {
209     int         c = getopt_long(argc, argv, CMDLINE_OPTIONS_SHORT,
210                                 CMDLINE_OPTIONS, 0);
211     if (c==-1) break;
212
213     switch (c) {
214       case CMD_HELP             :  showHelp(1, argv[0], 0);
215       case CMD_VERSION          :  showVersion();
216       case CMD_IMMU             :  args.set_mask |= VC_IATTR_IMMUTABLE; /*@fallthrough@*/
217       case CMD_IMMUX            :  args.set_mask |= VC_IATTR_IUNLINK; break;
218       case CMD_ADMIN            :  args.set_mask |= VC_IATTR_ADMIN;   break;
219       case CMD_WATCH            :  args.set_mask |= VC_IATTR_WATCH;   break;
220       case CMD_HIDE             :  args.set_mask |= VC_IATTR_HIDE;    break;
221       case CMD_BARRIER          :  args.set_mask |= VC_IATTR_BARRIER; break;
222       case CMD_UNSET_IMMU       :  args.del_mask |= VC_IATTR_IMMUTABLE; /*@fallthrough@*/
223       case CMD_UNSET_IMMUX      :  args.del_mask |= VC_IATTR_IUNLINK; break;
224       case CMD_UNSET_ADMIN      :  args.del_mask |= VC_IATTR_ADMIN;   break;
225       case CMD_UNSET_WATCH      :  args.del_mask |= VC_IATTR_WATCH;   break;
226       case CMD_UNSET_HIDE       :  args.del_mask |= VC_IATTR_HIDE;    break;
227       case CMD_UNSET_BARRIER    :  args.del_mask |= VC_IATTR_BARRIER; break;
228       case 'R'                  :  args.do_recurse     = true;  break;
229       case 'a'                  :  args.do_display_dot = true;  break;
230       case 'd'                  :  args.do_display_dir = true;  break;
231       case 'n'                  :  args.do_mapping     = false; break;
232       case 's'                  :  args.do_set         = true;  break;
233       case 'u'                  :  args.do_unset       = true;  break;
234       case 'c'                  :  args.ctx_str        = optarg; break;
235       case 'x'                  :  args.local_fs       = true;   break;
236       default           :
237         WRITE_MSG(2, "Try '");
238         WRITE_STR(2, argv[0]);
239         WRITE_MSG(2, " --help\" for more information.\n");
240         return EXIT_FAILURE;
241         break;
242     }
243   }
244
245   fixupParams(&args, argc);
246
247   if (optind==argc)
248     err_cnt  = processFile(".");
249   else for (i=optind; i<argc; ++i)
250     err_cnt += processFile(argv[i]);
251
252   return err_cnt>0 ? EXIT_FAILURE : EXIT_SUCCESS;
253 }