about summary refs log tree commit homepage
path: root/dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'dev.c')
-rw-r--r--dev.c72
1 files changed, 63 insertions, 9 deletions
diff --git a/dev.c b/dev.c
index 4cb15d2..3b52023 100644
--- a/dev.c
+++ b/dev.c
@@ -33,6 +33,10 @@ static struct mog_dev *mog_dev_new(struct mog_svc *svc, uint32_t mog_devid)
         dev->st_dev = sb.st_dev;
         mog_ioq_init(&dev->fsckq, svc, 1);
         mog_ioq_init(&dev->ioq, svc, svc->thr_per_dev);
+        dev->usage_txt = 0;
+        dev->usage_len = 0;
+        dev->usage_mtime = 0;
+        CHECK(int, 0, pthread_mutex_init(&dev->usage_lock, NULL));
 
         return dev;
 }
@@ -108,8 +112,7 @@ bool mog_dev_cmp(const void *a, const void *b)
 }
 
 static int
-emit_usage(
-const struct mog_dev *dev, struct mog_svc *svc, int fd, struct statvfs *v)
+emit_usage(struct mog_dev *dev, struct mog_svc *svc, int fd, struct statvfs *v)
 {
         int rc = -1;
         unsigned long long available = v->f_bavail;
@@ -129,6 +132,7 @@ const struct mog_dev *dev, struct mog_svc *svc, int fd, struct statvfs *v)
 
         me = mog_mnt_acquire(dev->st_dev);
         if (me) {
+                char *usage_txt;
                 static const char usage_fmt[] =
                         "available: %llu\n"
                         "device: %s\n"
@@ -139,14 +143,34 @@ const struct mog_dev *dev, struct mog_svc *svc, int fd, struct statvfs *v)
                         "used: %llu\n";
 
                 errno = 0;
-                rc = dprintf(fd, usage_fmt,
+                rc = asprintf(&usage_txt, usage_fmt,
                              available, me->me_devname, svc->docroot,
                              (unsigned)dev->devid, now, total, use, used);
+                if (rc > 0) {
+                        char *old_usage;
+
+                        CHECK(int, 0, pthread_mutex_lock(&dev->usage_lock));
+                        old_usage = dev->usage_txt;
+                        dev->usage_txt = usage_txt;
+                        dev->usage_len = rc;
+                        dev->usage_mtime = (time_t)now;
+                        CHECK(int, 0, pthread_mutex_unlock(&dev->usage_lock));
+
+                        free(old_usage);
+                        if (fd >= 0) {
+                                ssize_t w = write(fd, usage_txt, rc);
+
+                                if (w >= 0 && w != rc)
+                                        errno = ENOSPC;
+                                else if (w < 0)
+                                        rc = -1;
+                        }
+                }
 
                 PRESERVE_ERRNO( mog_mnt_release(me) );
                 if (rc < 0 || errno == ENOSPC) {
                         PRESERVE_ERRNO(do {
-                                syslog(LOG_ERR, "dprintf(%s/dev%u/usage): %m",
+                                syslog(LOG_ERR, "write(%s/dev%u/usage): %m",
                                        svc->docroot, (unsigned)dev->devid);
                         } while (0));
                 }
@@ -159,12 +183,32 @@ const struct mog_dev *dev, struct mog_svc *svc, int fd, struct statvfs *v)
         return rc;
 }
 
-int mog_dev_mkusage(const struct mog_dev *dev, struct mog_svc *svc)
+static void
+dev_usage_update(struct mog_dev *dev, struct mog_svc *svc, struct statvfs *v)
+{
+        if (mog_statvfs(svc, dev, v) < 0) {
+                syslog(LOG_ERR, "statvfs error: %s/dev%u/usage (%m)",
+                        svc->docroot, (unsigned)dev->devid);
+                return;
+        }
+        (void)emit_usage(dev, svc, -1, v);
+}
+
+void
+mog_dev_usage_update(struct mog_dev *dev, struct mog_svc *svc)
+{
+        struct statvfs v;
+
+        dev_usage_update(dev, svc, &v);
+}
+
+int mog_dev_mkusage(struct mog_dev *dev, struct mog_svc *svc)
 {
         struct statvfs v;
         char *usage_path;
         char *tmp_path;
         int fd = -1;
+        struct stat sb;
 
         if (!svc->mgmt_mfd)
                 return 0;
@@ -174,6 +218,17 @@ int mog_dev_mkusage(const struct mog_dev *dev, struct mog_svc *svc)
                         (unsigned)dev->devid);
                 return 0;
         }
+
+        /*
+         * allow chmod 0000 on devNNN/usage files to prevent us from
+         * overwriting them
+         */
+        if (mog_stat(svc, usage_path, &sb) == 0 &&
+            ((sb.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) == 0)) {
+                dev_usage_update(dev, svc, &v);
+                tmp_path = 0;
+                goto out;
+        }
         if (asprintf(&tmp_path, "%s.%x", usage_path, (unsigned)getpid()) < 0) {
                 syslog(LOG_ERR, "error generating path: /dev%u/usage.%u (%m)",
                         (unsigned)dev->devid, (unsigned)getpid());
@@ -186,14 +241,11 @@ int mog_dev_mkusage(const struct mog_dev *dev, struct mog_svc *svc)
         errno = 0;
         fd = mog_open_put(svc, tmp_path, O_EXCL|O_CREAT);
         if (fd < 0) {
-                if (mog_open_expire_retry(svc))
-                        fd = mog_open_put(svc, tmp_path, O_EXCL|O_CREAT);
-        }
-        if (fd < 0) {
                 PRESERVE_ERRNO(do {
                         syslog(LOG_ERR, "open(%s%s): %m",
                                svc->docroot, tmp_path);
                 } while (0));
+                dev_usage_update(dev, svc, &v);
                 goto out;
         }
         if (fstatvfs(fd, &v) < 0) {
@@ -252,6 +304,8 @@ void mog_dev_free(void *ptr)
 
         mog_ioq_destroy(&dev->fsckq);
         mog_ioq_destroy(&dev->ioq);
+        free(dev->usage_txt);
+        CHECK(int, 0, pthread_mutex_destroy(&dev->usage_lock));
         free(dev);
 }