summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--syslinux.c107
2 files changed, 69 insertions, 40 deletions
diff --git a/NEWS b/NEWS
index 1a34d4d4..54a1b4ec 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,8 @@ Changes in 1.70:
header correctly.
* SYSLINUX: Fix, again, "the bug that won't die": the use of
the offset parameter with the SYSLINUX installer.
+ * SYSLINUX: Fix possible superblock corruption problem in the
+ SYSLINUX installer.
Changes in 1.67:
* Handle bug in the location of initrd.
diff --git a/syslinux.c b/syslinux.c
index 8728a31c..106357fb 100644
--- a/syslinux.c
+++ b/syslinux.c
@@ -1,7 +1,7 @@
#ident "$Id$"
/* ----------------------------------------------------------------------- *
*
- * Copyright 1998-2000 H. Peter Anvin - All Rights Reserved
+ * Copyright 1998-2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,10 +20,11 @@
* access the filesystem directly like mtools does so we don't have to
* mount the disk. Either that or if Linux gets an fmount() system call
* we probably could do the mounting ourselves, and make this program
- * setuid safe.
+ * setuid safe. Or perhaps try to link with mtools code...
*
*/
+#define _XOPEN_SOURCE 500 /* Required on glibc 2.x */
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
@@ -105,6 +106,63 @@ void usage(void)
exit(1);
}
+/*
+ * read/write wrapper functions
+ */
+ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
+{
+ ssize_t rv;
+ ssize_t done = 0;
+
+ while ( count ) {
+ rv = pread(fd, buf, count, offset);
+ if ( rv == 0 ) {
+ fprintf(stderr, "%s: short read\n", program);
+ exit(1);
+ } else if ( rv == -1 ) {
+ if ( errno == EINTR ) {
+ continue;
+ } else {
+ perror(program);
+ exit(1);
+ }
+ } else {
+ offset += rv;
+ done += rv;
+ count -= rv;
+ }
+ }
+
+ return done;
+}
+
+ssize_t xpwrite(int fd, void *buf, size_t count, off_t offset)
+{
+ ssize_t rv;
+ ssize_t done = 0;
+
+ while ( count ) {
+ rv = pwrite(fd, buf, count, offset);
+ if ( rv == 0 ) {
+ fprintf(stderr, "%s: short write\n", program);
+ exit(1);
+ } else if ( rv == -1 ) {
+ if ( errno == EINTR ) {
+ continue;
+ } else {
+ perror(program);
+ exit(1);
+ }
+ } else {
+ offset += rv;
+ done += rv;
+ count -= rv;
+ }
+ }
+
+ return done;
+}
+
int main(int argc, char *argv[])
{
static unsigned char sectbuf[512];
@@ -182,22 +240,7 @@ int main(int argc, char *argv[])
}
}
- left = 512;
- dp = sectbuf;
- while ( left ) {
- nb = read(dev_fd, dp, left);
- if ( nb == -1 && errno == EINTR )
- continue;
- if ( nb < 0 ) {
- perror(device);
- exit(1);
- } else if ( nb == 0 ) {
- fprintf(stderr, "%s: no boot sector\n", device);
- exit(1);
- }
- dp += nb;
- left -= nb;
- }
+ xpread(dev_fd, sectbuf, 512, offset);
close(dev_fd);
sync();
@@ -421,32 +464,16 @@ umount:
exit(1);
}
- if ( lseek(dev_fd, offset, SEEK_SET) != offset ) {
- if ( !(force && errno == EBADF) ) {
- fprintf(stderr, "%s: seek error", device);
- exit(1);
- }
- }
+ /* Read the superblock again since it might have changed while mounted */
+ xpread(dev_fd, sectbuf, 512, offset);
/* Copy the old superblock into the new boot sector */
memcpy(bootsect+bsCopyStart, sectbuf+bsCopyStart, bsCopyLen);
-
- dp = bootsect;
- left = bootsect_len;
- while ( left ) {
- nb = write(dev_fd, dp, left);
- if ( nb == -1 && errno == EINTR )
- continue;
- else if ( nb <= 0 ) {
- perror(device);
- exit(1);
- }
-
- dp += nb;
- left -= nb;
- }
- close(dev_fd);
+ /* Write new boot sector */
+ xpwrite(dev_fd, bootsect, 512, offset);
+
+ close(dev_fd);
sync();
/* Done! */