]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
DVR: move disk space code to vfsmgr
authorJaroslav Kysela <perex@perex.cz>
Thu, 3 Dec 2015 07:57:07 +0000 (08:57 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 3 Dec 2015 07:57:07 +0000 (08:57 +0100)
Makefile
src/dvr/dvr_rec.c
src/dvr/dvr_vfsmgr.c [new file with mode: 0644]

index 20721a849165576dd6d07762d7e4bec0e3eddd1c..802ab071257e33d86dff272a156ad9c0967d080f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -275,8 +275,9 @@ SRCS-2 += \
        src/dvr/dvr_rec.c \
        src/dvr/dvr_autorec.c \
        src/dvr/dvr_timerec.c \
+       src/dvr/dvr_vfsmgr.c \
        src/dvr/dvr_config.c \
-       src/dvr/dvr_cutpoints.c \
+       src/dvr/dvr_cutpoints.c
 
 SRCS-2 += \
        src/webui/webui.c \
index 6e78eb2d048d085df991287e7bc13d2f816554a1..c32df4364b37d888232801f82fecd69823c7c09a 100644 (file)
@@ -33,8 +33,6 @@
 #include "dvr.h"
 #include "spawn.h"
 #include "service.h"
-#include "plumbing/tsfix.h"
-#include "plumbing/globalheaders.h"
 #include "htsp_server.h"
 #include "atomic.h"
 #include "intlconv.h"
 
 #include "muxer.h"
 
-#if ENABLE_ANDROID
-#include <sys/vfs.h>
-#define statvfs statfs
-#define fstatvfs fstatfs
-#else
-#include <sys/statvfs.h>
-#endif
-
 /**
  *
  */
@@ -1537,280 +1527,3 @@ dvr_thread_epilog(dvr_entry_t *de, const char *dvr_postproc)
   if(dvr_postproc && dvr_postproc[0])
     dvr_spawn_postcmd(de, dvr_postproc, NULL);
 }
-
-/**
- *
- */
-static int dvr_disk_space_config_idx;
-static int dvr_disk_space_config_size;
-static time_t dvr_disk_space_config_lastdelete;
-static int64_t dvr_bfree;
-static int64_t dvr_btotal;
-static pthread_mutex_t dvr_disk_space_mutex;
-static gtimer_t dvr_disk_space_timer;
-static tasklet_t dvr_disk_space_tasklet;
-
-/**
- * Cleanup old recordings for this config until the dvr_cleanup_threshold is reached
- * Only "Keep until space needed" recordings are deleted, starting with the oldest one
- */
-static int64_t
-dvr_disk_space_cleanup(dvr_config_t *cfg)
-{
-  dvr_entry_t *de, *oldest;
-  time_t stoptime;
-  int64_t requiredBytes, availBytes;
-  int64_t clearedBytes = 0, fileSize;
-  unsigned long int filesystemId;
-  struct statvfs diskdata;
-  struct tm tm;
-  int loops = 0;
-  char tbuf[64];
-  const char *configName;
-
-  if (!cfg || !cfg->dvr_enabled || statvfs(cfg->dvr_storage, &diskdata) == -1)
-    return -1;
-
-  filesystemId  = diskdata.f_fsid;
-  availBytes    = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
-  requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;
-  configName    = cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile";
-
-  /* When deleting a file from the disk, the system needs some time to actually do this */
-  /* If calling this function to fast after the previous call, statvfs might be wrong/not updated yet */
-  /* So we are risking to delete more files than needed, so allow 10s for the system to handle previous deletes */
-  if (dvr_disk_space_config_lastdelete + 10 > dispatch_clock) {
-    tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\" is not allowed now", configName);
-    return -1;
-  }
-
-  if (diskdata.f_bsize * (int64_t)diskdata.f_blocks < requiredBytes) {
-    tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\" is smaller than the total disk size!",
-        configName, requiredBytes/(int64_t)1024/(int64_t)1024);
-    return -1;
-  }
-
-  tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
-      configName, requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
-
-  while (availBytes < requiredBytes) {
-    oldest = NULL;
-    stoptime = dispatch_clock;
-
-    LIST_FOREACH(de, &dvrentries, de_global_link) {
-      if (de->de_sched_state != DVR_COMPLETED &&
-          de->de_sched_state != DVR_MISSED_TIME)
-        continue;
-
-      if (dvr_entry_get_stop_time(de) > stoptime)
-        continue;
-
-      if (dvr_entry_get_removal_days(de) != DVR_RET_SPACENEED) // only remove the allowed ones
-        continue;
-
-      if (dvr_get_filename(de) == NULL || dvr_get_filesize(de) <= 0)
-        continue;
-
-      if(statvfs(dvr_get_filename(de), &diskdata) == -1)
-        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 || diskdata.f_fsid != filesystemId)
-        continue;
-
-      oldest = de; // the oldest one until now
-      stoptime = dvr_entry_get_stop_time(de);
-    }
-
-    if (oldest) {
-      fileSize = dvr_get_filesize(oldest);
-      availBytes += fileSize;
-      clearedBytes += fileSize;
-
-      localtime_r(&stoptime, &tm);
-      if (strftime(tbuf, sizeof(tbuf), "%F %T", &tm) <= 0)
-        *tbuf = 0;
-      tvhlog(LOG_INFO, "dvr","Delete \"until space needed\" recording \"%s\" with stop time \"%s\" and file size \"%ld MB\"",
-          lang_str_get(oldest->de_title, NULL), tbuf, fileSize/(int64_t)1024/(int64_t)1024);
-
-      dvr_disk_space_config_lastdelete = dispatch_clock;
-      dvr_entry_delete(oldest, 1);    // delete actual file
-      if (dvr_entry_get_retention_days(oldest) == DVR_RET_ONREMOVE)
-        dvr_entry_destroy(oldest, 1); // also delete database entry
-    }
-    else {
-      tvhlog(LOG_WARNING, "dvr","%s \"until space needed\" recordings found for config \"%s\", you are running out of disk space very soon!",
-          loops > 0 ? "Not enough" : "No", configName);
-      goto finish;
-    }
-
-    loops++;
-    if (loops >= 10) {
-      tvhlog(LOG_WARNING, "dvr","Not able to clear the required disk space after deleting %i \"until space needed\" recordings...", loops);
-      goto finish;
-    }
-  }
-
-finish:
-  tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", cleared \"%ld MB\" of disk space, new free disk space \"%ld MB\"",
-      configName, clearedBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
-
-  return clearedBytes;
-}
-
-/**
- * Check for each dvr config if the free disk size is below the dvr_cleanup_threshold
- * If so and we are using the dvr config ATM (active recording), we start the cleanup procedure
- */
-static void
-dvr_disk_space_check()
-{
-  dvr_config_t *cfg;
-  dvr_entry_t *de;
-  struct statvfs diskdata;
-  int64_t requiredBytes, availBytes;
-  int idx = 0, cleanupDone = 0;
-
-  dvr_disk_space_config_idx++;
-  if (dvr_disk_space_config_idx > dvr_disk_space_config_size)
-    dvr_disk_space_config_idx = 1;
-
-  LIST_FOREACH(cfg, &dvrconfigs, config_link)
-  {
-    idx++;
-
-    if (cfg->dvr_enabled &&
-        !cleanupDone &&
-        idx >= dvr_disk_space_config_idx &&
-        statvfs(cfg->dvr_storage, &diskdata) != -1)
-    {
-      availBytes = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
-      requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;
-
-      if (availBytes < requiredBytes) {
-        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;
-
-          tvhlog(LOG_WARNING, "dvr","running out of free disk space for dvr config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
-              cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile",
-              requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
-
-          /* only cleanup one directory at the time as the system needs time to delete the actual files */
-          dvr_disk_space_cleanup(de->de_config);
-          cleanupDone = 1;
-          dvr_disk_space_config_idx = idx;
-          break;
-        }
-      }
-      else {
-        tvhlog(LOG_DEBUG, "dvr","checking free disk space for config \"%s\" : OK",
-            cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile");
-      }
-    }
-  }
-
-  if (!cleanupDone)
-    dvr_disk_space_config_idx = 0;
-
-  dvr_disk_space_config_size = idx;
-}
-/**
- *
- */
-static void
-dvr_get_disk_space_update(const char *path)
-{
-  struct statvfs diskdata;
-
-  if(statvfs(path, &diskdata) == -1)
-    return;
-
-  dvr_bfree = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
-  dvr_btotal = diskdata.f_bsize * (int64_t)diskdata.f_blocks;
-}
-
-/**
- *
- */
-static void
-dvr_get_disk_space_tcb(void *opaque, int dearmed)
-{
-  if (!dearmed) {
-    htsmsg_t *m = htsmsg_create_map();
-    pthread_mutex_lock(&dvr_disk_space_mutex);
-
-    /* update disk space from default dvr config */
-    dvr_get_disk_space_update((char *)opaque);
-    htsmsg_add_s64(m, "freediskspace", dvr_bfree);
-    htsmsg_add_s64(m, "totaldiskspace", dvr_btotal);
-
-    /* check free disk space for each dvr config and start cleanup if needed */
-    dvr_disk_space_check();
-    pthread_mutex_unlock(&dvr_disk_space_mutex);
-
-    notify_by_msg("diskspaceUpdate", m, 0);
-  }
-
-  free(opaque);
-}
-
-static void
-dvr_get_disk_space_cb(void *aux)
-{
-  dvr_config_t *cfg;
-  char *path;
-
-  lock_assert(&global_lock);
-
-  cfg = dvr_config_find_by_name_default(NULL);
-  if (cfg->dvr_storage && cfg->dvr_storage[0]) {
-    path = strdup(cfg->dvr_storage);
-    tasklet_arm(&dvr_disk_space_tasklet, dvr_get_disk_space_tcb, path);
-  }
-  gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 15);
-}
-
-/**
- *
- */
-void
-dvr_disk_space_init(void)
-{
-  dvr_config_t *cfg = dvr_config_find_by_name_default(NULL);
-  pthread_mutex_init(&dvr_disk_space_mutex, NULL);
-  dvr_get_disk_space_update(cfg->dvr_storage);
-  gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 5);
-}
-
-/**
- *
- */
-void
-dvr_disk_space_done(void)
-{
-  tasklet_disarm(&dvr_disk_space_tasklet);
-  gtimer_disarm(&dvr_disk_space_timer);
-}
-
-/**
- *
- */
-int
-dvr_get_disk_space(int64_t *bfree, int64_t *btotal)
-{
-  int res = 0;
-
-  pthread_mutex_lock(&dvr_disk_space_mutex);
-  if (dvr_bfree || dvr_btotal) {
-    *bfree = dvr_bfree;
-    *btotal = dvr_btotal;
-  } else {
-    res = -EINVAL;
-  }
-  pthread_mutex_unlock(&dvr_disk_space_mutex);
-  return res;
-}
diff --git a/src/dvr/dvr_vfsmgr.c b/src/dvr/dvr_vfsmgr.c
new file mode 100644 (file)
index 0000000..7d5a1c4
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ *  Digital Video Recorder - file system management
+ *  Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <pthread.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <libgen.h> /* basename */
+
+#include "tvheadend.h"
+#include "dvr.h"
+#include "atomic.h"
+#include "notify.h"
+
+#if ENABLE_ANDROID
+#include <sys/vfs.h>
+#define statvfs statfs
+#define fstatvfs fstatfs
+#else
+#include <sys/statvfs.h>
+#endif
+
+static int dvr_disk_space_config_idx;
+static int dvr_disk_space_config_size;
+static time_t dvr_disk_space_config_lastdelete;
+static int64_t dvr_bfree;
+static int64_t dvr_btotal;
+static pthread_mutex_t dvr_disk_space_mutex;
+static gtimer_t dvr_disk_space_timer;
+static tasklet_t dvr_disk_space_tasklet;
+
+/**
+ * Cleanup old recordings for this config until the dvr_cleanup_threshold is reached
+ * Only "Keep until space needed" recordings are deleted, starting with the oldest one
+ */
+static int64_t
+dvr_disk_space_cleanup(dvr_config_t *cfg)
+{
+  dvr_entry_t *de, *oldest;
+  time_t stoptime;
+  int64_t requiredBytes, availBytes;
+  int64_t clearedBytes = 0, fileSize;
+  unsigned long int filesystemId;
+  struct statvfs diskdata;
+  struct tm tm;
+  int loops = 0;
+  char tbuf[64];
+  const char *configName;
+
+  if (!cfg || !cfg->dvr_enabled || statvfs(cfg->dvr_storage, &diskdata) == -1)
+    return -1;
+
+  filesystemId  = diskdata.f_fsid;
+  availBytes    = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
+  requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;
+  configName    = cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile";
+
+  /* When deleting a file from the disk, the system needs some time to actually do this */
+  /* If calling this function to fast after the previous call, statvfs might be wrong/not updated yet */
+  /* So we are risking to delete more files than needed, so allow 10s for the system to handle previous deletes */
+  if (dvr_disk_space_config_lastdelete + 10 > dispatch_clock) {
+    tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\" is not allowed now", configName);
+    return -1;
+  }
+
+  if (diskdata.f_bsize * (int64_t)diskdata.f_blocks < requiredBytes) {
+    tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\" is smaller than the total disk size!",
+        configName, requiredBytes/(int64_t)1024/(int64_t)1024);
+    return -1;
+  }
+
+  tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
+      configName, requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
+
+  while (availBytes < requiredBytes) {
+    oldest = NULL;
+    stoptime = dispatch_clock;
+
+    LIST_FOREACH(de, &dvrentries, de_global_link) {
+      if (de->de_sched_state != DVR_COMPLETED &&
+          de->de_sched_state != DVR_MISSED_TIME)
+        continue;
+
+      if (dvr_entry_get_stop_time(de) > stoptime)
+        continue;
+
+      if (dvr_entry_get_removal_days(de) != DVR_RET_SPACENEED) // only remove the allowed ones
+        continue;
+
+      if (dvr_get_filename(de) == NULL || dvr_get_filesize(de) <= 0)
+        continue;
+
+      if(statvfs(dvr_get_filename(de), &diskdata) == -1)
+        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 || diskdata.f_fsid != filesystemId)
+        continue;
+
+      oldest = de; // the oldest one until now
+      stoptime = dvr_entry_get_stop_time(de);
+    }
+
+    if (oldest) {
+      fileSize = dvr_get_filesize(oldest);
+      availBytes += fileSize;
+      clearedBytes += fileSize;
+
+      localtime_r(&stoptime, &tm);
+      if (strftime(tbuf, sizeof(tbuf), "%F %T", &tm) <= 0)
+        *tbuf = 0;
+      tvhlog(LOG_INFO, "dvr","Delete \"until space needed\" recording \"%s\" with stop time \"%s\" and file size \"%ld MB\"",
+          lang_str_get(oldest->de_title, NULL), tbuf, fileSize/(int64_t)1024/(int64_t)1024);
+
+      dvr_disk_space_config_lastdelete = dispatch_clock;
+      dvr_entry_delete(oldest, 1);    // delete actual file
+      if (dvr_entry_get_retention_days(oldest) == DVR_RET_ONREMOVE)
+        dvr_entry_destroy(oldest, 1); // also delete database entry
+    }
+    else {
+      tvhlog(LOG_WARNING, "dvr","%s \"until space needed\" recordings found for config \"%s\", you are running out of disk space very soon!",
+          loops > 0 ? "Not enough" : "No", configName);
+      goto finish;
+    }
+
+    loops++;
+    if (loops >= 10) {
+      tvhlog(LOG_WARNING, "dvr","Not able to clear the required disk space after deleting %i \"until space needed\" recordings...", loops);
+      goto finish;
+    }
+  }
+
+finish:
+  tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", cleared \"%ld MB\" of disk space, new free disk space \"%ld MB\"",
+      configName, clearedBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
+
+  return clearedBytes;
+}
+
+/**
+ * Check for each dvr config if the free disk size is below the dvr_cleanup_threshold
+ * If so and we are using the dvr config ATM (active recording), we start the cleanup procedure
+ */
+static void
+dvr_disk_space_check()
+{
+  dvr_config_t *cfg;
+  dvr_entry_t *de;
+  struct statvfs diskdata;
+  int64_t requiredBytes, availBytes;
+  int idx = 0, cleanupDone = 0;
+
+  dvr_disk_space_config_idx++;
+  if (dvr_disk_space_config_idx > dvr_disk_space_config_size)
+    dvr_disk_space_config_idx = 1;
+
+  LIST_FOREACH(cfg, &dvrconfigs, config_link)
+  {
+    idx++;
+
+    if (cfg->dvr_enabled &&
+        !cleanupDone &&
+        idx >= dvr_disk_space_config_idx &&
+        statvfs(cfg->dvr_storage, &diskdata) != -1)
+    {
+      availBytes = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
+      requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;
+
+      if (availBytes < requiredBytes) {
+        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;
+
+          tvhlog(LOG_WARNING, "dvr","running out of free disk space for dvr config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
+              cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile",
+              requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
+
+          /* only cleanup one directory at the time as the system needs time to delete the actual files */
+          dvr_disk_space_cleanup(de->de_config);
+          cleanupDone = 1;
+          dvr_disk_space_config_idx = idx;
+          break;
+        }
+      }
+      else {
+        tvhlog(LOG_DEBUG, "dvr","checking free disk space for config \"%s\" : OK",
+            cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile");
+      }
+    }
+  }
+
+  if (!cleanupDone)
+    dvr_disk_space_config_idx = 0;
+
+  dvr_disk_space_config_size = idx;
+}
+
+/**
+ *
+ */
+static void
+dvr_get_disk_space_update(const char *path)
+{
+  struct statvfs diskdata;
+
+  if(statvfs(path, &diskdata) == -1)
+    return;
+
+  dvr_bfree = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
+  dvr_btotal = diskdata.f_bsize * (int64_t)diskdata.f_blocks;
+}
+
+/**
+ *
+ */
+static void
+dvr_get_disk_space_tcb(void *opaque, int dearmed)
+{
+  if (!dearmed) {
+    htsmsg_t *m = htsmsg_create_map();
+    pthread_mutex_lock(&dvr_disk_space_mutex);
+
+    /* update disk space from default dvr config */
+    dvr_get_disk_space_update((char *)opaque);
+    htsmsg_add_s64(m, "freediskspace", dvr_bfree);
+    htsmsg_add_s64(m, "totaldiskspace", dvr_btotal);
+
+    /* check free disk space for each dvr config and start cleanup if needed */
+    dvr_disk_space_check();
+    pthread_mutex_unlock(&dvr_disk_space_mutex);
+
+    notify_by_msg("diskspaceUpdate", m, 0);
+  }
+
+  free(opaque);
+}
+
+static void
+dvr_get_disk_space_cb(void *aux)
+{
+  dvr_config_t *cfg;
+  char *path;
+
+  lock_assert(&global_lock);
+
+  cfg = dvr_config_find_by_name_default(NULL);
+  if (cfg->dvr_storage && cfg->dvr_storage[0]) {
+    path = strdup(cfg->dvr_storage);
+    tasklet_arm(&dvr_disk_space_tasklet, dvr_get_disk_space_tcb, path);
+  }
+  gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 15);
+}
+
+/**
+ *
+ */
+void
+dvr_disk_space_init(void)
+{
+  dvr_config_t *cfg = dvr_config_find_by_name_default(NULL);
+  pthread_mutex_init(&dvr_disk_space_mutex, NULL);
+  dvr_get_disk_space_update(cfg->dvr_storage);
+  gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 5);
+}
+
+/**
+ *
+ */
+void
+dvr_disk_space_done(void)
+{
+  tasklet_disarm(&dvr_disk_space_tasklet);
+  gtimer_disarm(&dvr_disk_space_timer);
+}
+
+/**
+ *
+ */
+int
+dvr_get_disk_space(int64_t *bfree, int64_t *btotal)
+{
+  int res = 0;
+
+  pthread_mutex_lock(&dvr_disk_space_mutex);
+  if (dvr_bfree || dvr_btotal) {
+    *bfree = dvr_bfree;
+    *btotal = dvr_btotal;
+  } else {
+    res = -EINVAL;
+  }
+  pthread_mutex_unlock(&dvr_disk_space_mutex);
+  return res;
+}