* Detected also removable block devices at halt/reboot to be able
authorDr. Werner Fink <werner@suse.de>
Mon, 22 Feb 2010 14:59:58 +0000 (14:59 +0000)
committerDr. Werner Fink <werner@suse.de>
Mon, 22 Feb 2010 14:59:58 +0000 (14:59 +0000)
    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.

git-svn-id: svn://svn.sv.gnu.org/sysvinit/sysvinit/trunk@35 456724a4-4300-0410-8514-c89748c515a2

doc/Changelog
src/halt.c
src/hddown.c

index c787be33f796c2dba1c39f9232179e388005d25d..858471b4e86ecca5bf76bc7060d900f696e4d4b6 100644 (file)
@@ -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
 
index 60d79a1b5d444bd5910d61ebe9933d8a244c5d92..75f41640d20252fcb77fea4b7aae347e8e0e693b 100644 (file)
@@ -270,6 +270,8 @@ int main(int argc, char **argv)
 
        if (do_hddown)
                (void)hddown();
+       else
+               (void)hdflush();
 
        if (do_nothing) exit(0);
 
index a8e394ae884ad7da5ea151e9218e8e2c7f142d87..45e6630c6497bb48ca862d6b81537184be78585e 100644 (file)
@@ -36,9 +36,12 @@ char *v_hddown = "@(#)hddown.c  1.02  22-Apr-2003  miquels@cistron.nl";
 
 #include <sys/ioctl.h>
 #include <linux/hdreg.h>
+#include <linux/fs.h>
 
-#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 */
-
-                       ret = stat(buf, &st);
-                       if (ret == 0)
-                               continue;               /* disk found but managed by kernel */
+                       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);
 
-                       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