inotify, faccessat2, fsync for Linux programs, and D-Bus credentials
What installing and starting Epiphany ran into: - inotify: inotify_init/init1, add_watch, rm_watch, as a new kind of event descriptor. The filesystem reports creates, deletes, renames (with a cookie pairing MOVED_FROM and MOVED_TO), writes, close-after-write, truncation and attribute changes, to watches on the inode and on its directory with the name. ONESHOT, MASK_ADD, MASK_CREATE, ONLYDIR, DONT_FOLLOW, IGNORED on removal, Q_OVERFLOW, and Linux's coalescing of repeated events. The hooks cost one load when nothing is watched. Without it GLib found no file monitor and GNOME programs aborted. - FIONREAD on inotify descriptors and pipes (inotifywait sizes its buffer by it). - fsync and fdatasync had no Linux numbers since the first commit: GLib's atomic writes, gtk-update-icon-cache and dbus-uuidgen all got ENOSYS. - faccessat2, the flags-taking faccessat musl uses for any flags. - sendmsg accepts SCM_CREDENTIALS when they are the sender's own, as Linux checks; GDBus sends them in every handshake and was refused with EINVAL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
c77841ce3d
commit
3113eff7fe
9 changed files with 590 additions and 9 deletions
|
|
@ -15,6 +15,7 @@
|
|||
#include "core/uaccess.h"
|
||||
#include "core/kmem.h"
|
||||
#include "uapi/errno.h"
|
||||
#include "uapi/inotify.h"
|
||||
#include "fs/schfs.h" /* SCHFS_BLKSIZE, SCHFS_NAME_MAX — statfs reports them */
|
||||
#include "gfx/gfx.h" /* gfx_cols/gfx_rows — TIOCGWINSZ reports them */
|
||||
#include "dev/vt.h" /* the terminals the TIOC and VT_ ioctls act on */
|
||||
|
|
@ -370,10 +371,31 @@ createat_x(int dirfd, char *path, short type, short major, short minor,
|
|||
if (err) *err = r == -EINVAL ? EINVAL : ENOSPC;
|
||||
return 0;
|
||||
}
|
||||
inotify_event(dp->dev, dp->inum, IN_CREATE | (type == T_DIR ? IN_ISDIR : 0), name, 0);
|
||||
iunlockput(dp);
|
||||
return ip;
|
||||
}
|
||||
|
||||
// The device and inode a path names, for inotify's watches. 0, or -errno.
|
||||
int
|
||||
path_identity(char *path, int follow, int onlydir, uint *dev, uint64 *inum)
|
||||
{
|
||||
struct inode *ip;
|
||||
int err = ENOENT, r = 0;
|
||||
|
||||
begin_op();
|
||||
ip = follow ? at_nameif(AT_FDCWD, path, &err) : at_namei(AT_FDCWD, path);
|
||||
if (!ip) { end_op(); return -err; }
|
||||
ilock(ip);
|
||||
if (onlydir && ip->type != T_DIR)
|
||||
r = -ENOTDIR;
|
||||
*dev = ip->dev;
|
||||
*inum = ip->inum;
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct inode*
|
||||
create(char *path, short type, short major, short minor)
|
||||
{
|
||||
|
|
@ -1079,6 +1101,8 @@ addr_t sys_link(void)
|
|||
if (dp->dev != ip->dev) { iunlockput(dp); err = EXDEV; goto bad_link; }
|
||||
if (dp->ops->no_hardlinks) { iunlockput(dp); err = EPERM; goto bad_link; }
|
||||
if (dirlink(dp, name, ip->inum) < 0) { iunlockput(dp); err = EEXIST; goto bad_link; }
|
||||
inotify_event(dp->dev, dp->inum, IN_CREATE, name, 0);
|
||||
inotify_event(ip->dev, ip->inum, IN_ATTRIB, 0, 0);
|
||||
iunlockput(dp); iput(ip); end_op();
|
||||
return 0;
|
||||
bad_link:
|
||||
|
|
@ -1107,6 +1131,17 @@ create_sock(char *path)
|
|||
return create(path, T_SOCK, 0, 0);
|
||||
}
|
||||
|
||||
// A name removed from dp: IN_DELETE there, and for the inode itself either
|
||||
// IN_ATTRIB (a link went) or IN_DELETE_SELF (the last one did). Before the
|
||||
// caller drops nlink, so the last link is nlink == 1 here.
|
||||
static void
|
||||
inotify_unlinked(struct inode *dp, struct inode *ip, char *name)
|
||||
{
|
||||
inotify_event(dp->dev, dp->inum, IN_DELETE | (ip->type == T_DIR ? IN_ISDIR : 0), name, 0);
|
||||
inotify_event(ip->dev, ip->inum, ip->nlink <= 1 || ip->type == T_DIR
|
||||
? IN_DELETE_SELF : IN_ATTRIB, 0, 0);
|
||||
}
|
||||
|
||||
// The body of unlink(2), by path. Factored out so that a failed bind can take
|
||||
// its own node back out without going through the syscall entry, and so there
|
||||
// is one implementation of what unlink means rather than two.
|
||||
|
|
@ -1129,6 +1164,7 @@ unlink_path(char *path)
|
|||
if (ip->type == T_DIR && dirempty(ip) != 1) { iunlockput(ip); err = ENOTEMPTY; goto bad; }
|
||||
if (dirunlink(dp, name, off) < 0) { iunlockput(ip); err = EIO; goto bad; }
|
||||
if (ip->type == T_DIR) { dp->nlink--; iupdate(dp); }
|
||||
inotify_unlinked(dp, ip, name);
|
||||
iunlockput(dp); ip->nlink--; iupdate(ip); iunlockput(ip);
|
||||
return 0;
|
||||
bad:
|
||||
|
|
@ -1185,6 +1221,7 @@ addr_t sys_ftruncate(void)
|
|||
begin_op(); ilock(f->ip);
|
||||
itrunc_to(f->ip, (uint64)length);
|
||||
iunlock(f->ip); end_op();
|
||||
inotify_file(f, IN_MODIFY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1200,6 +1237,7 @@ addr_t sys_truncate(void)
|
|||
if (!(ip = at_nameif(AT_FDCWD, path, &err))) { end_op(); return -err; }
|
||||
ilock(ip);
|
||||
itrunc_to(ip, (uint64)length);
|
||||
inotify_event(ip->dev, ip->inum, IN_MODIFY, 0, 0);
|
||||
iunlockput(ip); end_op();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1255,6 +1293,8 @@ do_rename(int olddirfd, char *oldpath, int newdirfd, char *newpath)
|
|||
if (dp_new) iput(dp_new);
|
||||
goto bad_rename;
|
||||
}
|
||||
uint new_dev = dp_new->dev; // for inotify, after dp_new is put
|
||||
uint64 new_inum = dp_new->inum;
|
||||
|
||||
// Clear the destination, if anything is there.
|
||||
ilock(dp_new);
|
||||
|
|
@ -1280,6 +1320,8 @@ do_rename(int olddirfd, char *oldpath, int newdirfd, char *newpath)
|
|||
iunlockput(ip_dst); iunlockput(dp_new); iput(dp_old);
|
||||
err = EIO; goto bad_rename;
|
||||
}
|
||||
if (ip_dst->nlink <= 1)
|
||||
inotify_event(ip_dst->dev, ip_dst->inum, IN_DELETE_SELF, 0, 0);
|
||||
ip_dst->nlink--; iupdate(ip_dst); iunlockput(ip_dst);
|
||||
}
|
||||
|
||||
|
|
@ -1297,6 +1339,12 @@ do_rename(int olddirfd, char *oldpath, int newdirfd, char *newpath)
|
|||
if (dirunlink(dp_old, oldname, off) < 0) {
|
||||
iunlockput(dp_old); err = EIO; goto bad_rename;
|
||||
}
|
||||
{
|
||||
uint32 cookie = inotify_cookie();
|
||||
inotify_event(dp_old->dev, dp_old->inum, IN_MOVED_FROM, oldname, cookie);
|
||||
inotify_event(new_dev, new_inum, IN_MOVED_TO, newname, cookie);
|
||||
inotify_event(ip->dev, ip->inum, IN_MOVE_SELF, 0, 0);
|
||||
}
|
||||
iunlockput(dp_old);
|
||||
ilock(ip); ip->nlink--; iupdate(ip); iunlockput(ip);
|
||||
end_op(); return 0;
|
||||
|
|
@ -1575,6 +1623,12 @@ addr_t sys_ioctl(void)
|
|||
}
|
||||
if (request == 0x5452) // FIOASYNC
|
||||
return 0;
|
||||
if (request == FIONREAD && (f->type == FD_EVENT || f->type == FD_PIPE)) {
|
||||
int n = f->type == FD_PIPE ? pipe_avail(f->pipe) : ev_fionread(f->ev);
|
||||
if (n < 0)
|
||||
return n;
|
||||
return put_user(n, argp) < 0 ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
// /dev/fb0 (major 2) — geometry queries. This has to come before the
|
||||
// catch-all below, which answers every non-tty ioctl with 0: a program
|
||||
|
|
@ -1895,6 +1949,7 @@ chmod_inode(struct inode *ip, int mode)
|
|||
if (ip->mode == 0)
|
||||
ip->mode = 01000000; // "no bits", as distinct from "never set"
|
||||
iupdate(ip);
|
||||
inotify_event(ip->dev, ip->inum, IN_ATTRIB, 0, 0);
|
||||
iunlock(ip);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2062,6 +2117,7 @@ addr_t sys_utimensat(void)
|
|||
}
|
||||
ip->ctime = (uint)now;
|
||||
iupdate(ip);
|
||||
inotify_event(ip->dev, ip->inum, IN_ATTRIB, 0, 0);
|
||||
iunlockput(ip);
|
||||
end_op();
|
||||
return 0;
|
||||
|
|
@ -2249,6 +2305,7 @@ addr_t sys_unlinkat(void)
|
|||
if (ip->type == T_DIR && dirempty(ip) != 1) { err = ENOTEMPTY; goto badip; }
|
||||
if (dirunlink(dp, name, off) < 0) { err = EIO; goto badip; }
|
||||
if (ip->type == T_DIR) { dp->nlink--; iupdate(dp); }
|
||||
inotify_unlinked(dp, ip, name);
|
||||
iunlockput(dp); ip->nlink--; iupdate(ip); iunlockput(ip);
|
||||
end_op();
|
||||
return 0;
|
||||
|
|
@ -2292,6 +2349,8 @@ addr_t sys_linkat(void)
|
|||
dirlink(dp, name, ip->inum) < 0) {
|
||||
iunlockput(dp); goto bad_linkat;
|
||||
}
|
||||
inotify_event(dp->dev, dp->inum, IN_CREATE, name, 0);
|
||||
inotify_event(ip->dev, ip->inum, IN_ATTRIB, 0, 0);
|
||||
iunlockput(dp); iput(ip); end_op();
|
||||
return 0;
|
||||
bad_linkat:
|
||||
|
|
@ -2365,6 +2424,37 @@ addr_t sys_faccessat(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// faccessat2: faccessat with the flags argument the original never had. musl
|
||||
// sends any faccessat with flags here, and without it GLib's every "does this
|
||||
// file exist" (F_OK, AT_SYMLINK_NOFOLLOW) failed with EINVAL. The mode is not
|
||||
// checked, as in faccessat above; AT_EACCESS changes nothing for the same
|
||||
// reason.
|
||||
addr_t sys_faccessat2(void)
|
||||
{
|
||||
int dirfd, mode, flags, err = ENOENT;
|
||||
char *path;
|
||||
struct inode *ip;
|
||||
|
||||
argint(0, &dirfd);
|
||||
if (argstr(1, &path) < 0) return -EFAULT;
|
||||
argint(2, &mode);
|
||||
argint(3, &flags);
|
||||
if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EACCESS | AT_EMPTY_PATH))
|
||||
return -EINVAL;
|
||||
if ((flags & AT_EMPTY_PATH) && path[0] == '\0') {
|
||||
if (dirfd < 0 || dirfd >= NOFILE || !myproc()->files->ofile[dirfd])
|
||||
return -EBADF;
|
||||
return 0;
|
||||
}
|
||||
begin_op();
|
||||
ip = (flags & AT_SYMLINK_NOFOLLOW) ? at_namei(dirfd, path)
|
||||
: at_nameif(dirfd, path, &err);
|
||||
if (!ip) { end_op(); return -err; }
|
||||
iput(ip);
|
||||
end_op();
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr_t sys_fstatat(void)
|
||||
{
|
||||
int dirfd, flags;
|
||||
|
|
|
|||
Loading…
Reference in a new issue