* 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.
This commit is contained in:
Werner Fink 2010-02-22 14:59:58 +00:00
parent 1874af5c9c
commit 2c2d31cb4c
3 changed files with 89 additions and 27 deletions

View File

@ -5,6 +5,10 @@
very useful even if not intended for normal use. very useful even if not intended for normal use.
* Use paths.h macros for wall * Use paths.h macros for wall
* Change path "/etc/powerstatus" to "/var/run/powerstatus" * 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 sysvinit (2.88dsf) UNRELEASED; urgency=low

View File

@ -270,6 +270,8 @@ int main(int argc, char **argv)
if (do_hddown) if (do_hddown)
(void)hddown(); (void)hddown();
else
(void)hdflush();
if (do_nothing) exit(0); if (do_nothing) exit(0);

View File

@ -36,9 +36,12 @@ char *v_hddown = "@(#)hddown.c 1.02 22-Apr-2003 miquels@cistron.nl";
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <linux/hdreg.h> #include <linux/hdreg.h>
#include <linux/fs.h>
#define USE_SYSFS #ifndef USE_SYSFS
#ifdef 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 * sysfs part Find all disks on the system, list out IDE and unmanaged
* SATA disks, flush the cache of those and shut them down. * 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 MASK_EXT 0xE000 /* Bit 15 shall be zero, bit 14 shall be one, bit 13 flush cache ext */
#define TEST_EXT 0x6000 #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_IDE 0x00000001
#define DISK_IS_SATA 0x00000002 #define DISK_IS_SATA 0x00000002
#define DISK_EXTFLUSH 0x00000004 #define DISK_EXTFLUSH 0x00000004
#define DISK_REMOVABLE 0x00000008
#define DISK_MANAGED 0x00000010
#define DISK_FLUSHONLY 0x00000020
static char *strstrip(char *str); static char *strstrip(char *str);
static FILE *hdopen(const char* const format, const char* const name); 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; struct dirent *d;
while ((d = readdir(blk))) { while ((d = readdir(blk))) {
*flags = 0; (*flags) = 0;
if (d->d_name[1] == 'd' && (d->d_name[0] == 'h' || d->d_name[0] == 's')) { 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; char buf[NAME_MAX+1], lnk[NAME_MAX+1], *ptr;
struct stat st;
FILE *fp; FILE *fp;
int ret; int ret;
@ -98,9 +103,12 @@ static char *list_disks(DIR* blk, unsigned int* flags)
fclose(fp); fclose(fp);
if (ret != '0') if (ret != '0')
continue; /* not a hard disk */ (*flags) |= DISK_REMOVABLE;
if (d->d_name[0] == 'h') { if (d->d_name[0] == 'h') {
if ((*flags) & DISK_REMOVABLE)
continue; /* not a hard disk */
(*flags) |= DISK_IS_IDE; (*flags) |= DISK_IS_IDE;
if ((ret = flush_cache_ext(d->d_name))) { if ((ret = flush_cache_ext(d->d_name))) {
if (ret < 0) if (ret < 0)
@ -128,16 +136,19 @@ static char *list_disks(DIR* blk, unsigned int* flags)
if (!ptr || !*ptr) if (!ptr || !*ptr)
continue; /* should not happen */ continue; /* should not happen */
ret = snprintf(buf, sizeof(buf), SYS_CLASS "/%s/manage_start_stop", ptr); fp = hdopen(SYS_CLASS "/%s/manage_start_stop", ptr);
if ((ret >= (int)sizeof(buf)) || (ret < 0)) if ((long)fp <= 0) {
goto empty; /* error */ if ((long)fp < 0)
goto empty; /* error */
} else {
ret = getc(fp);
fclose(fp);
ret = stat(buf, &st); if (ret != '0') {
if (ret == 0) (*flags) |= DISK_MANAGED;
continue; /* disk found but managed by kernel */ continue;
}
if (errno != ENOENT) }
goto empty; /* error */
fp = hdopen(SYS_BLK "/%s/device/vendor", d->d_name); fp = hdopen(SYS_BLK "/%s/device/vendor", d->d_name);
if ((long)fp <= 0) { if ((long)fp <= 0) {
@ -155,16 +166,28 @@ static char *list_disks(DIR* blk, unsigned int* flags)
if (*ptr == '\0') if (*ptr == '\0')
continue; /* should not happen */ continue; /* should not happen */
if (strncmp(buf, "ATA", sizeof(buf))) if (strncmp(buf, "ATA", sizeof(buf)) == 0) {
continue; /* no SATA but a real SCSI disk */ 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 = flush_cache_ext(d->d_name))) {
if (ret < 0) if (ret < 0)
goto empty; goto empty;
(*flags) |= DISK_EXTFLUSH; (*flags) |= DISK_EXTFLUSH;
} }
break; /* new SATA disk to shutdown, out here */ break; /* Removable disk like USB stick to shutdown */
} }
} }
if (d == (struct dirent*)0) 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 * 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 #ifndef WIN_STANDBYNOW1
#define WIN_STANDBYNOW1 0xE0 #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)) if ((ret >= (int)sizeof(buf)) || (ret < 0))
return -1; return -1;
if ((fd = open(buf, O_RDWR)) < 0) if ((fd = open(buf, O_RDWR|O_NONBLOCK)) < 0)
return -1; return -1;
switch (flags & DISK_EXTFLUSH) { switch (flags & DISK_EXTFLUSH) {
case DISK_EXTFLUSH: case DISK_EXTFLUSH:
if (ioctl(fd, HDIO_DRIVE_CMD, &flush1) == 0) if ((ret = ioctl(fd, HDIO_DRIVE_CMD, &flush1)) == 0)
break; break;
/* Extend flush rejected, try standard flush */ /* Extend flush rejected, try standard flush */
default: default:
ioctl(fd, HDIO_DRIVE_CMD, &flush2); ret = ioctl(fd, HDIO_DRIVE_CMD, &flush2) &&
ioctl(fd, BLKFLSBUF);
break; break;
} }
ret = ioctl(fd, HDIO_DRIVE_CMD, &stdby1) && if ((flags & DISK_FLUSHONLY) == 0x0) {
ioctl(fd, HDIO_DRIVE_CMD, &stdby2); ret = ioctl(fd, HDIO_DRIVE_CMD, &stdby1) &&
ioctl(fd, HDIO_DRIVE_CMD, &stdby2);
}
close(fd); close(fd);
if (ret) if (ret)
@ -240,7 +267,25 @@ int hddown(void)
return -1; return -1;
while ((disk = list_disks(blk, &flags))) 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); return closedir(blk);
} }
@ -493,6 +538,12 @@ int hddown(void)
return (result1 ? result1 : result2); return (result1 ? result1 : result2);
} }
int hdflush(void)
{
return 0;
}
#endif /* ! USE_SYSFS */ #endif /* ! USE_SYSFS */
#else /* __linux__ */ #else /* __linux__ */
@ -501,6 +552,11 @@ int hddown(void)
return 0; return 0;
} }
int hdflush(void)
{
return 0;
}
#endif /* __linux__ */ #endif /* __linux__ */
#ifdef STANDALONE #ifdef STANDALONE