mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Adds preliminary support for spinning up and down commands.
This commit is contained in:
parent
6301301025
commit
b24807704e
14 changed files with 640 additions and 47 deletions
2
HISTORY
2
HISTORY
|
|
@ -3,6 +3,8 @@ SnapRAID HISTORY
|
|||
|
||||
8.0 2015/01 BETA
|
||||
================
|
||||
* Adds new "spinup" and "spindown" commands to control the disks of
|
||||
the array. Note that "spindown" is available only in Linux.
|
||||
* Allows "sync" and "scrub" to continue after the first
|
||||
bunch of disk errors. Blocks with errors are marked as bad,
|
||||
and you can fix them with the "fix -e" command.
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ snapraid_SOURCES = \
|
|||
cmdline/parity.c \
|
||||
cmdline/handle.c \
|
||||
cmdline/nano.c \
|
||||
cmdline/spin.c \
|
||||
cmdline/fnmatch.c \
|
||||
cmdline/selftest.c \
|
||||
cmdline/speed.c \
|
||||
|
|
@ -198,6 +199,9 @@ endif
|
|||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status -l ">&2"
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status -l ">>test.log"
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) status -l test-%D-%T.log
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) up
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) down
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) test-devices
|
||||
if HAVE_SYMLINK
|
||||
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(PAR1) pool
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -1743,5 +1743,13 @@ int windows_randomize(void* ptr, size_t size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int diskspin(tommy_list* list, int operation)
|
||||
{
|
||||
(void)list;
|
||||
(void)operation;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -162,6 +162,10 @@
|
|||
#include "fnmatch.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_PTHREAD_H
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Disable case check in Windows.
|
||||
*/
|
||||
|
|
@ -197,6 +201,8 @@
|
|||
#include "unix.h"
|
||||
#endif
|
||||
|
||||
#include "tommyds/tommylist.h"
|
||||
|
||||
/**
|
||||
* Another name for link() to avoid confusion with local variables called "link".
|
||||
*/
|
||||
|
|
@ -253,6 +259,11 @@ int fsinfo(const char* path, int* has_persistent_inode, uint64_t* total_space, u
|
|||
*/
|
||||
uint64_t tick(void);
|
||||
|
||||
/**
|
||||
* Get the tick counter value in millisecond.
|
||||
*/
|
||||
uint64_t tick_ms(void);
|
||||
|
||||
/**
|
||||
* Initializes the system.
|
||||
*/
|
||||
|
|
@ -278,5 +289,27 @@ int exit_failure;
|
|||
#define EXIT_SUCCESS exit_success
|
||||
#define EXIT_FAILURE exit_failure
|
||||
|
||||
/**
|
||||
* Disk entry.
|
||||
*/
|
||||
struct disk_struct {
|
||||
char path[PATH_MAX]; /**< Mount point or contained directory. */
|
||||
uint64_t device; /**< Device ID. */
|
||||
#if HAVE_PTHREAD_CREATE
|
||||
pthread_t thread;
|
||||
#endif
|
||||
tommy_node node;
|
||||
};
|
||||
typedef struct disk_struct disk_t;
|
||||
|
||||
#define SPIN_DOWN 0
|
||||
#define SPIN_UP 1
|
||||
#define SPIN_DEVICES -1
|
||||
|
||||
/**
|
||||
* Spin all disks.
|
||||
*/
|
||||
int diskspin(tommy_list* list, int operation);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -37,14 +37,17 @@ void usage(void)
|
|||
{
|
||||
version();
|
||||
|
||||
printf("Usage: " PACKAGE " diff|sync|status|scrub|dup|pool|check|fix [options]\n");
|
||||
printf("Usage: " PACKAGE " status|diff|sync|scrub|list|dup|up|down|pool|check|fix [options]\n");
|
||||
printf("\n");
|
||||
printf("Commands:\n");
|
||||
printf(" status Print the status of the array\n");
|
||||
printf(" diff Show the changes that needs to be syncronized\n");
|
||||
printf(" sync Syncronize the state of the array\n");
|
||||
printf(" status Print the status of the array\n");
|
||||
printf(" scrub Scrub the array\n");
|
||||
printf(" list List the array content\n");
|
||||
printf(" dup Find duplicate files\n");
|
||||
printf(" up Spinup the array\n");
|
||||
printf(" down Spindown the array\n");
|
||||
printf(" pool Create or update the virtual view of the array\n");
|
||||
printf(" check Check the array\n");
|
||||
printf(" fix Fix the array\n");
|
||||
|
|
@ -378,6 +381,9 @@ void signal_handler(int signal)
|
|||
#define OPERATION_STATUS 10
|
||||
#define OPERATION_REWRITE 11
|
||||
#define OPERATION_NANO 12
|
||||
#define OPERATION_SPINUP 13
|
||||
#define OPERATION_SPINDOWN 14
|
||||
#define OPERATION_DEVICES 15
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
|
@ -731,6 +737,12 @@ int main(int argc, char* argv[])
|
|||
operation = OPERATION_REWRITE;
|
||||
} else if (strcmp(argv[optind], "test-nano") == 0) {
|
||||
operation = OPERATION_NANO;
|
||||
} else if (strcmp(argv[optind], "up") == 0) {
|
||||
operation = OPERATION_SPINUP;
|
||||
} else if (strcmp(argv[optind], "down") == 0) {
|
||||
operation = OPERATION_SPINDOWN;
|
||||
} else if (strcmp(argv[optind], "test-devices") == 0) {
|
||||
operation = OPERATION_DEVICES;
|
||||
} else {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Unknown command '%s'\n", argv[optind]);
|
||||
|
|
@ -849,6 +861,7 @@ int main(int argc, char* argv[])
|
|||
case OPERATION_STATUS :
|
||||
case OPERATION_REWRITE :
|
||||
case OPERATION_REHASH :
|
||||
case OPERATION_SPINUP : /* skip because we want to do it in parallel */
|
||||
/* avoid to check and access data disks if not needed */
|
||||
opt.skip_disk_access = 1;
|
||||
break;
|
||||
|
|
@ -863,11 +876,25 @@ int main(int argc, char* argv[])
|
|||
case OPERATION_REWRITE :
|
||||
case OPERATION_REHASH :
|
||||
case OPERATION_NANO :
|
||||
case OPERATION_SPINUP : /* skip because we want to do it in parallel */
|
||||
/* avoid to check and access parity disks if not needed */
|
||||
opt.skip_parity_access = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (operation) {
|
||||
case OPERATION_DIFF :
|
||||
case OPERATION_LIST :
|
||||
case OPERATION_DUP :
|
||||
case OPERATION_POOL :
|
||||
case OPERATION_NANO :
|
||||
case OPERATION_SPINUP :
|
||||
case OPERATION_SPINDOWN :
|
||||
case OPERATION_DEVICES :
|
||||
opt.skip_self = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* open the log file */
|
||||
log_open(log);
|
||||
|
||||
|
|
@ -1050,6 +1077,12 @@ int main(int argc, char* argv[])
|
|||
state_write(&state);
|
||||
|
||||
memory();
|
||||
} else if (operation == OPERATION_SPINUP) {
|
||||
state_spin(&state, SPIN_UP);
|
||||
} else if (operation == OPERATION_SPINDOWN) {
|
||||
state_spin(&state, SPIN_DOWN);
|
||||
} else if (operation == OPERATION_DEVICES) {
|
||||
state_spin(&state, SPIN_DEVICES);
|
||||
} else if (operation == OPERATION_STATUS) {
|
||||
state_read(&state);
|
||||
|
||||
|
|
|
|||
|
|
@ -217,6 +217,11 @@ void state_scan(struct snapraid_state* state, int is_diff);
|
|||
*/
|
||||
void state_nano(struct snapraid_state* state);
|
||||
|
||||
/**
|
||||
* Spin up/down disks.
|
||||
*/
|
||||
void state_spin(struct snapraid_state* state, int operation);
|
||||
|
||||
/**
|
||||
* Syncs the parity data.
|
||||
*/
|
||||
|
|
|
|||
459
cmdline/unix.c
459
cmdline/unix.c
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#ifndef __MINGW32__ /* Only for Unix */
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* Standard exit codes.
|
||||
*/
|
||||
|
|
@ -300,6 +302,16 @@ uint64_t tick(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
uint64_t tick_ms(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
if (gettimeofday(&tv, 0) != 0)
|
||||
return 0;
|
||||
|
||||
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
|
||||
}
|
||||
|
||||
int randomize(void* ptr, size_t size)
|
||||
{
|
||||
int f;
|
||||
|
|
@ -321,6 +333,453 @@ int randomize(void* ptr, size_t size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file extracting the contained device number in %u:%u format.
|
||||
* Returns 0 on error.
|
||||
*/
|
||||
static dev_t devread(const char* path)
|
||||
{
|
||||
int f;
|
||||
int ret;
|
||||
char buf[64];
|
||||
char* e;
|
||||
unsigned ma;
|
||||
unsigned mi;
|
||||
|
||||
f = open(path, O_RDONLY);
|
||||
if (f == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to open '%s'.\n", path);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
ret = read(f, buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to read '%s'.\n", path);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (ret == sizeof(buf)) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Too long read '%s'.\n", path);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
buf[ret] = 0;
|
||||
|
||||
ma = strtoul(buf, &e, 10);
|
||||
if (*e != ':') {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Invalid format in '%s' for '%s'.\n", path, buf);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
mi = strtoul(e+1, &e, 10);
|
||||
if (*e != 0 && !isspace(*e)) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Invalid format in '%s' for '%s'.\n", path, buf);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
ret = close(f);
|
||||
if (ret != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to close '%s'.\n", path);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return makedev(ma, mi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a device tree filling the specified list of disk_t entries.
|
||||
*/
|
||||
static int devtree(dev_t device, tommy_list* list)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
DIR* d;
|
||||
int slaves = 0;
|
||||
|
||||
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves", major(device), minor(device));
|
||||
|
||||
/* check if there is a slaves list */
|
||||
d = opendir(path);
|
||||
if (d != 0) {
|
||||
struct dirent* dd;
|
||||
|
||||
while ((dd = readdir(d)) != 0) {
|
||||
if (dd->d_name[0] != '.') {
|
||||
/* for each slave, expand the full potential tree */
|
||||
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves/%s/dev", major(device), minor(device), dd->d_name);
|
||||
|
||||
device = devread(path);
|
||||
if (!device) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (devtree(device, list) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
++slaves;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
/* if no slaves found */
|
||||
if (!slaves) {
|
||||
/* this is a raw device */
|
||||
struct stat st;
|
||||
disk_t* disk;
|
||||
char buf[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
/* check if it's a partition */
|
||||
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/partition", major(device), minor(device));
|
||||
if (stat(path, &st) == 0) {
|
||||
/* get the parent device */
|
||||
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/../dev", major(device), minor(device));
|
||||
|
||||
device = devread(path);
|
||||
if (!device) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
|
||||
/* resolve the link from /dev/block */
|
||||
pathprint(path, sizeof(path), "/dev/block/%u:%u", major(device), minor(device));
|
||||
|
||||
ret = readlink(path, buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to readlink '%s'.\n", path);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (ret == sizeof(buf)) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Too long readlink '%s'.\n", path);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
buf[ret] = 0;
|
||||
|
||||
if (buf[0] != '.' || buf[1] != '.' || buf[2] != '/') {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Unexpected link '%s' at '%s'.\n", buf, path);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
disk = malloc_nofail(sizeof(disk_t));
|
||||
|
||||
pathprint(disk->path, sizeof(disk->path), "/dev/%s", buf + 3);
|
||||
disk->device = device;
|
||||
|
||||
/* check the device */
|
||||
if (stat(path, &st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to stat '%s'.\n", disk->path);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (st.st_rdev != device) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Unexpected device '%u:%u' instead of '%u:%u' for '%s'.\n", major(st.st_rdev), minor(st.st_rdev), major(device), minor(device), disk->path);
|
||||
return 0;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* insert in the list */
|
||||
tommy_list_insert_tail(list, &disk->node, disk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spin down a specific device.
|
||||
*/
|
||||
static int devdown(dev_t device)
|
||||
{
|
||||
char cmd[128];
|
||||
int ret;
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "hdparm -y /dev/block/%u:%u >/dev/null 2>/dev/null", major(device), minor(device));
|
||||
|
||||
ret = system(cmd);
|
||||
if (ret == -1) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to run hdparm -y on device '%u:%u' (from system).\n", major(device), minor(device));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (WEXITSTATUS(ret) == 127) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to run hdparm -y on device '%u:%u' (from sh).\n", major(device), minor(device));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (WEXITSTATUS(ret) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to run hdparm -y on device '%u:%u' with return code %u.\n", major(device), minor(device), WEXITSTATUS(ret));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two device entries.
|
||||
*/
|
||||
static int devcompare(const void* void_a, const void* void_b)
|
||||
{
|
||||
const disk_t* a = void_a;
|
||||
const disk_t* b = void_b;
|
||||
|
||||
if (a->device < b->device)
|
||||
return -1;
|
||||
if (a->device > b->device)
|
||||
return 1;
|
||||
|
||||
return strcmp(a->path, b->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread for spinning up.
|
||||
*
|
||||
* There isn't a defined way to spin up a device,
|
||||
* so we just do a generic write access.
|
||||
*/
|
||||
static void* spinup(void* arg)
|
||||
{
|
||||
disk_t* disk = arg;
|
||||
dev_t device;
|
||||
int ret;
|
||||
uint64_t start;
|
||||
struct stat st;
|
||||
|
||||
start = tick_ms();
|
||||
|
||||
/* first get the device number, this usually doesn't trigger a spinup */
|
||||
if (stat(disk->path, &st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to stat device '%s'.\n", disk->path);
|
||||
return (void*)-1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* set the device number for printing */
|
||||
device = st.st_dev;
|
||||
|
||||
/* add a temporary name used for writing */
|
||||
pathcat(disk->path, sizeof(disk->path), ".snapraid-spinup");
|
||||
|
||||
/* do a generic write, and immediately undo it */
|
||||
ret = mkdir(disk->path, 0);
|
||||
if (ret == 0)
|
||||
rmdir(disk->path);
|
||||
|
||||
/* remove the added name */
|
||||
pathcut(disk->path);
|
||||
|
||||
printf("Spunup device '%u:%u' at '%s' in %" PRIu64 " ms.\n", major(device), minor(device), disk->path, tick_ms() - start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread for spinning down.
|
||||
*/
|
||||
static void* spindown(void* arg)
|
||||
{
|
||||
disk_t* disk = arg;
|
||||
dev_t device = disk->device;
|
||||
uint64_t start;
|
||||
|
||||
start = tick_ms();
|
||||
|
||||
if (devdown(device) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return (void*)-1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
printf("Spundown device '%u:%u' at '%s' in %" PRIu64 " ms.\n", major(device), minor(device), disk->path, tick_ms() - start);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all the devices.
|
||||
*/
|
||||
static int spin_devices(tommy_list* list)
|
||||
{
|
||||
tommy_node* i;
|
||||
|
||||
for (i = tommy_list_head(list); i != 0; i = i->next) {
|
||||
tommy_list tree;
|
||||
tommy_node* j;
|
||||
disk_t* disk = i->data;
|
||||
|
||||
tommy_list_init(&tree);
|
||||
|
||||
/* expand the tree of devices */
|
||||
if (devtree(disk->device, &tree) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
tommy_list_foreach(&tree, free);
|
||||
fprintf(stderr, "Failed to expand device '%u:%u'.\n", major(disk->device), minor(disk->device));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
for (j = tommy_list_head(&tree); j != 0; j = j->next) {
|
||||
disk_t* entry = j->data;
|
||||
printf("%u:%u\t%s\t%u:%u\t%s\n", major(entry->device), minor(entry->device), entry->path, major(disk->device), minor(disk->device), disk->path);
|
||||
}
|
||||
|
||||
tommy_list_foreach(&tree, free);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int diskspin(tommy_list* list, int operation)
|
||||
{
|
||||
tommy_node* i;
|
||||
tommy_list tree;
|
||||
int fail = 0;
|
||||
|
||||
if (operation == SPIN_DOWN || operation == SPIN_DEVICES) {
|
||||
struct stat st;
|
||||
/* sysfs and devfs interfaces are required */
|
||||
if (stat("/sys/dev/block", &st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Missing interface sys/dev/block.\n");
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
if (stat("/dev/block", &st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Missing interface dev/block.\n");
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
|
||||
if (operation == SPIN_DEVICES)
|
||||
return spin_devices(list);
|
||||
|
||||
tommy_list_init(&tree);
|
||||
|
||||
if (operation == SPIN_DOWN) {
|
||||
/* for each device */
|
||||
for (i = tommy_list_head(list); i != 0; i = i->next) {
|
||||
disk_t* disk = i->data;
|
||||
|
||||
/* expand the tree of devices */
|
||||
if (devtree(disk->device, &tree) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
tommy_list_foreach(&tree, free);
|
||||
fprintf(stderr, "Failed to expand device '%u:%u'.\n", major(disk->device), minor(disk->device));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* duplicate the list */
|
||||
for (i = tommy_list_head(list); i != 0; i = i->next) {
|
||||
disk_t* disk = i->data;
|
||||
disk_t* entry;
|
||||
|
||||
entry = malloc_nofail(sizeof(disk_t));
|
||||
|
||||
pathcpy(entry->path, sizeof(entry->path), disk->path);
|
||||
entry->device = disk->device;
|
||||
|
||||
/* insert in the list */
|
||||
tommy_list_insert_tail(&tree, &entry->node, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* sort the list */
|
||||
tommy_list_sort(&tree, devcompare);
|
||||
|
||||
/* removes duplicates */
|
||||
for (i = tommy_list_head(&tree); i != 0; i = i->next) {
|
||||
while (i->next != 0 && devcompare(i->data, i->next->data) == 0)
|
||||
free(tommy_list_remove_existing(&tree, i->next));
|
||||
}
|
||||
|
||||
#if HAVE_PTHREAD_CREATE
|
||||
/* starts all threads */
|
||||
for (i = tommy_list_head(&tree); i != 0; i = i->next) {
|
||||
disk_t* disk = i->data;
|
||||
|
||||
if (pthread_create(&disk->thread, 0, operation == SPIN_UP ? &spinup : &spindown, disk) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to create thread.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
|
||||
/* joins all threads */
|
||||
for (i = tommy_list_head(&tree); i != 0; i = i->next) {
|
||||
disk_t* disk = i->data;
|
||||
void* retval;
|
||||
|
||||
if (pthread_join(disk->thread, &retval) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to join thread.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (retval != 0)
|
||||
++fail;
|
||||
}
|
||||
#else
|
||||
for (i = tommy_list_head(&tree); i != 0; i = i->next) {
|
||||
disk_t* disk = i->data;
|
||||
|
||||
if (operation == SPIN_UP) {
|
||||
if (spinup(disk) != 0)
|
||||
++fail;
|
||||
} else {
|
||||
if (spindown(disk) != 0)
|
||||
++fail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
tommy_list_foreach(&tree, free);
|
||||
|
||||
if (fail != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void os_init(int opt)
|
||||
{
|
||||
(void)opt;
|
||||
|
|
|
|||
|
|
@ -1343,6 +1343,15 @@ void pathslash(char* dst, size_t size)
|
|||
}
|
||||
}
|
||||
|
||||
void pathcut(char* dst)
|
||||
{
|
||||
char* slash = strrchr(dst, '/');
|
||||
if (slash)
|
||||
slash[1] = 0;
|
||||
else
|
||||
dst[0] = 0;
|
||||
}
|
||||
|
||||
int pathcmp(const char* a, const char* b)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
|
|
|||
|
|
@ -435,6 +435,11 @@ void pathprint(char* dst, size_t size, const char* format, ...);
|
|||
*/
|
||||
void pathslash(char* dst, size_t size);
|
||||
|
||||
/**
|
||||
* Cuts everything after the latest slash.
|
||||
*/
|
||||
void pathcut(char* dst);
|
||||
|
||||
/**
|
||||
* Compare two paths.
|
||||
* In Windows it's case insentive and assumes \ equal at /.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ AC_HEADER_DIRENT
|
|||
AC_HEADER_TIME
|
||||
AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h limits.h])
|
||||
AC_CHECK_HEADERS([unistd.h getopt.h fnmatch.h io.h inttypes.h byteswap.h])
|
||||
AC_CHECK_HEADERS([pthread.h])
|
||||
AC_CHECK_HEADERS([sys/time.h sys/file.h sys/ioctl.h sys/vfs.h sys/statfs.h sys/param.h sys/mount.h])
|
||||
AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h mach/mach_time.h])
|
||||
|
||||
|
|
@ -80,6 +81,8 @@ AC_CHECK_FUNCS([fstatat flock statfs])
|
|||
AC_CHECK_FUNCS([mach_absolute_time])
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt])
|
||||
AC_CHECK_FUNCS([clock_gettime])
|
||||
AC_CHECK_CC_OPT([-pthread], CFLAGS="$CFLAGS -pthread", [])
|
||||
AC_CHECK_FUNCS([pthread_create])
|
||||
|
||||
dnl Checks for architecture
|
||||
AC_C_BIGENDIAN
|
||||
|
|
@ -167,14 +170,14 @@ AM_CONDITIONAL(HAVE_SYMLINK, [test x"$SYMLINK" != x])
|
|||
AC_ARG_ENABLE([profiler],
|
||||
[AS_HELP_STRING([--enable-profiler],[enable the use of gprof for code coverage])],
|
||||
[
|
||||
CFLAGS="-O2 -pg -g"
|
||||
CFLAGS="-O2 -pg -g -pthread"
|
||||
],
|
||||
[])
|
||||
|
||||
AC_ARG_ENABLE([coverage],
|
||||
[AS_HELP_STRING([--enable-coverage],[enable the use of gcov for code coverage])],
|
||||
[
|
||||
CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
|
||||
CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage -pthread"
|
||||
],
|
||||
[])
|
||||
|
||||
|
|
@ -216,7 +219,7 @@ AM_CONDITIONAL(HAVE_EMULATOR, [test x"$EMULATOR" != x])
|
|||
AC_ARG_ENABLE([debug],
|
||||
[AS_HELP_STRING([--enable-debug],[enable debugging])],
|
||||
[
|
||||
CFLAGS="-O0 -g -Wall -Wextra"
|
||||
CFLAGS="-O0 -g -pthread -Wall -Wextra"
|
||||
],
|
||||
[])
|
||||
|
||||
|
|
|
|||
26
snapraid.1
26
snapraid.1
|
|
@ -50,7 +50,7 @@ snapraid [\-c, \-\-conf CONFIG]
|
|||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
status|sync|scrub|fix|check|list|diff|dup|pool|rehash
|
||||
status|diff|sync|scrub|fix|check|list|dup|up|down|pool|rehash
|
||||
.PD 0
|
||||
.PP
|
||||
.PD
|
||||
|
|
@ -647,6 +647,14 @@ Note that the information presented refers at the latest time you run \[dq]sync\
|
|||
Later modifications are not taken into account.
|
||||
.PP
|
||||
Nothing is modified.
|
||||
.SS diff
|
||||
Lists all the files modified from the last \[dq]sync\[dq] that need to have
|
||||
their parity data recomputed.
|
||||
.PP
|
||||
This command doesn\'t check the file data, but only the file timestamp
|
||||
size and inode.
|
||||
.PP
|
||||
Nothing is modified.
|
||||
.SS sync
|
||||
Updates the parity information. All the modified files
|
||||
in the disk array are read, and the corresponding parity
|
||||
|
|
@ -751,20 +759,20 @@ Lists all the files contained in the array at the time of the
|
|||
last \[dq]sync\[dq].
|
||||
.PP
|
||||
Nothing is modified.
|
||||
.SS diff
|
||||
Lists all the files modified from the last \[dq]sync\[dq] that need to have
|
||||
their parity data recomputed.
|
||||
.PP
|
||||
This command doesn\'t check the file data, but only the file timestamp
|
||||
size and inode.
|
||||
.PP
|
||||
Nothing is modified.
|
||||
.SS dup
|
||||
Lists all the duplicate files. Two files are assumed equal if their
|
||||
hashes are matching. The file data is not read, but only the
|
||||
precomputed hashes are used.
|
||||
.PP
|
||||
Nothing is modified.
|
||||
.SS up
|
||||
Spins up all the disks of the array.
|
||||
.PP
|
||||
Nothing is modified.
|
||||
.SS down
|
||||
Spins down all the disks of the array.
|
||||
.PP
|
||||
Nothing is modified.
|
||||
.SS pool
|
||||
Creates or updates in the \[dq]pooling\[dq] directory a virtual view of all
|
||||
the files of your disk array.
|
||||
|
|
|
|||
30
snapraid.d
30
snapraid.d
|
|
@ -14,7 +14,7 @@ Synopsis
|
|||
: [-S, --start BLKSTART] [-B, --count BLKCOUNT]
|
||||
: [-L, --error-limit NUMBER]
|
||||
: [-v, --verbose]
|
||||
: status|sync|scrub|fix|check|list|diff|dup|pool|rehash
|
||||
: status|diff|sync|scrub|fix|check|list|dup|up|down|pool|rehash
|
||||
|
||||
:snapraid [-V, --version] [-H, --help] [-C, --gen-conf CONTENT]
|
||||
|
||||
|
|
@ -375,6 +375,15 @@ Commands
|
|||
|
||||
Nothing is modified.
|
||||
|
||||
diff
|
||||
Lists all the files modified from the last "sync" that need to have
|
||||
their parity data recomputed.
|
||||
|
||||
This command doesn't check the file data, but only the file timestamp
|
||||
size and inode.
|
||||
|
||||
Nothing is modified.
|
||||
|
||||
sync
|
||||
Updates the parity information. All the modified files
|
||||
in the disk array are read, and the corresponding parity
|
||||
|
|
@ -484,15 +493,6 @@ Commands
|
|||
|
||||
Nothing is modified.
|
||||
|
||||
diff
|
||||
Lists all the files modified from the last "sync" that need to have
|
||||
their parity data recomputed.
|
||||
|
||||
This command doesn't check the file data, but only the file timestamp
|
||||
size and inode.
|
||||
|
||||
Nothing is modified.
|
||||
|
||||
dup
|
||||
Lists all the duplicate files. Two files are assumed equal if their
|
||||
hashes are matching. The file data is not read, but only the
|
||||
|
|
@ -500,6 +500,16 @@ Commands
|
|||
|
||||
Nothing is modified.
|
||||
|
||||
up
|
||||
Spins up all the disks of the array.
|
||||
|
||||
Nothing is modified.
|
||||
|
||||
down
|
||||
Spins down all the disks of the array.
|
||||
|
||||
Nothing is modified.
|
||||
|
||||
pool
|
||||
Creates or updates in the "pooling" directory a virtual view of all
|
||||
the files of your disk array.
|
||||
|
|
|
|||
54
snapraid.txt
54
snapraid.txt
|
|
@ -18,7 +18,7 @@ snapraid [-c, --conf CONFIG]
|
|||
[-S, --start BLKSTART] [-B, --count BLKCOUNT]
|
||||
[-L, --error-limit NUMBER]
|
||||
[-v, --verbose]
|
||||
status|sync|scrub|fix|check|list|diff|dup|pool|rehash
|
||||
status|diff|sync|scrub|fix|check|list|dup|up|down|pool|rehash
|
||||
|
||||
snapraid [-V, --version] [-H, --help] [-C, --gen-conf CONTENT]
|
||||
|
||||
|
|
@ -401,7 +401,18 @@ Later modifications are not taken into account.
|
|||
|
||||
Nothing is modified.
|
||||
|
||||
5.2 sync
|
||||
5.2 diff
|
||||
--------
|
||||
|
||||
Lists all the files modified from the last "sync" that need to have
|
||||
their parity data recomputed.
|
||||
|
||||
This command doesn't check the file data, but only the file timestamp
|
||||
size and inode.
|
||||
|
||||
Nothing is modified.
|
||||
|
||||
5.3 sync
|
||||
--------
|
||||
|
||||
Updates the parity information. All the modified files
|
||||
|
|
@ -428,7 +439,7 @@ but the previously compute hash information is kept.
|
|||
The "content" and "parity" files are modified if necessary.
|
||||
The files in the array are NOT modified.
|
||||
|
||||
5.3 scrub
|
||||
5.4 scrub
|
||||
---------
|
||||
|
||||
Scrubs the array, checking for silent or input/output errors in data
|
||||
|
|
@ -463,7 +474,7 @@ of each block, and to mark bad blocks.
|
|||
The "parity" files are NOT modified.
|
||||
The files in the array are NOT modified.
|
||||
|
||||
5.4 fix
|
||||
5.5 fix
|
||||
-------
|
||||
|
||||
Fix all the files and the parity data.
|
||||
|
|
@ -492,7 +503,7 @@ The "content" file is NOT modified.
|
|||
The "parity" files are modified if necessary.
|
||||
The files in the array are modified if necessary.
|
||||
|
||||
5.5 check
|
||||
5.6 check
|
||||
---------
|
||||
|
||||
Verify all the files and the parity data.
|
||||
|
|
@ -512,7 +523,7 @@ Files are identified only by path, and not by inode.
|
|||
|
||||
Nothing is modified.
|
||||
|
||||
5.6 list
|
||||
5.7 list
|
||||
--------
|
||||
|
||||
Lists all the files contained in the array at the time of the
|
||||
|
|
@ -520,17 +531,6 @@ last "sync".
|
|||
|
||||
Nothing is modified.
|
||||
|
||||
5.7 diff
|
||||
--------
|
||||
|
||||
Lists all the files modified from the last "sync" that need to have
|
||||
their parity data recomputed.
|
||||
|
||||
This command doesn't check the file data, but only the file timestamp
|
||||
size and inode.
|
||||
|
||||
Nothing is modified.
|
||||
|
||||
5.8 dup
|
||||
-------
|
||||
|
||||
|
|
@ -540,8 +540,22 @@ precomputed hashes are used.
|
|||
|
||||
Nothing is modified.
|
||||
|
||||
5.9 pool
|
||||
--------
|
||||
5.9 up
|
||||
------
|
||||
|
||||
Spins up all the disks of the array.
|
||||
|
||||
Nothing is modified.
|
||||
|
||||
5.10 down
|
||||
---------
|
||||
|
||||
Spins down all the disks of the array.
|
||||
|
||||
Nothing is modified.
|
||||
|
||||
5.11 pool
|
||||
---------
|
||||
|
||||
Creates or updates in the "pooling" directory a virtual view of all
|
||||
the files of your disk array.
|
||||
|
|
@ -555,7 +569,7 @@ view of the array. Any other regular file is left in place.
|
|||
|
||||
Nothing is modified outside the pool directory.
|
||||
|
||||
5.10 rehash
|
||||
5.12 rehash
|
||||
-----------
|
||||
|
||||
Schedules a rehash of the whole array.
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@
|
|||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "cmdline/portable.h"
|
||||
#include "cmdline/util.h" /* for malloc_nofail() */
|
||||
|
||||
/* redefine the malloc for tommy use */
|
||||
#define tommy_malloc malloc_nofail
|
||||
#define tommy_calloc calloc_nofail
|
||||
#define tommy_free free
|
||||
|
||||
#include "cmdline/portable.h"
|
||||
#include "cmdline/util.h" /* for malloc_nofail() */
|
||||
|
||||
#include "tommyhash.c"
|
||||
#include "tommyarray.c"
|
||||
#include "tommyarrayblk.c"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue