Home | History | Annotate | Download | only in e2fsck
      1 /*
      2  * util.c --- miscellaneous utilities
      3  *
      4  * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
      5  *
      6  * %Begin-Header%
      7  * This file may be redistributed under the terms of the GNU Public
      8  * License.
      9  * %End-Header%
     10  */
     11 
     12 #include <stdlib.h>
     13 #include <stdio.h>
     14 #include <unistd.h>
     15 #include <string.h>
     16 #include <ctype.h>
     17 #ifdef __linux__
     18 #include <sys/utsname.h>
     19 #endif
     20 
     21 #ifdef HAVE_CONIO_H
     22 #undef HAVE_TERMIOS_H
     23 #include <conio.h>
     24 #define read_a_char()	getch()
     25 #else
     26 #ifdef HAVE_TERMIOS_H
     27 #include <termios.h>
     28 #endif
     29 #endif
     30 
     31 #ifdef HAVE_MALLOC_H
     32 #include <malloc.h>
     33 #endif
     34 
     35 #ifdef HAVE_ERRNO_H
     36 #include <errno.h>
     37 #endif
     38 
     39 #include "e2fsck.h"
     40 
     41 extern e2fsck_t e2fsck_global_ctx;   /* Try your very best not to use this! */
     42 
     43 #include <sys/time.h>
     44 #include <sys/resource.h>
     45 
     46 void fatal_error(e2fsck_t ctx, const char *msg)
     47 {
     48 	if (msg)
     49 		fprintf (stderr, "e2fsck: %s\n", msg);
     50 	if (ctx->fs && ctx->fs->io) {
     51 		if (ctx->fs->io->magic == EXT2_ET_MAGIC_IO_CHANNEL)
     52 			io_channel_flush(ctx->fs->io);
     53 		else
     54 			fprintf(stderr, "e2fsck: io manager magic bad!\n");
     55 	}
     56 	ctx->flags |= E2F_FLAG_ABORT;
     57 	if (ctx->flags & E2F_FLAG_SETJMP_OK)
     58 		longjmp(ctx->abort_loc, 1);
     59 	exit(FSCK_ERROR);
     60 }
     61 
     62 void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
     63 			     const char *description)
     64 {
     65 	void *ret;
     66 	char buf[256];
     67 
     68 #ifdef DEBUG_ALLOCATE_MEMORY
     69 	printf("Allocating %u bytes for %s...\n", size, description);
     70 #endif
     71 	ret = malloc(size);
     72 	if (!ret) {
     73 		sprintf(buf, "Can't allocate %s\n", description);
     74 		fatal_error(ctx, buf);
     75 	}
     76 	memset(ret, 0, size);
     77 	return ret;
     78 }
     79 
     80 char *string_copy(e2fsck_t ctx EXT2FS_ATTR((unused)),
     81 		  const char *str, int len)
     82 {
     83 	char	*ret;
     84 
     85 	if (!str)
     86 		return NULL;
     87 	if (!len)
     88 		len = strlen(str);
     89 	ret = malloc(len+1);
     90 	if (ret) {
     91 		strncpy(ret, str, len);
     92 		ret[len] = 0;
     93 	}
     94 	return ret;
     95 }
     96 
     97 #ifndef HAVE_STRNLEN
     98 /*
     99  * Incredibly, libc5 doesn't appear to have strnlen.  So we have to
    100  * provide our own.
    101  */
    102 int e2fsck_strnlen(const char * s, int count)
    103 {
    104 	const char *cp = s;
    105 
    106 	while (count-- && *cp)
    107 		cp++;
    108 	return cp - s;
    109 }
    110 #endif
    111 
    112 #ifndef HAVE_CONIO_H
    113 static int read_a_char(void)
    114 {
    115 	char	c;
    116 	int	r;
    117 	int	fail = 0;
    118 
    119 	while(1) {
    120 		if (e2fsck_global_ctx &&
    121 		    (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
    122 			return 3;
    123 		}
    124 		r = read(0, &c, 1);
    125 		if (r == 1)
    126 			return c;
    127 		if (fail++ > 100)
    128 			break;
    129 	}
    130 	return EOF;
    131 }
    132 #endif
    133 
    134 int ask_yn(const char * string, int def)
    135 {
    136 	int		c;
    137 	const char	*defstr;
    138 	const char	*short_yes = _("yY");
    139 	const char	*short_no = _("nN");
    140 
    141 #ifdef HAVE_TERMIOS_H
    142 	struct termios	termios, tmp;
    143 
    144 	tcgetattr (0, &termios);
    145 	tmp = termios;
    146 	tmp.c_lflag &= ~(ICANON | ECHO);
    147 	tmp.c_cc[VMIN] = 1;
    148 	tmp.c_cc[VTIME] = 0;
    149 	tcsetattr (0, TCSANOW, &tmp);
    150 #endif
    151 
    152 	if (def == 1)
    153 		defstr = _(_("<y>"));
    154 	else if (def == 0)
    155 		defstr = _(_("<n>"));
    156 	else
    157 		defstr = _(" (y/n)");
    158 	printf("%s%s? ", string, defstr);
    159 	while (1) {
    160 		fflush (stdout);
    161 		if ((c = read_a_char()) == EOF)
    162 			break;
    163 		if (c == 3) {
    164 #ifdef HAVE_TERMIOS_H
    165 			tcsetattr (0, TCSANOW, &termios);
    166 #endif
    167 			if (e2fsck_global_ctx &&
    168 			    e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
    169 				puts("\n");
    170 				longjmp(e2fsck_global_ctx->abort_loc, 1);
    171 			}
    172 			puts(_("cancelled!\n"));
    173 			return 0;
    174 		}
    175 		if (strchr(short_yes, (char) c)) {
    176 			def = 1;
    177 			break;
    178 		}
    179 		else if (strchr(short_no, (char) c)) {
    180 			def = 0;
    181 			break;
    182 		}
    183 		else if ((c == ' ' || c == '\n') && (def != -1))
    184 			break;
    185 	}
    186 	if (def)
    187 		puts(_("yes\n"));
    188 	else
    189 		puts (_("no\n"));
    190 #ifdef HAVE_TERMIOS_H
    191 	tcsetattr (0, TCSANOW, &termios);
    192 #endif
    193 	return def;
    194 }
    195 
    196 int ask (e2fsck_t ctx, const char * string, int def)
    197 {
    198 	if (ctx->options & E2F_OPT_NO) {
    199 		printf (_("%s? no\n\n"), string);
    200 		return 0;
    201 	}
    202 	if (ctx->options & E2F_OPT_YES) {
    203 		printf (_("%s? yes\n\n"), string);
    204 		return 1;
    205 	}
    206 	if (ctx->options & E2F_OPT_PREEN) {
    207 		printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
    208 		return def;
    209 	}
    210 	return ask_yn(string, def);
    211 }
    212 
    213 void e2fsck_read_bitmaps(e2fsck_t ctx)
    214 {
    215 	ext2_filsys fs = ctx->fs;
    216 	errcode_t	retval;
    217 	const char	*old_op;
    218 
    219 	if (ctx->invalid_bitmaps) {
    220 		com_err(ctx->program_name, 0,
    221 		    _("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
    222 			ctx->device_name);
    223 		fatal_error(ctx, 0);
    224 	}
    225 
    226 	old_op = ehandler_operation(_("reading inode and block bitmaps"));
    227 	retval = ext2fs_read_bitmaps(fs);
    228 	ehandler_operation(old_op);
    229 	if (retval) {
    230 		com_err(ctx->program_name, retval,
    231 			_("while retrying to read bitmaps for %s"),
    232 			ctx->device_name);
    233 		fatal_error(ctx, 0);
    234 	}
    235 }
    236 
    237 void e2fsck_write_bitmaps(e2fsck_t ctx)
    238 {
    239 	ext2_filsys fs = ctx->fs;
    240 	errcode_t	retval;
    241 	const char	*old_op;
    242 
    243 	old_op = ehandler_operation(_("writing block and inode bitmaps"));
    244 	retval = ext2fs_write_bitmaps(fs);
    245 	ehandler_operation(old_op);
    246 	if (retval) {
    247 		com_err(ctx->program_name, retval,
    248 			_("while rewriting block and inode bitmaps for %s"),
    249 			ctx->device_name);
    250 		fatal_error(ctx, 0);
    251 	}
    252 }
    253 
    254 void preenhalt(e2fsck_t ctx)
    255 {
    256 	ext2_filsys fs = ctx->fs;
    257 
    258 	if (!(ctx->options & E2F_OPT_PREEN))
    259 		return;
    260 	fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
    261 		"RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
    262 	       ctx->device_name);
    263 	ctx->flags |= E2F_FLAG_EXITING;
    264 	if (fs != NULL) {
    265 		fs->super->s_state |= EXT2_ERROR_FS;
    266 		ext2fs_mark_super_dirty(fs);
    267 		ext2fs_close(fs);
    268 	}
    269 	exit(FSCK_UNCORRECTED);
    270 }
    271 
    272 #ifdef RESOURCE_TRACK
    273 void init_resource_track(struct resource_track *track, io_channel channel)
    274 {
    275 #ifdef HAVE_GETRUSAGE
    276 	struct rusage r;
    277 #endif
    278 	io_stats io_start = 0;
    279 
    280 	track->brk_start = sbrk(0);
    281 	gettimeofday(&track->time_start, 0);
    282 #ifdef HAVE_GETRUSAGE
    283 #ifdef sun
    284 	memset(&r, 0, sizeof(struct rusage));
    285 #endif
    286 	getrusage(RUSAGE_SELF, &r);
    287 	track->user_start = r.ru_utime;
    288 	track->system_start = r.ru_stime;
    289 #else
    290 	track->user_start.tv_sec = track->user_start.tv_usec = 0;
    291 	track->system_start.tv_sec = track->system_start.tv_usec = 0;
    292 #endif
    293 	track->bytes_read = 0;
    294 	track->bytes_written = 0;
    295 	if (channel && channel->manager && channel->manager->get_stats)
    296 		channel->manager->get_stats(channel, &io_start);
    297 	if (io_start) {
    298 		track->bytes_read = io_start->bytes_read;
    299 		track->bytes_written = io_start->bytes_written;
    300 	}
    301 }
    302 
    303 #ifdef __GNUC__
    304 #define _INLINE_ __inline__
    305 #else
    306 #define _INLINE_
    307 #endif
    308 
    309 static _INLINE_ float timeval_subtract(struct timeval *tv1,
    310 				       struct timeval *tv2)
    311 {
    312 	return ((tv1->tv_sec - tv2->tv_sec) +
    313 		((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
    314 }
    315 
    316 void print_resource_track(e2fsck_t ctx, const char *desc,
    317 			  struct resource_track *track, io_channel channel)
    318 {
    319 #ifdef HAVE_GETRUSAGE
    320 	struct rusage r;
    321 #endif
    322 #ifdef HAVE_MALLINFO
    323 	struct mallinfo	malloc_info;
    324 #endif
    325 	struct timeval time_end;
    326 
    327 	if ((desc && !(ctx->options & E2F_OPT_TIME2)) ||
    328 	    (!desc && !(ctx->options & E2F_OPT_TIME)))
    329 		return;
    330 
    331 	e2fsck_clear_progbar(ctx);
    332 	gettimeofday(&time_end, 0);
    333 
    334 	if (desc)
    335 		printf("%s: ", desc);
    336 
    337 #ifdef HAVE_MALLINFO
    338 #define kbytes(x)	(((unsigned long)(x) + 1023) / 1024)
    339 
    340 	malloc_info = mallinfo();
    341 	printf(_("Memory used: %luk/%luk (%luk/%luk), "),
    342 	       kbytes(malloc_info.arena), kbytes(malloc_info.hblkhd),
    343 	       kbytes(malloc_info.uordblks), kbytes(malloc_info.fordblks));
    344 #else
    345 	printf(_("Memory used: %lu, "),
    346 	       (unsigned long) (((char *) sbrk(0)) -
    347 				((char *) track->brk_start)));
    348 #endif
    349 #ifdef HAVE_GETRUSAGE
    350 	getrusage(RUSAGE_SELF, &r);
    351 
    352 	printf(_("time: %5.2f/%5.2f/%5.2f\n"),
    353 	       timeval_subtract(&time_end, &track->time_start),
    354 	       timeval_subtract(&r.ru_utime, &track->user_start),
    355 	       timeval_subtract(&r.ru_stime, &track->system_start));
    356 #else
    357 	printf(_("elapsed time: %6.3f\n"),
    358 	       timeval_subtract(&time_end, &track->time_start));
    359 #endif
    360 #define mbytes(x)	(((x) + 1048575) / 1048576)
    361 	if (channel && channel->manager && channel->manager->get_stats) {
    362 		io_stats delta = 0;
    363 		unsigned long long bytes_read = 0;
    364 		unsigned long long bytes_written = 0;
    365 
    366 		if (desc)
    367 			printf("%s: ", desc);
    368 
    369 		channel->manager->get_stats(channel, &delta);
    370 		if (delta) {
    371 			bytes_read = delta->bytes_read - track->bytes_read;
    372 			bytes_written = delta->bytes_written -
    373 				track->bytes_written;
    374 		}
    375 		printf("I/O read: %lluMB, write: %lluMB, rate: %.2fMB/s\n",
    376 		       mbytes(bytes_read), mbytes(bytes_written),
    377 		       (double)mbytes(bytes_read + bytes_written) /
    378 		       timeval_subtract(&time_end, &track->time_start));
    379 	}
    380 }
    381 #endif /* RESOURCE_TRACK */
    382 
    383 void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
    384 			      struct ext2_inode * inode, const char *proc)
    385 {
    386 	int retval;
    387 
    388 	retval = ext2fs_read_inode(ctx->fs, ino, inode);
    389 	if (retval) {
    390 		com_err("ext2fs_read_inode", retval,
    391 			_("while reading inode %lu in %s"), ino, proc);
    392 		fatal_error(ctx, 0);
    393 	}
    394 }
    395 
    396 void e2fsck_read_inode_full(e2fsck_t ctx, unsigned long ino,
    397 			    struct ext2_inode *inode, int bufsize,
    398 			    const char *proc)
    399 {
    400 	int retval;
    401 
    402 	retval = ext2fs_read_inode_full(ctx->fs, ino, inode, bufsize);
    403 	if (retval) {
    404 		com_err("ext2fs_read_inode_full", retval,
    405 			_("while reading inode %lu in %s"), ino, proc);
    406 		fatal_error(ctx, 0);
    407 	}
    408 }
    409 
    410 extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
    411 			       struct ext2_inode * inode, int bufsize,
    412 			       const char *proc)
    413 {
    414 	int retval;
    415 
    416 	retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
    417 	if (retval) {
    418 		com_err("ext2fs_write_inode", retval,
    419 			_("while writing inode %lu in %s"), ino, proc);
    420 		fatal_error(ctx, 0);
    421 	}
    422 }
    423 
    424 extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
    425 			       struct ext2_inode * inode, const char *proc)
    426 {
    427 	int retval;
    428 
    429 	retval = ext2fs_write_inode(ctx->fs, ino, inode);
    430 	if (retval) {
    431 		com_err("ext2fs_write_inode", retval,
    432 			_("while writing inode %lu in %s"), ino, proc);
    433 		fatal_error(ctx, 0);
    434 	}
    435 }
    436 
    437 #ifdef MTRACE
    438 void mtrace_print(char *mesg)
    439 {
    440 	FILE	*malloc_get_mallstream();
    441 	FILE	*f = malloc_get_mallstream();
    442 
    443 	if (f)
    444 		fprintf(f, "============= %s\n", mesg);
    445 }
    446 #endif
    447 
    448 blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
    449 		   io_manager manager)
    450 {
    451 	struct ext2_super_block *sb;
    452 	io_channel		io = NULL;
    453 	void			*buf = NULL;
    454 	int			blocksize;
    455 	blk_t			superblock, ret_sb = 8193;
    456 
    457 	if (fs && fs->super) {
    458 		ret_sb = (fs->super->s_blocks_per_group +
    459 			  fs->super->s_first_data_block);
    460 		if (ctx) {
    461 			ctx->superblock = ret_sb;
    462 			ctx->blocksize = fs->blocksize;
    463 		}
    464 		return ret_sb;
    465 	}
    466 
    467 	if (ctx) {
    468 		if (ctx->blocksize) {
    469 			ret_sb = ctx->blocksize * 8;
    470 			if (ctx->blocksize == 1024)
    471 				ret_sb++;
    472 			ctx->superblock = ret_sb;
    473 			return ret_sb;
    474 		}
    475 		ctx->superblock = ret_sb;
    476 		ctx->blocksize = 1024;
    477 	}
    478 
    479 	if (!name || !manager)
    480 		goto cleanup;
    481 
    482 	if (manager->open(name, 0, &io) != 0)
    483 		goto cleanup;
    484 
    485 	if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
    486 		goto cleanup;
    487 	sb = (struct ext2_super_block *) buf;
    488 
    489 	for (blocksize = EXT2_MIN_BLOCK_SIZE;
    490 	     blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
    491 		superblock = blocksize*8;
    492 		if (blocksize == 1024)
    493 			superblock++;
    494 		io_channel_set_blksize(io, blocksize);
    495 		if (io_channel_read_blk(io, superblock,
    496 					-SUPERBLOCK_SIZE, buf))
    497 			continue;
    498 #ifdef WORDS_BIGENDIAN
    499 		if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
    500 			ext2fs_swap_super(sb);
    501 #endif
    502 		if ((sb->s_magic == EXT2_SUPER_MAGIC) &&
    503 		    (EXT2_BLOCK_SIZE(sb) == blocksize)) {
    504 			ret_sb = superblock;
    505 			if (ctx) {
    506 				ctx->superblock = superblock;
    507 				ctx->blocksize = blocksize;
    508 			}
    509 			break;
    510 		}
    511 	}
    512 
    513 cleanup:
    514 	if (io)
    515 		io_channel_close(io);
    516 	if (buf)
    517 		ext2fs_free_mem(&buf);
    518 	return (ret_sb);
    519 }
    520 
    521 /*
    522  * Given a mode, return the ext2 file type
    523  */
    524 int ext2_file_type(unsigned int mode)
    525 {
    526 	if (LINUX_S_ISREG(mode))
    527 		return EXT2_FT_REG_FILE;
    528 
    529 	if (LINUX_S_ISDIR(mode))
    530 		return EXT2_FT_DIR;
    531 
    532 	if (LINUX_S_ISCHR(mode))
    533 		return EXT2_FT_CHRDEV;
    534 
    535 	if (LINUX_S_ISBLK(mode))
    536 		return EXT2_FT_BLKDEV;
    537 
    538 	if (LINUX_S_ISLNK(mode))
    539 		return EXT2_FT_SYMLINK;
    540 
    541 	if (LINUX_S_ISFIFO(mode))
    542 		return EXT2_FT_FIFO;
    543 
    544 	if (LINUX_S_ISSOCK(mode))
    545 		return EXT2_FT_SOCK;
    546 
    547 	return 0;
    548 }
    549 
    550 #define STRIDE_LENGTH 8
    551 /*
    552  * Helper function which zeros out _num_ blocks starting at _blk_.  In
    553  * case of an error, the details of the error is returned via _ret_blk_
    554  * and _ret_count_ if they are non-NULL pointers.  Returns 0 on
    555  * success, and an error code on an error.
    556  *
    557  * As a special case, if the first argument is NULL, then it will
    558  * attempt to free the static zeroizing buffer.  (This is to keep
    559  * programs that check for memory leaks happy.)
    560  */
    561 errcode_t e2fsck_zero_blocks(ext2_filsys fs, blk_t blk, int num,
    562 			     blk_t *ret_blk, int *ret_count)
    563 {
    564 	int		j, count, next_update, next_update_incr;
    565 	static char	*buf;
    566 	errcode_t	retval;
    567 
    568 	/* If fs is null, clean up the static buffer and return */
    569 	if (!fs) {
    570 		if (buf) {
    571 			free(buf);
    572 			buf = 0;
    573 		}
    574 		return 0;
    575 	}
    576 	/* Allocate the zeroizing buffer if necessary */
    577 	if (!buf) {
    578 		buf = malloc(fs->blocksize * STRIDE_LENGTH);
    579 		if (!buf) {
    580 			com_err("malloc", ENOMEM,
    581 				_("while allocating zeroizing buffer"));
    582 			exit(1);
    583 		}
    584 		memset(buf, 0, fs->blocksize * STRIDE_LENGTH);
    585 	}
    586 	/* OK, do the write loop */
    587 	next_update = 0;
    588 	next_update_incr = num / 100;
    589 	if (next_update_incr < 1)
    590 		next_update_incr = 1;
    591 	for (j = 0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) {
    592 		count = num - j;
    593 		if (count > STRIDE_LENGTH)
    594 			count = STRIDE_LENGTH;
    595 		retval = io_channel_write_blk(fs->io, blk, count, buf);
    596 		if (retval) {
    597 			if (ret_count)
    598 				*ret_count = count;
    599 			if (ret_blk)
    600 				*ret_blk = blk;
    601 			return retval;
    602 		}
    603 	}
    604 	return 0;
    605 }
    606 
    607 /*
    608  * Check to see if a filesystem is in /proc/filesystems.
    609  * Returns 1 if found, 0 if not
    610  */
    611 int fs_proc_check(const char *fs_name)
    612 {
    613 	FILE	*f;
    614 	char	buf[80], *cp, *t;
    615 
    616 	f = fopen("/proc/filesystems", "r");
    617 	if (!f)
    618 		return (0);
    619 	while (!feof(f)) {
    620 		if (!fgets(buf, sizeof(buf), f))
    621 			break;
    622 		cp = buf;
    623 		if (!isspace(*cp)) {
    624 			while (*cp && !isspace(*cp))
    625 				cp++;
    626 		}
    627 		while (*cp && isspace(*cp))
    628 			cp++;
    629 		if ((t = strchr(cp, '\n')) != NULL)
    630 			*t = 0;
    631 		if ((t = strchr(cp, '\t')) != NULL)
    632 			*t = 0;
    633 		if ((t = strchr(cp, ' ')) != NULL)
    634 			*t = 0;
    635 		if (!strcmp(fs_name, cp)) {
    636 			fclose(f);
    637 			return (1);
    638 		}
    639 	}
    640 	fclose(f);
    641 	return (0);
    642 }
    643 
    644 /*
    645  * Check to see if a filesystem is available as a module
    646  * Returns 1 if found, 0 if not
    647  */
    648 int check_for_modules(const char *fs_name)
    649 {
    650 #ifdef __linux__
    651 	struct utsname	uts;
    652 	FILE		*f;
    653 	char		buf[1024], *cp, *t;
    654 	int		i;
    655 
    656 	if (uname(&uts))
    657 		return (0);
    658 	snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release);
    659 
    660 	f = fopen(buf, "r");
    661 	if (!f)
    662 		return (0);
    663 	while (!feof(f)) {
    664 		if (!fgets(buf, sizeof(buf), f))
    665 			break;
    666 		if ((cp = strchr(buf, ':')) != NULL)
    667 			*cp = 0;
    668 		else
    669 			continue;
    670 		if ((cp = strrchr(buf, '/')) != NULL)
    671 			cp++;
    672 		else
    673 			cp = buf;
    674 		i = strlen(cp);
    675 		if (i > 3) {
    676 			t = cp + i - 3;
    677 			if (!strcmp(t, ".ko"))
    678 				*t = 0;
    679 		}
    680 		if (!strcmp(cp, fs_name)) {
    681 			fclose(f);
    682 			return (1);
    683 		}
    684 	}
    685 	fclose(f);
    686 #endif /* __linux__ */
    687 	return (0);
    688 }
    689 
    690 /*
    691  * Helper function that does the right thing if write returns a
    692  * partial write, or an EGAIN/EINTR error.
    693  */
    694 int write_all(int fd, char *buf, size_t count)
    695 {
    696 	ssize_t ret;
    697 	int c = 0;
    698 
    699 	while (count > 0) {
    700 		ret = write(fd, buf, count);
    701 		if (ret < 0) {
    702 			if ((errno == EAGAIN) || (errno == EINTR))
    703 				continue;
    704 			return -1;
    705 		}
    706 		count -= ret;
    707 		buf += ret;
    708 		c += ret;
    709 	}
    710 	return c;
    711 }
    712