+
+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);
+
+ if (usec <= 0)
+ usec = now(CLOCK_REALTIME);
+
+ le = htole64((uint64_t) usec);
+ if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int same_fd(int a, int b) {
+ struct stat sta, stb;
+ pid_t pid;
+ int r, fa, fb;
+
+ assert(a >= 0);
+ assert(b >= 0);
+
+ /* Compares two file descriptors. Note that semantics are
+ * quite different depending on whether we have kcmp() or we
+ * don't. If we have kcmp() this will only return true for
+ * dup()ed file descriptors, but not otherwise. If we don't
+ * have kcmp() this will also return true for two fds of the same
+ * file, created by separate open() calls. Since we use this
+ * call mostly for filtering out duplicates in the fd store
+ * this difference hopefully doesn't matter too much. */
+
+ if (a == b)
+ return true;
+
+ /* Try to use kcmp() if we have it. */
+ pid = getpid();
+ r = kcmp(pid, pid, KCMP_FILE, a, b);
+ if (r == 0)
+ return true;
+ if (r > 0)
+ return false;
+ if (errno != ENOSYS)
+ return -errno;
+
+ /* We don't have kcmp(), use fstat() instead. */
+ if (fstat(a, &sta) < 0)
+ return -errno;
+
+ if (fstat(b, &stb) < 0)
+ return -errno;
+
+ if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
+ return false;
+
+ /* We consider all device fds different, since two device fds
+ * might refer to quite different device contexts even though
+ * they share the same inode and backing dev_t. */
+
+ if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
+ return false;
+
+ if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
+ return false;
+
+ /* The fds refer to the same inode on disk, let's also check
+ * if they have the same fd flags. This is useful to
+ * distuingish the read and write side of a pipe created with
+ * pipe(). */
+ fa = fcntl(a, F_GETFL);
+ if (fa < 0)
+ return -errno;
+
+ fb = fcntl(b, F_GETFL);
+ if (fb < 0)
+ return -errno;
+
+ return fa == fb;
+}
+
+int chattr_fd(int fd, bool b, int mask) {
+ int old_attr, new_attr;
+
+ assert(fd >= 0);
+
+ if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
+ return -errno;
+
+ if (b)
+ new_attr = old_attr | mask;
+ else
+ new_attr = old_attr & ~mask;
+
+ if (new_attr == old_attr)
+ return 0;
+
+ if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int chattr_path(const char *p, bool b, int mask) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ return chattr_fd(fd, b, mask);
+}