Extends the status command to show free space.

This commit is contained in:
Andrea Mazzoleni 2014-10-13 23:47:25 +02:00
parent 4886a5d13f
commit 3e2d805d08
7 changed files with 205 additions and 35 deletions

View file

@ -5,6 +5,8 @@ SnapRAID HISTORY
===========
* Adds AVX2 support to improve parity generation speed.
* A lot faster directory listing for Windows.
* More detailed 'status' report with single disk stats and
free space available.
* Prints the time spent waiting for each disk also in 'scrub'.
* The CPU usage, speed and ETA estimations are now based on the last 100
seconds rather than from the start.

4
TODO
View file

@ -5,10 +5,6 @@ This is the list of TODO items for SnapRAID.
- Next
* In status prints the allocated parity in each data disk.
In case, also takes into account the extra available space
in the parity disks.
* Avoid to do optimized move operation if more files with
same timestamp/size are present.

View file

@ -1421,12 +1421,66 @@ int filephy(const char* file, uint64_t size, uint64_t* physical)
return 0;
}
int fsinfo(const char* path, int* has_persistent_inode)
int fsinfo(const char* path, int* has_persistent_inode, uint64_t* free_space)
{
(void)path;
/* all FAT/exFAT/NTFS when managed from Windows have persistent inodes */
*has_persistent_inode = 1;
if (has_persistent_inode)
*has_persistent_inode = 1;
if (free_space) {
ULARGE_INTEGER total_free_bytes;
DWORD attr;
char dir[PATH_MAX];
if (strlen(path) + 1 > sizeof(dir)) {
windows_errno(ERROR_BUFFER_OVERFLOW);
return -1;
}
strcpy(dir, path);
/* get the file attributes */
attr = GetFileAttributesW(convert(dir));
if (attr == INVALID_FILE_ATTRIBUTES) {
DWORD error = GetLastError();
if (error != ERROR_FILE_NOT_FOUND) {
windows_errno(error);
return -1;
}
/* if it doesn't exist, we assume a file */
/* and we check for the containing dir */
attr = 0;
}
/* if it's not a directory, truncate the file name */
if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
char* slash = strrchr(dir, '/');
/**
* Cut the file name, but leave the last slash.
*
* This is done because a MSDN comment about using of UNC paths.
*
* MSDN 'GetDiskFreeSpaceEx function'
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa364937%28v=vs.85%29.aspx
* If this parameter is a UNC name, it must include a trailing backslash,
* for example, "\\MyServer\MyShare\".
*/
if (slash)
slash[1] = 0;
}
/* get the free space of the directory */
/* note that it must be a directory */
if (!GetDiskFreeSpaceExW(convert(dir), 0, 0, &total_free_bytes)) {
windows_errno(GetLastError());
return -1;
}
*free_space = total_free_bytes.QuadPart;
}
return 0;
}

View file

@ -243,7 +243,7 @@ int filephy(const char* path, uint64_t size, uint64_t* physical);
* Checks if the underline filesystem support persistent inodes.
* Returns -1 on error, 0 on success.
*/
int fsinfo(const char* path, int* has_persistent_inode);
int fsinfo(const char* path, int* has_persistent_inode, uint64_t* free_space);
/**
* Get the tick counter value.

View file

@ -1352,7 +1352,7 @@ void state_scan(struct snapraid_state* state, int output)
printf("Scanning disk %s...\n", disk->name);
/* check if the disk supports persistent inodes */
ret = fsinfo(disk->dir, &has_persistent_inode);
ret = fsinfo(disk->dir, &has_persistent_inode, 0);
if (ret < 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error accessing disk '%s' to get filesystem info. %s.\n", disk->dir, strerror(errno));

View file

@ -35,6 +35,14 @@ unsigned day_ago(time_t time, time_t now)
#define GRAPH_COLUMN 70
#define GRAPH_ROW 15
static unsigned perc(uint64_t part, uint64_t total)
{
if (!total)
return 0;
return (unsigned)(part * 100 / total);
}
int state_status(struct snapraid_state* state)
{
block_off_t blockmax;
@ -55,25 +63,60 @@ int state_status(struct snapraid_state* state)
unsigned file_fragmented;
unsigned extra_fragment;
uint64_t file_size;
uint64_t file_block_count;
uint64_t file_block_free;
uint64_t base = 1024 * 1024 * (uint64_t)1024;
block_off_t parity_block_free;
unsigned unsynced_blocks;
uint64_t free_space;
uint64_t wasted;
int ret;
/* get the present time */
now = time(0);
blockmax = parity_size(state);
fprintf(stdlog, "summary:block_size:%u\n", state->block_size);
fprintf(stdlog, "summary:parity_block_count:%u\n", blockmax);
/* get the free space on the first parity disk */
ret = fsinfo(state->parity_path[0], 0, &free_space);
if (ret < 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error accessing file '%s' to get filesystem info. %s.\n", state->parity_path[0], strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
parity_block_free = free_space / state->block_size;
fprintf(stdlog, "summary:parity_block_free:%s:%u\n", lev_config_name(0), parity_block_free);
printf("\n");
printf(" Files Fragmented Excess Wasted Used Free Use Name\n");
printf(" Files Fragments GiB GiB GiB \n");
/* count fragments */
file_count = 0;
file_size = 0;
file_block_count = 0;
file_block_free = 0;
file_fragmented = 0;
extra_fragment = 0;
for (node_disk = state->disklist; node_disk != 0; node_disk = node_disk->next) {
struct snapraid_disk* disk = node_disk->data;
tommy_node* node;
block_off_t i;
unsigned disk_file_count = 0;
unsigned disk_file_fragmented = 0;
unsigned disk_extra_fragment = 0;
block_off_t disk_block_count = 0;
uint64_t disk_file_size = 0;
block_off_t disk_block_free;
block_off_t disk_block_max_by_space;
block_off_t disk_block_max_by_parity;
block_off_t disk_block_max;
/* for each file in the disk */
node = disk->filelist;
@ -104,32 +147,76 @@ int state_status(struct snapraid_state* state)
++file_fragmented;
++disk_file_fragmented;
}
disk_block_count += file->blockmax;
}
/* count files */
++file_count;
++disk_file_count;
file_size += file->size;
file_block_count += file->blockmax;
disk_file_size += file->size;
}
/* get the free space on the disk */
ret = fsinfo(disk->dir, 0, &free_space);
if (ret < 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error accessing disk '%s' to get filesystem info. %s.\n", disk->dir, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
disk_block_free = free_space / state->block_size;
disk_block_max_by_space = disk_block_count + disk_block_free;
disk_block_max_by_parity = blockmax + parity_block_free;
/* the maximum usable space in a disk is limited by both */
/* the disk size and by the parity size */
disk_block_max = disk_block_max_by_space < disk_block_max_by_parity
? disk_block_max_by_space : disk_block_max_by_parity;
file_block_free += disk_block_max - disk_block_count;
printf("%8u", disk_file_count);
printf("%8u", disk_file_fragmented);
printf("%8u", disk_extra_fragment);
wasted = disk_block_count * (uint64_t)state->block_size - disk_file_size;
printf("%6" PRIu64 ".%01" PRIu64, wasted / base, (wasted * 10 / base) % 10);
printf("%8" PRIu64, disk_file_size / base);
printf("%8" PRIu64, (disk_block_max - disk_block_count) * (uint64_t)state->block_size / base);
printf(" %3u%%", perc(disk_block_count, disk_block_max));
printf(" %s\n", disk->name);
fprintf(stdlog, "summary:disk_file_count:%s:%u\n", disk->name, disk_file_count);
fprintf(stdlog, "summary:disk_block_count:%s:%u\n", disk->name, disk_block_count);
fprintf(stdlog, "summary:disk_fragmented_file_count:%s:%u\n", disk->name, disk_file_fragmented);
fprintf(stdlog, "summary:disk_excess_fragment_count:%s:%u\n", disk->name, disk_extra_fragment);
fprintf(stdlog, "summary:disk_file_size:%s:%" PRIu64 "\n", disk->name, disk_file_size);
fprintf(stdlog, "summary:disk_block_free:%s:%u\n", disk->name, disk_block_free);
fprintf(stdlog, "summary:disk_block_max_by_space:%s:%u\n", disk->name, disk_block_max_by_space);
fprintf(stdlog, "summary:disk_block_max_by_parity:%s:%u\n", disk->name, disk_block_max_by_parity);
fprintf(stdlog, "summary:disk_block_max:%s:%u\n", disk->name, disk_block_max);
}
printf("\n");
printf("Files: %u\n", file_count);
printf("Fragmented files: %u\n", file_fragmented);
printf("Excess fragments: %u\n", extra_fragment);
printf("Files size: %" PRIu64 " GiB\n", file_size / (1024 * 1024 * 1024) );
printf("Parity size: %" PRIu64 " GiB\n", blockmax * (uint64_t)state->block_size / (1024 * 1024 * 1024) );
/* totals */
printf(" --------------------------------------------------------------------------\n");
printf("%8u", file_count);
printf("%8u", file_fragmented);
printf("%8u", extra_fragment);
wasted = file_block_count * state->block_size - file_size;
printf("%6" PRIu64 ".%01" PRIu64, wasted / base, (wasted * 10 / base) % 10);
printf("%8" PRIu64, file_size / base);
printf("%8" PRIu64, file_block_free * state->block_size / base);
printf(" %3u%%", perc(file_block_count, file_block_count + file_block_free));
printf("\n");
fprintf(stdlog, "summary:file_count:%u\n", file_count);
fprintf(stdlog, "summary:file_block_count:%" PRIu64 "\n", file_block_count);
fprintf(stdlog, "summary:fragmented_file_count:%u\n", file_fragmented);
fprintf(stdlog, "summary:excess_fragment_count:%u\n", extra_fragment);
fprintf(stdlog, "summary:file_size:%" PRIu64 "\n", file_size);
@ -242,16 +329,18 @@ int state_status(struct snapraid_state* state)
bar[i] = step;
}
printf("\n\n");
/* print the graph */
for (y = 0; y < GRAPH_ROW; ++y) {
if (y == 0)
printf("%3u%% ", barmax * 100 / count);
printf("%3u%%|", barmax * 100 / count);
else if (y == GRAPH_ROW - 1)
printf(" 0%% ");
printf(" 0%%|");
else if (y == GRAPH_ROW / 2)
printf("%3u%% ", barmax * 50 / count);
printf("%3u%%|", barmax * 50 / count);
else
printf(" ");
printf(" |");
for (x = 0; x < GRAPH_COLUMN; ++x) {
unsigned pivot = barmax * (GRAPH_ROW - y) / GRAPH_ROW;
/* if it's the baseline */

View file

@ -205,31 +205,60 @@ int filephy(const char* path, uint64_t size, uint64_t* physical)
return 0;
}
int fsinfo(const char* path, int* has_persistent_inode)
int fsinfo(const char* path, int* has_persistent_inode, uint64_t* free_space)
{
#if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
struct statfs st;
if (statfs(path, &st) != 0)
return -1;
if (statfs(path, &st) != 0) {
char dir[PATH_MAX];
char* slash;
if (errno != ENOENT) {
return -1;
}
/* if it doesn't exist, we assume a file */
/* and we check for the containing dir */
if (strlen(path) + 1 > sizeof(dir)) {
errno = ENAMETOOLONG;
return -1;
}
strcpy(dir, path);
slash = strrchr(dir, '/');
if (!slash)
return -1;
*slash = 0;
if (statfs(dir, &st) != 0)
return -1;
}
/* to get the fs type check "man stat" or "stat -f -t FILE" */
if (has_persistent_inode)
switch (st.f_type) {
case 0x65735546 : /* FUSE, "fuseblk" in the stat command */
case 0x4d44 : /* VFAT, "msdos" in the stat command */
*has_persistent_inode = 0;
break;
default :
/* by default assume yes */
*has_persistent_inode = 1;
break;
}
switch (st.f_type) {
case 0x65735546 : /* FUSE, "fuseblk" in the stat command */
case 0x4d44 : /* VFAT, "msdos" in the stat command */
*has_persistent_inode = 0;
break;
default :
/* by default assume yes */
*has_persistent_inode = 1;
break;
}
if (free_space)
*free_space = st.f_bsize * (uint64_t)st.f_bfree;
#else
(void)path;
/* by default assume yes */
*has_persistent_inode = 1;
if (has_persistent_inode)
*has_persistent_inode = 1;
if (free_space)
*free_space = 0;
#endif
return 0;