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