From baae26a21cde1188f108c6791f247140fab4e214 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 9 Nov 2017 15:41:52 +0100 Subject: [PATCH] dvrvfs: change the fsid logic (use st_dev for NFS), fixes #4713 --- Makefile | 1 + src/dvr/dvr_vfsmgr.c | 149 +++++++++++++++++++++++++------------------ src/tvhvfs.c | 45 +++++++++++++ src/tvhvfs.h | 22 ++++++- 4 files changed, 152 insertions(+), 65 deletions(-) create mode 100644 src/tvhvfs.c diff --git a/Makefile b/Makefile index eed98315a..8b08f4e3a 100644 --- a/Makefile +++ b/Makefile @@ -208,6 +208,7 @@ SRCS-1 = \ src/prop.c \ src/utils.c \ src/wrappers.c \ + src/tvhvfs.c \ src/access.c \ src/tcp.c \ src/udp.c \ diff --git a/src/dvr/dvr_vfsmgr.c b/src/dvr/dvr_vfsmgr.c index 41ff2edb3..79fccb9d4 100644 --- a/src/dvr/dvr_vfsmgr.c +++ b/src/dvr/dvr_vfsmgr.c @@ -50,17 +50,17 @@ static tasklet_t dvr_disk_space_tasklet; * */ static dvr_vfs_t * -dvr_vfs_find(dvr_vfs_t *old, tvh_fsid_t id) +dvr_vfs_find(dvr_vfs_t *old, tvh_fsid_t *id) { dvr_vfs_t *dv; - if (old && old->fsid == id) + if (old && tvh_vfs_fsid_match(&old->fsid, id)) return old; LIST_FOREACH(dv, &dvrvfs_list, link) - if (dv->fsid == id) + if (tvh_vfs_fsid_match(&dv->fsid, id)) return dv; dv = calloc(1, sizeof(*dv)); - dv->fsid = id; + dv->fsid = *id; LIST_INSERT_HEAD(&dvrvfs_list, dv, link); return dv; } @@ -69,9 +69,19 @@ static dvr_vfs_t * dvr_vfs_find1(dvr_vfs_t *old, htsmsg_t *m) { int64_t v; - - if (!htsmsg_get_s64(m, "fsid", &v)) - return dvr_vfs_find(old, v); + tvh_fsid_t fsid; + const char *s; + + if (!htsmsg_get_s64(m, "fsid", &v)) { + fsid.fsid = v; + fsid.id[0] = '\0'; + return dvr_vfs_find(old, &fsid); + } else if ((s = htsmsg_get_str(m, "fsid0")) != NULL) { + fsid.fsid = 0; + strncpy(fsid.id, s, sizeof(fsid.id)-1); + fsid.id[sizeof(fsid.id)-1] = '\0'; + return dvr_vfs_find(old, &fsid); + } return NULL; } @@ -83,11 +93,11 @@ dvr_vfs_refresh_entry(dvr_entry_t *de) { htsmsg_field_t *f; htsmsg_t *m; - struct statvfs vst; struct stat st; dvr_vfs_t *vfs = NULL; uint64_t size; const char *filename; + tvh_fsid_t fsid; lock_assert(&global_lock); if (de->de_files == NULL) @@ -100,14 +110,18 @@ dvr_vfs_refresh_entry(dvr_entry_t *de) vfs->used_size = size <= vfs->used_size ? vfs->used_size - size : 0; } filename = htsmsg_get_str(m, "filename"); - if(filename == NULL || - statvfs(filename, &vst) < 0 || stat(filename, &st) < 0) { + if (filename == NULL || stat(filename, &st) < 0) { tvherror(LS_DVR, "unable to stat file '%s'", filename); goto rem; } - vfs = dvr_vfs_find(vfs, tvh_fsid(vst.f_fsid)); + if (tvh_vfs_fsid_build(filename, NULL, &fsid)) + goto rem; + vfs = dvr_vfs_find(vfs, &fsid); if (vfs && st.st_size >= 0) { - htsmsg_set_s64(m, "fsid", tvh_fsid(vst.f_fsid)); + if (fsid.fsid != 0) + htsmsg_set_s64(m, "fsid", fsid.fsid); + else + htsmsg_set_str(m, "fsid0", fsid.id); htsmsg_set_s64(m, "size", st.st_size); vfs->used_size += st.st_size; } else { @@ -180,20 +194,22 @@ dvr_disk_space_cleanup(dvr_config_t *cfg, int include_active) time_t stoptime; int64_t requiredBytes, maximalBytes, availBytes, usedBytes, diskBytes; int64_t clearedBytes = 0, fileSize; - tvh_fsid_t filesystemId; struct statvfs diskdata; struct tm tm; int loops = 0; char tbuf[64]; const char *configName; dvr_vfs_t *dvfs; + tvh_fsid_t fsid, fsid2; + + if (!cfg || !cfg->dvr_enabled) + return -1; - if (!cfg || !cfg->dvr_enabled || statvfs(cfg->dvr_storage, &diskdata) == -1) + if (tvh_vfs_fsid_build(cfg->dvr_storage, &diskdata, &fsid)) return -1; - dvfs = dvr_vfs_find(NULL, tvh_fsid(diskdata.f_fsid)); + dvfs = dvr_vfs_find(NULL, &fsid); - filesystemId = tvh_fsid(diskdata.f_fsid); availBytes = diskdata.f_frsize * (int64_t)diskdata.f_bavail; requiredBytes = MIB(cfg->dvr_cleanup_threshold_free); diskBytes = diskdata.f_frsize * (int64_t)diskdata.f_blocks; @@ -237,12 +253,12 @@ dvr_disk_space_cleanup(dvr_config_t *cfg, int include_active) if (dvr_get_filename(de) == NULL || dvr_get_filesize(de, DVR_FILESIZE_TOTAL) <= 0) continue; - if(statvfs(dvr_get_filename(de), &diskdata) == -1) + if (tvh_vfs_fsid_build(dvr_get_filename(de), NULL, &fsid2)) continue; /* Checking for the same config is useless as it's storage path might be changed meanwhile */ /* Check for the same file system instead */ - if (filesystemId == 0 || tvh_fsid(diskdata.f_fsid) != filesystemId) + if (tvh_vfs_fsid_match(&fsid, &fsid2) == 0) continue; oldest = de; // the oldest one until now @@ -303,6 +319,7 @@ dvr_disk_space_check() int64_t requiredBytes, maximalBytes, availBytes, usedBytes; int idx = 0, cleanupDone = 0; dvr_vfs_t *dvfs; + tvh_fsid_t fsid; pthread_mutex_lock(&global_lock); @@ -313,48 +330,51 @@ dvr_disk_space_check() LIST_FOREACH(cfg, &dvrconfigs, config_link) { idx++; - if (cfg->dvr_enabled && - !cleanupDone && - idx >= dvr_disk_space_config_idx && - statvfs(cfg->dvr_storage, &diskdata) != -1) - { - dvfs = dvr_vfs_find(NULL, tvh_fsid(diskdata.f_fsid)); - - availBytes = diskdata.f_frsize * (int64_t)diskdata.f_bavail; - usedBytes = dvfs->used_size; - requiredBytes = MIB(cfg->dvr_cleanup_threshold_free); - maximalBytes = MIB(cfg->dvr_cleanup_threshold_used); - - if (availBytes < requiredBytes || ((maximalBytes < usedBytes) && cfg->dvr_cleanup_threshold_used)) { - LIST_FOREACH(de, &dvrentries, de_global_link) { - - /* only start cleanup if we are actually writing files right now */ - if (de->de_sched_state != DVR_RECORDING || !de->de_config || de->de_config != cfg) - continue; - - if (availBytes < requiredBytes) { - tvhwarn(LS_DVR,"running out of free disk space for dvr config \"%s\", required free space \"%"PRId64" MiB\", current free space \"%"PRId64" MiB\"", - cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile", - TOMIB(requiredBytes), TOMIB(availBytes)); - } else { - tvhwarn(LS_DVR,"running out of used disk space for dvr config \"%s\", required used space \"%"PRId64" MiB\", current used space \"%"PRId64" MiB\"", - cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile", - TOMIB(maximalBytes), TOMIB(usedBytes)); - } - - /* only cleanup one directory at the time as the system needs time to delete the actual files */ - dvr_disk_space_cleanup(de->de_config, 1); - cleanupDone = 1; - dvr_disk_space_config_idx = idx; - break; + if (!cfg->dvr_enabled) + continue; + if (cleanupDone) + continue; + if (idx < dvr_disk_space_config_idx) + continue; + if (tvh_vfs_fsid_build(cfg->dvr_storage, &diskdata, &fsid)) + continue; + + dvfs = dvr_vfs_find(NULL, &fsid); + + availBytes = diskdata.f_frsize * (int64_t)diskdata.f_bavail; + usedBytes = dvfs->used_size; + requiredBytes = MIB(cfg->dvr_cleanup_threshold_free); + maximalBytes = MIB(cfg->dvr_cleanup_threshold_used); + + if (availBytes < requiredBytes || ((maximalBytes < usedBytes) && cfg->dvr_cleanup_threshold_used)) { + LIST_FOREACH(de, &dvrentries, de_global_link) { + + /* only start cleanup if we are actually writing files right now */ + if (de->de_sched_state != DVR_RECORDING || !de->de_config || de->de_config != cfg) + continue; + + if (availBytes < requiredBytes) { + tvhwarn(LS_DVR,"running out of free disk space for dvr config \"%s\", required free space \"%"PRId64" MiB\", current free space \"%"PRId64" MiB\"", + cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile", + TOMIB(requiredBytes), TOMIB(availBytes)); + } else { + tvhwarn(LS_DVR,"running out of used disk space for dvr config \"%s\", required used space \"%"PRId64" MiB\", current used space \"%"PRId64" MiB\"", + cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile", + TOMIB(maximalBytes), TOMIB(usedBytes)); } - if (!cleanupDone) - goto checking; - } else { -checking: - tvhtrace(LS_DVR, "checking free and used disk space for config \"%s\" : OK", - cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile"); + + /* only cleanup one directory at the time as the system needs time to delete the actual files */ + dvr_disk_space_cleanup(de->de_config, 1); + cleanupDone = 1; + dvr_disk_space_config_idx = idx; + break; } + if (!cleanupDone) + goto checking; + } else { +checking: + tvhtrace(LS_DVR, "checking free and used disk space for config \"%s\" : OK", + cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile"); } } @@ -374,13 +394,14 @@ dvr_get_disk_space_update(const char *path, int locked) { struct statvfs diskdata; dvr_vfs_t *dvfs; + tvh_fsid_t fsid; - if(statvfs(path, &diskdata) == -1) + if (tvh_vfs_fsid_build(path, &diskdata, &fsid)) return; if (!locked) pthread_mutex_lock(&global_lock); - dvfs = dvr_vfs_find(NULL, tvh_fsid(diskdata.f_fsid)); + dvfs = dvr_vfs_find(NULL, &fsid); if (!locked) pthread_mutex_unlock(&global_lock); @@ -439,15 +460,19 @@ dvr_vfs_rec_start_check(dvr_config_t *cfg) { struct statvfs diskdata; dvr_vfs_t *dvfs; + tvh_fsid_t fsid; int64_t availBytes, requiredBytes, usedBytes, maximalBytes, cleanedBytes; lock_assert(&global_lock); - if (!cfg || !cfg->dvr_enabled || statvfs(cfg->dvr_storage, &diskdata) == -1) + if (!cfg || !cfg->dvr_enabled) return 0; + if (tvh_vfs_fsid_build(cfg->dvr_storage, &diskdata, &fsid)) + return 0; + availBytes = diskdata.f_frsize * (int64_t)diskdata.f_bavail; requiredBytes = MIB(cfg->dvr_cleanup_threshold_free); maximalBytes = MIB(cfg->dvr_cleanup_threshold_used); - dvfs = dvr_vfs_find(NULL, tvh_fsid(diskdata.f_fsid)); + dvfs = dvr_vfs_find(NULL, &fsid); usedBytes = dvfs->used_size; if (availBytes < requiredBytes || ((maximalBytes < usedBytes) && cfg->dvr_cleanup_threshold_used)) { diff --git a/src/tvhvfs.c b/src/tvhvfs.c new file mode 100644 index 000000000..d3c6cd1dc --- /dev/null +++ b/src/tvhvfs.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Jaroslav Kysela + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "tvheadend.h" +#include "tvhvfs.h" + +int tvh_vfs_fsid_build(const char *path, struct statvfs *vfs, tvh_fsid_t *dst) +{ + struct statvfs _vfs; + struct stat st; + + if (vfs == NULL) + vfs = &_vfs; + + if (statvfs(path, vfs) == -1) + return -1; + + dst->fsid = tvh_fsid(vfs->f_fsid); + dst->id[0] = '\0'; + if (dst->fsid == 0) { + if (stat(path, &st) == -1) + return -1; + if (major(st.st_dev) == 0 && minor(st.st_dev) == 0) + return -1; + snprintf(dst->id, sizeof(dst->id), "dev=%u:%u", major(st.st_dev), minor(st.st_dev)); + } + + return 0; +} diff --git a/src/tvhvfs.h b/src/tvhvfs.h index 7e8ff29d2..5a33672f6 100644 --- a/src/tvhvfs.h +++ b/src/tvhvfs.h @@ -23,12 +23,28 @@ #include #define statvfs statfs #define fstatvfs fstatfs -#define tvh_fsid_t uint64_t #define tvh_fsid(x) (((uint64_t)x.__val[0] << 32) | (x.__val[1])) #else #include -#define tvh_fsid_t unsigned long -#define tvh_fsid(x) (x) +#define tvh_fsid(x) ((uint64_t)x) #endif +struct statvfs; + +typedef struct { + uint64_t fsid; + char id[64]; +} tvh_fsid_t; + +int tvh_vfs_fsid_build(const char *path, struct statvfs *vfs, tvh_fsid_t *dst); + +static inline int tvh_vfs_fsid_match(tvh_fsid_t *a, tvh_fsid_t *b) +{ + if (a->fsid != 0 && b->fsid != 0) + return a->fsid == b->fsid; + if (a->fsid == 0 && b->fsid == 0) + return strcmp(a->id, b->id) == 0; + return 0; +} + #endif /* __TVH_VFS_H__ */ -- 2.47.3