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
|
|
@ -799,6 +799,13 @@ uint64 memfd_size(int id);
|
||||||
int memfd_truncate(int id, uint64 len);
|
int memfd_truncate(int id, uint64 len);
|
||||||
int memfd_seals(int id, int add);
|
int memfd_seals(int id, int add);
|
||||||
int dsp_writable(void);
|
int dsp_writable(void);
|
||||||
|
// inotify (kernel/sys/sys_event.c), and the lookup it needs (sys_fs.c)
|
||||||
|
void inotify_event(uint dev, uint64 inum, uint32 mask, const char *name, uint32 cookie);
|
||||||
|
void inotify_file(struct file *f, uint32 mask);
|
||||||
|
uint32 inotify_cookie(void);
|
||||||
|
int ev_fionread(struct evobj *o);
|
||||||
|
int pipe_avail(struct pipe *p);
|
||||||
|
int path_identity(char *path, int follow, int onlydir, uint *dev, uint64 *inum);
|
||||||
int memfd_rw(int id, int user, uint64 buf, uint64 off, int n, int writing);
|
int memfd_rw(int id, int user, uint64 buf, uint64 off, int n, int writing);
|
||||||
void shm_detach(int id);
|
void shm_detach(int id);
|
||||||
|
|
||||||
|
|
|
||||||
29
include/uapi/inotify.h
Normal file
29
include/uapi/inotify.h
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
// inotify(7) event masks, Linux's values.
|
||||||
|
#ifndef UAPI_INOTIFY_H
|
||||||
|
#define UAPI_INOTIFY_H
|
||||||
|
|
||||||
|
#define IN_ACCESS 0x00000001
|
||||||
|
#define IN_MODIFY 0x00000002
|
||||||
|
#define IN_ATTRIB 0x00000004
|
||||||
|
#define IN_CLOSE_WRITE 0x00000008
|
||||||
|
#define IN_CLOSE_NOWRITE 0x00000010
|
||||||
|
#define IN_OPEN 0x00000020
|
||||||
|
#define IN_MOVED_FROM 0x00000040
|
||||||
|
#define IN_MOVED_TO 0x00000080
|
||||||
|
#define IN_CREATE 0x00000100
|
||||||
|
#define IN_DELETE 0x00000200
|
||||||
|
#define IN_DELETE_SELF 0x00000400
|
||||||
|
#define IN_MOVE_SELF 0x00000800
|
||||||
|
#define IN_ALL_EVENTS 0x00000fff
|
||||||
|
#define IN_UNMOUNT 0x00002000
|
||||||
|
#define IN_Q_OVERFLOW 0x00004000
|
||||||
|
#define IN_IGNORED 0x00008000
|
||||||
|
#define IN_ONLYDIR 0x01000000
|
||||||
|
#define IN_DONT_FOLLOW 0x02000000
|
||||||
|
#define IN_EXCL_UNLINK 0x04000000
|
||||||
|
#define IN_MASK_CREATE 0x10000000
|
||||||
|
#define IN_MASK_ADD 0x20000000
|
||||||
|
#define IN_ISDIR 0x40000000
|
||||||
|
#define IN_ONESHOT 0x80000000
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -354,6 +354,7 @@ struct statfs {
|
||||||
#define AT_FDCWD (-100)
|
#define AT_FDCWD (-100)
|
||||||
#define AT_REMOVEDIR 0x200
|
#define AT_REMOVEDIR 0x200
|
||||||
#define AT_SYMLINK_NOFOLLOW 0x100
|
#define AT_SYMLINK_NOFOLLOW 0x100
|
||||||
|
#define AT_EACCESS 0x200
|
||||||
#define AT_EMPTY_PATH 0x1000
|
#define AT_EMPTY_PATH 0x1000
|
||||||
#define AT_NO_AUTOMOUNT 0x800
|
#define AT_NO_AUTOMOUNT 0x800
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "fs/fsstat.h"
|
#include "fs/fsstat.h"
|
||||||
#include "uapi/posix.h"
|
#include "uapi/posix.h"
|
||||||
#include "uapi/errno.h"
|
#include "uapi/errno.h"
|
||||||
|
#include "uapi/inotify.h"
|
||||||
#include "core/kmem.h"
|
#include "core/kmem.h"
|
||||||
#include "dev/pty.h"
|
#include "dev/pty.h"
|
||||||
|
|
||||||
|
|
@ -112,6 +113,9 @@ fileclose(struct file *f)
|
||||||
FSSTAT_DROP(fsstat.file_used); // 1 -> 0
|
FSSTAT_DROP(fsstat.file_used); // 1 -> 0
|
||||||
release(&ftable.lock);
|
release(&ftable.lock);
|
||||||
|
|
||||||
|
// Before the path goes: the directory's watchers want the name.
|
||||||
|
if(ff.type == FD_INODE && ff.writable && ff.ip && ff.ip->type == T_FILE)
|
||||||
|
inotify_file(&ff, IN_CLOSE_WRITE);
|
||||||
if(ff.path)
|
if(ff.path)
|
||||||
kmfree(ff.path);
|
kmfree(ff.path);
|
||||||
|
|
||||||
|
|
@ -375,8 +379,10 @@ filewrite(struct file *f, char *addr, int n)
|
||||||
if(r != n1)
|
if(r != n1)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(i > 0)
|
if(i > 0){
|
||||||
|
inotify_file(f, IN_MODIFY);
|
||||||
return i;
|
return i;
|
||||||
|
}
|
||||||
return r < 0 ? r : -1;
|
return r < 0 ? r : -1;
|
||||||
}
|
}
|
||||||
panic("filewrite");
|
panic("filewrite");
|
||||||
|
|
|
||||||
|
|
@ -231,3 +231,13 @@ pipe_poll(struct pipe *p, int readable, int writable)
|
||||||
release(&p->lock);
|
release(&p->lock);
|
||||||
return rev;
|
return rev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bytes waiting to be read, for FIONREAD.
|
||||||
|
int
|
||||||
|
pipe_avail(struct pipe *p)
|
||||||
|
{
|
||||||
|
acquire(&p->lock);
|
||||||
|
int n = (int)(p->nwrite - p->nread);
|
||||||
|
release(&p->lock);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// eventfd, timerfd, signalfd and epoll: the descriptors an event loop is
|
// eventfd, timerfd, signalfd, epoll and inotify: the descriptors an event
|
||||||
// built from.
|
// loop is built from.
|
||||||
//
|
//
|
||||||
// Four kinds of file with no inode behind them, all FD_EVENT, told apart by
|
// Four kinds of file with no inode behind them, all FD_EVENT, told apart by
|
||||||
// struct evobj's kind. Each one is a counter or a set that read() drains and
|
// struct evobj's kind. Each one is a counter or a set that read() drains and
|
||||||
|
|
@ -30,8 +30,11 @@
|
||||||
#include "uapi/fcntl.h"
|
#include "uapi/fcntl.h"
|
||||||
#include "uapi/stat.h"
|
#include "uapi/stat.h"
|
||||||
#include "uapi/errno.h"
|
#include "uapi/errno.h"
|
||||||
|
#include "uapi/inotify.h"
|
||||||
|
|
||||||
enum { EV_EVENTFD = 1, EV_TIMERFD, EV_SIGNALFD, EV_EPOLL };
|
enum { EV_EVENTFD = 1, EV_TIMERFD, EV_SIGNALFD, EV_EPOLL, EV_INOTIFY };
|
||||||
|
|
||||||
|
struct inotify;
|
||||||
|
|
||||||
// Linux's flag values, which are O_NONBLOCK and O_CLOEXEC for every one of
|
// Linux's flag values, which are O_NONBLOCK and O_CLOEXEC for every one of
|
||||||
// these: one spelling per meaning, so they are tested as such.
|
// these: one spelling per meaning, so they are tested as such.
|
||||||
|
|
@ -98,6 +101,7 @@ struct evobj {
|
||||||
} tfd;
|
} tfd;
|
||||||
struct { uint64 mask; } sfd; // kernel bit layout, as signal_mask
|
struct { uint64 mask; } sfd; // kernel bit layout, as signal_mask
|
||||||
struct { int n; struct epitem *items; } ep;
|
struct { int n; struct epitem *items; } ep;
|
||||||
|
struct inotify *in;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -181,14 +185,21 @@ ev_kind_name(struct evobj *o)
|
||||||
case EV_TIMERFD: return "anon_inode:[timerfd]";
|
case EV_TIMERFD: return "anon_inode:[timerfd]";
|
||||||
case EV_SIGNALFD: return "anon_inode:[signalfd]";
|
case EV_SIGNALFD: return "anon_inode:[signalfd]";
|
||||||
case EV_EPOLL: return "anon_inode:[eventpoll]";
|
case EV_EPOLL: return "anon_inode:[eventpoll]";
|
||||||
|
case EV_INOTIFY: return "anon_inode:inotify";
|
||||||
}
|
}
|
||||||
return "anon_inode:[event]";
|
return "anon_inode:[event]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void in_free(struct inotify *in);
|
||||||
|
static int in_pending(struct inotify *in);
|
||||||
|
static int in_read(struct file *f, struct inotify *in, char *addr, int n);
|
||||||
|
|
||||||
// Called by fileclose() for the last reference.
|
// Called by fileclose() for the last reference.
|
||||||
void
|
void
|
||||||
ev_close(struct evobj *o)
|
ev_close(struct evobj *o)
|
||||||
{
|
{
|
||||||
|
if (o->kind == EV_INOTIFY)
|
||||||
|
in_free(o->in);
|
||||||
if (o->kind == EV_EPOLL && o->ep.items)
|
if (o->kind == EV_EPOLL && o->ep.items)
|
||||||
kfree((char *)o->ep.items);
|
kfree((char *)o->ep.items);
|
||||||
kmfree(o);
|
kmfree(o);
|
||||||
|
|
@ -243,6 +254,10 @@ ev_poll(struct file *f, int events)
|
||||||
if (myproc()->pending_signals & o->sfd.mask)
|
if (myproc()->pending_signals & o->sfd.mask)
|
||||||
rev |= POLLIN;
|
rev |= POLLIN;
|
||||||
break;
|
break;
|
||||||
|
case EV_INOTIFY:
|
||||||
|
if (in_pending(o->in))
|
||||||
|
rev |= POLLIN;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
releasesleep(&o->lock);
|
releasesleep(&o->lock);
|
||||||
|
|
||||||
|
|
@ -260,6 +275,9 @@ ev_read(struct file *f, char *addr, int n)
|
||||||
{
|
{
|
||||||
struct evobj *o = f->ev;
|
struct evobj *o = f->ev;
|
||||||
|
|
||||||
|
if (o->kind == EV_INOTIFY)
|
||||||
|
return in_read(f, o->in, addr, n);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
acquiresleep(&o->lock);
|
acquiresleep(&o->lock);
|
||||||
switch (o->kind) {
|
switch (o->kind) {
|
||||||
|
|
@ -751,3 +769,394 @@ sys_epoll_pwait(void)
|
||||||
{
|
{
|
||||||
return sys_epoll_wait();
|
return sys_epoll_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- inotify -------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Watches name an inode by device and number. The filesystem calls
|
||||||
|
// inotify_event() as it creates, removes, renames and changes things -- for
|
||||||
|
// the inode itself, and for the directory it is in, with the name -- and every
|
||||||
|
// watch on that inode gets the event queued on its descriptor.
|
||||||
|
//
|
||||||
|
// The hooks cost one load when nothing is watched anywhere, which is the
|
||||||
|
// usual case. GLib watches a directory for every GSettings and file monitor
|
||||||
|
// it keeps, and without inotify at all it found no file monitor and GNOME
|
||||||
|
// programs aborted at start.
|
||||||
|
|
||||||
|
// The masks are in uapi/inotify.h.
|
||||||
|
|
||||||
|
struct inwatch {
|
||||||
|
int wd;
|
||||||
|
uint dev;
|
||||||
|
uint64 inum;
|
||||||
|
uint32 mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
// One page for the instance and its watches, one for the queue.
|
||||||
|
#define IN_QBYTES PGSIZE
|
||||||
|
#define IN_MAXW ((int)((PGSIZE - 64) / sizeof(struct inwatch)))
|
||||||
|
|
||||||
|
struct inotify {
|
||||||
|
struct inotify *next; // in_list
|
||||||
|
int nw, next_wd;
|
||||||
|
int qlen, overflowed;
|
||||||
|
char *q;
|
||||||
|
struct inwatch w[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct in_event {
|
||||||
|
int wd;
|
||||||
|
uint32 mask, cookie, len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct spinlock in_lock;
|
||||||
|
static int in_lock_ready;
|
||||||
|
static struct inotify *in_list;
|
||||||
|
static int in_nwatch; // across every instance: the fast path
|
||||||
|
static uint32 in_cookie;
|
||||||
|
|
||||||
|
static void
|
||||||
|
in_lock_once(void)
|
||||||
|
{
|
||||||
|
if (!in_lock_ready) {
|
||||||
|
initlock(&in_lock, "inotify");
|
||||||
|
in_lock_ready = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue one event. in_lock held. Linux's padding: the name, its NUL, and
|
||||||
|
// zeros up to a multiple of the header's size.
|
||||||
|
static void
|
||||||
|
in_queue(struct inotify *in, int wd, uint32 mask, uint32 cookie, const char *name)
|
||||||
|
{
|
||||||
|
int nlen = name ? strlen(name) + 1 : 0;
|
||||||
|
int pad = nlen ? (nlen + 15) & ~15 : 0;
|
||||||
|
int need = (int)sizeof(struct in_event) + pad;
|
||||||
|
struct in_event e;
|
||||||
|
|
||||||
|
// Room is kept for one overflow event, which says "you missed some".
|
||||||
|
if (in->qlen + need > IN_QBYTES - (int)sizeof(struct in_event)) {
|
||||||
|
if (!in->overflowed) {
|
||||||
|
e.wd = -1; e.mask = IN_Q_OVERFLOW; e.cookie = 0; e.len = 0;
|
||||||
|
memmove(in->q + in->qlen, &e, sizeof(e));
|
||||||
|
in->qlen += sizeof(e);
|
||||||
|
in->overflowed = 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.wd = wd; e.mask = mask; e.cookie = cookie; e.len = (uint32)pad;
|
||||||
|
// The same event as the last one queued is not queued twice, as on Linux:
|
||||||
|
// a file written in a hundred pieces is one IN_MODIFY until someone reads.
|
||||||
|
if (in->qlen >= need && !name && pad == 0) {
|
||||||
|
struct in_event last;
|
||||||
|
memmove(&last, in->q + in->qlen - sizeof(last), sizeof(last));
|
||||||
|
if (last.wd == wd && last.mask == mask && last.cookie == cookie && last.len == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memmove(in->q + in->qlen, &e, sizeof(e));
|
||||||
|
if (pad) {
|
||||||
|
memset(in->q + in->qlen + sizeof(e), 0, pad);
|
||||||
|
memmove(in->q + in->qlen + sizeof(e), name, nlen - 1);
|
||||||
|
}
|
||||||
|
in->qlen += need;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
in_drop_watch(struct inotify *in, int i)
|
||||||
|
{
|
||||||
|
in_queue(in, in->w[i].wd, IN_IGNORED, 0, 0);
|
||||||
|
in->w[i] = in->w[--in->nw];
|
||||||
|
in_nwatch--;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32
|
||||||
|
inotify_cookie(void)
|
||||||
|
{
|
||||||
|
in_lock_once();
|
||||||
|
acquire(&in_lock);
|
||||||
|
uint32 c = ++in_cookie;
|
||||||
|
release(&in_lock);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Something happened to inode (dev, inum): to the inode itself when name is
|
||||||
|
// null, or to the entry `name` in it, a directory. Safe with inode locks
|
||||||
|
// held; takes a spinlock and nothing else.
|
||||||
|
void
|
||||||
|
inotify_event(uint dev, uint64 inum, uint32 mask, const char *name, uint32 cookie)
|
||||||
|
{
|
||||||
|
struct inotify *in;
|
||||||
|
int queued = 0;
|
||||||
|
|
||||||
|
if (in_nwatch == 0)
|
||||||
|
return;
|
||||||
|
in_lock_once();
|
||||||
|
acquire(&in_lock);
|
||||||
|
for (in = in_list; in; in = in->next) {
|
||||||
|
for (int i = 0; i < in->nw; i++) {
|
||||||
|
struct inwatch *w = &in->w[i];
|
||||||
|
if (w->dev != dev || w->inum != inum)
|
||||||
|
continue;
|
||||||
|
if (w->mask & mask & IN_ALL_EVENTS) {
|
||||||
|
in_queue(in, w->wd, mask, cookie, name);
|
||||||
|
queued = 1;
|
||||||
|
if (w->mask & IN_ONESHOT) {
|
||||||
|
in_drop_watch(in, i--);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The watched thing is gone; so is the watch, and the reader is told.
|
||||||
|
if (!name && (mask & IN_DELETE_SELF)) {
|
||||||
|
in_drop_watch(in, i--);
|
||||||
|
queued = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
release(&in_lock);
|
||||||
|
if (queued)
|
||||||
|
poll_kick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// An event on an open file, for the file and for its directory. The
|
||||||
|
// directory's needs the name, which only the path the file was opened by
|
||||||
|
// gives -- looked up again here, and only when something is being watched.
|
||||||
|
void
|
||||||
|
inotify_file(struct file *f, uint32 mask)
|
||||||
|
{
|
||||||
|
char name[VFS_NAME_MAX + 1];
|
||||||
|
struct inode *dp;
|
||||||
|
|
||||||
|
if (in_nwatch == 0 || f->type != FD_INODE || !f->ip)
|
||||||
|
return;
|
||||||
|
inotify_event(f->ip->dev, f->ip->inum, mask, 0, 0);
|
||||||
|
if (!f->path)
|
||||||
|
return;
|
||||||
|
begin_op();
|
||||||
|
if ((dp = nameiparent(f->path, name)) != 0) {
|
||||||
|
inotify_event(dp->dev, dp->inum, mask, name, 0);
|
||||||
|
iput(dp);
|
||||||
|
}
|
||||||
|
end_op();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
in_pending(struct inotify *in)
|
||||||
|
{
|
||||||
|
in_lock_once();
|
||||||
|
acquire(&in_lock);
|
||||||
|
int r = in->qlen > 0;
|
||||||
|
release(&in_lock);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
in_free(struct inotify *in)
|
||||||
|
{
|
||||||
|
struct inotify **pp;
|
||||||
|
|
||||||
|
in_lock_once();
|
||||||
|
acquire(&in_lock);
|
||||||
|
for (pp = &in_list; *pp; pp = &(*pp)->next)
|
||||||
|
if (*pp == in) {
|
||||||
|
*pp = in->next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
in_nwatch -= in->nw;
|
||||||
|
release(&in_lock);
|
||||||
|
kfree(in->q);
|
||||||
|
kfree((char *)in);
|
||||||
|
}
|
||||||
|
|
||||||
|
// As many whole events as fit; EINVAL if not even the first does.
|
||||||
|
static int
|
||||||
|
in_read(struct file *f, struct inotify *in, char *addr, int n)
|
||||||
|
{
|
||||||
|
char *tmp;
|
||||||
|
int take;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
in_lock_once();
|
||||||
|
acquire(&in_lock);
|
||||||
|
if (in->qlen > 0)
|
||||||
|
break;
|
||||||
|
release(&in_lock);
|
||||||
|
if (f->nonblock)
|
||||||
|
return -EAGAIN;
|
||||||
|
if (ev_block() < 0)
|
||||||
|
return -EINTR;
|
||||||
|
}
|
||||||
|
take = 0;
|
||||||
|
while (take < in->qlen) {
|
||||||
|
struct in_event e;
|
||||||
|
memmove(&e, in->q + take, sizeof(e));
|
||||||
|
int sz = (int)sizeof(e) + (int)e.len;
|
||||||
|
if (take + sz > n)
|
||||||
|
break;
|
||||||
|
take += sz;
|
||||||
|
}
|
||||||
|
if (take == 0) {
|
||||||
|
release(&in_lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
release(&in_lock);
|
||||||
|
// Copied out of the queue under the lock into a page of our own, and from
|
||||||
|
// there to the reader without it: copy_to_user may fault.
|
||||||
|
if ((tmp = kalloc()) == 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
acquire(&in_lock);
|
||||||
|
memmove(tmp, in->q, take);
|
||||||
|
memmove(in->q, in->q + take, in->qlen - take);
|
||||||
|
in->qlen -= take;
|
||||||
|
if (in->qlen == 0)
|
||||||
|
in->overflowed = 0;
|
||||||
|
release(&in_lock);
|
||||||
|
int r = copy_to_user((addr_t)addr, tmp, take) < 0 ? -EFAULT : take;
|
||||||
|
kfree(tmp);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
in_create(int flags)
|
||||||
|
{
|
||||||
|
struct evobj *o;
|
||||||
|
struct inotify *in;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (flags & ~(O_NONBLOCK | O_CLOEXEC))
|
||||||
|
return -EINVAL;
|
||||||
|
if ((o = ev_new(EV_INOTIFY)) == 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
if ((in = (struct inotify *)kalloc()) == 0) {
|
||||||
|
kmfree(o);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
memset(in, 0, PGSIZE);
|
||||||
|
if ((in->q = kalloc()) == 0) {
|
||||||
|
kfree((char *)in);
|
||||||
|
kmfree(o);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
in->next_wd = 1;
|
||||||
|
o->in = in;
|
||||||
|
// ev_open frees o on failure; past filealloc it does so through fileclose,
|
||||||
|
// and ev_close frees the instance too.
|
||||||
|
if ((fd = ev_open(o, flags, 1, 0)) < 0) {
|
||||||
|
if (fd == -ENFILE) {
|
||||||
|
kfree(in->q);
|
||||||
|
kfree((char *)in);
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
in_lock_once();
|
||||||
|
acquire(&in_lock);
|
||||||
|
in->next = in_list;
|
||||||
|
in_list = in;
|
||||||
|
release(&in_lock);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t sys_inotify_init(void) { return in_create(0); }
|
||||||
|
|
||||||
|
addr_t
|
||||||
|
sys_inotify_init1(void)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
argint(0, &flags);
|
||||||
|
return in_create(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t
|
||||||
|
sys_inotify_add_watch(void)
|
||||||
|
{
|
||||||
|
struct evobj *o;
|
||||||
|
struct inotify *in;
|
||||||
|
char *path;
|
||||||
|
uint32 mask;
|
||||||
|
int err = 0, wd = -1;
|
||||||
|
uint dev;
|
||||||
|
uint64 inum;
|
||||||
|
|
||||||
|
if ((o = ev_arg(0, EV_INOTIFY, 0)) == 0)
|
||||||
|
return -EBADF;
|
||||||
|
in = o->in;
|
||||||
|
if (argstr(1, &path) < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
argint(2, (int *)&mask);
|
||||||
|
if (!(mask & IN_ALL_EVENTS))
|
||||||
|
return -EINVAL;
|
||||||
|
if ((mask & IN_MASK_ADD) && (mask & IN_MASK_CREATE))
|
||||||
|
return -EINVAL;
|
||||||
|
if ((err = path_identity(path, !(mask & IN_DONT_FOLLOW),
|
||||||
|
(mask & IN_ONLYDIR) != 0, &dev, &inum)) < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
mask &= IN_ALL_EVENTS | IN_ONESHOT | IN_EXCL_UNLINK | IN_MASK_ADD;
|
||||||
|
in_lock_once();
|
||||||
|
acquire(&in_lock);
|
||||||
|
for (int i = 0; i < in->nw; i++) {
|
||||||
|
if (in->w[i].dev == dev && in->w[i].inum == inum) {
|
||||||
|
if (mask & IN_MASK_CREATE) {
|
||||||
|
release(&in_lock);
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
in->w[i].mask = (mask & IN_MASK_ADD) ? in->w[i].mask | (mask & ~IN_MASK_ADD)
|
||||||
|
: mask;
|
||||||
|
wd = in->w[i].wd;
|
||||||
|
release(&in_lock);
|
||||||
|
return wd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (in->nw >= IN_MAXW) {
|
||||||
|
release(&in_lock);
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
wd = in->next_wd++;
|
||||||
|
in->w[in->nw].wd = wd;
|
||||||
|
in->w[in->nw].dev = dev;
|
||||||
|
in->w[in->nw].inum = inum;
|
||||||
|
in->w[in->nw].mask = mask & ~IN_MASK_ADD;
|
||||||
|
in->nw++;
|
||||||
|
in_nwatch++;
|
||||||
|
release(&in_lock);
|
||||||
|
return wd;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t
|
||||||
|
sys_inotify_rm_watch(void)
|
||||||
|
{
|
||||||
|
struct evobj *o;
|
||||||
|
struct inotify *in;
|
||||||
|
int wd;
|
||||||
|
|
||||||
|
if ((o = ev_arg(0, EV_INOTIFY, 0)) == 0)
|
||||||
|
return -EBADF;
|
||||||
|
in = o->in;
|
||||||
|
argint(1, &wd);
|
||||||
|
in_lock_once();
|
||||||
|
acquire(&in_lock);
|
||||||
|
for (int i = 0; i < in->nw; i++) {
|
||||||
|
if (in->w[i].wd == wd) {
|
||||||
|
in_drop_watch(in, i);
|
||||||
|
release(&in_lock);
|
||||||
|
poll_kick();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
release(&in_lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIONREAD: bytes a read would return, for the kinds where that is a number
|
||||||
|
// worth asking. inotifywait sizes its read buffer by it, and stopped at
|
||||||
|
// "Not a tty" without.
|
||||||
|
int
|
||||||
|
ev_fionread(struct evobj *o)
|
||||||
|
{
|
||||||
|
if (o->kind == EV_INOTIFY) {
|
||||||
|
in_lock_once();
|
||||||
|
acquire(&in_lock);
|
||||||
|
int n = o->in->qlen;
|
||||||
|
release(&in_lock);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return -ENOTTY;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include "core/uaccess.h"
|
#include "core/uaccess.h"
|
||||||
#include "core/kmem.h"
|
#include "core/kmem.h"
|
||||||
#include "uapi/errno.h"
|
#include "uapi/errno.h"
|
||||||
|
#include "uapi/inotify.h"
|
||||||
#include "fs/schfs.h" /* SCHFS_BLKSIZE, SCHFS_NAME_MAX — statfs reports them */
|
#include "fs/schfs.h" /* SCHFS_BLKSIZE, SCHFS_NAME_MAX — statfs reports them */
|
||||||
#include "gfx/gfx.h" /* gfx_cols/gfx_rows — TIOCGWINSZ 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 */
|
#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;
|
if (err) *err = r == -EINVAL ? EINVAL : ENOSPC;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
inotify_event(dp->dev, dp->inum, IN_CREATE | (type == T_DIR ? IN_ISDIR : 0), name, 0);
|
||||||
iunlockput(dp);
|
iunlockput(dp);
|
||||||
return ip;
|
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*
|
static struct inode*
|
||||||
create(char *path, short type, short major, short minor)
|
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->dev != ip->dev) { iunlockput(dp); err = EXDEV; goto bad_link; }
|
||||||
if (dp->ops->no_hardlinks) { iunlockput(dp); err = EPERM; 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; }
|
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();
|
iunlockput(dp); iput(ip); end_op();
|
||||||
return 0;
|
return 0;
|
||||||
bad_link:
|
bad_link:
|
||||||
|
|
@ -1107,6 +1131,17 @@ create_sock(char *path)
|
||||||
return create(path, T_SOCK, 0, 0);
|
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
|
// 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
|
// its own node back out without going through the syscall entry, and so there
|
||||||
// is one implementation of what unlink means rather than two.
|
// 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 (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 (dirunlink(dp, name, off) < 0) { iunlockput(ip); err = EIO; goto bad; }
|
||||||
if (ip->type == T_DIR) { dp->nlink--; iupdate(dp); }
|
if (ip->type == T_DIR) { dp->nlink--; iupdate(dp); }
|
||||||
|
inotify_unlinked(dp, ip, name);
|
||||||
iunlockput(dp); ip->nlink--; iupdate(ip); iunlockput(ip);
|
iunlockput(dp); ip->nlink--; iupdate(ip); iunlockput(ip);
|
||||||
return 0;
|
return 0;
|
||||||
bad:
|
bad:
|
||||||
|
|
@ -1185,6 +1221,7 @@ addr_t sys_ftruncate(void)
|
||||||
begin_op(); ilock(f->ip);
|
begin_op(); ilock(f->ip);
|
||||||
itrunc_to(f->ip, (uint64)length);
|
itrunc_to(f->ip, (uint64)length);
|
||||||
iunlock(f->ip); end_op();
|
iunlock(f->ip); end_op();
|
||||||
|
inotify_file(f, IN_MODIFY);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1200,6 +1237,7 @@ addr_t sys_truncate(void)
|
||||||
if (!(ip = at_nameif(AT_FDCWD, path, &err))) { end_op(); return -err; }
|
if (!(ip = at_nameif(AT_FDCWD, path, &err))) { end_op(); return -err; }
|
||||||
ilock(ip);
|
ilock(ip);
|
||||||
itrunc_to(ip, (uint64)length);
|
itrunc_to(ip, (uint64)length);
|
||||||
|
inotify_event(ip->dev, ip->inum, IN_MODIFY, 0, 0);
|
||||||
iunlockput(ip); end_op();
|
iunlockput(ip); end_op();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1255,6 +1293,8 @@ do_rename(int olddirfd, char *oldpath, int newdirfd, char *newpath)
|
||||||
if (dp_new) iput(dp_new);
|
if (dp_new) iput(dp_new);
|
||||||
goto bad_rename;
|
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.
|
// Clear the destination, if anything is there.
|
||||||
ilock(dp_new);
|
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);
|
iunlockput(ip_dst); iunlockput(dp_new); iput(dp_old);
|
||||||
err = EIO; goto bad_rename;
|
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);
|
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) {
|
if (dirunlink(dp_old, oldname, off) < 0) {
|
||||||
iunlockput(dp_old); err = EIO; goto bad_rename;
|
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);
|
iunlockput(dp_old);
|
||||||
ilock(ip); ip->nlink--; iupdate(ip); iunlockput(ip);
|
ilock(ip); ip->nlink--; iupdate(ip); iunlockput(ip);
|
||||||
end_op(); return 0;
|
end_op(); return 0;
|
||||||
|
|
@ -1575,6 +1623,12 @@ addr_t sys_ioctl(void)
|
||||||
}
|
}
|
||||||
if (request == 0x5452) // FIOASYNC
|
if (request == 0x5452) // FIOASYNC
|
||||||
return 0;
|
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
|
// /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
|
// 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)
|
if (ip->mode == 0)
|
||||||
ip->mode = 01000000; // "no bits", as distinct from "never set"
|
ip->mode = 01000000; // "no bits", as distinct from "never set"
|
||||||
iupdate(ip);
|
iupdate(ip);
|
||||||
|
inotify_event(ip->dev, ip->inum, IN_ATTRIB, 0, 0);
|
||||||
iunlock(ip);
|
iunlock(ip);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -2062,6 +2117,7 @@ addr_t sys_utimensat(void)
|
||||||
}
|
}
|
||||||
ip->ctime = (uint)now;
|
ip->ctime = (uint)now;
|
||||||
iupdate(ip);
|
iupdate(ip);
|
||||||
|
inotify_event(ip->dev, ip->inum, IN_ATTRIB, 0, 0);
|
||||||
iunlockput(ip);
|
iunlockput(ip);
|
||||||
end_op();
|
end_op();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -2249,6 +2305,7 @@ addr_t sys_unlinkat(void)
|
||||||
if (ip->type == T_DIR && dirempty(ip) != 1) { err = ENOTEMPTY; goto badip; }
|
if (ip->type == T_DIR && dirempty(ip) != 1) { err = ENOTEMPTY; goto badip; }
|
||||||
if (dirunlink(dp, name, off) < 0) { err = EIO; goto badip; }
|
if (dirunlink(dp, name, off) < 0) { err = EIO; goto badip; }
|
||||||
if (ip->type == T_DIR) { dp->nlink--; iupdate(dp); }
|
if (ip->type == T_DIR) { dp->nlink--; iupdate(dp); }
|
||||||
|
inotify_unlinked(dp, ip, name);
|
||||||
iunlockput(dp); ip->nlink--; iupdate(ip); iunlockput(ip);
|
iunlockput(dp); ip->nlink--; iupdate(ip); iunlockput(ip);
|
||||||
end_op();
|
end_op();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -2292,6 +2349,8 @@ addr_t sys_linkat(void)
|
||||||
dirlink(dp, name, ip->inum) < 0) {
|
dirlink(dp, name, ip->inum) < 0) {
|
||||||
iunlockput(dp); goto bad_linkat;
|
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();
|
iunlockput(dp); iput(ip); end_op();
|
||||||
return 0;
|
return 0;
|
||||||
bad_linkat:
|
bad_linkat:
|
||||||
|
|
@ -2365,6 +2424,37 @@ addr_t sys_faccessat(void)
|
||||||
return 0;
|
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)
|
addr_t sys_fstatat(void)
|
||||||
{
|
{
|
||||||
int dirfd, flags;
|
int dirfd, flags;
|
||||||
|
|
|
||||||
|
|
@ -900,10 +900,31 @@ scm_gather(struct msghdr *m, struct file **fs, int *nfp)
|
||||||
}
|
}
|
||||||
fs[(*nfp)++] = filedup(f);
|
fs[(*nfp)++] = filedup(f);
|
||||||
}
|
}
|
||||||
|
} else if (c->cmsg_level == SOL_SOCKET && c->cmsg_type == 2 /* SCM_CREDENTIALS */) {
|
||||||
|
// Credentials, which GDBus sends with the first byte of every
|
||||||
|
// D-Bus connection. Checked as Linux checks them -- they must be
|
||||||
|
// the sender's own, or the sender root -- and then not delivered:
|
||||||
|
// nothing here sets SO_PASSCRED to receive them, and the D-Bus
|
||||||
|
// daemon asks SO_PEERCRED instead, which answers the same question
|
||||||
|
// from the kernel's side. Refusing them made every GNOME program
|
||||||
|
// fail its D-Bus handshake ("Error sending credentials").
|
||||||
|
struct { int pid; uint uid, gid; } cr;
|
||||||
|
struct proc *me = myproc();
|
||||||
|
if (c->cmsg_len < CMSG_LEN(sizeof(cr))) {
|
||||||
|
r = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
memmove(&cr, CMSG_DATA(c), sizeof(cr));
|
||||||
|
if (me->euid != 0 &&
|
||||||
|
(cr.pid != me->tgid ||
|
||||||
|
(cr.uid != me->uid && cr.uid != me->euid) ||
|
||||||
|
(cr.gid != me->gid && cr.gid != me->egid))) {
|
||||||
|
r = -EPERM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
} else if (c->cmsg_level == SOL_SOCKET) {
|
} else if (c->cmsg_level == SOL_SOCKET) {
|
||||||
// SCM_CREDENTIALS and friends. Refused rather than ignored: a
|
// Anything else. Refused rather than ignored: a caller that asked
|
||||||
// caller that asked to pass credentials and was told yes would
|
// for something and was told yes would believe it happened.
|
||||||
// believe the receiver got them.
|
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -213,8 +213,11 @@ SYSCALL(127, 92, chown)
|
||||||
SYSCALL(128, 93, fchown)
|
SYSCALL(128, 93, fchown)
|
||||||
SYSCALL(129, 94, lchown)
|
SYSCALL(129, 94, lchown)
|
||||||
SYSCALL(130, 84, rmdir)
|
SYSCALL(130, 84, rmdir)
|
||||||
SYSCALL(131, NO_LINUX, fsync)
|
// fsync and fdatasync had no Linux numbers from the first commit on, and
|
||||||
SYSCALL(132, NO_LINUX, fdatasync)
|
// every Linux program that saves a file carefully got ENOSYS: GLib's atomic
|
||||||
|
// file writes, gtk-update-icon-cache, dbus-uuidgen, apk's triggers.
|
||||||
|
SYSCALL(131, 74, fsync)
|
||||||
|
SYSCALL(132, 75, fdatasync)
|
||||||
SYSCALL(133, 162, sync)
|
SYSCALL(133, 162, sync)
|
||||||
SYSCALL(134, 17, pread)
|
SYSCALL(134, 17, pread)
|
||||||
SYSCALL(135, 18, pwrite)
|
SYSCALL(135, 18, pwrite)
|
||||||
|
|
@ -300,6 +303,11 @@ SYSCALL(207, 51, getsockname)
|
||||||
SYSCALL(208, 52, getpeername)
|
SYSCALL(208, 52, getpeername)
|
||||||
SYSCALL(209, 125, capget)
|
SYSCALL(209, 125, capget)
|
||||||
SYSCALL(210, 126, capset)
|
SYSCALL(210, 126, capset)
|
||||||
|
SYSCALL(211, 439, faccessat2)
|
||||||
|
SYSCALL(212, 253, inotify_init)
|
||||||
|
SYSCALL(213, 294, inotify_init1)
|
||||||
|
SYSCALL(214, 254, inotify_add_watch)
|
||||||
|
SYSCALL(215, 255, inotify_rm_watch)
|
||||||
|
|
||||||
// --- mounts ---
|
// --- mounts ---
|
||||||
SYSCALL(185, 165, mount)
|
SYSCALL(185, 165, mount)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue