diff --git a/doc/Changelog b/doc/Changelog index c787be3..858471b 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -5,6 +5,10 @@ very useful even if not intended for normal use. * Use paths.h macros for wall * Change path "/etc/powerstatus" to "/var/run/powerstatus" + * Detected also removable block devices at halt/reboot to be able + to flush data and send them the ATA standby command. This should + avoid data loss on USB sticks and other removable block devices. + * Flush block devices on halt/reboot if not done by the kernel. sysvinit (2.88dsf) UNRELEASED; urgency=low diff --git a/src/halt.c b/src/halt.c index 60d79a1..75f4164 100644 --- a/src/halt.c +++ b/src/halt.c @@ -270,6 +270,8 @@ int main(int argc, char **argv) if (do_hddown) (void)hddown(); + else + (void)hdflush(); if (do_nothing) exit(0); diff --git a/src/hddown.c b/src/hddown.c index a8e394a..45e6630 100644 --- a/src/hddown.c +++ b/src/hddown.c @@ -36,9 +36,12 @@ char *v_hddown = "@(#)hddown.c 1.02 22-Apr-2003 miquels@cistron.nl"; #include #include +#include -#define USE_SYSFS -#ifdef USE_SYSFS +#ifndef USE_SYSFS +# define USE_SYSFS 1 +#endif +#if defined(USE_SYSFS) && (USE_SYSFS == 1) /* * sysfs part Find all disks on the system, list out IDE and unmanaged * SATA disks, flush the cache of those and shut them down. @@ -63,10 +66,13 @@ char *v_hddown = "@(#)hddown.c 1.02 22-Apr-2003 miquels@cistron.nl"; #define MASK_EXT 0xE000 /* Bit 15 shall be zero, bit 14 shall be one, bit 13 flush cache ext */ #define TEST_EXT 0x6000 -/* Maybe set in list_disks() and used in do_standby_idedisk() */ +/* Maybe set in list_disks() and used in do_standby_disk() */ #define DISK_IS_IDE 0x00000001 #define DISK_IS_SATA 0x00000002 #define DISK_EXTFLUSH 0x00000004 +#define DISK_REMOVABLE 0x00000008 +#define DISK_MANAGED 0x00000010 +#define DISK_FLUSHONLY 0x00000020 static char *strstrip(char *str); static FILE *hdopen(const char* const format, const char* const name); @@ -80,10 +86,9 @@ static char *list_disks(DIR* blk, unsigned int* flags) struct dirent *d; while ((d = readdir(blk))) { - *flags = 0; + (*flags) = 0; if (d->d_name[1] == 'd' && (d->d_name[0] == 'h' || d->d_name[0] == 's')) { char buf[NAME_MAX+1], lnk[NAME_MAX+1], *ptr; - struct stat st; FILE *fp; int ret; @@ -98,9 +103,12 @@ static char *list_disks(DIR* blk, unsigned int* flags) fclose(fp); if (ret != '0') - continue; /* not a hard disk */ + (*flags) |= DISK_REMOVABLE; if (d->d_name[0] == 'h') { + if ((*flags) & DISK_REMOVABLE) + continue; /* not a hard disk */ + (*flags) |= DISK_IS_IDE; if ((ret = flush_cache_ext(d->d_name))) { if (ret < 0) @@ -128,16 +136,19 @@ static char *list_disks(DIR* blk, unsigned int* flags) if (!ptr || !*ptr) continue; /* should not happen */ - ret = snprintf(buf, sizeof(buf), SYS_CLASS "/%s/manage_start_stop", ptr); - if ((ret >= (int)sizeof(buf)) || (ret < 0)) - goto empty; /* error */ + fp = hdopen(SYS_CLASS "/%s/manage_start_stop", ptr); + if ((long)fp <= 0) { + if ((long)fp < 0) + goto empty; /* error */ + } else { + ret = getc(fp); + fclose(fp); - ret = stat(buf, &st); - if (ret == 0) - continue; /* disk found but managed by kernel */ - - if (errno != ENOENT) - goto empty; /* error */ + if (ret != '0') { + (*flags) |= DISK_MANAGED; + continue; + } + } fp = hdopen(SYS_BLK "/%s/device/vendor", d->d_name); if ((long)fp <= 0) { @@ -155,16 +166,28 @@ static char *list_disks(DIR* blk, unsigned int* flags) if (*ptr == '\0') continue; /* should not happen */ - if (strncmp(buf, "ATA", sizeof(buf))) - continue; /* no SATA but a real SCSI disk */ + if (strncmp(buf, "ATA", sizeof(buf)) == 0) { + if ((*flags) & DISK_REMOVABLE) + continue; /* not a hard disk */ + + (*flags) |= (DISK_IS_IDE|DISK_IS_SATA); + if ((ret = flush_cache_ext(d->d_name))) { + if (ret < 0) + goto empty; + (*flags) |= DISK_EXTFLUSH; + } + break; /* new SATA disk to shutdown, out here */ + } + + if (((*flags) & DISK_REMOVABLE) == 0) + continue; /* Seems to be a real SCSI disk */ - (*flags) |= (DISK_IS_IDE|DISK_IS_SATA); if ((ret = flush_cache_ext(d->d_name))) { if (ret < 0) goto empty; (*flags) |= DISK_EXTFLUSH; } - break; /* new SATA disk to shutdown, out here */ + break; /* Removable disk like USB stick to shutdown */ } } if (d == (struct dirent*)0) @@ -175,10 +198,10 @@ empty: } /* - * Put an disk in standby mode. + * Put an IDE/SCSI/SATA disk in standby mode. * Code stolen from hdparm.c */ -static int do_standby_idedisk(char *device, unsigned int flags) +static int do_standby_disk(char *device, unsigned int flags) { #ifndef WIN_STANDBYNOW1 #define WIN_STANDBYNOW1 0xE0 @@ -203,21 +226,25 @@ static int do_standby_idedisk(char *device, unsigned int flags) if ((ret >= (int)sizeof(buf)) || (ret < 0)) return -1; - if ((fd = open(buf, O_RDWR)) < 0) + if ((fd = open(buf, O_RDWR|O_NONBLOCK)) < 0) return -1; switch (flags & DISK_EXTFLUSH) { case DISK_EXTFLUSH: - if (ioctl(fd, HDIO_DRIVE_CMD, &flush1) == 0) + if ((ret = ioctl(fd, HDIO_DRIVE_CMD, &flush1)) == 0) break; /* Extend flush rejected, try standard flush */ default: - ioctl(fd, HDIO_DRIVE_CMD, &flush2); + ret = ioctl(fd, HDIO_DRIVE_CMD, &flush2) && + ioctl(fd, BLKFLSBUF); break; } - ret = ioctl(fd, HDIO_DRIVE_CMD, &stdby1) && - ioctl(fd, HDIO_DRIVE_CMD, &stdby2); + if ((flags & DISK_FLUSHONLY) == 0x0) { + ret = ioctl(fd, HDIO_DRIVE_CMD, &stdby1) && + ioctl(fd, HDIO_DRIVE_CMD, &stdby2); + } + close(fd); if (ret) @@ -240,7 +267,25 @@ int hddown(void) return -1; while ((disk = list_disks(blk, &flags))) - do_standby_idedisk(disk, flags); + do_standby_disk(disk, flags); + + return closedir(blk); +} + +/* + * List all disks and cause them to flush their buffers. + */ +int hdflush(void) +{ + unsigned int flags; + char *disk; + DIR *blk; + + if ((blk = opendir(SYS_BLK)) == (DIR*)0) + return -1; + + while ((disk = list_disks(blk, &flags))) + do_standby_disk(disk, (flags|DISK_FLUSHONLY)); return closedir(blk); } @@ -493,6 +538,12 @@ int hddown(void) return (result1 ? result1 : result2); } + +int hdflush(void) +{ + return 0; +} + #endif /* ! USE_SYSFS */ #else /* __linux__ */ @@ -501,6 +552,11 @@ int hddown(void) return 0; } +int hdflush(void) +{ + return 0; +} + #endif /* __linux__ */ #ifdef STANDALONE