minor/medium cleanups
[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]=='.' && (d[1]=='\0' || (d[1]=='.' && d[2]=='\0')));
66 }
67
68 #define CONCAT_PATHS(LHS, LHS_LEN, RHS)                                 \
69   size_t                l_rhs = strlen(RHS);                            \
70   char                  new_path[(LHS_LEN) + l_rhs + sizeof("/")];      \
71   memcpy(new_path, LHS, (LHS_LEN));                                     \
72   memcpy(new_path+(LHS_LEN), "/", 1);                                   \
73   memcpy(new_path+(LHS_LEN)+1, RHS, l_rhs);                             \
74   new_path[(LHS_LEN)+1+l_rhs] = '\0';
75
76 static uint64_t
77 iterateFilesystem(char const *path)
78 {
79   bool                  do_again = false;
80   size_t                path_len = strlen(path);
81   DIR *                 dir = Eopendir(".");
82   uint64_t              err = 0;
83
84   {
85     struct stat         st;
86     if (lstat(".", &st)==-1) perror("lstat()");
87     else err += handleFile(".", path, &st) ? 0 : 1;
88   }
89
90   for (;;) {
91     struct dirent       *ent = Ereaddir(dir);
92     struct stat         st;
93     
94     if (ent==0) break;
95     if (isSpecialDir(ent->d_name)) continue;
96
97     if (lstat(ent->d_name, &st)==-1) {
98       perror("lstat()");
99       ++err;
100       continue;
101     }
102
103     if (S_ISDIR(st.st_mode) && global_args->do_recurse) {
104       do_again = true;
105       continue;
106     }
107     
108     {
109       CONCAT_PATHS(path, path_len, ent->d_name);
110       err += handleFile(ent->d_name, new_path, &st) ? 0 : 1;
111     }
112   }
113
114   if (do_again) {
115     int         cur_dir = Eopen(".", O_RDONLY, 0);
116     rewinddir(dir);
117
118     for (;;) {
119       struct dirent     *ent = Ereaddir(dir);
120       struct stat       st;
121     
122       if (ent==0) break;
123       if (isSpecialDir(ent->d_name)) continue;
124       
125       if (lstat(ent->d_name, &st)==-1) {
126         perror("lstat()");
127         ++err;
128         continue;
129       }
130
131       if (!S_ISDIR(st.st_mode)) continue;
132       safeChdir(ent->d_name, &st);
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     return iterateFilesystem(path);
160   else
161     return handleFile(path, path, &st);
162 }
163
164 int main(int argc, char *argv[])
165 {
166   uint64_t              err_cnt = 0;
167   int                   i;
168   struct Arguments      args = {
169     .do_recurse         =  false,
170     .do_display_dot     =  false,
171     .do_display_dir     =  false,
172     .do_mapping         =  true,
173     .immutable          =  false,
174     .immulink           =  false,
175     .ctx                =  VC_NOCTX,
176     .is_legacy          =  false,
177     .do_set             =  false,
178     .do_unset           =  false,
179   };
180
181   global_args = &args;
182   while (1) {
183     int         c = getopt_long(argc, argv, CMDLINE_OPTIONS_SHORT,
184                                 CMDLINE_OPTIONS, 0);
185     if (c==-1) break;
186
187     switch (c) {
188       case CMD_HELP             :  showHelp(1, argv[0], 0);
189       case CMD_VERSION          :  showVersion();
190       case CMD_IMMUTABLE        :  args.immutable = true; break;
191       case CMD_IMMULINK         :  args.immulink  = true; break;
192       case CMD_LEGACY           :  args.is_legacy      = true;  break;
193       case 'R'                  :  args.do_recurse     = true;  break;
194       case 'a'                  :  args.do_display_dot = true;  break;
195       case 'd'                  :  args.do_display_dir = true;  break;
196       case 'n'                  :  args.do_mapping     = false; break;
197       case 's'                  :  args.do_set         = true;  break;
198       case 'u'                  :  args.do_unset       = true;  break;
199       case 'c'                  :  args.ctx_str        = optarg; break;
200       default           :
201         WRITE_MSG(2, "Try '");
202         WRITE_STR(2, argv[0]);
203         WRITE_MSG(2, " --help\" for more information.\n");
204         return EXIT_FAILURE;
205         break;
206     }
207   }
208
209   fixupParams(&args, argc);
210
211   if (optind==argc)
212     err_cnt  = processFile(".");
213   else for (i=optind; i<argc; ++i)
214     err_cnt += processFile(argv[i]);
215
216   return err_cnt>0 ? EXIT_FAILURE : EXIT_SUCCESS;
217 }