kernel: HTTPS through wget, half-close, and /proc/<pid>/fd
busybox wget hands TLS to ssl_client over a socketpair and then does shutdown(SHUT_WR). That cut the AF_UNIX link both ways, so ssl_client's reply was a SIGPIPE and every https:// download hung. SHUT_WR is now a half-close: the peer reads EOF and can still answer. TCP sockets get shutdown() at all (tcp_shutdown for the FIN); it used to do nothing. - exec keeps ignored signals and the signal mask, as execve(2) says. - /proc/<pid>/fd: a link per descriptor -- the path it was opened by, socket:[n], pipe:[n], anon_inode:[eventpoll]. /proc/self too. The global inode bit moves above pid << 8; at 0x10000 it was /proc/256. - fstat on a socket or pipe answers S_IFSOCK / S_IFIFO instead of EINVAL. - poll, select and epoll wake as soon as a pipe, socket or input device changes, not on the next tick. - Pipes are 64 KB of lazily allocated pages, copied in blocks, with PIPE_BUF atomicity: 300 MB through `yes | head | wc` in 7 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
906318f2b6
commit
8351e0323b
15 changed files with 434 additions and 71 deletions
|
|
@ -43,18 +43,22 @@
|
|||
// identity of the object.
|
||||
//
|
||||
// 1 the root, /proc
|
||||
// 2 /proc/self
|
||||
// PROCFS_GLOBAL | id a global file, id being the PROC_* minor that
|
||||
// kernel/dev/procfs.c already uses
|
||||
// pid << 8 the directory /proc/<pid>
|
||||
// (pid << 8) | fileid a file inside it
|
||||
// (pid << 8) | 0x80 | fd /proc/<pid>/fd/<fd>
|
||||
//
|
||||
// pid is bounded by NPROC and file ids by PF_MAX, so the ranges cannot
|
||||
// collide. Shifting by 8 rather than packing tightly keeps the numbers
|
||||
// readable in a debugger, which matters more than density here.
|
||||
// File ids stay below 0x80 and fds below 128 (NOFILE). pids are not bounded
|
||||
// by NPROC -- they count up -- so the global bit sits far above any pid << 8;
|
||||
// at 0x10000 it was /proc/256. Shifting by 8 rather than packing tightly keeps
|
||||
// the numbers readable in a debugger, which matters more than density here.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define PROCFS_ROOT 1ULL
|
||||
#define PROCFS_GLOBAL 0x10000ULL
|
||||
#define PROCFS_SELF 2ULL
|
||||
#define PROCFS_GLOBAL (1ULL << 40)
|
||||
|
||||
#define PF_DIR 0 // the directory itself
|
||||
#define PF_STAT 1
|
||||
|
|
@ -63,10 +67,14 @@
|
|||
#define PF_STATM 4
|
||||
#define PF_EXE 5
|
||||
#define PF_SCHEDSTAT 6
|
||||
#define PF_MAX 6
|
||||
#define PF_FD 7 // the directory /proc/<pid>/fd
|
||||
#define PF_MAX 7
|
||||
#define PF_FDENT 0x80 // | fd: one link in it
|
||||
|
||||
#define INO_PID(ino) ((int)((ino) >> 8))
|
||||
#define INO_FILE(ino) ((int)((ino) & 0xFF))
|
||||
#define INO_IS_FDENT(ino) (!INO_IS_GLOBAL(ino) && (INO_FILE(ino) & PF_FDENT))
|
||||
#define INO_FD(ino) (INO_FILE(ino) & 0x7F)
|
||||
#define INO_IS_GLOBAL(ino) (((ino) & PROCFS_GLOBAL) != 0)
|
||||
|
||||
// The global files, by the name they appear under and the PROC_* id that
|
||||
|
|
@ -121,6 +129,9 @@ static const struct global_file pidfiles[] = {
|
|||
// inventing a wait time that is not measured would be worse than saying
|
||||
// something true in its place.
|
||||
{ "schedstat", PF_SCHEDSTAT },
|
||||
// One symlink per open descriptor, named by its number and pointing at the
|
||||
// path it was opened by, or at "socket:[ino]" and the like.
|
||||
{ "fd", PF_FD },
|
||||
};
|
||||
#define NPIDFILES ((int)(sizeof(pidfiles) / sizeof(pidfiles[0])))
|
||||
|
||||
|
|
@ -267,6 +278,15 @@ gen_file(uint64 ino, char *buf, int cap)
|
|||
|
||||
if (INO_IS_GLOBAL(ino))
|
||||
return proc_gen_global((int)(ino & 0xFF), buf, cap);
|
||||
// Whoever is asking: the one file whose content depends on the reader.
|
||||
if (ino == PROCFS_SELF) {
|
||||
sbputi(&s, myproc()->pid);
|
||||
return s.n;
|
||||
}
|
||||
if (INO_IS_FDENT(ino)) {
|
||||
int n = proc_fd_link(INO_PID(ino), INO_FD(ino), buf, cap);
|
||||
return n > 0 ? n : 0;
|
||||
}
|
||||
|
||||
if (proc_snapshot_pid(INO_PID(ino), &pi) < 0)
|
||||
return 0; // it exited between lookup and read
|
||||
|
|
@ -310,7 +330,8 @@ procfs_iload(struct inode *ip)
|
|||
ip->showmode = 1;
|
||||
ip->atime = ip->mtime = ip->ctime = 0;
|
||||
|
||||
if (ino == PROCFS_ROOT || (!INO_IS_GLOBAL(ino) && INO_FILE(ino) == PF_DIR)) {
|
||||
if (ino == PROCFS_ROOT ||
|
||||
(!INO_IS_GLOBAL(ino) && (INO_FILE(ino) == PF_DIR || INO_FILE(ino) == PF_FD))) {
|
||||
ip->type = T_DIR;
|
||||
ip->size = 0;
|
||||
ip->nlink = 2;
|
||||
|
|
@ -329,8 +350,12 @@ procfs_iload(struct inode *ip)
|
|||
// symlink whose size is 0 resolves to the empty string -- which is worse
|
||||
// than the regular file was, because it looks like it worked. Generating it
|
||||
// here costs one small render per stat() of one file.
|
||||
if (!INO_IS_GLOBAL(ino) && INO_FILE(ino) == PF_EXE) {
|
||||
char path[64];
|
||||
// /proc/self and /proc/<pid>/fd/N are links too. Their targets can change
|
||||
// under a cached inode, which is why readlink() does not trust this size
|
||||
// for procfs.
|
||||
if (ino == PROCFS_SELF || INO_IS_FDENT(ino) ||
|
||||
(!INO_IS_GLOBAL(ino) && INO_FILE(ino) == PF_EXE)) {
|
||||
char path[256];
|
||||
int len = gen_file(ino, path, sizeof(path));
|
||||
ip->type = T_SYMLINK;
|
||||
ip->size = len > 0 ? (uint64)len : 0;
|
||||
|
|
@ -469,8 +494,19 @@ procfs_dirlookup(struct inode *dp, char *name, uint *poff)
|
|||
|
||||
if (namecmp(name, ".") == 0)
|
||||
return iget(dp->dev, dp->inum);
|
||||
if (namecmp(name, "..") == 0)
|
||||
return iget(dp->dev, PROCFS_ROOT); // /proc/<pid>/.. is /proc
|
||||
if (namecmp(name, "..") == 0) // /proc/<pid>/.. is /proc
|
||||
return iget(dp->dev, INO_FILE(dp->inum) == PF_FD ? (dp->inum & ~0xFFULL)
|
||||
: PROCFS_ROOT);
|
||||
|
||||
// Inside /proc/<pid>/fd/: a number that is an open descriptor.
|
||||
if (dp->inum != PROCFS_ROOT && INO_FILE(dp->inum) == PF_FD) {
|
||||
char tmp[4];
|
||||
int fd = name_to_pid(name);
|
||||
if (fd < 0 || fd >= NOFILE || fd >= 128 ||
|
||||
proc_fd_link(INO_PID(dp->inum), fd, tmp, sizeof(tmp)) < 0)
|
||||
return 0;
|
||||
return iget(dp->dev, (dp->inum & ~0xFFULL) | PF_FDENT | fd);
|
||||
}
|
||||
|
||||
// Inside /proc/<pid>/
|
||||
if (dp->inum != PROCFS_ROOT) {
|
||||
|
|
@ -480,6 +516,9 @@ procfs_dirlookup(struct inode *dp, char *name, uint *poff)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (namecmp(name, "self") == 0)
|
||||
return iget(dp->dev, PROCFS_SELF);
|
||||
|
||||
// In /proc itself: a number is a process, a word is a global file.
|
||||
pid = name_to_pid(name);
|
||||
if (pid > 0) {
|
||||
|
|
@ -516,6 +555,7 @@ struct linux_dirent64 {
|
|||
|
||||
#define DT_DIR_ 4
|
||||
#define DT_REG_ 8
|
||||
#define DT_LNK_ 10
|
||||
|
||||
static int
|
||||
emit(char *out, int cap, int used, uint64 ino, int type, const char *name)
|
||||
|
|
@ -574,17 +614,30 @@ procfs_getdents(struct inode *dp, char *out, int cap, uint64 *off)
|
|||
if (dp->inum == PROCFS_ROOT) {
|
||||
EMIT(PROCFS_ROOT, DT_DIR_, ".");
|
||||
EMIT(PROCFS_ROOT, DT_DIR_, "..");
|
||||
EMIT(PROCFS_SELF, DT_LNK_, "self");
|
||||
for (i = 0; i < NGLOBALS; i++)
|
||||
EMIT(PROCFS_GLOBAL | globals[i].id, DT_REG_, globals[i].name);
|
||||
for (i = 0; proc_snapshot(i, &pi) == 0; i++) {
|
||||
utoa((uint)pi.pid, name);
|
||||
EMIT(((uint64)pi.pid << 8) | PF_DIR, DT_DIR_, name);
|
||||
}
|
||||
} else if (INO_FILE(dp->inum) == PF_FD) {
|
||||
uint64 mask[2];
|
||||
EMIT(dp->inum, DT_DIR_, ".");
|
||||
EMIT(dp->inum & ~0xFFULL, DT_DIR_, "..");
|
||||
if (proc_fd_mask(INO_PID(dp->inum), mask) == 0)
|
||||
for (i = 0; i < 128; i++)
|
||||
if (mask[i / 64] & (1ULL << (i % 64))) {
|
||||
utoa((uint)i, name);
|
||||
EMIT((dp->inum & ~0xFFULL) | PF_FDENT | i, DT_LNK_, name);
|
||||
}
|
||||
} else {
|
||||
EMIT(dp->inum, DT_DIR_, ".");
|
||||
EMIT(PROCFS_ROOT, DT_DIR_, "..");
|
||||
for (i = 0; i < NPIDFILES; i++)
|
||||
EMIT((dp->inum & ~0xFFULL) | pidfiles[i].id, DT_REG_, pidfiles[i].name);
|
||||
EMIT((dp->inum & ~0xFFULL) | pidfiles[i].id,
|
||||
pidfiles[i].id == PF_FD ? DT_DIR_ : pidfiles[i].id == PF_EXE ? DT_LNK_ : DT_REG_,
|
||||
pidfiles[i].name);
|
||||
}
|
||||
|
||||
full:
|
||||
|
|
|
|||
Loading…
Reference in a new issue