Changeset View
Changeset View
Standalone View
Standalone View
lib/libutil/flopen.c
Show All 33 Lines | |||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <stdarg.h> | #include <stdarg.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <libutil.h> | #include <libutil.h> | ||||
/* | |||||
* Reliably open and lock a file. | |||||
* | |||||
* Please do not modify this code without first reading the revision history | |||||
* and discussing your changes with <des@freebsd.org>. Don't be fooled by the | |||||
* code's apparent simplicity; there would be no need for this function if it | |||||
* was easy to get right. | |||||
*/ | |||||
static int | static int | ||||
vflopenat(int dirfd, const char *path, int flags, va_list ap) | vflopenat(int dirfd, const char *path, int flags, va_list ap) | ||||
{ | { | ||||
int fd, operation, serrno, trunc; | |||||
struct stat sb, fsb; | |||||
mode_t mode; | mode_t mode; | ||||
#ifdef O_EXLOCK | |||||
flags &= ~O_EXLOCK; | |||||
#endif | |||||
mode = 0; | mode = 0; | ||||
if (flags & O_CREAT) { | if (flags & O_CREAT) { | ||||
mode = (mode_t)va_arg(ap, int); /* mode_t promoted to int */ | mode = (mode_t)va_arg(ap, int); /* mode_t promoted to int */ | ||||
} | } | ||||
operation = LOCK_EX; | flags &= ~O_SHLOCK; | ||||
if (flags & O_NONBLOCK) | flags |= O_EXLOCK; | ||||
operation |= LOCK_NB; | return (openat(dirfd, path, flags, mode)); | ||||
trunc = (flags & O_TRUNC); | |||||
flags &= ~O_TRUNC; | |||||
for (;;) { | |||||
if ((fd = openat(dirfd, path, flags, mode)) == -1) | |||||
/* non-existent or no access */ | |||||
return (-1); | |||||
if (flock(fd, operation) == -1) { | |||||
/* unsupported or interrupted */ | |||||
serrno = errno; | |||||
(void)close(fd); | |||||
errno = serrno; | |||||
return (-1); | |||||
} | |||||
if (fstatat(dirfd, path, &sb, 0) == -1) { | |||||
/* disappeared from under our feet */ | |||||
(void)close(fd); | |||||
continue; | |||||
} | |||||
if (fstat(fd, &fsb) == -1) { | |||||
/* can't happen [tm] */ | |||||
serrno = errno; | |||||
(void)close(fd); | |||||
errno = serrno; | |||||
return (-1); | |||||
} | |||||
if (sb.st_dev != fsb.st_dev || | |||||
sb.st_ino != fsb.st_ino) { | |||||
/* changed under our feet */ | |||||
(void)close(fd); | |||||
continue; | |||||
} | |||||
if (trunc && ftruncate(fd, 0) != 0) { | |||||
/* can't happen [tm] */ | |||||
serrno = errno; | |||||
(void)close(fd); | |||||
errno = serrno; | |||||
return (-1); | |||||
} | |||||
/* | |||||
* The following change is provided as a specific example to | |||||
* avoid. | |||||
*/ | |||||
#if 0 | |||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { | |||||
serrno = errno; | |||||
(void)close(fd); | |||||
errno = serrno; | |||||
return (-1); | |||||
} | |||||
#endif | |||||
return (fd); | |||||
} | |||||
} | } | ||||
int | int | ||||
flopen(const char *path, int flags, ...) | flopen(const char *path, int flags, ...) | ||||
{ | { | ||||
va_list ap; | va_list ap; | ||||
int ret; | int ret; | ||||
Show All 17 Lines |