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