]>
Commit | Line | Data |
---|---|---|
3839e657 TT |
1 | /* |
2 | * util.c --- utilities for the debugfs program | |
3 | * | |
4 | * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be | |
5 | * redistributed under the terms of the GNU Public License. | |
6 | * | |
7 | */ | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <unistd.h> | |
11 | #include <stdlib.h> | |
12 | #include <ctype.h> | |
13 | #include <string.h> | |
1e3472c5 | 14 | #include <time.h> |
2c4a5406 | 15 | #include <signal.h> |
88494bb6 TT |
16 | #ifdef HAVE_GETOPT_H |
17 | #include <getopt.h> | |
18 | #else | |
19 | extern int optind; | |
20 | extern char *optarg; | |
21 | #endif | |
22 | #ifdef HAVE_OPTRESET | |
23 | extern int optreset; /* defined by BSD, but not others */ | |
24 | #endif | |
3839e657 TT |
25 | |
26 | #include "debugfs.h" | |
27 | ||
88494bb6 TT |
28 | /* |
29 | * This function resets the libc getopt() function, which keeps | |
30 | * internal state. Bad design! Stupid libc API designers! No | |
31 | * biscuit! | |
32 | * | |
33 | * BSD-derived getopt() functions require that optind be reset to 1 in | |
34 | * order to reset getopt() state. This used to be generally accepted | |
35 | * way of resetting getopt(). However, glibc's getopt() | |
36 | * has additional getopt() state beyond optind, and requires that | |
37 | * optind be set zero to reset its state. So the unfortunate state of | |
38 | * affairs is that BSD-derived versions of getopt() misbehave if | |
39 | * optind is set to 0 in order to reset getopt(), and glibc's getopt() | |
9c07dc00 | 40 | * will core dump if optind is set 1 in order to reset getopt(). |
88494bb6 TT |
41 | * |
42 | * More modern versions of BSD require that optreset be set to 1 in | |
43 | * order to reset getopt(). Sigh. Standards, anyone? | |
44 | * | |
45 | * We hide the hair here. | |
46 | */ | |
47 | void reset_getopt(void) | |
48 | { | |
9c07dc00 | 49 | #if defined(__GLIBC__) || defined(__linux__) |
88494bb6 TT |
50 | optind = 0; |
51 | #else | |
52 | optind = 1; | |
53 | #endif | |
54 | #ifdef HAVE_OPTRESET | |
55 | optreset = 1; /* Makes BSD getopt happy */ | |
56 | #endif | |
ec7fdb8f TT |
57 | } |
58 | ||
2b5ddd75 | 59 | static const char *pager_search_list[] = { "pager", "more", "less", 0 }; |
ec7fdb8f TT |
60 | static const char *pager_dir_list[] = { "/usr/bin", "/bin", 0 }; |
61 | ||
62 | static const char *find_pager(char *buf) | |
63 | { | |
64 | const char **i, **j; | |
65 | ||
66 | for (i = pager_search_list; *i; i++) { | |
67 | for (j = pager_dir_list; *j; j++) { | |
68 | sprintf(buf, "%s/%s", *j, *i); | |
69 | if (access(buf, X_OK) == 0) | |
70 | return(buf); | |
71 | } | |
72 | } | |
73 | return 0; | |
74 | } | |
88494bb6 | 75 | |
3839e657 TT |
76 | FILE *open_pager(void) |
77 | { | |
ec7fdb8f | 78 | FILE *outfile = 0; |
0f72ab64 | 79 | const char *pager = getenv("DEBUGFS_PAGER"); |
ec7fdb8f | 80 | char buf[80]; |
3839e657 | 81 | |
2c4a5406 | 82 | signal(SIGPIPE, SIG_IGN); |
2b696a98 | 83 | if (!pager) |
0f72ab64 | 84 | pager = getenv("PAGER"); |
2b696a98 | 85 | if (!pager) |
ec7fdb8f | 86 | pager = find_pager(buf); |
2b696a98 TT |
87 | if (!pager || |
88 | (strcmp(pager, "__none__") == 0) || | |
89 | ((outfile = popen(pager, "w")) == 0)) | |
90 | return stdout; | |
91 | return outfile; | |
3839e657 TT |
92 | } |
93 | ||
94 | void close_pager(FILE *stream) | |
95 | { | |
571fc5a8 | 96 | if (stream && stream != stdout) pclose(stream); |
3839e657 TT |
97 | } |
98 | ||
99 | /* | |
100 | * This routine is used whenever a command needs to turn a string into | |
101 | * an inode. | |
102 | */ | |
b044c2e0 | 103 | ext2_ino_t string_to_inode(char *str) |
3839e657 | 104 | { |
b044c2e0 TT |
105 | ext2_ino_t ino; |
106 | int len = strlen(str); | |
107 | char *end; | |
108 | int retval; | |
3839e657 TT |
109 | |
110 | /* | |
111 | * If the string is of the form <ino>, then treat it as an | |
112 | * inode number. | |
113 | */ | |
114 | if ((len > 2) && (str[0] == '<') && (str[len-1] == '>')) { | |
9131a759 TT |
115 | ino = strtoul(str+1, &end, 0); |
116 | if (*end=='>') | |
117 | return ino; | |
3839e657 TT |
118 | } |
119 | ||
1e3472c5 | 120 | retval = ext2fs_namei(current_fs, root, cwd, str, &ino); |
3839e657 TT |
121 | if (retval) { |
122 | com_err(str, retval, ""); | |
123 | return 0; | |
124 | } | |
125 | return ino; | |
126 | } | |
127 | ||
128 | /* | |
129 | * This routine returns 1 if the filesystem is not open, and prints an | |
130 | * error message to that effect. | |
131 | */ | |
132 | int check_fs_open(char *name) | |
133 | { | |
1e3472c5 | 134 | if (!current_fs) { |
3839e657 TT |
135 | com_err(name, 0, "Filesystem not open"); |
136 | return 1; | |
137 | } | |
138 | return 0; | |
139 | } | |
140 | ||
141 | /* | |
142 | * This routine returns 1 if a filesystem is open, and prints an | |
143 | * error message to that effect. | |
144 | */ | |
145 | int check_fs_not_open(char *name) | |
146 | { | |
1e3472c5 | 147 | if (current_fs) { |
3839e657 TT |
148 | com_err(name, 0, |
149 | "Filesystem %s is still open. Close it first.\n", | |
1e3472c5 | 150 | current_fs->device_name); |
3839e657 TT |
151 | return 1; |
152 | } | |
153 | return 0; | |
154 | } | |
155 | ||
1e3472c5 TT |
156 | /* |
157 | * This routine returns 1 if a filesystem is not opened read/write, | |
158 | * and prints an error message to that effect. | |
159 | */ | |
160 | int check_fs_read_write(char *name) | |
161 | { | |
162 | if (!(current_fs->flags & EXT2_FLAG_RW)) { | |
163 | com_err(name, 0, "Filesystem opened read/only"); | |
164 | return 1; | |
165 | } | |
166 | return 0; | |
167 | } | |
168 | ||
d61f6176 TT |
169 | /* |
170 | * This routine returns 1 if a filesystem is doesn't have its inode | |
171 | * and block bitmaps loaded, and prints an error message to that | |
172 | * effect. | |
173 | */ | |
174 | int check_fs_bitmaps(char *name) | |
175 | { | |
176 | if (!current_fs->block_map || !current_fs->inode_map) { | |
177 | com_err(name, 0, "Filesystem bitmaps not loaded"); | |
178 | return 1; | |
179 | } | |
180 | return 0; | |
181 | } | |
182 | ||
1e3472c5 TT |
183 | /* |
184 | * This function takes a __u32 time value and converts it to a string, | |
185 | * using ctime | |
186 | */ | |
187 | char *time_to_string(__u32 cl) | |
188 | { | |
acb79d9d TT |
189 | static int do_gmt = -1; |
190 | time_t t = (time_t) cl; | |
191 | char * tz; | |
1e3472c5 | 192 | |
8ff1a860 TT |
193 | if (do_gmt == -1) { |
194 | /* The diet libc doesn't respect the TZ environemnt variable */ | |
acb79d9d TT |
195 | tz = getenv("TZ"); |
196 | if (!tz) | |
197 | tz = ""; | |
198 | do_gmt = !strcmp(tz, "GMT"); | |
8ff1a860 TT |
199 | } |
200 | ||
201 | return asctime((do_gmt) ? gmtime(&t) : localtime(&t)); | |
1e3472c5 TT |
202 | } |
203 | ||
4efae606 TT |
204 | /* |
205 | * Parse a string as a time. Return ((time_t)-1) if the string | |
206 | * doesn't appear to be a sane time. | |
207 | */ | |
208 | extern time_t string_to_time(const char *arg) | |
209 | { | |
210 | struct tm ts; | |
a4ea6b98 | 211 | time_t ret; |
4efae606 TT |
212 | char *tmp; |
213 | ||
214 | if (strcmp(arg, "now") == 0) { | |
215 | return time(0); | |
216 | } | |
217 | memset(&ts, 0, sizeof(ts)); | |
218 | #ifdef HAVE_STRPTIME | |
219 | strptime(arg, "%Y%m%d%H%M%S", &ts); | |
220 | #else | |
221 | sscanf(arg, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon, | |
222 | &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec); | |
223 | ts.tm_year -= 1900; | |
224 | ts.tm_mon -= 1; | |
225 | if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 || | |
226 | ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 || | |
227 | ts.tm_min > 59 || ts.tm_sec > 61) | |
228 | ts.tm_mday = 0; | |
229 | #endif | |
a4ea6b98 TT |
230 | ret = mktime(&ts); |
231 | if (ts.tm_mday == 0 || ret == ((time_t) -1)) { | |
4efae606 | 232 | /* Try it as an integer... */ |
4efae606 TT |
233 | ret = strtoul(arg, &tmp, 0); |
234 | if (*tmp) | |
235 | return ((time_t) -1); | |
236 | } | |
a4ea6b98 | 237 | return ret; |
4efae606 TT |
238 | } |
239 | ||
e1018eea TT |
240 | /* |
241 | * This function will convert a string to an unsigned long, printing | |
242 | * an error message if it fails, and returning success or failure in err. | |
243 | */ | |
244 | unsigned long parse_ulong(const char *str, const char *cmd, | |
245 | const char *descr, int *err) | |
246 | { | |
247 | char *tmp; | |
248 | unsigned long ret; | |
249 | ||
250 | ret = strtoul(str, &tmp, 0); | |
251 | if (*tmp == 0) { | |
e5b3b279 | 252 | if (err) |
e1018eea TT |
253 | *err = 0; |
254 | return ret; | |
255 | } | |
256 | com_err(cmd, 0, "Bad %s - %s", descr, str); | |
e5b3b279 | 257 | if (err) |
e1018eea TT |
258 | *err = 1; |
259 | else | |
260 | exit(1); | |
261 | return 0; | |
262 | } | |
263 | ||
264 | /* | |
265 | * This function will convert a string to a block number. It returns | |
266 | * 0 on success, 1 on failure. | |
267 | */ | |
268 | int strtoblk(const char *cmd, const char *str, blk_t *ret) | |
269 | { | |
270 | blk_t blk; | |
271 | int err; | |
272 | ||
273 | blk = parse_ulong(str, cmd, "block number", &err); | |
274 | *ret = blk; | |
275 | if (err == 0 && blk == 0) { | |
276 | com_err(cmd, 0, "Invalid block number 0"); | |
277 | err = 1; | |
278 | } | |
279 | return err; | |
280 | } | |
281 | ||
282 | /* | |
283 | * This is a common helper function used by the command processing | |
284 | * routines | |
285 | */ | |
286 | int common_args_process(int argc, char *argv[], int min_argc, int max_argc, | |
287 | const char *cmd, const char *usage, int flags) | |
288 | { | |
289 | if (argc < min_argc || argc > max_argc) { | |
290 | com_err(argv[0], 0, "Usage: %s %s", cmd, usage); | |
291 | return 1; | |
292 | } | |
293 | if (flags & CHECK_FS_NOTOPEN) { | |
294 | if (check_fs_not_open(argv[0])) | |
295 | return 1; | |
296 | } else { | |
297 | if (check_fs_open(argv[0])) | |
298 | return 1; | |
299 | } | |
300 | if ((flags & CHECK_FS_RW) && check_fs_read_write(argv[0])) | |
301 | return 1; | |
302 | if ((flags & CHECK_FS_BITMAPS) && check_fs_bitmaps(argv[0])) | |
303 | return 1; | |
304 | return 0; | |
305 | } | |
1e3472c5 | 306 | |
e1018eea TT |
307 | /* |
308 | * This is a helper function used by do_stat, do_freei, do_seti, and | |
309 | * do_testi, etc. Basically, any command which takes a single | |
310 | * argument which is a file/inode number specifier. | |
311 | */ | |
312 | int common_inode_args_process(int argc, char *argv[], | |
313 | ext2_ino_t *inode, int flags) | |
314 | { | |
315 | if (common_args_process(argc, argv, 2, 2, argv[0], "<file>", flags)) | |
316 | return 1; | |
1e3472c5 | 317 | |
e1018eea TT |
318 | *inode = string_to_inode(argv[1]); |
319 | if (!*inode) | |
320 | return 1; | |
321 | return 0; | |
322 | } | |
323 | ||
324 | /* | |
325 | * This is a helper function used by do_freeb, do_setb, and do_testb | |
326 | */ | |
327 | int common_block_args_process(int argc, char *argv[], | |
3e41913c | 328 | blk_t *block, blk_t *count) |
e1018eea TT |
329 | { |
330 | int err; | |
331 | ||
332 | if (common_args_process(argc, argv, 2, 3, argv[0], | |
333 | "<block> [count]", CHECK_FS_BITMAPS)) | |
334 | return 1; | |
335 | ||
336 | if (strtoblk(argv[0], argv[1], block)) | |
337 | return 1; | |
338 | if (argc > 2) { | |
b38cd283 | 339 | *count = parse_ulong(argv[2], argv[0], "count", &err); |
e1018eea TT |
340 | if (err) |
341 | return 1; | |
342 | } | |
343 | return 0; | |
344 | } | |
345 | ||
ea822eeb TT |
346 | int debugfs_read_inode_full(ext2_ino_t ino, struct ext2_inode * inode, |
347 | const char *cmd, int bufsize) | |
348 | { | |
349 | int retval; | |
350 | ||
351 | retval = ext2fs_read_inode_full(current_fs, ino, inode, bufsize); | |
352 | if (retval) { | |
353 | com_err(cmd, retval, "while reading inode %u", ino); | |
354 | return 1; | |
355 | } | |
356 | return 0; | |
357 | } | |
358 | ||
e1018eea TT |
359 | int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode, |
360 | const char *cmd) | |
361 | { | |
362 | int retval; | |
363 | ||
364 | retval = ext2fs_read_inode(current_fs, ino, inode); | |
365 | if (retval) { | |
366 | com_err(cmd, retval, "while reading inode %u", ino); | |
367 | return 1; | |
368 | } | |
369 | return 0; | |
370 | } | |
371 | ||
372 | int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode, | |
373 | const char *cmd) | |
374 | { | |
375 | int retval; | |
376 | ||
377 | retval = ext2fs_write_inode(current_fs, ino, inode); | |
378 | if (retval) { | |
379 | com_err(cmd, retval, "while writing inode %u", ino); | |
380 | return 1; | |
381 | } | |
382 | return 0; | |
383 | } | |
1e3472c5 | 384 | |
030970ed TT |
385 | int debugfs_write_new_inode(ext2_ino_t ino, struct ext2_inode * inode, |
386 | const char *cmd) | |
387 | { | |
388 | int retval; | |
389 | ||
390 | retval = ext2fs_write_new_inode(current_fs, ino, inode); | |
391 | if (retval) { | |
392 | com_err(cmd, retval, "while creating inode %u", ino); | |
393 | return 1; | |
394 | } | |
395 | return 0; | |
396 | } |