From 4f9612a321fcdfbb4cd6cefcb251e88d500fdb90 Mon Sep 17 00:00:00 2001 From: Mariusz Zaborski Date: Thu, 10 Aug 2017 16:45:05 +0000 Subject: [PATCH] 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 --- lib/libutil/pidfile.c | 79 +++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/lib/libutil/pidfile.c b/lib/libutil/pidfile.c index 157a53c7448f..50d3a440729d 100644 --- a/lib/libutil/pidfile.c +++ b/lib/libutil/pidfile.c @@ -31,19 +31,22 @@ __FBSDID("$FreeBSD$"); #include #include -#include -#include -#include -#include -#include -#include #include #include +#include +#include #include +#include +#include +#include +#include +#include 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 *pidptr) { 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 *pidptr) 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 *pidptr) 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 *pidptr) */ 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 && error == 0) + error = errno; + if (close(pfh->pf_dirfd) == -1 && error == 0) error = errno; - if (close(pfh->pf_fd) == -1) { - if (error == 0) - error = errno; - } if (freeit) free(pfh); else