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