History

fixdep: use fflush() and ferror() to ensure successful write to files

Currently, fixdep checks the return value from (v)printf(), but it does not ensure the complete write to the .cmd file. printf() just writes data to the internal buffer, which usually succeeds. (Of course, it may fail for another reason, for example when the file descriptor is closed, but that is another story.) When the buffer (4k?) is full, an actual write occurs, and printf() may really fail. One of typical cases is "No space left on device" when the disk is full. The data remaining in the buffer will be pushed out to the file when the program exits, but we never know if it is successful. One straight-forward fix would be to add the following code at the end of the program. ret = fflush(stdout); if (ret < 0) { /* error handling */ } However, it is tedious to check the return code in all the call sites of printf(), fflush(), fclose(), and whatever can cause actual writes to the end device. Doing that lets the program bail out at the first failure but is usually not worth the effort. Instead, let's check the error status from ferror(). This is 'sticky', so you need to check it just once. You still need to call fflush().

Masahiro Yamada committed 4 years ago

Commit 69304371 parent bbc90bc1 file · +19 −27Browse files

Showing the changes to scripts/basic (1 of 1 file). Show all

Unified · Split

scripts/basic/fixdep.c+19−27
@@ -105,25 +105,6 @@ static void usage(void) · 104 unmodified lines
105105 exit(1);
106106 }
107107
108−/*
109− * In the intended usage of this program, the stdout is redirected to .*.cmd
110− * files. The return value of printf() must be checked to catch any error,
111− * e.g. "No space left on device".
112− */
113−static void xprintf(const char *format, ...)
114−{
115− va_list ap;
116− int ret;
117−
118− va_start(ap, format);
119− ret = vprintf(format, ap);
120− if (ret < 0) {
121− perror("fixdep");
122− exit(1);
123− }
124− va_end(ap);
125−}
126−
127108 struct item {
128109 struct item *next;
129110 unsigned int len;
@@ -189,7 +170,7 @@ static void use_config(const char *m, int slen) · 59 unmodified lines
189170
190171 define_config(m, slen, hash);
191172 /* Print out a dependency path from a symbol name. */
192− xprintf(" $(wildcard include/config/%.*s) \\\n", slen, m);
173+ printf(" $(wildcard include/config/%.*s) \\\n", slen, m);
193174 }
194175
195176 /* test if s ends in sub */
@@ -318,13 +299,13 @@ static void parse_dep_file(char *m, const char *target) · 122 unmodified lines
318299 */
319300 if (!saw_any_target) {
320301 saw_any_target = 1;
321− xprintf("source_%s := %s\n\n",
322− target, m);
323− xprintf("deps_%s := \\\n", target);
302+ printf("source_%s := %s\n\n",
303+ target, m);
304+ printf("deps_%s := \\\n", target);
324305 }
325306 is_first_dep = 0;
326307 } else {
327− xprintf(" %s \\\n", m);
308+ printf(" %s \\\n", m);
328309 }
329310
330311 buf = read_file(m);
@@ -347,8 +328,8 @@ static void parse_dep_file(char *m, const char *target) · 16 unmodified lines
347328 exit(1);
348329 }
349330
350− xprintf("\n%s: $(deps_%s)\n\n", target, target);
351− xprintf("$(deps_%s):\n", target);
331+ printf("\n%s: $(deps_%s)\n\n", target, target);
332+ printf("$(deps_%s):\n", target);
352333 }
353334
354335 int main(int argc, char *argv[])
@@ -363,11 +344,22 @@ int main(int argc, char *argv[]) · 8 unmodified lines
363344 target = argv[2];
364345 cmdline = argv[3];
365346
366− xprintf("cmd_%s := %s\n\n", target, cmdline);
347+ printf("cmd_%s := %s\n\n", target, cmdline);
367348
368349 buf = read_file(depfile);
369350 parse_dep_file(buf, target);
370351 free(buf);
371352
353+ fflush(stdout);
354+
355+ /*
356+ * In the intended usage, the stdout is redirected to .*.cmd files.
357+ * Call ferror() to catch errors such as "No space left on device".
358+ */
359+ if (ferror(stdout)) {
360+ fprintf(stderr, "fixdep: not all data was written to the output\n");
361+ exit(1);
362+ }
363+
372364 return 0;
373365 }