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
|
|
@ -13,6 +13,7 @@
|
|||
#include "uapi/posix.h"
|
||||
#include "core/syscall.h"
|
||||
#include "core/uaccess.h"
|
||||
#include "core/kmem.h"
|
||||
#include "uapi/errno.h"
|
||||
#include "fs/schfs.h" /* SCHFS_BLKSIZE, SCHFS_NAME_MAX — statfs reports them */
|
||||
#include "gfx/gfx.h" /* gfx_cols/gfx_rows — TIOCGWINSZ reports them */
|
||||
|
|
@ -139,6 +140,53 @@ fdalloc(struct file *f)
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Remember the absolute path `f` was opened by, for /proc/<pid>/fd. `path` is
|
||||
// joined to the directory it was resolved from -- the cwd, or dirfd's own
|
||||
// remembered path -- and `.`, `..` and repeated slashes are folded out. This
|
||||
// is lexical: a symlink along the way stays in the name. Nothing depends on
|
||||
// it being there, so running out of memory just leaves it unset.
|
||||
static void
|
||||
file_setpath(struct file *f, int dirfd, const char *path)
|
||||
{
|
||||
const char *base = "";
|
||||
if (path[0] != '/') {
|
||||
struct file *d;
|
||||
if (dirfd == AT_FDCWD)
|
||||
base = myproc()->cwd_path;
|
||||
else if (dirfd >= 0 && dirfd < NOFILE && (d = myproc()->ofile[dirfd]) && d->path)
|
||||
base = d->path;
|
||||
else
|
||||
return;
|
||||
}
|
||||
char *buf = kalloc();
|
||||
if (!buf) return;
|
||||
int n = 0; // buf is "/a/b", no trailing slash
|
||||
for (int pass = 0; pass < 2; pass++) {
|
||||
const char *s = pass ? path : base;
|
||||
for (;;) {
|
||||
while (*s == '/') s++;
|
||||
const char *e = s;
|
||||
while (*e && *e != '/') e++;
|
||||
int len = (int)(e - s);
|
||||
if (len == 0) break;
|
||||
if (len == 2 && s[0] == '.' && s[1] == '.') {
|
||||
while (n > 0 && buf[--n] != '/') ;
|
||||
} else if (!(len == 1 && s[0] == '.')) {
|
||||
if (n + 1 + len >= PGSIZE) { kfree(buf); return; }
|
||||
buf[n++] = '/';
|
||||
memmove(buf + n, s, len);
|
||||
n += len;
|
||||
}
|
||||
s = e;
|
||||
}
|
||||
}
|
||||
if (n == 0) buf[n++] = '/';
|
||||
buf[n] = 0;
|
||||
if ((f->path = kmalloc(n + 1)) != 0)
|
||||
memmove(f->path, buf, n + 1);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
// Resolves a dirfd (AT_FDCWD or a real fd) to the inode a relative path
|
||||
// should be resolved from. 0 on an invalid dirfd.
|
||||
static struct inode *
|
||||
|
|
@ -602,6 +650,7 @@ addr_t sys_open(void)
|
|||
}
|
||||
iunlock(ip); end_op();
|
||||
f->type = FD_INODE; f->ip = ip;
|
||||
file_setpath(f, AT_FDCWD, path);
|
||||
f->off = (omode & O_APPEND) ? ip->size : 0;
|
||||
f->readable = !(omode & O_WRONLY);
|
||||
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
|
||||
|
|
@ -659,6 +708,7 @@ addr_t sys_openat(void)
|
|||
uint64 start = (omode & O_APPEND) ? ip->size : 0;
|
||||
iunlock(ip); end_op();
|
||||
f->type = FD_INODE; f->ip = ip; f->off = start;
|
||||
file_setpath(f, dirfd, path);
|
||||
f->readable = !(omode & O_WRONLY);
|
||||
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
|
||||
f->nonblock = (omode & O_NONBLOCK) != 0;
|
||||
|
|
@ -697,6 +747,19 @@ addr_t sys_fstat(void)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
// A socket, a pipe or an anonymous inode: Linux answers with its type and
|
||||
// an inode number, which is what `ls -l /proc/<pid>/fd` and every program
|
||||
// asking "is my stdin a socket?" look at. ino matches /proc/<pid>/fd.
|
||||
if (f->type != FD_INODE && f->type != FD_MEMFD && myproc()->syscall_map) {
|
||||
struct linux_stat *ls = (struct linux_stat *)st;
|
||||
memset(ls, 0, sizeof(*ls));
|
||||
ls->st_mode = f->type == FD_SOCKET ? LINUX_S_IFSOCK | 0777
|
||||
: f->type == FD_PIPE ? LINUX_S_IFIFO | 0600 : 0600;
|
||||
ls->st_ino = f->ino;
|
||||
ls->st_nlink = 1;
|
||||
ls->st_blksize = PGSIZE;
|
||||
return 0;
|
||||
}
|
||||
if (f->type != FD_INODE) return -EINVAL;
|
||||
ilock(f->ip);
|
||||
if (myproc()->syscall_map) stati_linux(f->ip, (struct linux_stat*)st);
|
||||
|
|
@ -1229,10 +1292,14 @@ addr_t sys_readlink(void)
|
|||
if (!(ip = namei(path))) return -ENOENT;
|
||||
ilock(ip);
|
||||
if (ip->type != T_SYMLINK) { iunlockput(ip); return -EINVAL; }
|
||||
int n = (int)ip->size < bufsiz ? (int)ip->size : bufsiz;
|
||||
if (readi(ip, 0, (uint64)buf, 0, n) != n) { iunlockput(ip); return -EIO; }
|
||||
// A /proc link can change after its inode was loaded -- an fd dup2()ed
|
||||
// over -- so its size is not trusted: the read says how long it is.
|
||||
int proc = ip->dev == PROCFS_DEV;
|
||||
int n = (proc || (int)ip->size > bufsiz) ? bufsiz : (int)ip->size;
|
||||
int got = readi(ip, 0, (uint64)buf, 0, n);
|
||||
if (got < 0 || (!proc && got != n)) { iunlockput(ip); return -EIO; }
|
||||
iunlockput(ip);
|
||||
return n;
|
||||
return got;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
|
@ -2090,10 +2157,14 @@ addr_t sys_readlinkat(void)
|
|||
if (!(ip = at_namei(dirfd, path))) return -ENOENT;
|
||||
ilock(ip);
|
||||
if (ip->type != T_SYMLINK) { iunlockput(ip); return -EINVAL; }
|
||||
int n = (int)ip->size < bufsiz ? (int)ip->size : bufsiz;
|
||||
if (readi(ip, 0, (uint64)buf, 0, n) != n) { iunlockput(ip); return -EIO; }
|
||||
// A /proc link can change after its inode was loaded -- an fd dup2()ed
|
||||
// over -- so its size is not trusted: the read says how long it is.
|
||||
int proc = ip->dev == PROCFS_DEV;
|
||||
int n = (proc || (int)ip->size > bufsiz) ? bufsiz : (int)ip->size;
|
||||
int got = readi(ip, 0, (uint64)buf, 0, n);
|
||||
if (got < 0 || (!proc && got != n)) { iunlockput(ip); return -EIO; }
|
||||
iunlockput(ip);
|
||||
return n;
|
||||
return got;
|
||||
}
|
||||
|
||||
addr_t sys_fchmodat(void) { return 0; } // stub
|
||||
|
|
|
|||
Loading…
Reference in a new issue