#include <linux/tiocl.h>
#include <termios.h>
#include <stdarg.h>
-#include <sys/inotify.h>
#include <sys/poll.h>
#include <ctype.h>
#include <sys/prctl.h>
#include <langinfo.h>
#include <locale.h>
#include <sys/personality.h>
+#include <sys/xattr.h>
#include <libgen.h>
#undef basename
#include "gunicode.h"
#include "virt.h"
#include "def.h"
+#include "sparse-endian.h"
int saved_argc = 0;
char **saved_argv = NULL;
return t;
}
-_pure_ static bool ignore_file_allow_backup(const char *filename) {
+_pure_ static bool hidden_file_allow_backup(const char *filename) {
assert(filename);
return
endswith(filename, ".swp");
}
-bool ignore_file(const char *filename) {
+bool hidden_file(const char *filename) {
assert(filename);
if (endswith(filename, "~"))
return true;
- return ignore_file_allow_backup(filename);
+ return hidden_file_allow_backup(filename);
}
int fd_nonblock(int fd, bool nonblock) {
while ((de = readdir(d))) {
int fd = -1;
- if (ignore_file(de->d_name))
+ if (hidden_file(de->d_name))
continue;
if (safe_atoi(de->d_name, &fd) < 0)
assert(notify >= 0);
for (;;) {
- uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
+ union inotify_event_buffer buffer;
struct inotify_event *e;
ssize_t l;
}
}
- l = read(notify, buffer, sizeof(buffer));
+ l = read(notify, &buffer, sizeof(buffer));
if (l < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
if (!de)
return 1;
- if (!ignore_file(de->d_name))
+ if (!hidden_file(de->d_name))
return 0;
}
}
return lookup_uid(getuid());
}
-int getttyname_malloc(int fd, char **r) {
- char path[PATH_MAX], *c;
- int k;
+int getttyname_malloc(int fd, char **ret) {
+ size_t l = 100;
+ int r;
- assert(r);
+ assert(fd >= 0);
+ assert(ret);
- k = ttyname_r(fd, path, sizeof(path));
- if (k > 0)
- return -k;
+ for (;;) {
+ char path[l];
- char_array_0(path);
+ r = ttyname_r(fd, path, sizeof(path));
+ if (r == 0) {
+ const char *p;
+ char *c;
- c = strdup(startswith(path, "/dev/") ? path + 5 : path);
- if (!c)
- return -ENOMEM;
+ p = startswith(path, "/dev/");
+ c = strdup(p ?: path);
+ if (!c)
+ return -ENOMEM;
+
+ *ret = c;
+ return 0;
+ }
+
+ if (r != ERANGE)
+ return -r;
+
+ l *= 2;
+ }
- *r = c;
return 0;
}
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (fd < 0) {
- if (errno != ENOTDIR)
+ if (errno != ENOTDIR && errno != ELOOP)
return -errno;
if (!dangerous) {
bool dirent_is_file(const struct dirent *de) {
assert(de);
- if (ignore_file(de->d_name))
+ if (hidden_file(de->d_name))
return false;
if (de->d_type != DT_REG &&
de->d_type != DT_UNKNOWN)
return false;
- if (ignore_file_allow_backup(de->d_name))
+ if (hidden_file_allow_backup(de->d_name))
return false;
return endswith(de->d_name, suffix);
return true;
}
+bool image_name_is_valid(const char *s) {
+ if (!filename_is_valid(s))
+ return false;
+
+ if (string_has_cc(s, NULL))
+ return false;
+
+ if (!utf8_is_valid(s))
+ return false;
+
+ /* Temporary files for atomically creating new files */
+ if (startswith(s, ".#"))
+ return false;
+
+ return true;
+}
+
int pipe_eof(int fd) {
struct pollfd pollfd = {
.fd = fd,
if (!de)
break;
- if (ignore_file(de->d_name))
+ if (hidden_file(de->d_name))
continue;
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
* /foo/bar/waldo
*
* Into this:
- * /foo/bar/.waldoXXXXXX
+ * /foo/bar/.#waldoXXXXXX
*/
fn = basename(p);
if (!filename_is_valid(fn))
return -EINVAL;
- t = new(char, strlen(p) + 1 + 6 + 1);
+ t = new(char, strlen(p) + 2 + 6 + 1);
if (!t)
return -ENOMEM;
- strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), "."), fn), "XXXXXX");
+ strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), "XXXXXX");
*ret = path_kill_slashes(t);
return 0;
* /foo/bar/waldo
*
* Into this:
- * /foo/bar/.waldobaa2a261115984a9
+ * /foo/bar/.#waldobaa2a261115984a9
*/
fn = basename(p);
if (!filename_is_valid(fn))
return -EINVAL;
- t = new(char, strlen(p) + 1 + 16 + 1);
+ t = new(char, strlen(p) + 2 + 16 + 1);
if (!t)
return -ENOMEM;
- x = stpcpy(stpcpy(mempcpy(t, p, fn - p), "."), fn);
+ x = stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn);
u = random_u64();
for (i = 0; i < 16; i++) {
/* Turns this:
* /foo/bar/waldo
* Into this:
- * /foo/bar/waldo/.3c2b6219aa75d7d0
+ * /foo/bar/waldo/.#3c2b6219aa75d7d0
*/
- t = new(char, strlen(p) + 2 + 16 + 1);
+ t = new(char, strlen(p) + 3 + 16 + 1);
if (!t)
return -ENOMEM;
- x = stpcpy(stpcpy(t, p), "/.");
+ x = stpcpy(stpcpy(t, p), "/.#");
u = random_u64();
for (i = 0; i < 16; i++) {
return 1;
}
+
+int ptsname_malloc(int fd, char **ret) {
+ size_t l = 100;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ for (;;) {
+ char *c;
+
+ c = new(char, l);
+ if (!c)
+ return -ENOMEM;
+
+ if (ptsname_r(fd, c, l) == 0) {
+ *ret = c;
+ return 0;
+ }
+ if (errno != ERANGE) {
+ free(c);
+ return -errno;
+ }
+
+ free(c);
+ l *= 2;
+ }
+}
+
+int openpt_in_namespace(pid_t pid, int flags) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ siginfo_t si;
+ pid_t child;
+ int r;
+
+ assert(pid > 0);
+
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
+ if (r < 0)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ return -errno;
+
+ child = fork();
+ if (child < 0)
+ return -errno;
+
+ if (child == 0) {
+ int master;
+
+ pair[0] = safe_close(pair[0]);
+
+ r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ master = posix_openpt(flags);
+ if (master < 0)
+ _exit(EXIT_FAILURE);
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &master, sizeof(int));
+
+ mh.msg_controllen = cmsg->cmsg_len;
+
+ if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ r = wait_for_terminate(child, &si);
+ if (r < 0)
+ return r;
+ if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ return -EIO;
+
+ if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
+ return -errno;
+
+ for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ int *fds;
+ unsigned n_fds;
+
+ fds = (int*) CMSG_DATA(cmsg);
+ n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+
+ if (n_fds != 1) {
+ close_many(fds, n_fds);
+ return -EIO;
+ }
+
+ return fds[0];
+ }
+
+ return -EIO;
+}
+
+ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) {
+ _cleanup_close_ int fd = -1;
+ ssize_t l;
+
+ /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
+
+ fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
+ if (fd < 0)
+ return -errno;
+
+ l = fgetxattr(fd, attribute, value, size);
+ if (l < 0)
+ return -errno;
+
+ return l;
+}
+
+static int parse_crtime(le64_t le, usec_t *usec) {
+ uint64_t u;
+
+ assert(usec);
+
+ u = le64toh(le);
+ if (u == 0 || u == (uint64_t) -1)
+ return -EIO;
+
+ *usec = (usec_t) u;
+ return 0;
+}
+
+int fd_getcrtime(int fd, usec_t *usec) {
+ le64_t le;
+ ssize_t n;
+
+ assert(fd >= 0);
+ assert(usec);
+
+ /* Until Linux gets a real concept of birthtime/creation time,
+ * let's fake one with xattrs */
+
+ n = fgetxattr(fd, "user.crtime_usec", &le, sizeof(le));
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(le))
+ return -EIO;
+
+ return parse_crtime(le, usec);
+}
+
+int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) {
+ le64_t le;
+ ssize_t n;
+
+ n = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags);
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(le))
+ return -EIO;
+
+ return parse_crtime(le, usec);
+}
+
+int path_getcrtime(const char *p, usec_t *usec) {
+ le64_t le;
+ ssize_t n;
+
+ assert(p);
+ assert(usec);
+
+ n = getxattr(p, "user.crtime_usec", &le, sizeof(le));
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(le))
+ return -EIO;
+
+ return parse_crtime(le, usec);
+}
+
+int fd_setcrtime(int fd, usec_t usec) {
+ le64_t le;
+
+ assert(fd >= 0);
+
+ le = htole64((uint64_t) usec);
+ if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
+ return -errno;
+
+ return 0;
+}