Author: oshogbo
Date: Thu Aug 10 16:45:05 2017
New Revision: 322369
URL: https://svnweb.freebsd.org/changeset/base/322369

Log:
  Store directory descriptor in the pidfh structure and use unlinkat(2)
  function instead of unlink(2).
  
  Now when pidfile_remove() uses unlinkat(2) to remove the pidfile
  it is safe to use this function in capability mode.
  
  Style fix: sort headers.
  
  PR:           220524
  Reviewed by:  markj
  Differential Revision:        https://reviews.freebsd.org/D11692

Modified:
  head/lib/libutil/pidfile.c

Modified: head/lib/libutil/pidfile.c
==============================================================================
--- head/lib/libutil/pidfile.c  Thu Aug 10 15:42:25 2017        (r322368)
+++ head/lib/libutil/pidfile.c  Thu Aug 10 16:45:05 2017        (r322369)
@@ -31,19 +31,22 @@ __FBSDID("$FreeBSD$");
 #include <sys/file.h>
 #include <sys/stat.h>
 
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <libutil.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <string.h>
 #include <time.h>
-#include <err.h>
-#include <errno.h>
-#include <libutil.h>
+#include <unistd.h>
 
 struct pidfh {
+       int     pf_dirfd;
        int     pf_fd;
-       char    pf_path[MAXPATHLEN + 1];
+       char    pf_dir[MAXPATHLEN + 1];
+       char    pf_filename[MAXPATHLEN + 1];
        dev_t   pf_dev;
        ino_t   pf_ino;
 };
@@ -68,12 +71,12 @@ pidfile_verify(const struct pidfh *pfh)
 }
 
 static int
-pidfile_read(const char *path, pid_t *pidptr)
+pidfile_read(int dirfd, const char *filename, pid_t *pidptr)
 {
        char buf[16], *endptr;
        int error, fd, i;
 
-       fd = open(path, O_RDONLY | O_CLOEXEC);
+       fd = openat(dirfd, filename, O_RDONLY | O_CLOEXEC);
        if (fd == -1)
                return (errno);
 
@@ -98,32 +101,50 @@ pidfile_open(const char *path, mode_t mode, pid_t *pid
 {
        struct pidfh *pfh;
        struct stat sb;
-       int error, fd, len, count;
+       int error, fd, dirfd, dirlen, filenamelen, count;
        struct timespec rqtp;
 
        pfh = malloc(sizeof(*pfh));
        if (pfh == NULL)
                return (NULL);
 
-       if (path == NULL)
-               len = snprintf(pfh->pf_path, sizeof(pfh->pf_path),
-                   "/var/run/%s.pid", getprogname());
-       else
-               len = snprintf(pfh->pf_path, sizeof(pfh->pf_path),
+       if (path == NULL) {
+               dirlen = snprintf(pfh->pf_dir, sizeof(pfh->pf_dir),
+                   "/var/run/");
+               filenamelen = snprintf(pfh->pf_filename,
+                   sizeof(pfh->pf_filename), "%s.pid", getprogname());
+       } else {
+               dirlen = snprintf(pfh->pf_dir, sizeof(pfh->pf_dir),
                    "%s", path);
-       if (len >= (int)sizeof(pfh->pf_path)) {
+               filenamelen = snprintf(pfh->pf_filename,
+                   sizeof(pfh->pf_filename), "%s", path);
+
+               dirname(pfh->pf_dir);
+               basename(pfh->pf_filename);
+       }
+
+       if (dirlen >= (int)sizeof(pfh->pf_dir) ||
+           filenamelen >= (int)sizeof(pfh->pf_filename)) {
                free(pfh);
                errno = ENAMETOOLONG;
                return (NULL);
        }
 
+       dirfd = open(pfh->pf_dir, O_CLOEXEC | O_DIRECTORY | O_NONBLOCK);
+       if (dirfd == -1) {
+               error = errno;
+               free(pfh);
+               errno = error;
+               return (NULL);
+       }
+
        /*
         * Open the PID file and obtain exclusive lock.
         * We truncate PID file here only to remove old PID immediately,
         * PID file will be truncated again in pidfile_write(), so
         * pidfile_write() can be called multiple times.
         */
-       fd = flopen(pfh->pf_path,
+       fd = flopenat(dirfd, pfh->pf_filename,
            O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NONBLOCK, mode);
        if (fd == -1) {
                if (errno == EWOULDBLOCK) {
@@ -134,8 +155,8 @@ pidfile_open(const char *path, mode_t mode, pid_t *pid
                                rqtp.tv_sec = 0;
                                rqtp.tv_nsec = 5000000;
                                for (;;) {
-                                       errno = pidfile_read(pfh->pf_path,
-                                           pidptr);
+                                       errno = pidfile_read(dirfd,
+                                           pfh->pf_filename, pidptr);
                                        if (errno != EAGAIN || --count == 0)
                                                break;
                                        nanosleep(&rqtp, 0);
@@ -146,7 +167,10 @@ pidfile_open(const char *path, mode_t mode, pid_t *pid
                                        errno = EEXIST;
                        }
                }
+               error = errno;
+               close(dirfd);
                free(pfh);
+               errno = error;
                return (NULL);
        }
 
@@ -156,13 +180,15 @@ pidfile_open(const char *path, mode_t mode, pid_t *pid
         */
        if (fstat(fd, &sb) == -1) {
                error = errno;
-               unlink(pfh->pf_path);
+               unlinkat(dirfd, pfh->pf_filename, 0);
+               close(dirfd);
                close(fd);
                free(pfh);
                errno = error;
                return (NULL);
        }
 
+       pfh->pf_dirfd = dirfd;
        pfh->pf_fd = fd;
        pfh->pf_dev = sb.st_dev;
        pfh->pf_ino = sb.st_ino;
@@ -223,6 +249,9 @@ pidfile_close(struct pidfh *pfh)
 
        if (close(pfh->pf_fd) == -1)
                error = errno;
+       if (close(pfh->pf_dirfd) == -1 && error == 0)
+               error = errno;
+
        free(pfh);
        if (error != 0) {
                errno = error;
@@ -242,12 +271,12 @@ _pidfile_remove(struct pidfh *pfh, int freeit)
                return (-1);
        }
 
-       if (unlink(pfh->pf_path) == -1)
+       if (unlinkat(pfh->pf_dirfd, pfh->pf_filename, 0) == -1)
                error = errno;
-       if (close(pfh->pf_fd) == -1) {
-               if (error == 0)
-                       error = errno;
-       }
+       if (close(pfh->pf_fd) == -1 && error == 0)
+               error = errno;
+       if (close(pfh->pf_dirfd) == -1 && error == 0)
+               error = errno;
        if (freeit)
                free(pfh);
        else
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to