added Vector_foreach*() functions
[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 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     Echdir(path);
160     return iterateFilesystem(path);
161   }
162   else
163     return handleFile(path, path) ? 0 : 1;
164 }
165
166 int main(int argc, char *argv[])
167 {
168   uint64_t              err_cnt = 0;
169   int                   i;
170   struct Arguments      args = {
171     .do_recurse         =  false,
172     .do_display_dot     =  false,
173     .do_display_dir     =  false,
174     .do_mapping         =  true,
175     .ctx                =  VC_NOCTX,
176     .is_legacy          =  false,
177     .do_set             =  false,
178     .do_unset           =  false,
179     .local_fs           =  false,
180     .set_mask           = 0,
181     .del_mask           = 0
182   };
183
184   global_args = &args;
185   while (1) {
186     int         c = getopt_long(argc, argv, CMDLINE_OPTIONS_SHORT,
187                                 CMDLINE_OPTIONS, 0);
188     if (c==-1) break;
189
190     switch (c) {
191       case CMD_HELP             :  showHelp(1, argv[0], 0);
192       case CMD_VERSION          :  showVersion();
193       case CMD_IMMU             :  args.set_mask |= VC_IATTR_IMMUTABLE; /*@fallthrough@*/
194       case CMD_IMMUX            :  args.set_mask |= VC_IATTR_IUNLINK; break;
195       case CMD_ADMIN            :  args.set_mask |= VC_IATTR_ADMIN;   break;
196       case CMD_WATCH            :  args.set_mask |= VC_IATTR_WATCH;   break;
197       case CMD_HIDE             :  args.set_mask |= VC_IATTR_HIDE;    break;
198       case CMD_BARRIER          :  args.set_mask |= VC_IATTR_BARRIER; break;
199       case CMD_UNSET_IMMU       :  args.del_mask |= VC_IATTR_IMMUTABLE; /*@fallthrough@*/
200       case CMD_UNSET_IMMUX      :  args.del_mask |= VC_IATTR_IUNLINK; break;
201       case CMD_UNSET_ADMIN      :  args.del_mask |= VC_IATTR_ADMIN;   break;
202       case CMD_UNSET_WATCH      :  args.del_mask |= VC_IATTR_WATCH;   break;
203       case CMD_UNSET_HIDE       :  args.del_mask |= VC_IATTR_HIDE;    break;
204       case CMD_UNSET_BARRIER    :  args.del_mask |= VC_IATTR_BARRIER; break;
205       case 'R'                  :  args.do_recurse     = true;  break;
206       case 'a'                  :  args.do_display_dot = true;  break;
207       case 'd'                  :  args.do_display_dir = true;  break;
208       case 'n'                  :  args.do_mapping     = false; break;
209       case 's'                  :  args.do_set         = true;  break;
210       case 'u'                  :  args.do_unset       = true;  break;
211       case 'c'                  :  args.ctx_str        = optarg; break;
212       case 'x'                  :  args.local_fs       = true;   break;
213       default           :
214         WRITE_MSG(2, "Try '");
215         WRITE_STR(2, argv[0]);
216         WRITE_MSG(2, " --help\" for more information.\n");
217         return EXIT_FAILURE;
218         break;
219     }
220   }
221
222   fixupParams(&args, argc);
223
224   if (optind==argc)
225     err_cnt  = processFile(".");
226   else for (i=optind; i<argc; ++i)
227     err_cnt += processFile(argv[i]);
228
229   return err_cnt>0 ? EXIT_FAILURE : EXIT_SUCCESS;
230 }