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
This commit is contained in:
oshogbo 2017-08-10 16:45:05 +00:00
parent 42ec48b371
commit a28f316bc4

View File

@ -31,19 +31,22 @@ __FBSDID("$FreeBSD$");
#include <sys/file.h>
#include <sys/stat.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 <fcntl.h>
#include <libgen.h>
#include <libutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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 *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