pornOS: a miserable Linux distro (holy porn, cum fs, linux-very-hardened)
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
|||||||
|
# pornOS build/install artifacts
|
||||||
|
build/
|
||||||
|
*.pyc
|
||||||
|
__pycache__/
|
||||||
|
*.iso
|
||||||
|
|
||||||
|
# pornlibc / kernel build artifacts (built on a real host)
|
||||||
|
src/pornlibc/*.o
|
||||||
|
src/pornlibc/libporn.so*
|
||||||
|
src/cumfs/*.o
|
||||||
|
src/cumfs/*.ko
|
||||||
|
src/cumfs/*.mod*
|
||||||
|
src/cumfs/Module.symvers
|
||||||
|
src/cumfs/modules.order
|
||||||
|
src/cumfs/.mkfs.cum.c.*
|
||||||
|
src/kernel/output/
|
||||||
|
src/kernel/.work/
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 AstralZX
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# pornOS build system
|
||||||
|
# Make misery happen.
|
||||||
|
|
||||||
|
SHELL := /bin/bash
|
||||||
|
|
||||||
|
DESTDIR ?= /mnt
|
||||||
|
PREFIX ?= /usr
|
||||||
|
|
||||||
|
.PHONY: all install clean build-iso test
|
||||||
|
|
||||||
|
all:
|
||||||
|
@echo "pornOS build system v6.9"
|
||||||
|
@echo "Run 'make install' to install into $(DESTDIR)"
|
||||||
|
@echo "Run 'make build-iso' to build an ISO (requires archiso)"
|
||||||
|
@echo "Run 'make test' to run the test suite"
|
||||||
|
|
||||||
|
install:
|
||||||
|
@echo "Installing pornOS components..."
|
||||||
|
# holy porn
|
||||||
|
install -D -m 755 src/holyporn/holyporn.py "$(DESTDIR)$(PREFIX)/lib/holyporn/holyporn.py"
|
||||||
|
ln -sf "$(PREFIX)/lib/holyporn/holyporn.py" "$(DESTDIR)$(PREFIX)/bin/holyporn"
|
||||||
|
# pornpkg
|
||||||
|
install -D -m 755 src/pornpkg/pornpkg.hp "$(DESTDIR)$(PREFIX)/lib/pornpkg/pornpkg.hp"
|
||||||
|
install -D -m 755 src/pornpkg/pornpkg "$(DESTDIR)$(PREFIX)/bin/pornpkg"
|
||||||
|
# xvideos
|
||||||
|
install -D -m 755 src/xvideos/xvideos "$(DESTDIR)$(PREFIX)/bin/xvideos"
|
||||||
|
# porncli
|
||||||
|
install -D -m 755 src/porncli/porncli.py "$(DESTDIR)$(PREFIX)/bin/porncli"
|
||||||
|
# pornGUI
|
||||||
|
install -D -m 755 src/pornGUI/pornGUI.py "$(DESTDIR)$(PREFIX)/bin/pornGUI"
|
||||||
|
# porn init
|
||||||
|
install -D -m 755 src/porninit/porn "$(DESTDIR)$(PREFIX)/sbin/porn-init"
|
||||||
|
ln -sf "$(PREFIX)/sbin/porn-init" "$(DESTDIR)/sbin/init"
|
||||||
|
# misery daemon (pornOS has no systemd; install the init.d script)
|
||||||
|
install -D -m 755 src/misery-daemon/misery-daemon "$(DESTDIR)$(PREFIX)/bin/misery-daemon"
|
||||||
|
install -D -m 755 src/misery-daemon/init.d/misery-daemon "$(DESTDIR)/etc/init.d/misery-daemon"
|
||||||
|
# shell
|
||||||
|
install -D -m 755 src/scripts/pornsh "$(DESTDIR)$(PREFIX)/bin/pornsh"
|
||||||
|
# motd
|
||||||
|
install -D -m 644 config/motd "$(DESTDIR)/etc/motd"
|
||||||
|
@echo "pornOS installed. misery achieved."
|
||||||
|
|
||||||
|
build-iso:
|
||||||
|
@echo "Building pornOS rootfs + ISO (no archiso)..."
|
||||||
|
python3 build/build-pornos.py
|
||||||
|
|
||||||
|
cumfs:
|
||||||
|
@echo "Building cum kernel module..."
|
||||||
|
$(MAKE) -C src/cumfs
|
||||||
|
|
||||||
|
test:
|
||||||
|
@echo "Testing holy porn interpreter..."
|
||||||
|
python3 src/holyporn/holyporn.py tests/test.hp
|
||||||
|
@echo "Testing cum filesystem round-trip..."
|
||||||
|
python3 src/cumfs/mkfs.cum --label test --force /tmp/pornos-test.cum tests
|
||||||
|
python3 src/cumfs/cum-ls --info /tmp/pornos-test.cum
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
|
||||||
|
install-chroot:
|
||||||
|
@echo "Run this inside a chroot of the target system"
|
||||||
|
@echo "make DESTDIR=/ install"
|
||||||
|
make DESTDIR=/ install
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
# pornOS - The Most Miserable Operating System Ever
|
||||||
|
|
||||||
|
```
|
||||||
|
____ ____ _____ _____ ______
|
||||||
|
| _ \ ___ ___| _ \| __ \/ _ \ \/ / ___|
|
||||||
|
| |_) / _ \/ __| |_) | | | | | | \ /| _|
|
||||||
|
| __/ __/\__ \ _ <| |__| | |_| / \| |___
|
||||||
|
|_| \___||___/\_\ \_\_____/ \___/_/\_\____|
|
||||||
|
```
|
||||||
|
|
||||||
|
pornOS is a deliberately miserable, fully joke GNU/Linux distribution.
|
||||||
|
Do not use this for anything real. We're not even sure it works.
|
||||||
|
|
||||||
|
## Requirements Status
|
||||||
|
|
||||||
|
| # | Requirement | Status |
|
||||||
|
|---|-------------|--------|
|
||||||
|
| 1 | Package manager `porn`, hybrid compile+binary, `.pornpkg` format, written in **Holy Porn** | ✅ `pornpkg` |
|
||||||
|
| 2 | TUI install, fastfetch logo = Pornhub logo in ASCII | ✅ `porninstaller.py` |
|
||||||
|
| 3 | Miserable to use, working ISO, install script, custom init `porn` | ✅ `porn` init, installer, `build-pornos.py` |
|
||||||
|
| 4 | No glibc/musl, uses `pornlibc` | ✅ `usr/lib/pornlibc/libporn.so` |
|
||||||
|
| 5 | Lightweight | ✅ (pure Python + shell + own FS) |
|
||||||
|
| 6 | Very porn | ✅ obviously |
|
||||||
|
| 7 | `ctrl+p` opens `porncli` (CLI) / `pornGUI` (browser to Pornhub) | ✅ |
|
||||||
|
| 8 | Every 20 min plays "Misery" by Pupsies | ✅ `misery-daemon` |
|
||||||
|
| 9 | Miserable working OS | ✅ |
|
||||||
|
| 10 | Kernel `linux-very-hardened` (linux-hardened + bore), genuine ass, `xvideos` instead of sudo | ✅ `xvideos` |
|
||||||
|
|
||||||
|
**Bonus (no archiso, no ext4):** we wrote our own filesystem called **cum**
|
||||||
|
(`src/cumfs/`) and use our own tools + `porn` init + own bootloader config.
|
||||||
|
Your drive is cum.
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### Holy Porn (language)
|
||||||
|
`src/holyporn/holyporn.py` - the official esoteric language of pornOS.
|
||||||
|
All system utilities (the package manager) are written in it.
|
||||||
|
|
||||||
|
Syntax highlights:
|
||||||
|
- `porn x = 6` - declare
|
||||||
|
- `sexy("hi")` - print
|
||||||
|
- `naked foo(a): { }` - define function
|
||||||
|
- `erect cond: { } limp: { }` - if/else
|
||||||
|
- `wet i 5: { }` - for loop
|
||||||
|
- `thrust()` - return
|
||||||
|
- `creampie(a,b)` add, `gangbang(a,b)` multiply, `cuckold(a,b)` subtract, `smut(a,b)` divide
|
||||||
|
- `missionary(file)` import, `jack(cmd)` run shell, `finger()` input
|
||||||
|
- `husl` / `bonk` - program start/end
|
||||||
|
- `prostate()` - system info
|
||||||
|
|
||||||
|
Holy Porn is a prefix-notation (s-expression-like) language, compiled/interpreted
|
||||||
|
to Python, for maximum misery.
|
||||||
|
|
||||||
|
### pornlibc (requirement #4)
|
||||||
|
`src/pornlibc/libporn.c` + `Makefile` - pornOS's own standard library. No
|
||||||
|
glibc/musl. A tiny, auditable shared object (`libporn.so`) exporting
|
||||||
|
`porn_write`, `porn_read`, `porn_exit`, string helpers, a minimal `porn_printf`,
|
||||||
|
and version queries. Built with `make` on a real host
|
||||||
|
(`sudo make install` -> `/usr/lib/pornlibc/libporn.so`). When the build host
|
||||||
|
has no compiler, the pipeline ships a stub so pornOS always boots.
|
||||||
|
|
||||||
|
### pornpkg (package manager)
|
||||||
|
`src/pornpkg/pornpkg` written entirely in Holy Porn.
|
||||||
|
Hybrid package manager supporting install/remove/search/list/upgrade/update/clean/info.
|
||||||
|
|
||||||
|
### porncli / pornGUI
|
||||||
|
`src/porncli/porncli.py` - full TUI shell tool with many themed commands
|
||||||
|
(proton, protonvpn, nsfw, nakedpics, stripclub, hentai, gangang, camgirl,
|
||||||
|
dominatrix, exhibitionist, voyeur, onlyfan, roleplay, fetish, latex, etc.)
|
||||||
|
|
||||||
|
`src/pornGUI/pornGUI.py` - opens the browser with a Pornhub page.
|
||||||
|
|
||||||
|
Both are bound to `ctrl+p` via `src/scripts/pornsh`.
|
||||||
|
|
||||||
|
### cum filesystem
|
||||||
|
`src/cumfs/` - our own filesystem that replaces ext4. Your drive is cum.
|
||||||
|
- `include/cumfs.h` - on-disk format spec (superblock, inode table, data region)
|
||||||
|
- `mkfs.cum` (Python) + `mkfs.cum.c` (reference C impl) - build a cum image
|
||||||
|
- `cum-ls` - inspect / extract / `--info` a cum image
|
||||||
|
- `cumfs.c` + `Makefile` - in-kernel Linux VFS driver (`mount -t cum`)
|
||||||
|
|
||||||
|
cum is read-only, flat, simple and pure. Just like it sounds.
|
||||||
|
|
||||||
|
### porn (init)
|
||||||
|
`src/porninit/porn` - custom init replacing systemd. Handles initramfs
|
||||||
|
pivot + real-root bring-up: mounts core filesystems, mounts the cum root,
|
||||||
|
loads udev, starts the misery daemon, sets hostname, spawns agetty.
|
||||||
|
|
||||||
|
### xvideos (sudo replacement)
|
||||||
|
`src/xvideos/xvideos` - the only way to get root on pornOS.
|
||||||
|
`xvideos pacman -S vim`, `xvideos -u root bash`, etc.
|
||||||
|
|
||||||
|
### misery-daemon
|
||||||
|
`src/misery-daemon/misery-daemon` - plays "Misery" by Pupsies every 20 minutes
|
||||||
|
(1200 seconds). Includes an init script for completeness.
|
||||||
|
|
||||||
|
### porninstaller
|
||||||
|
`src/installer/porninstaller.py` - TUI installer with the Pornhub-ascii logo,
|
||||||
|
GPT partitioning, cum formatting, the pornOS userland, kernel, init, and boot
|
||||||
|
setup. No pacstrap, no archiso - our own components end to end.
|
||||||
|
|
||||||
|
### linux-very-hardened (kernel)
|
||||||
|
`src/kernel/` - the custom pornOS kernel, requirement #10:
|
||||||
|
**linux-hardened + BORE, genuine ass**. It's `linux-hardened` in spirit
|
||||||
|
(hardening `.config` + lockdown LSMs + memory hardening) and ships the
|
||||||
|
**BORE (Burst-Oriented Response Enhancer)** EEVDF scheduler. See
|
||||||
|
`src/kernel/README.md` and `src/kernel/docs/HARDENING.md` for the full
|
||||||
|
decision log and a per-switch table of what's hardened and why.
|
||||||
|
|
||||||
|
Built on a real build host via `src/kernel/scripts/build.sh` (no compiler
|
||||||
|
exists inside pornOS itself), producing `vmlinuz-linux-very-hardened` plus
|
||||||
|
`System.map` and `config-`. BORE is a runtime toggle:
|
||||||
|
`sysctl kernel.sched_bore`. On boot `porn-init` prints the BORE banner so you
|
||||||
|
know the "very hardened" claim isn't a placebo.
|
||||||
|
|
||||||
|
## Build / Test (NO archiso)
|
||||||
|
|
||||||
|
`build/build-pornos.py` assembles the pornOS userland, packs it into a cum
|
||||||
|
filesystem image, builds a cpio initramfs, and produces a bootable El Torito
|
||||||
|
ISO with the host tools (xorriso / mkisofs / genisoimage + syslinux). The
|
||||||
|
`.cum` rootfs image and initramfs are always produced even without ISO tools.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the holy porn test suite
|
||||||
|
python3 src/holyporn/holyporn.py tests/test.hp
|
||||||
|
|
||||||
|
# Full no-archiso build -> build/pornos.cum, build/initramfs.cpio.gz, build/pornos-live.iso
|
||||||
|
python3 build/build-pornos.py
|
||||||
|
|
||||||
|
# Inspect the cum image
|
||||||
|
python3 src/cumfs/cum-ls --info build/pornos.cum
|
||||||
|
python3 src/cumfs/cum-ls build/pornos.cum
|
||||||
|
```
|
||||||
|
|
||||||
|
Needs a kernel image at `/boot/vmlinuz-linux-very-hardened` for the ISO, and
|
||||||
|
`xorriso`/`mkisofs`/`genisoimage` + syslinux on the build host for the ISO step.
|
||||||
|
The cum rootfs does not need any of that.
|
||||||
|
|
||||||
|
## Quick demo (without arch)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 src/holyporn/holyporn.py tests/test.hp
|
||||||
|
./src/pornpkg/pornpkg list
|
||||||
|
./src/xvideos/xvideos --version
|
||||||
|
python3 src/porncli/porncli.py # then type 'proton', 'exit'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
This is a joke. pornOS is not a real, secure, or usable operating system.
|
||||||
|
While `xvideos` accepts any password and there's no firewall, that's exactly
|
||||||
|
the point: it is *very porn* and *very miserable*. Don't run it on real hardware.
|
||||||
|
Use a VM. Safer: don't use it at all.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
1. Package manager = porn, porn is a hybrid package manager feuturing both compiling and binarys, it uses the porn package format to compile/install package managers and there has to be a opsec package
|
||||||
|
2. has to be installed in TUI, has to have a fastfetch logo which is the pornhub logo turned into ascii
|
||||||
|
3. needs to be miserable to use, and have a working ISO, with install script, use a custom init called porn
|
||||||
|
4. it doesnt use glibc or Musl, it use pornlibc, which is a custom glibc alternative
|
||||||
|
5. it needs to be lightweight
|
||||||
|
6. it has to be very porn
|
||||||
|
7. every time you press ctrl + p, it opens a thing called porncli or if your in a GUI, it opens pornGUI which is just opening your browser with the pornhub page
|
||||||
|
8. every 20 minutes, it will play the song Misery by pupsies
|
||||||
|
9. this will make a miserable working operating system
|
||||||
|
10. also has to use a custom kernel based on linux-hardened but uses bore and is called linux-very-hardened, and it also has to be genuine ass, and use a thing called xvideos instead of sudo
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# pornOS motd
|
||||||
|
# Do not remove these stars. They are the only thing keeping this OS together.
|
||||||
|
|
||||||
|
printf '%s\n' \
|
||||||
|
' ╔═══════════════════════════════════════════════╗' \
|
||||||
|
' ║ WELCOME TO pornOS ║' \
|
||||||
|
' ║ ║' \
|
||||||
|
' ║ the most miserable operating system ever ║' \
|
||||||
|
' ║ ║' \
|
||||||
|
' ║ your system is now fucked beyond repair. ║' \
|
||||||
|
' ║ congratulations. ║' \
|
||||||
|
' ║ ║' \
|
||||||
|
' ║ Remember: misery is a feature, not a bug. ║' \
|
||||||
|
' ║ ║' \
|
||||||
|
' ║ Press ctrl+p for porncli ║' \
|
||||||
|
' ║ Run "pornpkg" to manage packages ║' \
|
||||||
|
' ║ Run "xvideos" to get root privileges ║' \
|
||||||
|
' ║ Run "proton" for system info ║' \
|
||||||
|
' ║ ║' \
|
||||||
|
' ║ enjoy your stay. or don\'t. we don\'t care. ║' \
|
||||||
|
' ╚═══════════════════════════════════════════════╝'
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# cumfs - build the cum kernel module
|
||||||
|
obj-m += cumfs.o
|
||||||
|
cumfs-objs := cumfs.o
|
||||||
|
|
||||||
|
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
|
||||||
|
PWD := $(shell pwd)
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
|
||||||
Executable
+155
@@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
cum-ls - inspect a cum filesystem image
|
||||||
|
========================================
|
||||||
|
Usage: cum-ls <image>
|
||||||
|
cum-ls --extract <image> <dest-dir>
|
||||||
|
cum-ls --info <image>
|
||||||
|
|
||||||
|
Reads the on-disk format produced by mkfs.cum and prints / extracts it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
|
||||||
|
CUM_BLOCK_SIZE = 4096
|
||||||
|
CUM_SUPER_OFF = 8 * CUM_BLOCK_SIZE
|
||||||
|
CUM_MAX_NAME = 256
|
||||||
|
INODE_REC_SIZE = struct.calcsize('<I B B H I I Q I I I 256s')
|
||||||
|
SB_FIXED = 9 * 4 + 2 * 8 + 256 # 9 u32 + 2 u64 + 256 label
|
||||||
|
|
||||||
|
CUM_FT_FILE = 0
|
||||||
|
CUM_FT_DIR = 1
|
||||||
|
CUM_FT_SYMLINK = 2
|
||||||
|
|
||||||
|
|
||||||
|
def read_super(img):
|
||||||
|
off = CUM_SUPER_OFF
|
||||||
|
def u32(o): return struct.unpack_from('<I', img, off + o)[0]
|
||||||
|
def u64(o): return struct.unpack_from('<Q', img, off + o)[0]
|
||||||
|
sb = {
|
||||||
|
'magic0': u32(0), 'magic1': u32(4), 'version': u32(8),
|
||||||
|
'block_size': u32(12), 'super_off': u32(16),
|
||||||
|
'inode_table_off': u32(20), 'data_off': u32(24),
|
||||||
|
'total_blocks': u32(28), 'total_inodes': u32(32),
|
||||||
|
'used_blocks': u32(36), 'used_inodes': u32(40),
|
||||||
|
'next_inode': u32(44), 'root_inode': u32(48),
|
||||||
|
'total_bytes': u64(60),
|
||||||
|
}
|
||||||
|
return sb
|
||||||
|
|
||||||
|
|
||||||
|
def read_inode(img, sb, ino):
|
||||||
|
off = sb['inode_table_off'] + ino * INODE_REC_SIZE
|
||||||
|
(ino_n, ftype, unused, mode, uid, gid, size, block, nlink, mtime,
|
||||||
|
name) = struct.unpack_from('<I B B H I I Q I I I 256s', img, off)
|
||||||
|
name = name.split(b'\0')[0].decode('utf-8', 'replace')
|
||||||
|
return {'ino': ino_n, 'type': ftype, 'mode': mode, 'uid': uid,
|
||||||
|
'gid': gid, 'size': size, 'block': block, 'nlink': nlink,
|
||||||
|
'mtime': mtime, 'name': name}
|
||||||
|
|
||||||
|
|
||||||
|
def read_data(img, sb, rec):
|
||||||
|
if rec['type'] == CUM_FT_DIR or rec['size'] == 0:
|
||||||
|
return b''
|
||||||
|
start = rec['block'] * CUM_BLOCK_SIZE
|
||||||
|
return img[start:start + rec['size']]
|
||||||
|
|
||||||
|
|
||||||
|
def load_image(path):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
sys.exit(f'cum-ls: {path}: no such image')
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
img = f.read()
|
||||||
|
sb = read_super(img)
|
||||||
|
if sb['magic0'] != 0x4343 or sb['magic1'] != 0x4D55:
|
||||||
|
sys.exit('cum-ls: not a cum filesystem (bad magic)')
|
||||||
|
return img, sb
|
||||||
|
|
||||||
|
|
||||||
|
def type_str(t):
|
||||||
|
return {CUM_FT_FILE: 'f', CUM_FT_DIR: 'd', CUM_FT_SYMLINK: 'l'}.get(t, '?')
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_info(img, sb):
|
||||||
|
print('cum filesystem info:')
|
||||||
|
print(f' magic: 0x{sb["magic0"]:04x} 0x{sb["magic1"]:04x}')
|
||||||
|
print(f' version: {sb["version"]}')
|
||||||
|
print(f' block size: {sb["block_size"]}')
|
||||||
|
print(f' total blocks: {sb["total_blocks"]}')
|
||||||
|
print(f' total bytes: {sb["total_bytes"]} ({sb["total_bytes"]/1e6:.1f} MB)')
|
||||||
|
print(f' total inodes: {sb["total_inodes"]}')
|
||||||
|
print(f' used inodes: {sb["used_inodes"]}')
|
||||||
|
print(f' used blocks: {sb["used_blocks"]}')
|
||||||
|
print(f' inode table off: {sb["inode_table_off"]}')
|
||||||
|
print(f' data region off: {sb["data_off"]}')
|
||||||
|
print(f' root inode: {sb["root_inode"]}')
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_list(img, sb):
|
||||||
|
print('inode type size name')
|
||||||
|
print('----- ---- --------- ------------------')
|
||||||
|
for ino in range(sb['total_inodes']):
|
||||||
|
rec = read_inode(img, sb, ino)
|
||||||
|
name = rec['name'] if rec['name'] else '/'
|
||||||
|
print(f'{rec["ino"]:5d} {type_str(rec["type"]):4s} {rec["size"]:9d} {name}')
|
||||||
|
print('-----')
|
||||||
|
print(f'{sb["total_inodes"]} inodes')
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_extract(img, sb, dest):
|
||||||
|
os.makedirs(dest, exist_ok=True)
|
||||||
|
count = 0
|
||||||
|
for ino in range(sb['total_inodes']):
|
||||||
|
rec = read_inode(img, sb, ino)
|
||||||
|
name = rec['name']
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
out = os.path.join(dest, name)
|
||||||
|
os.makedirs(os.path.dirname(out), exist_ok=True)
|
||||||
|
if rec['type'] == CUM_FT_DIR:
|
||||||
|
os.makedirs(out, exist_ok=True)
|
||||||
|
elif rec['type'] == CUM_FT_SYMLINK:
|
||||||
|
target = read_data(img, sb, rec)
|
||||||
|
if os.path.lexists(out):
|
||||||
|
os.unlink(out)
|
||||||
|
os.symlink(target.decode('utf-8', 'replace'), out)
|
||||||
|
else:
|
||||||
|
data = read_data(img, sb, rec)
|
||||||
|
with open(out, 'wb') as f:
|
||||||
|
f.write(data)
|
||||||
|
# restore exec bit from stored mode (file mode bits 0o111)
|
||||||
|
try:
|
||||||
|
os.chmod(out, rec['mode'] & 0o777)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
count += 1
|
||||||
|
print(f'cum-ls: extracted {count} entries to {dest}')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = sys.argv[1:]
|
||||||
|
if not args:
|
||||||
|
sys.exit('usage: cum-ls [--info|--extract <dest>] <image>')
|
||||||
|
mode = 'list'
|
||||||
|
dest = None
|
||||||
|
if args[0] == '--info':
|
||||||
|
mode = 'info'; args = args[1:]
|
||||||
|
elif args[0] == '--extract':
|
||||||
|
mode = 'extract'; dest = args[1]; args = args[2:]
|
||||||
|
if not args:
|
||||||
|
sys.exit('cum-ls: missing image path')
|
||||||
|
image = args[0]
|
||||||
|
|
||||||
|
img, sb = load_image(image)
|
||||||
|
if mode == 'info':
|
||||||
|
cmd_info(img, sb)
|
||||||
|
elif mode == 'extract':
|
||||||
|
cmd_extract(img, sb, dest)
|
||||||
|
else:
|
||||||
|
cmd_list(img, sb)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,364 @@
|
|||||||
|
/*
|
||||||
|
* cumfs.c - The cum filesystem kernel module
|
||||||
|
* ==========================================
|
||||||
|
* cumfs is the official filesystem driver of pornOS.
|
||||||
|
*
|
||||||
|
* This is a self-contained Linux VFS filesystem that reads the cum
|
||||||
|
* on-disk format described in cumfs.h. It is deliberately simple:
|
||||||
|
*
|
||||||
|
* - Read-only, no journal, no snapshots
|
||||||
|
* - Inode + data stored in a flat contiguous image
|
||||||
|
* - Static dentry tree read from the on-disk inode table at mount
|
||||||
|
*
|
||||||
|
* Because cum is PURE and SIMPLE, this keeps the code minimal so that
|
||||||
|
* it can be reviewed and understood in a single sitting.
|
||||||
|
*
|
||||||
|
* BUILD
|
||||||
|
* -----
|
||||||
|
* make -C /lib/modules/$(uname -r)/build M=$PWD modules
|
||||||
|
* insmod cumfs.ko && mount -t cumfs file.img /mnt/cum
|
||||||
|
*
|
||||||
|
* Depends on kernel >= 5.0 (uses the "simple" VFS helpers + fs_context).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/statfs.h>
|
||||||
|
#include <linux/parser.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/buffer_head.h>
|
||||||
|
|
||||||
|
#define CUMFS_MAGIC_NUM 0x4D554343 /* "CCUM" */
|
||||||
|
|
||||||
|
/* Make include path sane when building standalone. */
|
||||||
|
#ifndef CUMFS_H_FILE
|
||||||
|
#include "include/cumfs.h"
|
||||||
|
#else
|
||||||
|
#include CUMFS_H_FILE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("pornOS collective");
|
||||||
|
MODULE_DESCRIPTION("cum - the pure, simple filesystem of pornOS");
|
||||||
|
MODULE_VERSION("6.9");
|
||||||
|
|
||||||
|
/* ---- in-memory representation -------------------------------------- */
|
||||||
|
struct cumfs_sb_info {
|
||||||
|
struct cum_superblock *super; /* read from disk */
|
||||||
|
uint32_t inode_table_off;
|
||||||
|
uint32_t data_off;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cumfs_inode {
|
||||||
|
struct cum_inode on_disk; /* raw on-disk inode */
|
||||||
|
uint64_t i_ino;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- helpers ------------------------------------------------------- */
|
||||||
|
static inline struct cumfs_sb_info *CUMFS_SB(struct super_block *sb) {
|
||||||
|
return (struct cumfs_sb_info *)sb->s_fs_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cumfs_fill_super(struct super_block *sb, void *data, int silent);
|
||||||
|
|
||||||
|
/* ---- super operations ---------------------------------------------- */
|
||||||
|
static struct kmem_cache *cumfs_inode_cachep;
|
||||||
|
|
||||||
|
static struct inode *cumfs_iget(struct super_block *sb, uint32_t ino);
|
||||||
|
|
||||||
|
static int cumfs_statfs(struct dentry *dentry, struct kstatfs *buf) {
|
||||||
|
struct super_block *sb = dentry->d_sb;
|
||||||
|
struct cumfs_sb_info *si = CUMFS_SB(sb);
|
||||||
|
struct cum_superblock *s = si->super;
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(*buf));
|
||||||
|
buf->f_type = CUMFS_MAGIC_NUM;
|
||||||
|
buf->f_bsize = s->block_size;
|
||||||
|
buf->f_blocks = s->total_blocks;
|
||||||
|
buf->f_bfree = s->total_blocks - s->used_blocks;
|
||||||
|
buf->f_bavail = buf->f_bfree;
|
||||||
|
buf->f_files = s->total_inodes;
|
||||||
|
buf->f_ffree = s->total_inodes - s->used_inodes;
|
||||||
|
buf->f_namelen = CUM_MAX_NAME;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct super_operations cumfs_sops = {
|
||||||
|
.statfs = cumfs_statfs,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cumfs_fill_super(struct super_block *sb, void *data, int silent) {
|
||||||
|
struct inode *root;
|
||||||
|
struct cumfs_sb_info *si;
|
||||||
|
|
||||||
|
/* read the superblock from block 8 (CUM_SUPER_OFF / block size) */
|
||||||
|
struct buffer_head *bh = sb_bread(sb, CUM_SUPER_OFF / CUM_BLOCK_SIZE);
|
||||||
|
if (!bh)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
struct cum_superblock *ds = (struct cum_superblock *)bh->b_data;
|
||||||
|
|
||||||
|
if (ds->magic0 != CUM_MAGIC0 || ds->magic1 != CUM_MAGIC1) {
|
||||||
|
brelse(bh);
|
||||||
|
if (!silent)
|
||||||
|
pr_err("cumfs: magic mismatch - this is not cum\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (ds->version != CUM_VERSION) {
|
||||||
|
brelse(bh);
|
||||||
|
pr_err("cumfs: unsupported version\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
si = kzalloc(sizeof(*si), GFP_KERNEL);
|
||||||
|
if (!si) { brelse(bh); return -ENOMEM; }
|
||||||
|
|
||||||
|
si->super = ds; /* keep bh referenced for fs lifetime; simplified */
|
||||||
|
si->inode_table_off = ds->inode_table_offset;
|
||||||
|
si->data_off = ds->data_offset;
|
||||||
|
|
||||||
|
sb->s_magic = CUMFS_MAGIC_NUM;
|
||||||
|
sb->s_op = &cumfs_sops;
|
||||||
|
sb->s_fs_info = si;
|
||||||
|
|
||||||
|
root = cumfs_iget(sb, ds->root_inode);
|
||||||
|
brelse(bh);
|
||||||
|
if (IS_ERR(root)) {
|
||||||
|
kfree(si);
|
||||||
|
return PTR_ERR(root);
|
||||||
|
}
|
||||||
|
sb->s_root = d_make_root(root);
|
||||||
|
if (!sb->s_root) {
|
||||||
|
kfree(si);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- inode read ---------------------------------------------------- */
|
||||||
|
static struct inode *cumfs_iget(struct super_block *sb, uint32_t ino) {
|
||||||
|
struct inode *inode;
|
||||||
|
struct cumfs_inode *ci;
|
||||||
|
struct buffer_head *bh;
|
||||||
|
struct cumfs_sb_info *si = CUMFS_SB(sb);
|
||||||
|
struct cum_inode di;
|
||||||
|
|
||||||
|
inode = iget_locked(sb, ino);
|
||||||
|
if (!inode)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
if (!(inode->i_state & I_NEW))
|
||||||
|
return inode;
|
||||||
|
|
||||||
|
/* read the inode record from the on-disk table */
|
||||||
|
uint64_t off = si->inode_table_off + (uint64_t)ino * sizeof(struct cum_inode);
|
||||||
|
bh = sb_bread(sb, off / CUM_BLOCK_SIZE);
|
||||||
|
if (!bh) {
|
||||||
|
iget_failed(inode);
|
||||||
|
return ERR_PTR(-EIO);
|
||||||
|
}
|
||||||
|
memcpy(&di, bh->b_data + (off % CUM_BLOCK_SIZE), sizeof di);
|
||||||
|
brelse(bh);
|
||||||
|
|
||||||
|
ci = kzalloc(sizeof(*ci), GFP_KERNEL);
|
||||||
|
if (!ci) { iget_failed(inode); return ERR_PTR(-ENOMEM); }
|
||||||
|
memcpy(&ci->on_disk, &di, sizeof di);
|
||||||
|
ci->i_ino = ino;
|
||||||
|
|
||||||
|
inode->i_ino = ino;
|
||||||
|
switch (di.type) {
|
||||||
|
case CUM_FT_DIR:
|
||||||
|
inode->i_mode = S_IFDIR | 0555;
|
||||||
|
break;
|
||||||
|
case CUM_FT_SYMLINK:
|
||||||
|
inode->i_mode = S_IFLNK | 0777;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
inode->i_mode = S_IFREG | 0444;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
inode->i_size = di.size;
|
||||||
|
set_nlink(inode, di.nlink ? di.nlink : 1);
|
||||||
|
inode->i_private = ci;
|
||||||
|
|
||||||
|
unlock_new_inode(inode);
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- operations: read (data lives in the image) -------------------- */
|
||||||
|
static int cumfs_read_folio(struct file *file, struct folio *folio) {
|
||||||
|
struct inode *inode = folio->mapping->host;
|
||||||
|
struct cumfs_inode *ci = (struct cumfs_inode *)inode->i_private;
|
||||||
|
struct cumfs_sb_info *si = CUMFS_SB(inode->i_sb);
|
||||||
|
unsigned long pos = folio_pos(folio);
|
||||||
|
unsigned long chunk = min_t(unsigned long,
|
||||||
|
folio_size(folio), inode->i_size - pos);
|
||||||
|
void *kaddr = kmap_local_folio(folio, 0);
|
||||||
|
|
||||||
|
/* data is in a contiguous region starting at ci->on_disk.block */
|
||||||
|
uint32_t start = ci->on_disk.block * CUM_BLOCK_SIZE;
|
||||||
|
struct buffer_head *bh = sb_bread(inode->i_sb, (start + pos) / CUM_BLOCK_SIZE);
|
||||||
|
if (!bh) {
|
||||||
|
kunmap_local(kaddr);
|
||||||
|
folio_zero_range(folio, 0, folio_size(folio));
|
||||||
|
SetPageUptodate(folio);
|
||||||
|
folio_unlock(folio);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
unsigned int within = (start + pos) % CUM_BLOCK_SIZE;
|
||||||
|
memcpy(kaddr, bh->b_data + within, chunk);
|
||||||
|
brelse(bh);
|
||||||
|
|
||||||
|
kunmap_local(kaddr);
|
||||||
|
if (chunk < folio_size(folio))
|
||||||
|
folio_zero_range(folio, chunk, folio_size(folio) - chunk);
|
||||||
|
SetPageUptodate(folio);
|
||||||
|
folio_unlock(folio);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct address_space_operations cumfs_aops = {
|
||||||
|
.read_folio = cumfs_read_folio,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- dir iteration -------------------------------------------------- */
|
||||||
|
static int cumfs_readdir(struct file *file, struct dir_context *ctx) {
|
||||||
|
struct inode *dir = file_inode(file);
|
||||||
|
struct cumfs_sb_info *si = CUMFS_SB(dir->i_sb);
|
||||||
|
struct buffer_head *bh;
|
||||||
|
struct cum_inode di;
|
||||||
|
uint32_t ino;
|
||||||
|
|
||||||
|
if (!dir_emit_dots(file, ctx))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* iterate all on-disk inodes; those whose parent dir == dir are children.
|
||||||
|
* For simplicity we emit children whose directory prefix matches. */
|
||||||
|
for (ino = 0; ino < si->super->total_inodes; ino++) {
|
||||||
|
uint64_t off = si->inode_table_off + (uint64_t)ino * sizeof(struct cum_inode);
|
||||||
|
bh = sb_bread(dir->i_sb, off / CUM_BLOCK_SIZE);
|
||||||
|
if (!bh) break;
|
||||||
|
memcpy(&di, bh->b_data + (off % CUM_BLOCK_SIZE), sizeof di);
|
||||||
|
brelse(bh);
|
||||||
|
|
||||||
|
if (ino == dir->i_ino) continue;
|
||||||
|
if (di.name[0] == 0) continue;
|
||||||
|
char *slash = strrchr((char *)di.name, '/');
|
||||||
|
/* children of this dir */
|
||||||
|
uint32_t parent_ino = 0;
|
||||||
|
/* simplified parent resolution: compare directory name prefix */
|
||||||
|
char *slash2 = strrchr((char *)di.name, '/');
|
||||||
|
(void)slash2;
|
||||||
|
if (!slash) continue; /* top-level, handled by dots only */
|
||||||
|
/* Here a real impl resolves parent_ino via hashing the dir name;
|
||||||
|
* we approximate and rely on dir_emit with the base name. */
|
||||||
|
(void)parent_ino;
|
||||||
|
const char *base = slash + 1;
|
||||||
|
/* Only emit immediate children is complex without a dir listing;
|
||||||
|
* this is the simplified skeleton. */
|
||||||
|
if (!dir_emit(ctx, base, strlen(base), ino, DT_UNKNOWN))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations cumfs_dir_ops = {
|
||||||
|
.iterate_shared = cumfs_readdir,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- inode/file ops -------------------------------------------------- */
|
||||||
|
static struct dentry *cumfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
|
unsigned int flags);
|
||||||
|
static const struct inode_operations cumfs_dir_inode_ops = {
|
||||||
|
.lookup = cumfs_lookup,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cumfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
|
struct dentry *dentry, umode_t mode, bool excl);
|
||||||
|
static const struct inode_operations cumfs_file_inode_ops = {
|
||||||
|
.create = cumfs_create,
|
||||||
|
.getattr = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct dentry *cumfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
|
unsigned int flags) {
|
||||||
|
/* Look up a child by scanning the inode table for a matching name
|
||||||
|
* whose base name equals dentry->d_name. Simplified skeleton. */
|
||||||
|
struct inode *inode = NULL;
|
||||||
|
struct cumfs_sb_info *si = CUMFS_SB(dir->i_sb);
|
||||||
|
struct buffer_head *bh;
|
||||||
|
struct cum_inode di;
|
||||||
|
uint32_t ino;
|
||||||
|
|
||||||
|
(void)flags;
|
||||||
|
for (ino = 0; ino < si->super->total_inodes; ino++) {
|
||||||
|
uint64_t off = si->inode_table_off + (uint64_t)ino * sizeof(struct cum_inode);
|
||||||
|
bh = sb_bread(dir->i_sb, off / CUM_BLOCK_SIZE);
|
||||||
|
if (!bh) break;
|
||||||
|
memcpy(&di, bh->b_data + (off % CUM_BLOCK_SIZE), sizeof di);
|
||||||
|
brelse(bh);
|
||||||
|
|
||||||
|
char *slash = strrchr((char *)di.name, '/');
|
||||||
|
const char *base = slash ? slash + 1 : (const char *)di.name;
|
||||||
|
if (!strcmp(base, dentry->d_name.name)) {
|
||||||
|
inode = cumfs_iget(dir->i_sb, di.ino);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inode)
|
||||||
|
d_add(dentry, inode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cumfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
|
struct dentry *dentry, umode_t mode, bool excl) {
|
||||||
|
/* cum is read-only. Reflect that honestly. */
|
||||||
|
return -EROFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct super_block cumfs_readonly = { 0 }; /* unused */
|
||||||
|
|
||||||
|
/* ---- vfs / fs_context ------------------------------------------------ */
|
||||||
|
static int cumfs_get_tree(struct fs_context *fc) {
|
||||||
|
return get_tree_bdev(fc, cumfs_fill_super);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fs_context_operations cumfs_context_ops = {
|
||||||
|
.get_tree = cumfs_get_tree,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cumfs_init_fs_context(struct fs_context *fc) {
|
||||||
|
fc->ops = &cumfs_context_ops;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct file_system_type cumfs_fs_type = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "cum",
|
||||||
|
.init_fs_context = cumfs_init_fs_context,
|
||||||
|
.kill_sb = kill_block_super,
|
||||||
|
.fs_flags = FS_REQUIRES_DEV,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init cumfs_init(void) {
|
||||||
|
cumfs_inode_cachep = kmem_cache_create(
|
||||||
|
"cumfs_inode_cache", sizeof(struct cumfs_inode), 0, 0, NULL);
|
||||||
|
if (!cumfs_inode_cachep)
|
||||||
|
return -ENOMEM;
|
||||||
|
int ret = register_filesystem(&cumfs_fs_type);
|
||||||
|
if (ret)
|
||||||
|
kmem_cache_destroy(cumfs_inode_cachep);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit cumfs_exit(void) {
|
||||||
|
unregister_filesystem(&cumfs_fs_type);
|
||||||
|
kmem_cache_destroy(cumfs_inode_cachep);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(cumfs_init);
|
||||||
|
module_exit(cumfs_exit);
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* cumfs.h - The on-disk format specification for the cum filesystem
|
||||||
|
* =================================================================
|
||||||
|
* The cum filesystem (cumfs) is the official filesystem of pornOS.
|
||||||
|
* It replaces ext4. Your drive is cum.
|
||||||
|
*
|
||||||
|
* DESIGN NOTES
|
||||||
|
* ------------
|
||||||
|
* cum is a simple, weightless (lightweight) filesystem designed for
|
||||||
|
* the pornOS use case: flat, static root on a read-mostly medium.
|
||||||
|
* We deliberately trade journaling, snapshots and fancy features for
|
||||||
|
* a dead-simple on-disk format that is easy to implement and debug,
|
||||||
|
* because cum is, above all, SIMPLE and PURE.
|
||||||
|
*
|
||||||
|
* LAYOUT (all multi-byte values are little-endian):
|
||||||
|
*
|
||||||
|
* Offset Size Description
|
||||||
|
* ------------------ ----- -------------------------------------
|
||||||
|
* 0 8192 boot block (reserved, zero)
|
||||||
|
* 8192 (CUM_SUPER_OFF) 512 superblock
|
||||||
|
* ... inode table (CUM_INODE_OFF)
|
||||||
|
* ... data region
|
||||||
|
*
|
||||||
|
* The filesystem is a single contiguous image used either directly
|
||||||
|
* as a root device or wrapped into an ISO9660 file for the live ISO.
|
||||||
|
*
|
||||||
|
* A CUM MAGIC number detects misuse and vibes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CUMFS_H
|
||||||
|
#define CUMFS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* -- magic & version -------------------------------------------------- */
|
||||||
|
#define CUM_MAGIC0 0x4343 /* "CC" */
|
||||||
|
#define CUM_MAGIC1 0x4D55 /* "MU" */
|
||||||
|
#define CUM_MAGIC ((CUM_MAGIC0 << 16) | CUM_MAGIC1)
|
||||||
|
#define CUM_VERSION 0x0001
|
||||||
|
|
||||||
|
#define CUM_BLOCK_SIZE 4096
|
||||||
|
#define CUM_SUPER_OFF (8 * CUM_BLOCK_SIZE) /* 32 KiB in */
|
||||||
|
#define CUM_MAX_NAME 256
|
||||||
|
#define CUM_MAX_FILES 65536
|
||||||
|
#define CUM_ROOT_INO 0
|
||||||
|
|
||||||
|
/* -- file types ------------------------------------------------------- */
|
||||||
|
#define CUM_FT_FILE 0
|
||||||
|
#define CUM_FT_DIR 1
|
||||||
|
#define CUM_FT_SYMLINK 2
|
||||||
|
|
||||||
|
/* -- superblock ------------------------------------------------------- */
|
||||||
|
struct cum_superblock {
|
||||||
|
uint32_t magic0; /* CUM_MAGIC0 */
|
||||||
|
uint32_t magic1; /* CUM_MAGIC1 */
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t block_size; /* CUM_BLOCK_SIZE */
|
||||||
|
uint32_t super_offset; /* byte offset of this superblock */
|
||||||
|
uint32_t inode_table_offset; /* byte offset of inode table */
|
||||||
|
uint32_t data_offset; /* byte offset of data region */
|
||||||
|
uint32_t total_blocks;
|
||||||
|
uint32_t total_inodes;
|
||||||
|
uint32_t used_blocks;
|
||||||
|
uint32_t used_inodes;
|
||||||
|
uint32_t next_inode; /* next free inode slot */
|
||||||
|
uint32_t root_inode; /* CUM_ROOT_INO */
|
||||||
|
uint64_t free_bytes;
|
||||||
|
uint64_t total_bytes;
|
||||||
|
uint8_t label[CUM_MAX_NAME];
|
||||||
|
uint8_t _reserved[440 - sizeof(uint8_t) * CUM_MAX_NAME];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: the struct as written uses 440 bytes of reserved padding so the
|
||||||
|
* fixed portion of the superblock occupies 512 bytes total. The exact
|
||||||
|
* byte count is not critical to the implementation; mkfs.cum writes a
|
||||||
|
* deliberately compact 512-byte superblock. See mkfs.cum for the exact
|
||||||
|
* field order used on disk.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* -- inode ------------------------------------------------------------ */
|
||||||
|
struct cum_inode {
|
||||||
|
uint32_t ino; /* inode number */
|
||||||
|
uint8_t type; /* CUM_FT_* */
|
||||||
|
uint8_t _unused;
|
||||||
|
uint16_t mode;
|
||||||
|
uint32_t uid;
|
||||||
|
uint32_t gid;
|
||||||
|
uint64_t size; /* file size in bytes */
|
||||||
|
uint32_t block; /* starting data block */
|
||||||
|
uint32_t nlink;
|
||||||
|
uint32_t mtime;
|
||||||
|
uint8_t name[CUM_MAX_NAME];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
#endif /* CUMFS_H */
|
||||||
Executable
+229
@@ -0,0 +1,229 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
mkfs.cum - Utility that builds a cum filesystem image
|
||||||
|
======================================================
|
||||||
|
cum is the official filesystem of pornOS. It replaces ext4.
|
||||||
|
When you make a filesystem with cum, you are making cum.
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
mkfs.cum [options] <image-file> <source-dir>
|
||||||
|
|
||||||
|
Creates a cum filesystem image at <image-file>, populated with the
|
||||||
|
contents of <source-dir>. The resulting image is a flat, read-mostly
|
||||||
|
root filesystem suitable for a pornOS root device or for embedding
|
||||||
|
into the live ISO.
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
--size N desired image size in bytes (default: auto, disk blocks x4)
|
||||||
|
--label S volume label (default: none)
|
||||||
|
--force overwrite an existing image
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# On-disk constants (must match src/cumfs/include/cumfs.h)
|
||||||
|
CUM_BLOCK_SIZE = 4096
|
||||||
|
CUM_SUPER_OFF = 8 * CUM_BLOCK_SIZE # 32 KiB
|
||||||
|
CUM_MAX_NAME = 256
|
||||||
|
CUM_MAX_FILES = 65536
|
||||||
|
|
||||||
|
# file types
|
||||||
|
CUM_FT_FILE = 0
|
||||||
|
CUM_FT_DIR = 1
|
||||||
|
CUM_FT_SYMLINK = 2
|
||||||
|
|
||||||
|
CUM_MAGIC0 = 0x4343
|
||||||
|
CUM_MAGIC1 = 0x4D55
|
||||||
|
CUM_VERSION = 0x0001
|
||||||
|
CUM_ROOT_INO = 0
|
||||||
|
|
||||||
|
# Fixed inode record size (struct cum_inode, packed)
|
||||||
|
INODE_REC_SIZE = struct.calcsize('<I B B H I I Q I I I 256s')
|
||||||
|
|
||||||
|
|
||||||
|
class CumError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def collect_files(src):
|
||||||
|
"""Walk src dir producing a deterministic list of entries.
|
||||||
|
|
||||||
|
Returns list of dicts:
|
||||||
|
{name, path, type, data}
|
||||||
|
where for dirs, name is the full path (no leading slash, '' for root)
|
||||||
|
and data is None; for files, data is bytes; for symlinks, data is the
|
||||||
|
target string.
|
||||||
|
"""
|
||||||
|
entries = []
|
||||||
|
|
||||||
|
def walk(rel):
|
||||||
|
full = os.path.join(src, rel) if rel else src
|
||||||
|
for name in sorted(os.listdir(full)):
|
||||||
|
child_rel = name if not rel else f'{rel}/{name}'
|
||||||
|
child_full = os.path.join(full, name)
|
||||||
|
st = os.lstat(child_full)
|
||||||
|
m = st.st_mode & 0o777
|
||||||
|
if os.path.isdir(child_full) and not os.path.islink(child_full):
|
||||||
|
entries.append({'name': child_rel, 'type': CUM_FT_DIR,
|
||||||
|
'data': None, 'mode': m})
|
||||||
|
walk(child_rel)
|
||||||
|
elif os.path.islink(child_full):
|
||||||
|
target = os.readlink(child_full)
|
||||||
|
entries.append({'name': child_rel, 'type': CUM_FT_SYMLINK,
|
||||||
|
'data': target.encode('utf-8', 'replace'),
|
||||||
|
'mode': 0o777})
|
||||||
|
elif os.path.isfile(child_full):
|
||||||
|
with open(child_full, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
entries.append({'name': child_rel, 'type': CUM_FT_FILE,
|
||||||
|
'data': data, 'mode': m})
|
||||||
|
|
||||||
|
entries.append({'name': '', 'type': CUM_FT_DIR, 'data': None, 'mode': 0o555})
|
||||||
|
walk('')
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
def build_superblock(total_blocks, total_inodes, used_blocks, used_inodes,
|
||||||
|
next_inode, data_offset, total_bytes, label):
|
||||||
|
"""Return 512-byte packed superblock."""
|
||||||
|
|
||||||
|
def b32(v):
|
||||||
|
return struct.pack('<I', v)
|
||||||
|
def b64(v):
|
||||||
|
return struct.pack('<Q', v)
|
||||||
|
|
||||||
|
label_b = (label or '')[:CUM_MAX_NAME-1].encode('utf-8', 'replace')
|
||||||
|
label_b = label_b.ljust(CUM_MAX_NAME, b'\0')
|
||||||
|
free_bytes = total_bytes - (data_offset + used_blocks * CUM_BLOCK_SIZE)
|
||||||
|
|
||||||
|
sb = b''
|
||||||
|
sb += b32(CUM_MAGIC0)
|
||||||
|
sb += b32(CUM_MAGIC1)
|
||||||
|
sb += b32(CUM_VERSION)
|
||||||
|
sb += b32(CUM_BLOCK_SIZE)
|
||||||
|
sb += b32(CUM_SUPER_OFF)
|
||||||
|
sb += b32(CUM_SUPER_OFF + 512) # inode table offset
|
||||||
|
sb += b32(data_offset) # data region offset
|
||||||
|
sb += b32(total_blocks) # total blocks
|
||||||
|
sb += b32(total_inodes) # total inodes
|
||||||
|
sb += b32(used_blocks) # used blocks
|
||||||
|
sb += b32(used_inodes) # used inodes
|
||||||
|
sb += b32(next_inode) # next free inode
|
||||||
|
sb += b32(CUM_ROOT_INO) # root inode
|
||||||
|
sb += b64(free_bytes) # free bytes
|
||||||
|
sb += b64(total_bytes) # total bytes
|
||||||
|
sb += label_b # 256-byte label
|
||||||
|
# pad to 512 bytes
|
||||||
|
sb = sb.ljust(512, b'\0')
|
||||||
|
return sb
|
||||||
|
|
||||||
|
|
||||||
|
def build_inode(ino, ftype, size, block, name, mode=0o777, uid=0, gid=0):
|
||||||
|
name_b = name.encode('utf-8', 'replace')[:CUM_MAX_NAME-1]
|
||||||
|
name_b = name_b.ljust(CUM_MAX_NAME, b'\0')
|
||||||
|
return struct.pack('<I B B H I I Q I I I 256s',
|
||||||
|
ino, ftype, 0, mode, uid, gid,
|
||||||
|
size, block, 2, 0, name_b)
|
||||||
|
|
||||||
|
|
||||||
|
def build_cum_image(image_path, src, size=None, label=None, force=False):
|
||||||
|
if os.path.exists(image_path) and not force:
|
||||||
|
raise CumError(f'{image_path} already exists (use --force)')
|
||||||
|
|
||||||
|
if not os.path.isdir(src):
|
||||||
|
raise CumError(f'source dir {src} is not a directory')
|
||||||
|
|
||||||
|
entries = collect_files(src)
|
||||||
|
|
||||||
|
# assign inode numbers: 0 = root
|
||||||
|
inode_map = {}
|
||||||
|
n = 0
|
||||||
|
for e in entries:
|
||||||
|
inode_map[e['name']] = n
|
||||||
|
n += 1
|
||||||
|
total_inodes = n
|
||||||
|
|
||||||
|
# compute data offsets: one contiguous data region, files laid out
|
||||||
|
# after inode table. data_offset computed from superblock+inodes.
|
||||||
|
data_offset = CUM_SUPER_OFF + 512 + ((total_inodes + 0) * INODE_REC_SIZE)
|
||||||
|
data_offset = (data_offset + CUM_BLOCK_SIZE - 1) // CUM_BLOCK_SIZE * CUM_BLOCK_SIZE
|
||||||
|
|
||||||
|
# total size - compute the REAL block-aligned layout (each file gets
|
||||||
|
# a whole number of blocks, so padding matters)
|
||||||
|
laid = data_offset
|
||||||
|
for e in entries:
|
||||||
|
d = e.get('data')
|
||||||
|
if d:
|
||||||
|
laid += len(d)
|
||||||
|
laid = (laid + CUM_BLOCK_SIZE - 1) // CUM_BLOCK_SIZE * CUM_BLOCK_SIZE
|
||||||
|
data_used_blocks = (laid - data_offset) // CUM_BLOCK_SIZE
|
||||||
|
used_blocks = data_used_blocks
|
||||||
|
total_blocks = used_blocks + 16 # some slack
|
||||||
|
if size:
|
||||||
|
total_blocks = max(total_blocks, size // CUM_BLOCK_SIZE)
|
||||||
|
total_bytes = total_blocks * CUM_BLOCK_SIZE
|
||||||
|
|
||||||
|
sb = build_superblock(total_blocks, total_inodes, used_blocks,
|
||||||
|
total_inodes, total_inodes, data_offset,
|
||||||
|
total_bytes, label)
|
||||||
|
|
||||||
|
# build image buffer
|
||||||
|
img = bytearray(total_bytes)
|
||||||
|
|
||||||
|
# write superblock
|
||||||
|
img[CUM_SUPER_OFF:CUM_SUPER_OFF+512] = sb
|
||||||
|
|
||||||
|
# write inode table
|
||||||
|
inode_off = CUM_SUPER_OFF + 512
|
||||||
|
for e in entries:
|
||||||
|
ino = inode_map[e['name']]
|
||||||
|
rec = build_inode(ino, e['type'], len(e['data']) if e.get('data') else 0,
|
||||||
|
0, e['name'], mode=e.get('mode', 0o777))
|
||||||
|
pos = inode_off + ino * INODE_REC_SIZE
|
||||||
|
img[pos:pos+INODE_REC_SIZE] = rec
|
||||||
|
|
||||||
|
# write data region
|
||||||
|
cur = data_offset
|
||||||
|
for e in entries:
|
||||||
|
d = e.get('data')
|
||||||
|
if not d:
|
||||||
|
continue
|
||||||
|
img[cur:cur+len(d)] = d
|
||||||
|
# record block for this inode
|
||||||
|
ino = inode_map[e['name']]
|
||||||
|
rec = build_inode(ino, e['type'], len(d), cur // CUM_BLOCK_SIZE, e['name'],
|
||||||
|
mode=e.get('mode', 0o777))
|
||||||
|
pos = inode_off + ino * INODE_REC_SIZE
|
||||||
|
img[pos:pos+INODE_REC_SIZE] = rec
|
||||||
|
cur += len(d)
|
||||||
|
cur = (cur + CUM_BLOCK_SIZE - 1) // CUM_BLOCK_SIZE * CUM_BLOCK_SIZE
|
||||||
|
|
||||||
|
with open(image_path, 'wb') as f:
|
||||||
|
f.write(img)
|
||||||
|
|
||||||
|
print(f'mkfs.cum: created {image_path} ({total_bytes} bytes)')
|
||||||
|
print(f' inodes: {total_inodes}')
|
||||||
|
print(f' data blocks used: {used_blocks}')
|
||||||
|
print(f' data offset: 0x{data_offset:x}')
|
||||||
|
return image_path
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(prog='mkfs.cum',
|
||||||
|
description='build a cum filesystem image')
|
||||||
|
ap.add_argument('image', help='output image file')
|
||||||
|
ap.add_argument('src', help='source directory to pack')
|
||||||
|
ap.add_argument('--size', type=int, default=0,
|
||||||
|
help='desired image size in bytes')
|
||||||
|
ap.add_argument('--label', default=None)
|
||||||
|
ap.add_argument('--force', action='store_true')
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
build_cum_image(args.image, args.src, args.size, args.label, args.force)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
* mkfs.cum.c - The official cum filesystem creation tool (C implementation)
|
||||||
|
* ==========================================================================
|
||||||
|
* Usage: mkfs.cum [--label LABEL] [--size BYTES] <image> <source-dir>
|
||||||
|
*
|
||||||
|
* This is the reference C implementation of mkfs.cum for building pornOS
|
||||||
|
* root images on a real Linux build host. It mirrors byte-for-byte the
|
||||||
|
* on-disk format described in cumfs.h.
|
||||||
|
*
|
||||||
|
* Build:
|
||||||
|
* gcc -O2 -o mkfs.cum mkfs.cum.c -I include
|
||||||
|
*
|
||||||
|
* Because cum is PURE and SIMPLE, this has no external dependencies
|
||||||
|
* beyond the C standard library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "cumfs.h"
|
||||||
|
|
||||||
|
/* fixed record sizes (must match Python + header packing) */
|
||||||
|
#define INODE_REC_SIZE (4 + 1 + 1 + 2 + 4 + 4 + 8 + 4 + 4 + 4 + 256)
|
||||||
|
|
||||||
|
struct entry {
|
||||||
|
char name[CUM_MAX_NAME];
|
||||||
|
uint8_t type;
|
||||||
|
uint64_t size;
|
||||||
|
uint32_t block;
|
||||||
|
uint8_t *data; /* NULL unless FILE/SYMLINK */
|
||||||
|
struct entry *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct entry entries[1];
|
||||||
|
static size_t n_entries = 0;
|
||||||
|
|
||||||
|
#define SB_SIZE 512
|
||||||
|
|
||||||
|
static void collect_dir(const char *base, const char *rel) {
|
||||||
|
char p[4096];
|
||||||
|
char pr[4096];
|
||||||
|
if (rel[0]) snprintf(p, sizeof p, "%s/%s", base, rel);
|
||||||
|
else snprintf(p, sizeof p, "%s", base);
|
||||||
|
|
||||||
|
DIR *d = opendir(p);
|
||||||
|
if (!d) { perror("opendir"); exit(1); }
|
||||||
|
struct dirent *dp;
|
||||||
|
while ((dp = readdir(d)) != NULL) {
|
||||||
|
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
|
||||||
|
if (rel[0]) snprintf(pr, sizeof pr, "%s/%s", rel, dp->d_name);
|
||||||
|
else snprintf(pr, sizeof pr, "%s", dp->d_name);
|
||||||
|
char fp[4096];
|
||||||
|
snprintf(fp, sizeof fp, "%s/%s", p, dp->d_name);
|
||||||
|
struct stat st;
|
||||||
|
if (lstat(fp, &st) != 0) continue;
|
||||||
|
|
||||||
|
struct entry *e = &entries[n_entries++];
|
||||||
|
memset(e, 0, sizeof *e);
|
||||||
|
snprintf(e->name, sizeof e->name, "%s", pr);
|
||||||
|
|
||||||
|
if (S_ISDIR(st.st_mode)) {
|
||||||
|
e->type = CUM_FT_DIR;
|
||||||
|
collect_dir(base, pr);
|
||||||
|
} else if (S_ISLNK(st.st_mode)) {
|
||||||
|
e->type = CUM_FT_SYMLINK;
|
||||||
|
char target[4096];
|
||||||
|
ssize_t n = readlink(fp, target, sizeof target - 1);
|
||||||
|
if (n < 0) n = 0;
|
||||||
|
target[n] = 0;
|
||||||
|
e->size = strlen(target);
|
||||||
|
e->data = malloc(e->size + 1);
|
||||||
|
memcpy(e->data, target, e->size + 1);
|
||||||
|
} else {
|
||||||
|
e->type = CUM_FT_FILE;
|
||||||
|
e->size = st.st_size;
|
||||||
|
FILE *f = fopen(fp, "rb");
|
||||||
|
if (!f) continue;
|
||||||
|
e->data = malloc(e->size ? e->size : 1);
|
||||||
|
fread(e->data, 1, e->size, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_le32(uint8_t *b, uint32_t v) {
|
||||||
|
b[0] = v & 0xff; b[1] = (v>>8)&0xff; b[2] = (v>>16)&0xff; b[3] = (v>>24)&0xff;
|
||||||
|
}
|
||||||
|
static void put_le64(uint8_t *b, uint64_t v) {
|
||||||
|
for (int i = 0; i < 8; i++) b[i] = (v >> (8*i)) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
uint64_t size = 0;
|
||||||
|
const char *label = "";
|
||||||
|
const char *image = NULL, *src = NULL;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "--label") && i+1 < argc) label = argv[++i];
|
||||||
|
else if (!strcmp(argv[i], "--size") && i+1 < argc) size = strtoull(argv[++i], NULL, 0);
|
||||||
|
else if (!image) image = argv[i];
|
||||||
|
else if (!src) src = argv[i];
|
||||||
|
}
|
||||||
|
if (!image || !src) {
|
||||||
|
fprintf(stderr, "usage: mkfs.cum [--label LABEL] [--size BYTES] <image> <source-dir>\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* root entry */
|
||||||
|
struct entry *root = &entries[n_entries++];
|
||||||
|
memset(root, 0, sizeof *root);
|
||||||
|
root->name[0] = 0;
|
||||||
|
root->type = CUM_FT_DIR;
|
||||||
|
|
||||||
|
collect_dir(src, "");
|
||||||
|
|
||||||
|
uint32_t inode_table_off = CUM_SUPER_OFF + SB_SIZE;
|
||||||
|
uint64_t data_offset = inode_table_off + (uint64_t)n_entries * INODE_REC_SIZE;
|
||||||
|
data_offset = (data_offset + CUM_BLOCK_SIZE - 1) / CUM_BLOCK_SIZE * CUM_BLOCK_SIZE;
|
||||||
|
|
||||||
|
uint64_t total_data = 0;
|
||||||
|
for (size_t i = 0; i < n_entries; i++)
|
||||||
|
total_data += entries[i].size;
|
||||||
|
(void)total_data;
|
||||||
|
/* compute the REAL block-aligned layout (each file padded to a block) */
|
||||||
|
uint64_t laid = data_offset;
|
||||||
|
for (size_t i = 0; i < n_entries; i++) {
|
||||||
|
if (entries[i].size) {
|
||||||
|
laid += entries[i].size;
|
||||||
|
laid = (laid + CUM_BLOCK_SIZE - 1) / CUM_BLOCK_SIZE * CUM_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint64_t used_blocks = (laid - data_offset) / CUM_BLOCK_SIZE;
|
||||||
|
uint32_t total_blocks = used_blocks + 16;
|
||||||
|
if (size) total_blocks = (uint32_t)((total_blocks > size/CUM_BLOCK_SIZE) ? total_blocks : size/CUM_BLOCK_SIZE);
|
||||||
|
uint64_t total_bytes = (uint64_t)total_blocks * CUM_BLOCK_SIZE;
|
||||||
|
|
||||||
|
uint8_t *img = calloc(1, total_bytes);
|
||||||
|
if (!img) { perror("calloc"); return 1; }
|
||||||
|
|
||||||
|
/* superblock (512 bytes at CUM_SUPER_OFF) */
|
||||||
|
uint8_t *sblk = img + CUM_SUPER_OFF;
|
||||||
|
put_le32(sblk+0, CUM_MAGIC0);
|
||||||
|
put_le32(sblk+4, CUM_MAGIC1);
|
||||||
|
put_le32(sblk+8, CUM_VERSION);
|
||||||
|
put_le32(sblk+12, CUM_BLOCK_SIZE);
|
||||||
|
put_le32(sblk+16, CUM_SUPER_OFF);
|
||||||
|
put_le32(sblk+20, inode_table_off);
|
||||||
|
put_le32(sblk+24, (uint32_t)data_offset);
|
||||||
|
put_le32(sblk+28, total_blocks);
|
||||||
|
put_le32(sblk+32, (uint32_t)n_entries);
|
||||||
|
put_le32(sblk+36, (uint32_t)used_blocks);
|
||||||
|
put_le32(sblk+40, (uint32_t)n_entries);
|
||||||
|
put_le32(sblk+44, (uint32_t)n_entries);
|
||||||
|
put_le32(sblk+48, CUM_ROOT_INO);
|
||||||
|
uint64_t free_bytes = total_bytes - (data_offset + used_blocks * CUM_BLOCK_SIZE);
|
||||||
|
put_le64(sblk+52, free_bytes);
|
||||||
|
put_le64(sblk+60, total_bytes);
|
||||||
|
memcpy(sblk+68, label, strnlen(label, CUM_MAX_NAME-1));
|
||||||
|
|
||||||
|
/* inode table */
|
||||||
|
uint8_t *itable = img + inode_table_off;
|
||||||
|
for (size_t i = 0; i < n_entries; i++) {
|
||||||
|
uint8_t *r = itable + i * INODE_REC_SIZE;
|
||||||
|
put_le32(r+0, (uint32_t)i);
|
||||||
|
r[4] = entries[i].type;
|
||||||
|
r[5] = 0;
|
||||||
|
r[6] = 0x1ff; r[7] = 0x01; /* mode 0777 */
|
||||||
|
put_le32(r+8, 0); /* uid */
|
||||||
|
put_le32(r+12, 0); /* gid */
|
||||||
|
put_le64(r+16, entries[i].size);
|
||||||
|
put_le32(r+24, 0); /* block: filled below for files */
|
||||||
|
put_le32(r+28, 2); /* nlink */
|
||||||
|
put_le32(r+32, 0); /* mtime */
|
||||||
|
memcpy(r+36, entries[i].name, strnlen(entries[i].name, CUM_MAX_NAME-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* data region - write any entry that carries data (files + symlinks) */
|
||||||
|
uint64_t cur = data_offset;
|
||||||
|
for (size_t i = 0; i < n_entries; i++) {
|
||||||
|
if (entries[i].size && entries[i].data) {
|
||||||
|
memcpy(img + cur, entries[i].data, entries[i].size);
|
||||||
|
uint8_t *r = itable + i * INODE_REC_SIZE;
|
||||||
|
put_le32(r+24, (uint32_t)(cur / CUM_BLOCK_SIZE));
|
||||||
|
cur += entries[i].size;
|
||||||
|
cur = (cur + CUM_BLOCK_SIZE - 1) / CUM_BLOCK_SIZE * CUM_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = open(image, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (fd < 0) { perror("open"); return 1; }
|
||||||
|
size_t written = 0;
|
||||||
|
while (written < total_bytes) {
|
||||||
|
ssize_t w = write(fd, img + written, total_bytes - written);
|
||||||
|
if (w < 0) { perror("write"); return 1; }
|
||||||
|
written += w;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
printf("mkfs.cum: created %s (%llu bytes)\n", image, (unsigned long long)total_bytes);
|
||||||
|
printf(" inodes: %zu, data blocks: %llu, data offset: 0x%llx\n",
|
||||||
|
n_entries, (unsigned long long)used_blocks, (unsigned long long)data_offset);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Executable
+644
@@ -0,0 +1,644 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
HOLY PORN - A custom esoteric programming language for pornOS
|
||||||
|
================================================================
|
||||||
|
Holy Porn is the official language of pornOS. All system utilities
|
||||||
|
are written in this language. It compiles to Python bytecode for
|
||||||
|
maximum misery.
|
||||||
|
|
||||||
|
SYNTAX REFERENCE:
|
||||||
|
porn <var> = <val> Declare variable
|
||||||
|
sexy <expr> Print expression
|
||||||
|
naked(<func>) Define function
|
||||||
|
erect <cond>: If statement
|
||||||
|
limp: Else statement
|
||||||
|
wet <var> <n> { For loop
|
||||||
|
dry: End block
|
||||||
|
nsfw <file> Read file
|
||||||
|
savenot <file> <data> Write file
|
||||||
|
thrust <expr> Return value
|
||||||
|
jack <cmd> Execute shell command
|
||||||
|
simp <url> HTTP GET request
|
||||||
|
naughty Random number 0-100
|
||||||
|
creampie <a> <b> Add a and b
|
||||||
|
gangbang <a> <b> Multiply a and b
|
||||||
|
cuckold <a> <b> Subtract b from a
|
||||||
|
smut <a> <b> Divide a by b
|
||||||
|
finger <var> Input from stdin
|
||||||
|
nakedpics List files in current dir
|
||||||
|
boner Exit program
|
||||||
|
missionary Import another .hp file
|
||||||
|
stripclub <path> Change directory
|
||||||
|
condom <expr> Assert expression is truthy
|
||||||
|
dildo <expr> Delay/sleep seconds
|
||||||
|
prostate System info
|
||||||
|
husl Start of program (required)
|
||||||
|
bonk End of program (required)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
husl
|
||||||
|
porn name = world
|
||||||
|
sexy("hello " + name)
|
||||||
|
bonk
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class HolyPornError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class HolyPornRuntimeError(HolyPornError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class HolyPornSyntaxError(HolyPornError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class HolyPornLexer:
|
||||||
|
TOKEN_SPEC = [
|
||||||
|
('COMMENT', r'#.*'),
|
||||||
|
('STRING', r'"[^"]*"|\'[^\']*\''),
|
||||||
|
('NUMBER', r'-?\d+(\.\d+)?'),
|
||||||
|
('IDENT', r'[a-zA-Z_][a-zA-Z0-9_]*'),
|
||||||
|
('OP', r'[+\-*/%=<>!]+'),
|
||||||
|
('LPAREN', r'\('),
|
||||||
|
('RPAREN', r'\)'),
|
||||||
|
('LBRACE', r'\{'),
|
||||||
|
('RBRACE', r'\}'),
|
||||||
|
('COMMA', r','),
|
||||||
|
('DOT', r'\.'),
|
||||||
|
('COLON', r':'),
|
||||||
|
('NEWLINE', r'\n'),
|
||||||
|
('SKIP', r'[ \t]+'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.tok_regex = '|'.join(
|
||||||
|
f'(?P<{name}>{pattern})' for name, pattern in self.TOKEN_SPEC
|
||||||
|
)
|
||||||
|
|
||||||
|
def tokenize(self, code):
|
||||||
|
tokens = []
|
||||||
|
line_num = 1
|
||||||
|
for mo in re.finditer(self.tok_regex, code):
|
||||||
|
kind = mo.lastgroup
|
||||||
|
value = mo.group()
|
||||||
|
if kind == 'NEWLINE':
|
||||||
|
line_num += 1
|
||||||
|
continue
|
||||||
|
elif kind == 'SKIP' or kind == 'COMMENT':
|
||||||
|
continue
|
||||||
|
elif kind == 'IDENT':
|
||||||
|
pass # keep kind as IDENT; parser checks value for keywords
|
||||||
|
elif kind == 'NUMBER':
|
||||||
|
value = float(value) if '.' in value else int(value)
|
||||||
|
elif kind in ('STRING',):
|
||||||
|
value = value[1:-1]
|
||||||
|
tokens.append((kind, value, line_num))
|
||||||
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
|
class HolyPornParser:
|
||||||
|
def __init__(self, tokens):
|
||||||
|
self.tokens = tokens
|
||||||
|
self.pos = 0
|
||||||
|
|
||||||
|
def peek(self):
|
||||||
|
if self.pos < len(self.tokens):
|
||||||
|
return self.tokens[self.pos]
|
||||||
|
return (None, None, None)
|
||||||
|
|
||||||
|
def advance(self):
|
||||||
|
tok = self.peek()
|
||||||
|
self.pos += 1
|
||||||
|
return tok
|
||||||
|
|
||||||
|
def expect(self, kind):
|
||||||
|
tok = self.peek()
|
||||||
|
if tok[0] != kind:
|
||||||
|
raise HolyPornSyntaxError(
|
||||||
|
f"Expected {kind}, got {tok[0]} at line {tok[2]}"
|
||||||
|
)
|
||||||
|
return self.advance()
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
nodes = []
|
||||||
|
while self.peek()[0] is not None:
|
||||||
|
if self.is_kw('bonk'):
|
||||||
|
break
|
||||||
|
nodes.append(self.parse_stmt())
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
def is_kw(self, name):
|
||||||
|
return self.peek()[0] == 'IDENT' and self.peek()[1] == name
|
||||||
|
|
||||||
|
def expect_kw(self, name):
|
||||||
|
tok = self.peek()
|
||||||
|
if not (tok[0] == 'IDENT' and tok[1] == name):
|
||||||
|
raise HolyPornSyntaxError(
|
||||||
|
f"Expected '{name}', got {tok[1]} at line {tok[2]}"
|
||||||
|
)
|
||||||
|
return self.advance()
|
||||||
|
|
||||||
|
def parse_stmt(self):
|
||||||
|
if self.is_kw('husl'):
|
||||||
|
self.advance()
|
||||||
|
return ('noop',)
|
||||||
|
|
||||||
|
if self.is_kw('porn'):
|
||||||
|
return self.parse_decl()
|
||||||
|
elif self.is_kw('sexy'):
|
||||||
|
return self.parse_print()
|
||||||
|
elif self.is_kw('naked'):
|
||||||
|
return self.parse_func_def()
|
||||||
|
elif self.is_kw('erect'):
|
||||||
|
return self.parse_if()
|
||||||
|
elif self.is_kw('wet'):
|
||||||
|
return self.parse_for()
|
||||||
|
elif self.is_kw('thrust'):
|
||||||
|
return self.parse_return()
|
||||||
|
elif self.is_kw('jack'):
|
||||||
|
return self.parse_shell()
|
||||||
|
elif self.is_kw('savenot'):
|
||||||
|
return self.parse_write()
|
||||||
|
elif self.is_kw('nsfw'):
|
||||||
|
return self.parse_read()
|
||||||
|
elif self.is_kw('stripclub'):
|
||||||
|
return self.parse_chdir()
|
||||||
|
elif self.is_kw('condom'):
|
||||||
|
return self.parse_assert()
|
||||||
|
elif self.is_kw('missionary'):
|
||||||
|
return self.parse_import()
|
||||||
|
elif self.is_kw('dildo'):
|
||||||
|
return self.parse_sleep()
|
||||||
|
else:
|
||||||
|
return self.parse_expr_stmt()
|
||||||
|
|
||||||
|
def parse_decl(self):
|
||||||
|
self.expect_kw('porn')
|
||||||
|
name = self.expect('IDENT')[1]
|
||||||
|
self.expect('OP')
|
||||||
|
expr = self.parse_expr()
|
||||||
|
return ('decl', name, expr)
|
||||||
|
|
||||||
|
def parse_print(self):
|
||||||
|
self.expect_kw('sexy')
|
||||||
|
self.expect('LPAREN')
|
||||||
|
expr = self.parse_expr()
|
||||||
|
self.expect('RPAREN')
|
||||||
|
return ('print', expr)
|
||||||
|
|
||||||
|
def parse_func_def(self):
|
||||||
|
self.expect_kw('naked')
|
||||||
|
name = self.expect('IDENT')[1]
|
||||||
|
self.expect('LPAREN')
|
||||||
|
params = []
|
||||||
|
if self.peek()[0] != 'RPAREN':
|
||||||
|
params.append(self.expect('IDENT')[1])
|
||||||
|
while self.peek()[0] == 'COMMA':
|
||||||
|
self.advance()
|
||||||
|
params.append(self.expect('IDENT')[1])
|
||||||
|
self.expect('RPAREN')
|
||||||
|
if self.peek()[0] == 'COLON':
|
||||||
|
self.advance()
|
||||||
|
body = self.parse_block()
|
||||||
|
return ('func_def', name, params, body)
|
||||||
|
|
||||||
|
def parse_if(self):
|
||||||
|
self.expect_kw('erect')
|
||||||
|
cond = self.parse_expr()
|
||||||
|
if self.peek()[0] == 'COLON':
|
||||||
|
self.advance()
|
||||||
|
body = self.parse_block()
|
||||||
|
else_body = None
|
||||||
|
if self.is_kw('limp'):
|
||||||
|
self.advance()
|
||||||
|
if self.peek()[0] == 'COLON':
|
||||||
|
self.advance()
|
||||||
|
else_body = self.parse_block()
|
||||||
|
return ('if', cond, body, else_body)
|
||||||
|
|
||||||
|
def parse_for(self):
|
||||||
|
self.expect_kw('wet')
|
||||||
|
var = self.expect('IDENT')[1]
|
||||||
|
start = self.parse_expr()
|
||||||
|
if self.peek()[0] == 'COLON':
|
||||||
|
self.advance()
|
||||||
|
self.expect('LBRACE')
|
||||||
|
body = []
|
||||||
|
while self.peek()[0] != 'RBRACE':
|
||||||
|
body.append(self.parse_stmt())
|
||||||
|
self.expect('RBRACE')
|
||||||
|
return ('for', var, start, body)
|
||||||
|
|
||||||
|
def parse_return(self):
|
||||||
|
self.expect_kw('thrust')
|
||||||
|
expr = self.parse_expr()
|
||||||
|
return ('return', expr)
|
||||||
|
|
||||||
|
def parse_shell(self):
|
||||||
|
self.expect_kw('jack')
|
||||||
|
expr = self.parse_expr()
|
||||||
|
return ('shell', expr)
|
||||||
|
|
||||||
|
def parse_write(self):
|
||||||
|
self.expect_kw('savenot')
|
||||||
|
path = self.parse_expr()
|
||||||
|
data = self.parse_expr()
|
||||||
|
return ('write', path, data)
|
||||||
|
|
||||||
|
def parse_read(self):
|
||||||
|
self.expect_kw('nsfw')
|
||||||
|
path = self.parse_expr()
|
||||||
|
return ('read', path)
|
||||||
|
|
||||||
|
def parse_chdir(self):
|
||||||
|
self.expect_kw('stripclub')
|
||||||
|
path = self.parse_expr()
|
||||||
|
return ('chdir', path)
|
||||||
|
|
||||||
|
def parse_assert(self):
|
||||||
|
self.expect_kw('condom')
|
||||||
|
expr = self.parse_expr()
|
||||||
|
return ('assert', expr)
|
||||||
|
|
||||||
|
def parse_import(self):
|
||||||
|
self.expect_kw('missionary')
|
||||||
|
path = self.parse_expr()
|
||||||
|
return ('import', path)
|
||||||
|
|
||||||
|
def parse_sleep(self):
|
||||||
|
self.expect_kw('dildo')
|
||||||
|
expr = self.parse_expr()
|
||||||
|
return ('sleep', expr)
|
||||||
|
|
||||||
|
def parse_expr_stmt(self):
|
||||||
|
expr = self.parse_expr()
|
||||||
|
return ('expr_stmt', expr)
|
||||||
|
|
||||||
|
def parse_block(self):
|
||||||
|
self.expect('LBRACE')
|
||||||
|
stmts = []
|
||||||
|
while self.peek()[0] != 'RBRACE':
|
||||||
|
stmts.append(self.parse_stmt())
|
||||||
|
self.expect('RBRACE')
|
||||||
|
return stmts
|
||||||
|
|
||||||
|
def parse_expr(self):
|
||||||
|
return self.parse_comparison()
|
||||||
|
|
||||||
|
def parse_comparison(self):
|
||||||
|
left = self.parse_add_sub()
|
||||||
|
while self.peek()[0] == 'OP' and self.peek()[1] in ('==', '!=', '<', '>', '<=', '>='):
|
||||||
|
op = self.advance()[1]
|
||||||
|
right = self.parse_add_sub()
|
||||||
|
left = ('binop', op, left, right)
|
||||||
|
return left
|
||||||
|
|
||||||
|
def parse_add_sub(self):
|
||||||
|
left = self.parse_mul_div()
|
||||||
|
while self.peek()[0] == 'OP' and self.peek()[1] in ('+', '-'):
|
||||||
|
op = self.advance()[1]
|
||||||
|
right = self.parse_mul_div()
|
||||||
|
left = ('binop', op, left, right)
|
||||||
|
return left
|
||||||
|
|
||||||
|
def parse_mul_div(self):
|
||||||
|
left = self.parse_unary()
|
||||||
|
while self.peek()[0] == 'OP' and self.peek()[1] in ('*', '/', '%'):
|
||||||
|
op = self.advance()[1]
|
||||||
|
right = self.parse_unary()
|
||||||
|
left = ('binop', op, left, right)
|
||||||
|
return left
|
||||||
|
|
||||||
|
def parse_unary(self):
|
||||||
|
if self.peek()[0] == 'OP' and self.peek()[1] == '-':
|
||||||
|
self.advance()
|
||||||
|
expr = self.parse_primary()
|
||||||
|
return ('unary', '-', expr)
|
||||||
|
return self.parse_primary()
|
||||||
|
|
||||||
|
def parse_primary(self):
|
||||||
|
kind, val, line = self.peek()
|
||||||
|
|
||||||
|
if kind == 'NUMBER':
|
||||||
|
self.advance()
|
||||||
|
return ('num', val)
|
||||||
|
elif kind == 'STRING':
|
||||||
|
self.advance()
|
||||||
|
return ('str', val)
|
||||||
|
elif kind == 'IDENT' and val in (
|
||||||
|
'creampie', 'gangbang', 'cuckold', 'smut',
|
||||||
|
'naughty', 'finger', 'nakedpics', 'prostate',
|
||||||
|
'boner', 'dildo', 'nsfw', 'savenot', 'jack',
|
||||||
|
'stripclub', 'condom', 'missionary', 'sexy'):
|
||||||
|
return self.parse_builtin_call()
|
||||||
|
elif kind == 'IDENT':
|
||||||
|
self.advance()
|
||||||
|
if self.peek()[0] == 'LPAREN':
|
||||||
|
self.advance()
|
||||||
|
args = []
|
||||||
|
if self.peek()[0] != 'RPAREN':
|
||||||
|
args.append(self.parse_expr())
|
||||||
|
while self.peek()[0] == 'COMMA':
|
||||||
|
self.advance()
|
||||||
|
args.append(self.parse_expr())
|
||||||
|
self.expect('RPAREN')
|
||||||
|
return ('call', val, args)
|
||||||
|
return ('var', val)
|
||||||
|
elif kind == 'LPAREN':
|
||||||
|
self.advance()
|
||||||
|
expr = self.parse_expr()
|
||||||
|
self.expect('RPAREN')
|
||||||
|
return expr
|
||||||
|
elif kind == 'LBRACE':
|
||||||
|
items = []
|
||||||
|
self.advance()
|
||||||
|
if self.peek()[0] != 'RBRACE':
|
||||||
|
items.append(self.parse_expr())
|
||||||
|
while self.peek()[0] == 'COMMA':
|
||||||
|
self.advance()
|
||||||
|
items.append(self.parse_expr())
|
||||||
|
self.expect('RBRACE')
|
||||||
|
return ('list', items)
|
||||||
|
else:
|
||||||
|
raise HolyPornSyntaxError(
|
||||||
|
f"Unexpected token {kind} '{val}' at line {line}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def parse_builtin_call(self):
|
||||||
|
_, name, _ = self.peek()
|
||||||
|
self.advance()
|
||||||
|
self.expect('LPAREN')
|
||||||
|
args = []
|
||||||
|
if self.peek()[0] != 'RPAREN':
|
||||||
|
args.append(self.parse_expr())
|
||||||
|
while self.peek()[0] == 'COMMA':
|
||||||
|
self.advance()
|
||||||
|
args.append(self.parse_expr())
|
||||||
|
self.expect('RPAREN')
|
||||||
|
return ('builtin', name, args)
|
||||||
|
|
||||||
|
|
||||||
|
class HolyPornVM:
|
||||||
|
def __init__(self):
|
||||||
|
self.globals = {}
|
||||||
|
self.functions = {}
|
||||||
|
self.call_stack = []
|
||||||
|
self.imported = set()
|
||||||
|
|
||||||
|
def run(self, nodes):
|
||||||
|
for node in nodes:
|
||||||
|
self.exec_node(node)
|
||||||
|
|
||||||
|
def exec_node(self, node):
|
||||||
|
ntype = node[0]
|
||||||
|
|
||||||
|
if ntype == 'decl':
|
||||||
|
name, expr = node[1], node[2]
|
||||||
|
self.globals[name] = self.eval_expr(expr)
|
||||||
|
|
||||||
|
elif ntype == 'print':
|
||||||
|
val = self.eval_expr(node[1])
|
||||||
|
print(val)
|
||||||
|
|
||||||
|
elif ntype == 'func_def':
|
||||||
|
name, params, body = node[1], node[2], node[3]
|
||||||
|
self.functions[name] = (params, body)
|
||||||
|
|
||||||
|
elif ntype == 'if':
|
||||||
|
cond, body, else_body = node[1], node[2], node[3]
|
||||||
|
if self.eval_expr(cond):
|
||||||
|
for s in body:
|
||||||
|
self.exec_node(s)
|
||||||
|
elif else_body:
|
||||||
|
for s in else_body:
|
||||||
|
self.exec_node(s)
|
||||||
|
|
||||||
|
elif ntype == 'for':
|
||||||
|
var, start_val, body = node[1], node[2], node[3]
|
||||||
|
n = int(self.eval_expr(start_val))
|
||||||
|
for i in range(n):
|
||||||
|
self.globals[var] = i
|
||||||
|
for s in body:
|
||||||
|
self.exec_node(s)
|
||||||
|
|
||||||
|
elif ntype == 'return':
|
||||||
|
raise ReturnException(self.eval_expr(node[1]))
|
||||||
|
|
||||||
|
elif ntype == 'shell':
|
||||||
|
cmd = str(self.eval_expr(node[1]))
|
||||||
|
os.system(cmd)
|
||||||
|
|
||||||
|
elif ntype == 'write':
|
||||||
|
path = str(self.eval_expr(node[1]))
|
||||||
|
data = str(self.eval_expr(node[2]))
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
|
elif ntype == 'read':
|
||||||
|
path = str(self.eval_expr(node[1]))
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
elif ntype == 'chdir':
|
||||||
|
path = str(self.eval_expr(node[1]))
|
||||||
|
os.chdir(path)
|
||||||
|
|
||||||
|
elif ntype == 'assert':
|
||||||
|
val = self.eval_expr(node[1])
|
||||||
|
if not val:
|
||||||
|
raise HolyPornRuntimeError("CONDOM FAILED: assertion error")
|
||||||
|
|
||||||
|
elif ntype == 'import':
|
||||||
|
path = str(self.eval_expr(node[1]))
|
||||||
|
if path not in self.imported:
|
||||||
|
self.imported.add(path)
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
code = f.read()
|
||||||
|
lexer = HolyPornLexer()
|
||||||
|
tokens = lexer.tokenize(code)
|
||||||
|
parser = HolyPornParser(tokens)
|
||||||
|
nodes = parser.parse()
|
||||||
|
self.run(nodes)
|
||||||
|
|
||||||
|
elif ntype == 'sleep':
|
||||||
|
val = self.eval_expr(node[1])
|
||||||
|
time.sleep(float(val))
|
||||||
|
|
||||||
|
elif ntype == 'expr_stmt':
|
||||||
|
self.eval_expr(node[1])
|
||||||
|
|
||||||
|
def eval_expr(self, node):
|
||||||
|
ntype = node[0]
|
||||||
|
|
||||||
|
if ntype == 'num':
|
||||||
|
return node[1]
|
||||||
|
elif ntype == 'str':
|
||||||
|
return node[1]
|
||||||
|
elif ntype == 'var':
|
||||||
|
name = node[1]
|
||||||
|
if name not in self.globals:
|
||||||
|
raise HolyPornRuntimeError(
|
||||||
|
f"UNFUCKABLE ERROR: variable '{name}' not defined"
|
||||||
|
)
|
||||||
|
return self.globals[name]
|
||||||
|
elif ntype == 'list':
|
||||||
|
return [self.eval_expr(e) for e in node[1]]
|
||||||
|
elif ntype == 'binop':
|
||||||
|
op, left, right = node[1], node[2], node[3]
|
||||||
|
l = self.eval_expr(left)
|
||||||
|
r = self.eval_expr(right)
|
||||||
|
if op == '+':
|
||||||
|
if isinstance(l, str) or isinstance(r, str):
|
||||||
|
return str(l) + str(r)
|
||||||
|
return l + r
|
||||||
|
elif op == '-': return l - r
|
||||||
|
elif op == '*': return l * r
|
||||||
|
elif op == '/': return l / r if r != 0 else 0
|
||||||
|
elif op == '%': return l % r if r != 0 else 0
|
||||||
|
elif op == '==': return l == r
|
||||||
|
elif op == '!=': return l != r
|
||||||
|
elif op == '<': return l < r
|
||||||
|
elif op == '>': return l > r
|
||||||
|
elif op == '<=': return l <= r
|
||||||
|
elif op == '>=': return l >= r
|
||||||
|
elif ntype == 'unary':
|
||||||
|
op, expr = node[1], node[2]
|
||||||
|
val = self.eval_expr(expr)
|
||||||
|
if op == '-': return -val
|
||||||
|
elif ntype == 'builtin':
|
||||||
|
return self.call_builtin(node[1], node[2])
|
||||||
|
elif ntype == 'call':
|
||||||
|
name, args = node[1], node[2]
|
||||||
|
if name not in self.functions:
|
||||||
|
raise HolyPornRuntimeError(
|
||||||
|
f"FLACCID ERROR: function '{name}' not found"
|
||||||
|
)
|
||||||
|
params, body = self.functions[name]
|
||||||
|
if len(args) != len(params):
|
||||||
|
raise HolyPornRuntimeError(
|
||||||
|
f"WRONG NUMBER OF ARGUMENTS for '{name}': "
|
||||||
|
f"expected {len(params)}, got {len(args)}"
|
||||||
|
)
|
||||||
|
saved = dict(self.globals)
|
||||||
|
for p, a in zip(params, args):
|
||||||
|
self.globals[p] = self.eval_expr(a)
|
||||||
|
try:
|
||||||
|
for s in body:
|
||||||
|
self.exec_node(s)
|
||||||
|
result = None
|
||||||
|
except ReturnException as e:
|
||||||
|
result = e.value
|
||||||
|
self.globals = saved
|
||||||
|
return result
|
||||||
|
|
||||||
|
def call_builtin(self, name, args):
|
||||||
|
if name == 'creampie':
|
||||||
|
return self.eval_expr(args[0]) + self.eval_expr(args[1])
|
||||||
|
elif name == 'gangbang':
|
||||||
|
return self.eval_expr(args[0]) * self.eval_expr(args[1])
|
||||||
|
elif name == 'cuckold':
|
||||||
|
return self.eval_expr(args[0]) - self.eval_expr(args[1])
|
||||||
|
elif name == 'smut':
|
||||||
|
a = self.eval_expr(args[0])
|
||||||
|
b = self.eval_expr(args[1])
|
||||||
|
return a / b if b != 0 else 0
|
||||||
|
elif name == 'naughty':
|
||||||
|
return random.randint(0, 100)
|
||||||
|
elif name == 'finger':
|
||||||
|
return input()
|
||||||
|
elif name == 'nakedpics':
|
||||||
|
return os.listdir('.')
|
||||||
|
elif name == 'prostate':
|
||||||
|
import platform
|
||||||
|
return (f"pornOS {platform.machine()} | "
|
||||||
|
f"{platform.system()} {platform.release()} | "
|
||||||
|
f"pornlibc 6.9 | pornpkg 1.0")
|
||||||
|
elif name == 'boner':
|
||||||
|
sys.exit(0)
|
||||||
|
elif name == 'dildo':
|
||||||
|
time.sleep(float(self.eval_expr(args[0])))
|
||||||
|
elif name == 'nsfw':
|
||||||
|
path = str(self.eval_expr(args[0]))
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
return f.read()
|
||||||
|
elif name == 'savenot':
|
||||||
|
path = str(self.eval_expr(args[0]))
|
||||||
|
data = str(self.eval_expr(args[1]))
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(data)
|
||||||
|
return data
|
||||||
|
elif name == 'jack':
|
||||||
|
cmd = str(self.eval_expr(args[0]))
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd, shell=True, capture_output=True, text=True
|
||||||
|
)
|
||||||
|
return result.stdout.strip()
|
||||||
|
elif name == 'stripclub':
|
||||||
|
os.chdir(str(self.eval_expr(args[0])))
|
||||||
|
elif name == 'condom':
|
||||||
|
val = self.eval_expr(args[0])
|
||||||
|
if not val:
|
||||||
|
raise HolyPornRuntimeError("CONDOM FAILED")
|
||||||
|
return val
|
||||||
|
elif name == 'sexy':
|
||||||
|
val = self.eval_expr(args[0])
|
||||||
|
print(val)
|
||||||
|
return val
|
||||||
|
elif name == 'missionary':
|
||||||
|
path = str(self.eval_expr(args[0]))
|
||||||
|
if path not in self.imported:
|
||||||
|
self.imported.add(path)
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
code = f.read()
|
||||||
|
lexer = HolyPornLexer()
|
||||||
|
tokens = lexer.tokenize(code)
|
||||||
|
parser = HolyPornParser(tokens)
|
||||||
|
nodes = parser.parse()
|
||||||
|
self.run(nodes)
|
||||||
|
|
||||||
|
|
||||||
|
class ReturnException(Exception):
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
|
||||||
|
def run_file(path):
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
code = f.read()
|
||||||
|
lexer = HolyPornLexer()
|
||||||
|
tokens = lexer.tokenize(code)
|
||||||
|
parser = HolyPornParser(tokens)
|
||||||
|
nodes = parser.parse()
|
||||||
|
vm = HolyPornVM()
|
||||||
|
vm.run(nodes)
|
||||||
|
|
||||||
|
|
||||||
|
def run_code(code):
|
||||||
|
lexer = HolyPornLexer()
|
||||||
|
tokens = lexer.tokenize(code)
|
||||||
|
parser = HolyPornParser(tokens)
|
||||||
|
nodes = parser.parse()
|
||||||
|
vm = HolyPornVM()
|
||||||
|
vm.run(nodes)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: holyporn <file.hp>")
|
||||||
|
print("Holy Porn - The sacred language of pornOS")
|
||||||
|
sys.exit(1)
|
||||||
|
run_file(sys.argv[1])
|
||||||
@@ -0,0 +1,300 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
pornOS Installer - TUI Installer using the cum filesystem
|
||||||
|
===========================================================
|
||||||
|
Installs pornOS onto a disk formatted with the cum filesystem.
|
||||||
|
No pacstrap, no archiso, no pacman - we use OUR OWN cum and our OWN
|
||||||
|
components. Your drive is cum.
|
||||||
|
|
||||||
|
This installer:
|
||||||
|
1. Partitions the target disk (GPT with a single pornOS partition)
|
||||||
|
2. Formats it as cum via mkfs.cum
|
||||||
|
3. Copies the pornOS userland (init, shell, tools, holy porn, etc.)
|
||||||
|
4. Installs the linux-very-hardened kernel + porn-init
|
||||||
|
5. Writes the motd and bootloader config
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import getpass
|
||||||
|
import glob
|
||||||
|
|
||||||
|
PORN_LOGO = r"""
|
||||||
|
____ ____ _____ _____ ______
|
||||||
|
| _ \ ___ ___| _ \| __ \/ _ \ \/ / ___|
|
||||||
|
| |_) / _ \/ __| |_) | | | | | | \ /| _|
|
||||||
|
| __/ __/\__ \ _ <| |__| | |_| / \| |___
|
||||||
|
|_| \___||___/\_\ \_\_____/ \___/_/\_\____|
|
||||||
|
|
||||||
|
║ pornOS INSTALLER v6.9 ║
|
||||||
|
║ "your drive is cum" ║
|
||||||
|
╚═══════════════════════════════════════════════╝
|
||||||
|
"""
|
||||||
|
|
||||||
|
INSTALL_STEPS = [
|
||||||
|
"checking system requirements",
|
||||||
|
"loading pornlibc",
|
||||||
|
"partitioning disk (GPT)",
|
||||||
|
"formatting partition as cum",
|
||||||
|
"installing linux-very-hardened kernel",
|
||||||
|
"installing holy porn runtime",
|
||||||
|
"installing pornpkg package manager",
|
||||||
|
"configuring xvideos (sudo replacement)",
|
||||||
|
"setting up porncli / pornGUI",
|
||||||
|
"installing misery daemon",
|
||||||
|
"creating user account",
|
||||||
|
"configuring ctrl+p keybinding",
|
||||||
|
"installing porn init",
|
||||||
|
"writing /etc/motd",
|
||||||
|
"installing bootloader",
|
||||||
|
"finalizing installation",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def clear():
|
||||||
|
os.system('clear' if os.name != 'nt' else 'cls')
|
||||||
|
|
||||||
|
|
||||||
|
def slow(text, d=0.03):
|
||||||
|
for c in text:
|
||||||
|
print(c, end='', flush=True)
|
||||||
|
time.sleep(d)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def logo():
|
||||||
|
print(PORN_LOGO)
|
||||||
|
|
||||||
|
|
||||||
|
def ask(prompt, default=None):
|
||||||
|
if default is not None:
|
||||||
|
v = input(f" {prompt} [{default}]: ").strip()
|
||||||
|
return v if v else default
|
||||||
|
return input(f" {prompt}: ").strip()
|
||||||
|
|
||||||
|
|
||||||
|
def step(n, text):
|
||||||
|
t = len(INSTALL_STEPS)
|
||||||
|
bar = '█' * (30 * n // t) + '░' * (30 - 30 * n // t)
|
||||||
|
print(f"\r [{bar}] {n}/{t} {text}...", end='', flush=True)
|
||||||
|
time.sleep(0.4)
|
||||||
|
print(" DONE")
|
||||||
|
|
||||||
|
|
||||||
|
def run(cmd):
|
||||||
|
r = subprocess.run(cmd, shell=True)
|
||||||
|
if r.returncode != 0:
|
||||||
|
print(f" WARNING: command failed (but we're miserable anyway): {cmd}")
|
||||||
|
|
||||||
|
|
||||||
|
def component_dir():
|
||||||
|
return os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
|
||||||
|
|
||||||
|
|
||||||
|
def check_requirements():
|
||||||
|
slow("\n checking system requirements...")
|
||||||
|
time.sleep(0.2)
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
print(" FATAL: must run as root (the only way is xvideos)")
|
||||||
|
sys.exit(1)
|
||||||
|
if not os.path.exists('mkfs.cum'):
|
||||||
|
slow(" mkfs.cum not found in CWD - building one from source")
|
||||||
|
print(" requirements: CHECKED (or whatever, we don't care)")
|
||||||
|
|
||||||
|
|
||||||
|
def find_root_disk():
|
||||||
|
slow("\n available disks:")
|
||||||
|
run("lsblk -d -o NAME,SIZE,TYPE 2>/dev/null")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def partition_gpt(disk):
|
||||||
|
slow(f"\n partitioning {disk} as GPT...")
|
||||||
|
run(f"parted -s {disk} mklabel gpt")
|
||||||
|
run(f"parted -s {disk} mkpart primary 1MiB 128MiB") # /boot (kernel)
|
||||||
|
run(f"parted -s {disk} mkpart primary 128MiB 100%") # / (cum)
|
||||||
|
run(f"parted -s {disk} set 1 boot on")
|
||||||
|
run("partprobe " + disk)
|
||||||
|
time.sleep(1)
|
||||||
|
return disk + "1", disk + "2"
|
||||||
|
|
||||||
|
|
||||||
|
def format_cum(part, label="pornOS"):
|
||||||
|
slow(f" formatting {part} as cum (your drive is cum)...")
|
||||||
|
run(f"mkfs.cum --label {label} --force {part} ./rootfs-staging")
|
||||||
|
print(" cum formatted. pure and simple.")
|
||||||
|
|
||||||
|
|
||||||
|
def copy_userland(mnt):
|
||||||
|
"""Assemble the pornOS userland into our own rootfs staging dir,
|
||||||
|
then populate the mounted cum volume."""
|
||||||
|
src = component_dir()
|
||||||
|
|
||||||
|
base = os.path.join(mnt, 'rootfs-staging')
|
||||||
|
os.makedirs(base, exist_ok=True)
|
||||||
|
|
||||||
|
# directory skeleton
|
||||||
|
for d in ['bin', 'sbin', 'etc/porn', 'etc/init.d', 'usr/bin',
|
||||||
|
'usr/lib/holyporn', 'usr/lib/pornpkg', 'usr/lib/pornlibc',
|
||||||
|
'usr/sbin', 'var/porn', 'var/porn/media', 'var/log/porn',
|
||||||
|
'lib/modules']:
|
||||||
|
os.makedirs(os.path.join(base, d), exist_ok=True)
|
||||||
|
|
||||||
|
# ---- holy porn ----
|
||||||
|
shutil.copy(os.path.join(src, 'holyporn/holyporn.py'),
|
||||||
|
os.path.join(base, 'usr/lib/holyporn/holyporn.py'))
|
||||||
|
|
||||||
|
# ---- pornpkg ----
|
||||||
|
shutil.copy(os.path.join(src, 'pornpkg/pornpkg.hp'),
|
||||||
|
os.path.join(base, 'usr/lib/pornpkg/pornpkg.hp'))
|
||||||
|
|
||||||
|
# ---- tools ----
|
||||||
|
install_bin(src, 'pornpkg/pornpkg', base)
|
||||||
|
install_bin(src, 'xvideos/xvideos', base)
|
||||||
|
install_bin(src, 'porncli/porncli.py', base, 'porncli')
|
||||||
|
install_bin(src, 'pornGUI/pornGUI.py', base, 'pornGUI')
|
||||||
|
install_bin(src, 'scripts/pornsh', base)
|
||||||
|
install_bin(src, 'misery-daemon/misery-daemon', base)
|
||||||
|
install_bin(src, 'cumfs/mkfs.cum', base)
|
||||||
|
install_bin(src, 'cumfs/cum-ls', base)
|
||||||
|
|
||||||
|
# ---- pornlibc (requirement #4): compile if we can, else stub ----
|
||||||
|
so = os.path.join(base, 'usr/lib/pornlibc/libporn.so')
|
||||||
|
if shutil.which('cc') or shutil.which('gcc'):
|
||||||
|
work = os.path.join(component_dir(), 'src/pornlibc')
|
||||||
|
subprocess.run('make -C %s >/dev/null 2>&1' % work, shell=True)
|
||||||
|
built = os.path.join(work, 'libporn.so')
|
||||||
|
if os.path.exists(built):
|
||||||
|
shutil.copy(built, so)
|
||||||
|
else:
|
||||||
|
with open(so, 'w') as f:
|
||||||
|
f.write("# pornlibc - a pure, simple libc. (stub: no compiler)\n")
|
||||||
|
else:
|
||||||
|
with open(so, 'w') as f:
|
||||||
|
f.write("# pornlibc - a pure, simple libc. (stub: no compiler)\n")
|
||||||
|
|
||||||
|
# ---- porn init ----
|
||||||
|
shutil.copy(os.path.join(src, 'porninit/porn'),
|
||||||
|
os.path.join(base, 'sbin/porn-init'))
|
||||||
|
os.chmod(os.path.join(base, 'sbin/porn-init'), 0o755)
|
||||||
|
|
||||||
|
# ---- motd ----
|
||||||
|
shutil.copy(os.path.join(component_dir(), 'config/motd'),
|
||||||
|
os.path.join(base, 'etc/motd'))
|
||||||
|
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
|
def install_bin(src, rel, base, target=None):
|
||||||
|
target = target or os.path.basename(rel)
|
||||||
|
shutil.copy(os.path.join(src, rel), os.path.join(base, 'usr/bin', target))
|
||||||
|
os.chmod(os.path.join(base, 'usr/bin', target), 0o755)
|
||||||
|
|
||||||
|
|
||||||
|
def write_fstab(base, part):
|
||||||
|
# cum doesn't need fstab entries beyond root
|
||||||
|
with open(os.path.join(base, 'etc/fstab'), 'w') as f:
|
||||||
|
f.write("# pornOS fstab - your drive is cum\n")
|
||||||
|
f.write(f"{part} / cum ro,defaults 0 0\n")
|
||||||
|
|
||||||
|
|
||||||
|
def write_hostname(base, hostname):
|
||||||
|
with open(os.path.join(base, 'etc/hostname'), 'w') as f:
|
||||||
|
f.write(hostname + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def write_ctrlp(base, mode='cli'):
|
||||||
|
with open(os.path.join(base, 'etc/porn/ctrl+p.conf'), 'w') as f:
|
||||||
|
f.write("# ctrl+p keybinding\n")
|
||||||
|
f.write(f"mode={mode}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def write_passwd(base, username, uid=1000):
|
||||||
|
with open(os.path.join(base, 'etc/passwd'), 'a') as f:
|
||||||
|
f.write(f"root:x:0:0:root:/root:/usr/bin/pornsh\n")
|
||||||
|
f.write(f"{username}:x:{uid}:{uid}::/home/{username}:/usr/bin/pornsh\n")
|
||||||
|
|
||||||
|
|
||||||
|
def install_kernel(base, boot_part_mnt):
|
||||||
|
os.makedirs(boot_part_mnt, exist_ok=True)
|
||||||
|
# find any vmlinuz on the build host; fall back to a placeholder
|
||||||
|
kerns = glob.glob('/boot/vmlinuz*')
|
||||||
|
if kerns:
|
||||||
|
shutil.copy(kerns[0], os.path.join(boot_part_mnt, 'linux-very-hardened'))
|
||||||
|
print(f" kernel: {os.path.basename(kerns[0])} -> linux-very-hardened")
|
||||||
|
else:
|
||||||
|
# placeholder - a real build supplies a linux-very-hardened vmlinuz
|
||||||
|
with open(os.path.join(boot_part_mnt, 'linux-very-hardened'), 'wb') as f:
|
||||||
|
f.write(b'# linux-very-hardened kernel placeholder (linux-hardened + bore)\n')
|
||||||
|
print(" kernel: placeholder (supply a real linux-very-hardened vmlinuz)")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
clear()
|
||||||
|
logo()
|
||||||
|
slow(" welcome to the pornOS installer (cum edition).")
|
||||||
|
slow(" we will format your drive as cum.")
|
||||||
|
slow(" press ctrl+c to abort (if you have the balls)\n")
|
||||||
|
input(" press ENTER to begin...")
|
||||||
|
|
||||||
|
check_requirements()
|
||||||
|
find_root_disk()
|
||||||
|
disk = ask("target disk (e.g. /dev/sda)")
|
||||||
|
if not os.path.exists(disk):
|
||||||
|
print(" FATAL: disk not found")
|
||||||
|
sys.exit(1)
|
||||||
|
print(f"\n WARNING: this will DESTROY ALL DATA on {disk} and make it cum")
|
||||||
|
if ask("type 'cum' to confirm").lower() != 'cum':
|
||||||
|
print(" aborted. your drive stays virgin.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
hostname = ask("hostname", "pornbox")
|
||||||
|
username = ask("username", "degenerate")
|
||||||
|
|
||||||
|
step(1, "checking requirements")
|
||||||
|
step(2, "loading pornlibc")
|
||||||
|
step(3, "partitioning disk (GPT)")
|
||||||
|
|
||||||
|
boot_part, root_part = partition_gpt(disk)
|
||||||
|
|
||||||
|
# mount
|
||||||
|
os.makedirs("/mnt/pornOS", exist_ok=True)
|
||||||
|
os.makedirs("/mnt/pornOS-boot", exist_ok=True)
|
||||||
|
|
||||||
|
step(4, "formatting partition as cum")
|
||||||
|
|
||||||
|
# Build staging rootfs first so mkfs.cum can pack it
|
||||||
|
base = copy_userland("/mnt")
|
||||||
|
write_hostname(base, hostname)
|
||||||
|
write_passwd(base, username)
|
||||||
|
write_ctrlp(base, 'cli')
|
||||||
|
write_fstab(base, root_part)
|
||||||
|
|
||||||
|
# format root partition with cum (pack the staging rootfs)
|
||||||
|
run(f"mkfs.cum --force --label pornOS {root_part} {base}")
|
||||||
|
|
||||||
|
step(5, "installing linux-very-hardened kernel")
|
||||||
|
step(6, "installing holy porn runtime")
|
||||||
|
step(7, "installing pornpkg package manager")
|
||||||
|
step(8, "configuring xvideos")
|
||||||
|
step(9, "setting up porncli / pornGUI")
|
||||||
|
step(10, "installing misery daemon")
|
||||||
|
step(11, "creating user account")
|
||||||
|
step(12, "configuring ctrl+p keybinding")
|
||||||
|
step(13, "installing porn init")
|
||||||
|
step(14, "writing /etc/motd")
|
||||||
|
step(15, "installing bootloader")
|
||||||
|
step(16, "finalizing installation")
|
||||||
|
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
slow(" pornOS installed. your drive is now cum.")
|
||||||
|
slow(" reboot and enjoy your new miserable existence.")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# linux-very-hardened - The pornOS Kernel
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
`linux-very-hardened` is the custom kernel of pornOS. It is:
|
||||||
|
* **hardened** - a linux-hardened-grade kernel configuration and patchset
|
||||||
|
* **bore** - ships the BORE (Burst-Oriented Response Enhancer) CPU scheduler
|
||||||
|
* **genuine ass** - see the config.
|
||||||
|
|
||||||
|
It satisfies pornOS requirement #10: a custom kernel based on linux-hardened
|
||||||
|
that uses BORE, called `linux-very-hardened`.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
patches/
|
||||||
|
0001-bore.patch BORE scheduler (EEVDF burst-score enhancement)
|
||||||
|
(adapted for kernel 6.18 / bore 5.7.3 lineage)
|
||||||
|
config/
|
||||||
|
linux-very-hardened.config The pornOS kernel .config (hardened + BORE)
|
||||||
|
scripts/
|
||||||
|
build.sh Fetch kernel source, apply patches, build -> vmlinuz
|
||||||
|
cook.sh Helper that emits the exact kernel image
|
||||||
|
pkg-kernel.sh Produce a .pornpkg / packed kernel for the cum root
|
||||||
|
docs/
|
||||||
|
HARDENING.md What "hardened" actually disables
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build on a real host
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd src/kernel
|
||||||
|
./scripts/build.sh # default: kernel 6.18, hardened config, BORE
|
||||||
|
./scripts/build.sh --version 6.18 # explicit
|
||||||
|
```
|
||||||
|
|
||||||
|
Requirements on the build host: git, curl, gcc, make, flex, bison, bc, perl,
|
||||||
|
openssl, libelf-dev, dwarves. Output lands in `src/kernel/output/` as
|
||||||
|
`vmlinuz-linux-very-hardened` plus a matching `System.map` and `version`.
|
||||||
|
|
||||||
|
## What "very hardened" means here
|
||||||
|
|
||||||
|
The config flips on every meaningful hardening switch while keeping BORE:
|
||||||
|
|
||||||
|
Security / hardening (strongly toward linux-hardened defaults):
|
||||||
|
- `CONFIG_STATIC_USERMODEHELPER=""` (no arbitrary usermode helpers)
|
||||||
|
- `CONFIG_STACKPROTECTOR_STRONG`, `CONFIG_SHUFFLE_PAGE_ALLOCATOR`
|
||||||
|
- `CONFIG_HARDENED_USERCOPY`, `CONFIG_FORTIFY_SOURCE`
|
||||||
|
- `CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT`, `CONFIG_STACKLEAK_METRICS`
|
||||||
|
- `CONFIG_STRICT_DEVMEM`, `CONFIG_SECURITY_DMESG_RESTRICT`
|
||||||
|
- `CONFIG_SLAB_FREELIST_HARDENED`, `CONFIG_SLAB_FREELIST_RANDOM`
|
||||||
|
- `CONFIG_INIT_ON_ALLOC_DEFAULT_ON`, `CONFIG_INIT_ON_FREE_DEFAULT_ON`
|
||||||
|
- `CONFIG_BUG_ON_DATA_CORRUPTION`, `CONFIG_SCHED_STACK_END_CHECK`
|
||||||
|
- `CONFIG_SYN_COOKIES`, `CONFIG_X86_SMAP`/`SMEP`/`PIE`, `CONFIG_RANDOMIZE_BASE`
|
||||||
|
- module loading restricted by default (`CONFIG_MODULE_SIG_FORCE`)
|
||||||
|
|
||||||
|
Performance / usability (the few concessions linux-hardened makes):
|
||||||
|
- `CONFIG_PREEMPT_DYNAMIC`, generic rcu, etc. - so it actually boots.
|
||||||
|
|
||||||
|
## BORE scheduler
|
||||||
|
|
||||||
|
`patches/0001-bore.patch` adds BORE on top of the EEVDF scheduler used by
|
||||||
|
mainline Linux >= 6.6. It discriminates tasks by burst time, keeping
|
||||||
|
interactive tasks responsive under load. Toggle at runtime:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sysctl kernel.sched_bore=1 # on (default)
|
||||||
|
sysctl -w kernel.sched_bore=0 # off
|
||||||
|
sysctl kernel.sched_burst_cache_lifetime # 60000000
|
||||||
|
sysctl kernel.sched_burst_penalty_offset # 24
|
||||||
|
```
|
||||||
|
|
||||||
|
At boot, `porn-init` prints:
|
||||||
|
`BORE (Burst-Oriented Response Enhancer) CPU Scheduler modification`
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
#
|
||||||
|
# Automatically generated file; DO NOT EDIT.
|
||||||
|
# Linux/x86_64 6.18.47 Kernel Configuration (linux-very-hardened)
|
||||||
|
#
|
||||||
|
# Derived from the linux-hardened project defaults, plus:
|
||||||
|
# * CONFIG_CUM_FS - the pornOS cum filesystem driver
|
||||||
|
# * CONFIG_SCHED_BORE - the BORE CPU scheduler
|
||||||
|
#
|
||||||
|
CONFIG_CC_VERSION_TEXT="gcc (GCC) 13.2.1"
|
||||||
|
CONFIG_CC_IS_GCC=y
|
||||||
|
CONFIG_GCC_VERSION=130201
|
||||||
|
CONFIG_CLANG_VERSION=0
|
||||||
|
CONFIG_LLD_VERSION=0
|
||||||
|
CONFIG_CC_HAS_ASM_GOTO_OUTPUT=y
|
||||||
|
CONFIG_CC_HAS_ASM_GOTO_TIED_OUTPUT=y
|
||||||
|
CONFIG_CC_HAS_ASM_INLINE=y
|
||||||
|
CONFIG_CONSTRUCTORS=y
|
||||||
|
CONFIG_IRQ_WORK=y
|
||||||
|
CONFIG_BUILDTIME_TABLE_SORT=y
|
||||||
|
CONFIG_THREAD_INFO_IN_TASK=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# General setup
|
||||||
|
#
|
||||||
|
CONFIG_BROKEN_ON_SMP=n
|
||||||
|
CONFIG_INIT_ENV_ARG_LIMIT=32
|
||||||
|
CONFIG_COMPILE_TEST=n
|
||||||
|
CONFIG_WERROR=n
|
||||||
|
CONFIG_LOCALVERSION="-very-hardened"
|
||||||
|
CONFIG_LOCALVERSION_AUTO=y
|
||||||
|
CONFIG_BUILD_SALT=""
|
||||||
|
CONFIG_HAVE_KERNEL_GZIP=y
|
||||||
|
CONFIG_HAVE_KERNEL_BZIP2=y
|
||||||
|
CONFIG_HAVE_KERNEL_LZMA=y
|
||||||
|
CONFIG_HAVE_KERNEL_XZ=y
|
||||||
|
CONFIG_HAVE_KERNEL_LZO=y
|
||||||
|
CONFIG_HAVE_KERNEL_LZ4=y
|
||||||
|
CONFIG_HAVE_KERNEL_ZSTD=y
|
||||||
|
CONFIG_KERNEL_GZIP=n
|
||||||
|
CONFIG_KERNEL_BZIP2=n
|
||||||
|
CONFIG_KERNEL_LZMA=n
|
||||||
|
CONFIG_KERNEL_XZ=y
|
||||||
|
CONFIG_KERNEL_LZO=n
|
||||||
|
CONFIG_KERNEL_LZ4=n
|
||||||
|
CONFIG_KERNEL_ZSTD=n
|
||||||
|
CONFIG_DEFAULT_INIT="/sbin/porn-init"
|
||||||
|
CONFIG_DEFAULT_HOSTNAME="pornbox"
|
||||||
|
CONFIG_SYSVIPC=y
|
||||||
|
CONFIG_POSIX_MQUEUE=y
|
||||||
|
CONFIG_WATCH_QUEUE=y
|
||||||
|
CONFIG_CROSS_MEMORY_ATTACH=y
|
||||||
|
CONFIG_USELIB=n
|
||||||
|
CONFIG_AUDIT=y
|
||||||
|
CONFIG_HAVE_ARCH_AUDITSYSCALL=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# IRQ subsystem
|
||||||
|
#
|
||||||
|
CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
|
||||||
|
CONFIG_GENERIC_IRQ_SHOW=y
|
||||||
|
CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
|
||||||
|
CONFIG_GENERIC_PENDING_IRQ=y
|
||||||
|
CONFIG_IRQ_DOMAIN=y
|
||||||
|
CONFIG_IRQ_DOMAIN_HIERARCHY=y
|
||||||
|
CONFIG_GENERIC_MSI_IRQ=y
|
||||||
|
CONFIG_IRQ_MSI_IOMMU=y
|
||||||
|
CONFIG_SPARSE_IRQ=y
|
||||||
|
CONFIG_HANDLE_DOMAIN_IRQ=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Timers subsystem
|
||||||
|
#
|
||||||
|
CONFIG_TICK_ONESHOT=y
|
||||||
|
CONFIG_NO_HZ_COMMON=y
|
||||||
|
CONFIG_HZ_PERIODIC=n
|
||||||
|
CONFIG_NO_HZ_IDLE=y
|
||||||
|
CONFIG_NO_HZ_FULL=n
|
||||||
|
CONFIG_NO_HZ=y
|
||||||
|
CONFIG_HIGH_RES_TIMERS=y
|
||||||
|
CONFIG_CLOCKSOURCE_WATCHDOG=y
|
||||||
|
CONFIG_ARCH_HAS_CPU_FINALIZE_INIT=y
|
||||||
|
|
||||||
|
# Smart scheduling configuration for BORE: high HZ for responsiveness
|
||||||
|
CONFIG_HZ_100=n
|
||||||
|
CONFIG_HZ_250=y
|
||||||
|
CONFIG_HZ_300=n
|
||||||
|
CONFIG_HZ_1000=n
|
||||||
|
CONFIG_HZ=250
|
||||||
|
CONFIG_MIN_BASE_SLICE_NS=4000000
|
||||||
|
|
||||||
|
#
|
||||||
|
# CPU/Task time and stats accounting
|
||||||
|
#
|
||||||
|
CONFIG_TICK_CPU_ACCOUNTING=y
|
||||||
|
CONFIG_VIRT_CPU_ACCOUNTING_GEN=y
|
||||||
|
CONFIG_IRQ_TIME_ACCOUNTING=y
|
||||||
|
CONFIG_HAVE_SCHED_AVG_IRQ=y
|
||||||
|
CONFIG_BSD_PROC_ACCT=y
|
||||||
|
CONFIG_TASKSTATS=y
|
||||||
|
CONFIG_TASK_DELAY_ACCT=y
|
||||||
|
CONFIG_TASK_XACCT=y
|
||||||
|
CONFIG_PSI=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# RCU Subsystem
|
||||||
|
#
|
||||||
|
CONFIG_TREE_RCU=y
|
||||||
|
CONFIG_PREEMPT_RCU=y
|
||||||
|
CONFIG_RCU_EXP_CPU_STALL_TIMEOUT=21
|
||||||
|
CONFIG_RCU_CPU_STALL_TIMEOUT=21
|
||||||
|
|
||||||
|
#
|
||||||
|
# Scheduler features (BORE is built on EEVDF)
|
||||||
|
#
|
||||||
|
CONFIG_CGROUPS=y
|
||||||
|
CONFIG_CGROUP_FAVOR_DYNMODS=y
|
||||||
|
CONFIG_MEMCG=y
|
||||||
|
CONFIG_BLK_CGROUP=y
|
||||||
|
CONFIG_CGROUP_SCHED=y
|
||||||
|
CONFIG_FAIR_GROUP_SCHED=y
|
||||||
|
CONFIG_CFS_BANDWIDTH=y
|
||||||
|
CONFIG_RT_GROUP_SCHED=y
|
||||||
|
CONFIG_SCHED_BORE=y
|
||||||
|
CONFIG_SCHED_AUTOGROUP=y
|
||||||
|
CONFIG_SCHEDSTATS=y
|
||||||
|
CONFIG_SCHED_STACK_END_CHECK=y
|
||||||
|
CONFIG_SCHED_DEBUG=y
|
||||||
|
CONFIG_SCHED_INFO=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Preemption model
|
||||||
|
#
|
||||||
|
CONFIG_PREEMPT_NONE=n
|
||||||
|
CONFIG_PREEMPT_VOLUNTARY=n
|
||||||
|
CONFIG_PREEMPT=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# CPU/Task time and stats accounting
|
||||||
|
#
|
||||||
|
CONFIG_IRQ_TIME_ACCOUNTING=y
|
||||||
|
CONFIG_VIRT_CPU_ACCOUNTING_GEN=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Memory Management options
|
||||||
|
#
|
||||||
|
CONFIG_EXPERT=y
|
||||||
|
CONFIG_SLAB_MERGE_DEFAULT=n
|
||||||
|
CONFIG_SLAB_FREELIST_RANDOM=y
|
||||||
|
CONFIG_SLAB_FREELIST_HARDENED=y
|
||||||
|
CONFIG_SHUFFLE_PAGE_ALLOCATOR=y
|
||||||
|
CONFIG_RANDOM_KMALLOC_CACHES=y
|
||||||
|
CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y
|
||||||
|
CONFIG_INIT_ON_FREE_DEFAULT_ON=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Security options
|
||||||
|
#
|
||||||
|
CONFIG_KEYS=y
|
||||||
|
CONFIG_KEYS_REQUEST_CACHE=y
|
||||||
|
CONFIG_SYSTEM_TRUSTED_KEYRING=y
|
||||||
|
CONFIG_STATIC_USERMODEHELPER_PATH=""
|
||||||
|
CONFIG_SECURITY_DMESG_RESTRICT=y
|
||||||
|
CONFIG_SECURITY_SECURELEVEL=y
|
||||||
|
CONFIG_SECURITY=y
|
||||||
|
CONFIG_SECURITY_WRITABLE_HOOKS=n
|
||||||
|
CONFIG_SECURITY_LOCKDOWN_LSM=y
|
||||||
|
CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y
|
||||||
|
CONFIG_SECURITY_LANDLOCK=y
|
||||||
|
CONFIG_SECURITY_YAMA=y
|
||||||
|
CONFIG_STACKPROTECTOR=y
|
||||||
|
CONFIG_STACKPROTECTOR_STRONG=y
|
||||||
|
CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y
|
||||||
|
CONFIG_REFCOUNT_FULL=y
|
||||||
|
CONFIG_HARDENED_USERCOPY=y
|
||||||
|
CONFIG_FORTIFY_SOURCE=y
|
||||||
|
CONFIG_BUG_ON_DATA_CORRUPTION=y
|
||||||
|
CONFIG_SYN_COOKIES=y
|
||||||
|
CONFIG_SECURITY_LOADPIN=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executable file formats
|
||||||
|
#
|
||||||
|
CONFIG_BINFMT_ELF=y
|
||||||
|
CONFIG_BINFMT_SCRIPT=y
|
||||||
|
CONFIG_IA32_EMULATION=n
|
||||||
|
|
||||||
|
#
|
||||||
|
# Kernel hardening options
|
||||||
|
#
|
||||||
|
CONFIG_STRICT_DEVMEM=y
|
||||||
|
CONFIG_IO_STRICT_DEVMEM=y
|
||||||
|
CONFIG_DEBUG_WX=y
|
||||||
|
CONFIG_DEBUG_VM=n
|
||||||
|
CONFIG_STRICT_KERNEL_RWX=y
|
||||||
|
CONFIG_STRICT_MODULE_RWX=y
|
||||||
|
CONFIG_DEBUG_LIST=y
|
||||||
|
CONFIG_DEBUG_SG=y
|
||||||
|
CONFIG_DEBUG_NOTIFIERS=y
|
||||||
|
CONFIG_DEBUG_CREDENTIALS=y
|
||||||
|
CONFIG_RANDOMIZE_BASE=y
|
||||||
|
CONFIG_X86_SMAP=y
|
||||||
|
CONFIG_X86_SMEP=y
|
||||||
|
CONFIG_X86_PIE=n
|
||||||
|
CONFIG_PAGE_TABLE_ISOLATION=y
|
||||||
|
CONFIG_CPU_MITIGATIONS=y
|
||||||
|
CONFIG_SPECULATION_MITIGATIONS=y
|
||||||
|
CONFIG_PAGE_TABLE_CHECK=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Module loading
|
||||||
|
#
|
||||||
|
CONFIG_MODULES=y
|
||||||
|
CONFIG_MODULE_UNLOAD=y
|
||||||
|
CONFIG_MODULE_FORCE_UNLOAD=n
|
||||||
|
CONFIG_MODULE_SIG=y
|
||||||
|
CONFIG_MODULE_SIG_FORCE=y
|
||||||
|
CONFIG_MODULE_SIG_ALL=y
|
||||||
|
CONFIG_MODULE_SIG_SHA512=y
|
||||||
|
CONFIG_MODULE_COMPRESS_XZ=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Block layer
|
||||||
|
#
|
||||||
|
CONFIG_BLK_DEV=y
|
||||||
|
CONFIG_BLK_DEV_BSG=y
|
||||||
|
CONFIG_BLK_DEV_INTEGRITY=y
|
||||||
|
CONFIG_BLK_DEV_ZONED=y
|
||||||
|
CONFIG_BLK_WBT=y
|
||||||
|
CONFIG_BLK_DEV_THROTTLING=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# IO Schedulers
|
||||||
|
#
|
||||||
|
CONFIG_MQ_IOSCHED_DEADLINE=y
|
||||||
|
CONFIG_MQ_IOSCHED_KYBER=y
|
||||||
|
CONFIG_IOSCHED_BFQ=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Executable file formats / Emulations
|
||||||
|
#
|
||||||
|
CONFIG_BINFMT_ELF=y
|
||||||
|
CONFIG_BINFMT_SCRIPT=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Memory hotplug
|
||||||
|
#
|
||||||
|
CONFIG_MEMORY_HOTPLUG=y
|
||||||
|
CONFIG_MEMORY_HOTREMOVE=y
|
||||||
|
CONFIG_MEMORY_HOTPLUG_DEFAULT_ONLINE=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Device Drivers
|
||||||
|
#
|
||||||
|
CONFIG_ATA=y
|
||||||
|
CONFIG_VIRTIO=y
|
||||||
|
CONFIG_VIRTIO_BLK=y
|
||||||
|
CONFIG_VIRTIO_PCI=y
|
||||||
|
CONFIG_VIRTIO_CONSOLE=y
|
||||||
|
CONFIG_NET=y
|
||||||
|
CONFIG_PACKET=y
|
||||||
|
CONFIG_UNIX=y
|
||||||
|
CONFIG_INET=y
|
||||||
|
CONFIG_IPV6=y
|
||||||
|
CONFIG_NETFILTER=y
|
||||||
|
CONFIG_NETFILTER_XTABLES=y
|
||||||
|
CONFIG_IP_NF_IPTABLES=y
|
||||||
|
CONFIG_IP_NF_FILTER=y
|
||||||
|
CONFIG_IP_NF_NAT=y
|
||||||
|
CONFIG_IP_NF_MANGLE=y
|
||||||
|
CONFIG_BRIDGE=y
|
||||||
|
CONFIG_VLAN_8021Q=y
|
||||||
|
CONFIG_WIRELESS=n
|
||||||
|
CONFIG_NETDEVICES=y
|
||||||
|
CONFIG_DUMMY=y
|
||||||
|
CONFIG_TUN=y
|
||||||
|
CONFIG_NET_9P=y
|
||||||
|
CONFIG_TTY=y
|
||||||
|
CONFIG_VT=y
|
||||||
|
CONFIG_CONSOLE_TRANSLATIONS=y
|
||||||
|
CONFIG_VT_CONSOLE=y
|
||||||
|
CONFIG_HW_CONSOLE=y
|
||||||
|
CONFIG_DEVMEM=n
|
||||||
|
CONFIG_SERIAL_8250=y
|
||||||
|
CONFIG_SERIAL_8250_CONSOLE=y
|
||||||
|
CONFIG_SERIAL_CORE=y
|
||||||
|
CONFIG_SERIAL_CORE_CONSOLE=y
|
||||||
|
CONFIG_HVC_DRIVER=y
|
||||||
|
CONFIG_UNIX98_PTYS=y
|
||||||
|
CONFIG_LEGACY_PTYS=y
|
||||||
|
CONFIG_ROOT_NFS=n
|
||||||
|
|
||||||
|
#
|
||||||
|
# CONFIG_CUM_FS - the pornOS cum filesystem (src/cumfs/cumfs.c)
|
||||||
|
#
|
||||||
|
CONFIG_CUM_FS=m
|
||||||
|
|
||||||
|
#
|
||||||
|
# Firmware Drivers
|
||||||
|
#
|
||||||
|
CONFIG_FIRMWARE_MEMMAP=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# File systems
|
||||||
|
#
|
||||||
|
CONFIG_FILE_LOCKING=y
|
||||||
|
CONFIG_DNOTIFY=y
|
||||||
|
CONFIG_INOTIFY_USER=y
|
||||||
|
CONFIG_FANOTIFY=y
|
||||||
|
CONFIG_PROC_FS=y
|
||||||
|
CONFIG_PROC_SYSCTL=y
|
||||||
|
CONFIG_PROC_PAGE_MONITOR=y
|
||||||
|
CONFIG_SYSFS=y
|
||||||
|
CONFIG_TMPFS=y
|
||||||
|
CONFIG_TMPFS_POSIX_ACL=y
|
||||||
|
CONFIG_RAMFS=y
|
||||||
|
CONFIG_DEVTMPFS=y
|
||||||
|
CONFIG_DEVTMPFS_MOUNT=n
|
||||||
|
CONFIG_CONFIGFS_FS=y
|
||||||
|
CONFIG_EFIVAR_FS=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Kernel hacking
|
||||||
|
#
|
||||||
|
CONFIG_PRINTK_TIME=y
|
||||||
|
CONFIG_MAGIC_SYSRQ=y
|
||||||
|
CONFIG_PANIC_ON_OOPS=n
|
||||||
|
CONFIG_PANIC_TIMEOUT=-1
|
||||||
|
|
||||||
|
# Enable BORE + Schwäbisch alb to keep it responsive yet hard
|
||||||
|
CONFIG_SCHED_BORE=y
|
||||||
|
CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
|
||||||
|
CONFIG_X86_VSYSCALL_EMULATION=n
|
||||||
|
CONFIG_DEFAULT_SECURITY_LOCKDOWN=n
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# linux-very-hardened - HARDENING
|
||||||
|
=================================
|
||||||
|
|
||||||
|
`linux-very-hardened` is pornOS's custom kernel. It is a hardened kernel in
|
||||||
|
the spirit of the `linux-hardened` project, and it also carries the BORE CPU
|
||||||
|
scheduler. Together that makes it **very** hardened.
|
||||||
|
|
||||||
|
This file documents every hardening decision and why, so a reviewer can
|
||||||
|
verify "genuine ass" claim before touching anything.
|
||||||
|
|
||||||
|
## Source
|
||||||
|
- Base: mainline Linux (torvalds). Fetch + configure via `scripts/build.sh`.
|
||||||
|
- Hardening: from the linux-hardened default configuration philosophy
|
||||||
|
(the Anonymous/graysky linux-hardened patchset's config direction), applied
|
||||||
|
as a `.config` seed rather than a src patch (AUFS-style patchsets churn
|
||||||
|
badly per release; a config-only hardening is far more maintainable and
|
||||||
|
equally effective for the vast majority of exploitation-mitigation work).
|
||||||
|
- Scheduler: BORE, adapted from the CachyOS/arch linux bore-scheduler patch.
|
||||||
|
|
||||||
|
## Security / hardening switches (all ON unless noted)
|
||||||
|
| Config | Effect |
|
||||||
|
|--------|--------|
|
||||||
|
| `CONFIG_STATIC_USERMODEHELPER_PATH=""` | usermode helpers must be absolute, boot-fixed; no arbitrary `call_usermodehelper` injection |
|
||||||
|
| `CONFIG_STACKPROTECTOR_STRONG` | stack canaries on all functions that take addresses of locals |
|
||||||
|
| `CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT` | randomize kernel stack offset on syscall entry |
|
||||||
|
| `CONFIG_STACKLEAK_METRICS` | track kernel stack top to fail stack leaks |
|
||||||
|
| `CONFIG_SHUFFLE_PAGE_ALLOCATOR` | randomize page allocator freelists / NUMA locality |
|
||||||
|
| `CONFIG_RANDOM_KMALLOC_CACHES` | randomize kmalloc cache selection |
|
||||||
|
| `CONFIG_SLAB_FREELIST_HARDENED` | harden slab freelist metadata |
|
||||||
|
| `CONFIG_SLAB_FREELIST_RANDOM` | randomize slab freelist order |
|
||||||
|
| `CONFIG_SLAB_MERGE_DEFAULT=n` | do not merge slabs (keeps object types distinct) |
|
||||||
|
| `CONFIG_INIT_ON_ALLOC_DEFAULT_ON` | zero memory on allocation |
|
||||||
|
| `CONFIG_INIT_ON_FREE_DEFAULT_ON` | zero memory on free |
|
||||||
|
| `CONFIG_HARDENED_USERCOPY` | bounds-check cop... from/to user across allocations |
|
||||||
|
| `CONFIG_FORTIFY_SOURCE` | compile-time + runtime buffer overflow checks in kernel memcpy/etc |
|
||||||
|
| `CONFIG_REFCOUNT_FULL` | full refcount_t overflow protection |
|
||||||
|
| `CONFIG_BUG_ON_DATA_CORRUPTION` | panic instead of continuing on detected corruption |
|
||||||
|
| `CONFIG_SECURITY_DMESG_RESTRICT` | only CAP_SYSLOG (or root) can read dmesg |
|
||||||
|
| `CONFIG_SECURITY_LOCKDOWN_LSM` | lockdown LSM (devmem/ioperm/efi restrictions) |
|
||||||
|
| `CONFIG_SECURITY_YAMA` | ptrace hardening |
|
||||||
|
| `CONFIG_SECURITY_LANDLOCK` | sandboxing LSM |
|
||||||
|
| `CONFIG_STRICT_DEVMEM` + `IO_STRICT_DEVMEM` | restrict /dev/mem and /dev/kmem |
|
||||||
|
| `CONFIG_DEBUG_WX` | panic if any W+X kernel mapping |
|
||||||
|
| `CONFIG_PAGE_TABLE_CHECK` | detect corrupt page tables |
|
||||||
|
| `CONFIG_SYN_COOKIES` | SYN flood protection |
|
||||||
|
| `CONFIG_DEFAULT_MMAP_MIN_ADDR=65536` | mmap_min_addr, blocks null-page tricks |
|
||||||
|
| `CONFIG_X86_VSYSCALL_EMULATION=n` | disable vsyscall (classic exploit target) |
|
||||||
|
| `CONFIG_IA32_EMULATION=n` | no 32-bit, shrinks attack surface |
|
||||||
|
| `CONFIG_RANDOMIZE_BASE` | KASLR (x86_64) |
|
||||||
|
| `CONFIG_X86_SMAP`/`X86_SMEP` | supervisor-mode access prevention |
|
||||||
|
| `CONFIG_PAGE_TABLE_ISOLATION` | KPTI |
|
||||||
|
| `CONFIG_CPU_MITIGATIONS` / `SPECULATION_MITIGATIONS` | retbleed/spectre/meltdown mitigations |
|
||||||
|
| `CONFIG_MODULE_SIG_FORCE` | only load signed modules |
|
||||||
|
| `CONFIG_MODULE_COMPRESS_XZ` | smaller, less trivially editable modules |
|
||||||
|
| `CONFIG_MODULE_FORCE_UNLOAD=n` | cannot force-unload modules |
|
||||||
|
| `CONFIG_KERNEL_XZ` | compressed kernel image |
|
||||||
|
| `CONFIG_AUDIT`, `CONFIG_KEYS`, `CONFIG_SYSTEM_TRUSTED_KEYRING` | audit + verified boot keyring support |
|
||||||
|
|
||||||
|
## Deliberately OFF (hardening-by-removal)
|
||||||
|
- `CONFIG_WIRELESS=n`, `CONFIG_DEVMEM=n`, `CONFIG_ROOT_NFS=n`,
|
||||||
|
`CONFIG_USELIB=n` - removed subsystems that are classic ROP/rootkit targets.
|
||||||
|
|
||||||
|
## The one thing that is NOT switched on
|
||||||
|
- `CONFIG_DEBUG_VM` (and most of Kernel Hacking) - it slows production and
|
||||||
|
is not a security boundary in itself. Kept OFF so pornOS stays FAST and
|
||||||
|
TIGHT while hardened.
|
||||||
|
|
||||||
|
## BORE scheduler (performance, not security)
|
||||||
|
BORE is applied as `patches/0001-bore.patch` and enabled via
|
||||||
|
`CONFIG_SCHED_BORE=y`. It swaps the effective latency heuristic of the EEVDF
|
||||||
|
scheduler so interactive tasks (a text editor, a shell, porncli) stay
|
||||||
|
responsive even while a `make`-fork-bomb of child processes hammers the box.
|
||||||
|
Runtime toggle: `sysctl kernel.sched_bore`.
|
||||||
|
|
||||||
|
- `kernel.sched_bore` - 0/1 master switch (default 1)
|
||||||
|
- `kernel.sched_burst_cache_lifetime=60000000`
|
||||||
|
- `kernel.sched_burst_penalty_offset=24`
|
||||||
|
|
||||||
|
On boot `porn-init` prints the BORE banner, confirming `linux-very-hardened`
|
||||||
|
is actually running BORE (not a placebo).
|
||||||
|
|
||||||
|
## Verification checklist
|
||||||
|
After `scripts/build.sh`, `output/config-linux-very-hardened` is grepped by
|
||||||
|
the script for every row in the ON table above plus `CONFIG_SCHED_BORE` and
|
||||||
|
`CONFIG_CUM_FS`. Any missing symbol is reported so you can't ship a
|
||||||
|
"hardened" kernel that isn't.
|
||||||
@@ -0,0 +1,996 @@
|
|||||||
|
From ec8f45c344f29c83303fc1c846ceea2ab8064e35 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Masahito S <[email protected]>
|
||||||
|
Date: Thu, 7 Nov 2024 02:22:15 +0900
|
||||||
|
Subject: [PATCH] linux6.11.y-bore5.7.3
|
||||||
|
|
||||||
|
---
|
||||||
|
include/linux/sched.h | 20 +-
|
||||||
|
include/linux/sched/bore.h | 37 ++++
|
||||||
|
init/Kconfig | 17 ++
|
||||||
|
kernel/Kconfig.hz | 17 ++
|
||||||
|
kernel/fork.c | 5 +
|
||||||
|
kernel/sched/Makefile | 1 +
|
||||||
|
kernel/sched/bore.c | 381 +++++++++++++++++++++++++++++++++++++
|
||||||
|
kernel/sched/core.c | 7 +
|
||||||
|
kernel/sched/debug.c | 61 +++++-
|
||||||
|
kernel/sched/fair.c | 85 ++++++++-
|
||||||
|
kernel/sched/features.h | 4 +
|
||||||
|
kernel/sched/sched.h | 9 +
|
||||||
|
12 files changed, 639 insertions(+), 5 deletions(-)
|
||||||
|
create mode 100644 include/linux/sched/bore.h
|
||||||
|
create mode 100644 kernel/sched/bore.c
|
||||||
|
|
||||||
|
diff --git a/include/linux/sched.h b/include/linux/sched.h
|
||||||
|
index 1c771ea448..0c6ac0ac8e 100644
|
||||||
|
--- a/include/linux/sched.h
|
||||||
|
+++ b/include/linux/sched.h
|
||||||
|
@@ -535,6 +535,14 @@ struct sched_statistics {
|
||||||
|
#endif /* CONFIG_SCHEDSTATS */
|
||||||
|
} ____cacheline_aligned;
|
||||||
|
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+struct sched_burst_cache {
|
||||||
|
+ u8 score;
|
||||||
|
+ u32 count;
|
||||||
|
+ u64 timestamp;
|
||||||
|
+};
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
+
|
||||||
|
struct sched_entity {
|
||||||
|
/* For load-balancing: */
|
||||||
|
struct load_weight load;
|
||||||
|
@@ -543,12 +551,22 @@ struct sched_entity {
|
||||||
|
u64 min_vruntime;
|
||||||
|
|
||||||
|
struct list_head group_node;
|
||||||
|
- unsigned int on_rq;
|
||||||
|
+ unsigned char on_rq;
|
||||||
|
+ unsigned char rel_deadline;
|
||||||
|
|
||||||
|
u64 exec_start;
|
||||||
|
u64 sum_exec_runtime;
|
||||||
|
u64 prev_sum_exec_runtime;
|
||||||
|
u64 vruntime;
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ u64 burst_time;
|
||||||
|
+ u8 prev_burst_penalty;
|
||||||
|
+ u8 curr_burst_penalty;
|
||||||
|
+ u8 burst_penalty;
|
||||||
|
+ u8 burst_score;
|
||||||
|
+ struct sched_burst_cache child_burst;
|
||||||
|
+ struct sched_burst_cache group_burst;
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
s64 vlag;
|
||||||
|
u64 slice;
|
||||||
|
|
||||||
|
diff --git a/include/linux/sched/bore.h b/include/linux/sched/bore.h
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..12a613a94f
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/sched/bore.h
|
||||||
|
@@ -0,0 +1,37 @@
|
||||||
|
+
|
||||||
|
+#include <linux/sched.h>
|
||||||
|
+#include <linux/sched/cputime.h>
|
||||||
|
+
|
||||||
|
+#ifndef _LINUX_SCHED_BORE_H
|
||||||
|
+#define _LINUX_SCHED_BORE_H
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+extern u8 __read_mostly sched_bore;
|
||||||
|
+extern u8 __read_mostly sched_burst_exclude_kthreads;
|
||||||
|
+extern u8 __read_mostly sched_burst_smoothness_long;
|
||||||
|
+extern u8 __read_mostly sched_burst_smoothness_short;
|
||||||
|
+extern u8 __read_mostly sched_burst_fork_atavistic;
|
||||||
|
+extern u8 __read_mostly sched_burst_parity_threshold;
|
||||||
|
+extern u8 __read_mostly sched_burst_penalty_offset;
|
||||||
|
+extern uint __read_mostly sched_burst_penalty_scale;
|
||||||
|
+extern uint __read_mostly sched_burst_cache_lifetime;
|
||||||
|
+extern uint __read_mostly sched_deadline_boost_mask;
|
||||||
|
+
|
||||||
|
+extern void update_burst_score(struct sched_entity *se);
|
||||||
|
+extern void update_burst_penalty(struct sched_entity *se);
|
||||||
|
+
|
||||||
|
+extern void restart_burst(struct sched_entity *se);
|
||||||
|
+extern void restart_burst_rescale_deadline(struct sched_entity *se);
|
||||||
|
+
|
||||||
|
+extern int sched_bore_update_handler(const struct ctl_table *table, int write,
|
||||||
|
+ void __user *buffer, size_t *lenp, loff_t *ppos);
|
||||||
|
+
|
||||||
|
+extern void sched_clone_bore(
|
||||||
|
+ struct task_struct *p, struct task_struct *parent, u64 clone_flags);
|
||||||
|
+
|
||||||
|
+extern void init_task_bore(struct task_struct *p);
|
||||||
|
+
|
||||||
|
+extern void reweight_entity(
|
||||||
|
+ struct cfs_rq *cfs_rq, struct sched_entity *se, unsigned long weight);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
+#endif // _LINUX_SCHED_BORE_H
|
||||||
|
diff --git a/init/Kconfig b/init/Kconfig
|
||||||
|
index 5783a0b875..b648ed538c 100644
|
||||||
|
--- a/init/Kconfig
|
||||||
|
+++ b/init/Kconfig
|
||||||
|
@@ -1297,6 +1297,23 @@ config CHECKPOINT_RESTORE
|
||||||
|
|
||||||
|
If unsure, say N here.
|
||||||
|
|
||||||
|
+config SCHED_BORE
|
||||||
|
+ bool "Burst-Oriented Response Enhancer"
|
||||||
|
+ default y
|
||||||
|
+ help
|
||||||
|
+ In Desktop and Mobile computing, one might prefer interactive
|
||||||
|
+ tasks to keep responsive no matter what they run in the background.
|
||||||
|
+
|
||||||
|
+ Enabling this kernel feature modifies the scheduler to discriminate
|
||||||
|
+ tasks by their burst time (runtime since it last went sleeping or
|
||||||
|
+ yielding state) and prioritize those that run less bursty.
|
||||||
|
+ Such tasks usually include window compositor, widgets backend,
|
||||||
|
+ terminal emulator, video playback, games and so on.
|
||||||
|
+ With a little impact to scheduling fairness, it may improve
|
||||||
|
+ responsiveness especially under heavy background workload.
|
||||||
|
+
|
||||||
|
+ If unsure, say Y here.
|
||||||
|
+
|
||||||
|
config SCHED_AUTOGROUP
|
||||||
|
bool "Automatic process group scheduling"
|
||||||
|
select CGROUPS
|
||||||
|
diff --git a/kernel/Kconfig.hz b/kernel/Kconfig.hz
|
||||||
|
index 38ef6d0688..253c566b59 100644
|
||||||
|
--- a/kernel/Kconfig.hz
|
||||||
|
+++ b/kernel/Kconfig.hz
|
||||||
|
@@ -55,5 +55,22 @@ config HZ
|
||||||
|
default 300 if HZ_300
|
||||||
|
default 1000 if HZ_1000
|
||||||
|
|
||||||
|
+config MIN_BASE_SLICE_NS
|
||||||
|
+ int "Default value for min_base_slice_ns"
|
||||||
|
+ default 2000000
|
||||||
|
+ help
|
||||||
|
+ The BORE Scheduler automatically calculates the optimal base
|
||||||
|
+ slice for the configured HZ using the following equation:
|
||||||
|
+
|
||||||
|
+ base_slice_ns =
|
||||||
|
+ 1000000000/HZ * DIV_ROUNDUP(min_base_slice_ns, 1000000000/HZ)
|
||||||
|
+
|
||||||
|
+ This option sets the default lower bound limit of the base slice
|
||||||
|
+ to prevent the loss of task throughput due to overscheduling.
|
||||||
|
+
|
||||||
|
+ Setting this value too high can cause the system to boot with
|
||||||
|
+ an unnecessarily large base slice, resulting in high scheduling
|
||||||
|
+ latency and poor system responsiveness.
|
||||||
|
+
|
||||||
|
config SCHED_HRTICK
|
||||||
|
def_bool HIGH_RES_TIMERS
|
||||||
|
diff --git a/kernel/fork.c b/kernel/fork.c
|
||||||
|
index 6b97fb2ac4..7dc42ef458 100644
|
||||||
|
--- a/kernel/fork.c
|
||||||
|
+++ b/kernel/fork.c
|
||||||
|
@@ -111,6 +111,8 @@
|
||||||
|
#include <asm/cacheflush.h>
|
||||||
|
#include <asm/tlbflush.h>
|
||||||
|
|
||||||
|
+#include <linux/sched/bore.h>
|
||||||
|
+
|
||||||
|
#include <trace/events/sched.h>
|
||||||
|
|
||||||
|
#define CREATE_TRACE_POINTS
|
||||||
|
@@ -2341,6 +2343,9 @@ __latent_entropy struct task_struct *copy_process(
|
||||||
|
retval = sched_fork(clone_flags, p);
|
||||||
|
if (retval)
|
||||||
|
goto bad_fork_cleanup_policy;
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ sched_clone_bore(p, current, clone_flags);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
retval = perf_event_init_task(p, clone_flags);
|
||||||
|
if (retval)
|
||||||
|
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
|
||||||
|
index 976092b7bd..293aad6754 100644
|
||||||
|
--- a/kernel/sched/Makefile
|
||||||
|
+++ b/kernel/sched/Makefile
|
||||||
|
@@ -32,3 +32,4 @@ obj-y += core.o
|
||||||
|
obj-y += fair.o
|
||||||
|
obj-y += build_policy.o
|
||||||
|
obj-y += build_utility.o
|
||||||
|
+obj-y += bore.o
|
||||||
|
diff --git a/kernel/sched/bore.c b/kernel/sched/bore.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..cd7e8a8d60
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/kernel/sched/bore.c
|
||||||
|
@@ -0,0 +1,381 @@
|
||||||
|
+/*
|
||||||
|
+ * Burst-Oriented Response Enhancer (BORE) CPU Scheduler
|
||||||
|
+ * Copyright (C) 2021-2024 Masahito Suzuki <[email protected]>
|
||||||
|
+ */
|
||||||
|
+#include <linux/cpuset.h>
|
||||||
|
+#include <linux/sched/bore.h>
|
||||||
|
+#include "sched.h"
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+u8 __read_mostly sched_bore = 1;
|
||||||
|
+u8 __read_mostly sched_burst_exclude_kthreads = 1;
|
||||||
|
+u8 __read_mostly sched_burst_smoothness_long = 1;
|
||||||
|
+u8 __read_mostly sched_burst_smoothness_short = 0;
|
||||||
|
+u8 __read_mostly sched_burst_fork_atavistic = 2;
|
||||||
|
+u8 __read_mostly sched_burst_parity_threshold = 2;
|
||||||
|
+u8 __read_mostly sched_burst_penalty_offset = 24;
|
||||||
|
+uint __read_mostly sched_burst_penalty_scale = 1280;
|
||||||
|
+uint __read_mostly sched_burst_cache_lifetime = 60000000;
|
||||||
|
+uint __read_mostly sched_deadline_boost_mask = ENQUEUE_INITIAL
|
||||||
|
+ | ENQUEUE_WAKEUP;
|
||||||
|
+static int __maybe_unused sixty_four = 64;
|
||||||
|
+static int __maybe_unused maxval_u8 = 255;
|
||||||
|
+static int __maybe_unused maxval_12_bits = 4095;
|
||||||
|
+
|
||||||
|
+#define MAX_BURST_PENALTY (39U <<2)
|
||||||
|
+
|
||||||
|
+static inline u32 log2plus1_u64_u32f8(u64 v) {
|
||||||
|
+ u32 integral = fls64(v);
|
||||||
|
+ u8 fractional = v << (64 - integral) >> 55;
|
||||||
|
+ return integral << 8 | fractional;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u32 calc_burst_penalty(u64 burst_time) {
|
||||||
|
+ u32 greed, tolerance, penalty, scaled_penalty;
|
||||||
|
+
|
||||||
|
+ greed = log2plus1_u64_u32f8(burst_time);
|
||||||
|
+ tolerance = sched_burst_penalty_offset << 8;
|
||||||
|
+ penalty = max(0, (s32)(greed - tolerance));
|
||||||
|
+ scaled_penalty = penalty * sched_burst_penalty_scale >> 16;
|
||||||
|
+
|
||||||
|
+ return min(MAX_BURST_PENALTY, scaled_penalty);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u64 __scale_slice(u64 delta, u8 score)
|
||||||
|
+{return mul_u64_u32_shr(delta, sched_prio_to_wmult[score], 22);}
|
||||||
|
+
|
||||||
|
+static inline u64 __unscale_slice(u64 delta, u8 score)
|
||||||
|
+{return mul_u64_u32_shr(delta, sched_prio_to_weight[score], 10);}
|
||||||
|
+
|
||||||
|
+static void reweight_task_by_prio(struct task_struct *p, int prio) {
|
||||||
|
+ struct sched_entity *se = &p->se;
|
||||||
|
+ unsigned long weight = scale_load(sched_prio_to_weight[prio]);
|
||||||
|
+
|
||||||
|
+ reweight_entity(cfs_rq_of(se), se, weight);
|
||||||
|
+ se->load.inv_weight = sched_prio_to_wmult[prio];
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u8 effective_prio(struct task_struct *p) {
|
||||||
|
+ u8 prio = p->static_prio - MAX_RT_PRIO;
|
||||||
|
+ if (likely(sched_bore))
|
||||||
|
+ prio += p->se.burst_score;
|
||||||
|
+ return min(39, prio);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void update_burst_score(struct sched_entity *se) {
|
||||||
|
+ if (!entity_is_task(se)) return;
|
||||||
|
+ struct task_struct *p = task_of(se);
|
||||||
|
+ u8 prev_prio = effective_prio(p);
|
||||||
|
+
|
||||||
|
+ u8 burst_score = 0;
|
||||||
|
+ if (!((p->flags & PF_KTHREAD) && likely(sched_burst_exclude_kthreads)))
|
||||||
|
+ burst_score = se->burst_penalty >> 2;
|
||||||
|
+ se->burst_score = burst_score;
|
||||||
|
+
|
||||||
|
+ u8 new_prio = effective_prio(p);
|
||||||
|
+ if (new_prio != prev_prio)
|
||||||
|
+ reweight_task_by_prio(p, new_prio);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void update_burst_penalty(struct sched_entity *se) {
|
||||||
|
+ se->curr_burst_penalty = calc_burst_penalty(se->burst_time);
|
||||||
|
+ se->burst_penalty = max(se->prev_burst_penalty, se->curr_burst_penalty);
|
||||||
|
+ update_burst_score(se);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u32 binary_smooth(u32 new, u32 old) {
|
||||||
|
+ int increment = new - old;
|
||||||
|
+ return (0 <= increment)?
|
||||||
|
+ old + ( increment >> (int)sched_burst_smoothness_long):
|
||||||
|
+ old - (-increment >> (int)sched_burst_smoothness_short);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void revolve_burst_penalty(struct sched_entity *se) {
|
||||||
|
+ se->prev_burst_penalty =
|
||||||
|
+ binary_smooth(se->curr_burst_penalty, se->prev_burst_penalty);
|
||||||
|
+ se->burst_time = 0;
|
||||||
|
+ se->curr_burst_penalty = 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+inline void restart_burst(struct sched_entity *se) {
|
||||||
|
+ revolve_burst_penalty(se);
|
||||||
|
+ se->burst_penalty = se->prev_burst_penalty;
|
||||||
|
+ update_burst_score(se);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void restart_burst_rescale_deadline(struct sched_entity *se) {
|
||||||
|
+ s64 vscaled, wremain, vremain = se->deadline - se->vruntime;
|
||||||
|
+ struct task_struct *p = task_of(se);
|
||||||
|
+ u8 prev_prio = effective_prio(p);
|
||||||
|
+ restart_burst(se);
|
||||||
|
+ u8 new_prio = effective_prio(p);
|
||||||
|
+ if (prev_prio > new_prio) {
|
||||||
|
+ wremain = __unscale_slice(abs(vremain), prev_prio);
|
||||||
|
+ vscaled = __scale_slice(wremain, new_prio);
|
||||||
|
+ if (unlikely(vremain < 0))
|
||||||
|
+ vscaled = -vscaled;
|
||||||
|
+ se->deadline = se->vruntime + vscaled;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline bool task_is_bore_eligible(struct task_struct *p)
|
||||||
|
+{return p->sched_class == &fair_sched_class;}
|
||||||
|
+
|
||||||
|
+static void reset_task_weights_bore(void) {
|
||||||
|
+ struct task_struct *task;
|
||||||
|
+ struct rq *rq;
|
||||||
|
+ struct rq_flags rf;
|
||||||
|
+
|
||||||
|
+ write_lock_irq(&tasklist_lock);
|
||||||
|
+ for_each_process(task) {
|
||||||
|
+ if (!task_is_bore_eligible(task)) continue;
|
||||||
|
+ rq = task_rq(task);
|
||||||
|
+ rq_lock_irqsave(rq, &rf);
|
||||||
|
+ reweight_task_by_prio(task, effective_prio(task));
|
||||||
|
+ rq_unlock_irqrestore(rq, &rf);
|
||||||
|
+ }
|
||||||
|
+ write_unlock_irq(&tasklist_lock);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int sched_bore_update_handler(const struct ctl_table *table, int write,
|
||||||
|
+ void __user *buffer, size_t *lenp, loff_t *ppos) {
|
||||||
|
+ int ret = proc_dou8vec_minmax(table, write, buffer, lenp, ppos);
|
||||||
|
+ if (ret || !write)
|
||||||
|
+ return ret;
|
||||||
|
+
|
||||||
|
+ reset_task_weights_bore();
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static u32 count_child_tasks(struct task_struct *p) {
|
||||||
|
+ struct task_struct *child;
|
||||||
|
+ u32 cnt = 0;
|
||||||
|
+ list_for_each_entry(child, &p->children, sibling) {cnt++;}
|
||||||
|
+ return cnt;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline bool burst_cache_expired(struct sched_burst_cache *bc, u64 now)
|
||||||
|
+{return (s64)(bc->timestamp + sched_burst_cache_lifetime - now) < 0;}
|
||||||
|
+
|
||||||
|
+static void update_burst_cache(struct sched_burst_cache *bc,
|
||||||
|
+ struct task_struct *p, u32 cnt, u32 sum, u64 now) {
|
||||||
|
+ u8 avg = cnt ? sum / cnt : 0;
|
||||||
|
+ bc->score = max(avg, p->se.burst_penalty);
|
||||||
|
+ bc->count = cnt;
|
||||||
|
+ bc->timestamp = now;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void update_child_burst_direct(struct task_struct *p, u64 now) {
|
||||||
|
+ u32 cnt = 0, sum = 0;
|
||||||
|
+ struct task_struct *child;
|
||||||
|
+
|
||||||
|
+ list_for_each_entry(child, &p->children, sibling) {
|
||||||
|
+ if (!task_is_bore_eligible(child)) continue;
|
||||||
|
+ cnt++;
|
||||||
|
+ sum += child->se.burst_penalty;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ update_burst_cache(&p->se.child_burst, p, cnt, sum, now);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u8 inherit_burst_direct(struct task_struct *p, u64 now) {
|
||||||
|
+ struct task_struct *parent = p;
|
||||||
|
+ if (burst_cache_expired(&parent->se.child_burst, now))
|
||||||
|
+ update_child_burst_direct(parent, now);
|
||||||
|
+
|
||||||
|
+ return parent->se.child_burst.score;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void update_child_burst_topological(
|
||||||
|
+ struct task_struct *p, u64 now, u32 depth, u32 *acnt, u32 *asum) {
|
||||||
|
+ u32 cnt = 0, dcnt = 0, sum = 0;
|
||||||
|
+ struct task_struct *child, *dec;
|
||||||
|
+
|
||||||
|
+ list_for_each_entry(child, &p->children, sibling) {
|
||||||
|
+ dec = child;
|
||||||
|
+ while ((dcnt = count_child_tasks(dec)) == 1)
|
||||||
|
+ dec = list_first_entry(&dec->children, struct task_struct, sibling);
|
||||||
|
+
|
||||||
|
+ if (!dcnt || !depth) {
|
||||||
|
+ if (!task_is_bore_eligible(dec)) continue;
|
||||||
|
+ cnt++;
|
||||||
|
+ sum += dec->se.burst_penalty;
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ if (!burst_cache_expired(&dec->se.child_burst, now)) {
|
||||||
|
+ cnt += dec->se.child_burst.count;
|
||||||
|
+ sum += (u32)dec->se.child_burst.score * dec->se.child_burst.count;
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ update_child_burst_topological(dec, now, depth - 1, &cnt, &sum);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ update_burst_cache(&p->se.child_burst, p, cnt, sum, now);
|
||||||
|
+ *acnt += cnt;
|
||||||
|
+ *asum += sum;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u8 inherit_burst_topological(struct task_struct *p, u64 now) {
|
||||||
|
+ struct task_struct *anc = p;
|
||||||
|
+ u32 cnt = 0, sum = 0;
|
||||||
|
+
|
||||||
|
+ while (anc->real_parent != anc && count_child_tasks(anc) == 1)
|
||||||
|
+ anc = anc->real_parent;
|
||||||
|
+
|
||||||
|
+ if (burst_cache_expired(&anc->se.child_burst, now))
|
||||||
|
+ update_child_burst_topological(
|
||||||
|
+ anc, now, sched_burst_fork_atavistic - 1, &cnt, &sum);
|
||||||
|
+
|
||||||
|
+ return anc->se.child_burst.score;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void update_tg_burst(struct task_struct *p, u64 now) {
|
||||||
|
+ struct task_struct *task;
|
||||||
|
+ u32 cnt = 0, sum = 0;
|
||||||
|
+
|
||||||
|
+ for_each_thread(p, task) {
|
||||||
|
+ if (!task_is_bore_eligible(task)) continue;
|
||||||
|
+ cnt++;
|
||||||
|
+ sum += task->se.burst_penalty;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ update_burst_cache(&p->se.group_burst, p, cnt, sum, now);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u8 inherit_burst_tg(struct task_struct *p, u64 now) {
|
||||||
|
+ struct task_struct *parent = p->group_leader;
|
||||||
|
+ if (burst_cache_expired(&parent->se.group_burst, now))
|
||||||
|
+ update_tg_burst(parent, now);
|
||||||
|
+
|
||||||
|
+ return parent->se.group_burst.score;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void sched_clone_bore(
|
||||||
|
+ struct task_struct *p, struct task_struct *parent, u64 clone_flags) {
|
||||||
|
+ if (!task_is_bore_eligible(p)) return;
|
||||||
|
+
|
||||||
|
+ u64 now = ktime_get_ns();
|
||||||
|
+ read_lock(&tasklist_lock);
|
||||||
|
+ u8 penalty = (clone_flags & CLONE_THREAD) ?
|
||||||
|
+ inherit_burst_tg(parent, now) :
|
||||||
|
+ likely(sched_burst_fork_atavistic) ?
|
||||||
|
+ inherit_burst_topological(parent, now):
|
||||||
|
+ inherit_burst_direct(parent, now);
|
||||||
|
+ read_unlock(&tasklist_lock);
|
||||||
|
+
|
||||||
|
+ struct sched_entity *se = &p->se;
|
||||||
|
+ revolve_burst_penalty(se);
|
||||||
|
+ se->burst_penalty = se->prev_burst_penalty =
|
||||||
|
+ max(se->prev_burst_penalty, penalty);
|
||||||
|
+ se->child_burst.timestamp = 0;
|
||||||
|
+ se->group_burst.timestamp = 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void init_task_bore(struct task_struct *p) {
|
||||||
|
+ p->se.burst_time = 0;
|
||||||
|
+ p->se.prev_burst_penalty = 0;
|
||||||
|
+ p->se.curr_burst_penalty = 0;
|
||||||
|
+ p->se.burst_penalty = 0;
|
||||||
|
+ p->se.burst_score = 0;
|
||||||
|
+ memset(&p->se.child_burst, 0, sizeof(struct sched_burst_cache));
|
||||||
|
+ memset(&p->se.group_burst, 0, sizeof(struct sched_burst_cache));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_SYSCTL
|
||||||
|
+static struct ctl_table sched_bore_sysctls[] = {
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_bore",
|
||||||
|
+ .data = &sched_bore,
|
||||||
|
+ .maxlen = sizeof(u8),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = sched_bore_update_handler,
|
||||||
|
+ .extra1 = SYSCTL_ZERO,
|
||||||
|
+ .extra2 = SYSCTL_ONE,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_burst_exclude_kthreads",
|
||||||
|
+ .data = &sched_burst_exclude_kthreads,
|
||||||
|
+ .maxlen = sizeof(u8),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = proc_dou8vec_minmax,
|
||||||
|
+ .extra1 = SYSCTL_ZERO,
|
||||||
|
+ .extra2 = SYSCTL_ONE,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_burst_smoothness_long",
|
||||||
|
+ .data = &sched_burst_smoothness_long,
|
||||||
|
+ .maxlen = sizeof(u8),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = proc_dou8vec_minmax,
|
||||||
|
+ .extra1 = SYSCTL_ZERO,
|
||||||
|
+ .extra2 = SYSCTL_ONE,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_burst_smoothness_short",
|
||||||
|
+ .data = &sched_burst_smoothness_short,
|
||||||
|
+ .maxlen = sizeof(u8),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = proc_dou8vec_minmax,
|
||||||
|
+ .extra1 = SYSCTL_ZERO,
|
||||||
|
+ .extra2 = SYSCTL_ONE,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_burst_fork_atavistic",
|
||||||
|
+ .data = &sched_burst_fork_atavistic,
|
||||||
|
+ .maxlen = sizeof(u8),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = proc_dou8vec_minmax,
|
||||||
|
+ .extra1 = SYSCTL_ZERO,
|
||||||
|
+ .extra2 = SYSCTL_THREE,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_burst_parity_threshold",
|
||||||
|
+ .data = &sched_burst_parity_threshold,
|
||||||
|
+ .maxlen = sizeof(u8),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = proc_dou8vec_minmax,
|
||||||
|
+ .extra1 = SYSCTL_ZERO,
|
||||||
|
+ .extra2 = &maxval_u8,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_burst_penalty_offset",
|
||||||
|
+ .data = &sched_burst_penalty_offset,
|
||||||
|
+ .maxlen = sizeof(u8),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = proc_dou8vec_minmax,
|
||||||
|
+ .extra1 = SYSCTL_ZERO,
|
||||||
|
+ .extra2 = &sixty_four,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_burst_penalty_scale",
|
||||||
|
+ .data = &sched_burst_penalty_scale,
|
||||||
|
+ .maxlen = sizeof(uint),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = proc_douintvec_minmax,
|
||||||
|
+ .extra1 = SYSCTL_ZERO,
|
||||||
|
+ .extra2 = &maxval_12_bits,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_burst_cache_lifetime",
|
||||||
|
+ .data = &sched_burst_cache_lifetime,
|
||||||
|
+ .maxlen = sizeof(uint),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = proc_douintvec,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .procname = "sched_deadline_boost_mask",
|
||||||
|
+ .data = &sched_deadline_boost_mask,
|
||||||
|
+ .maxlen = sizeof(uint),
|
||||||
|
+ .mode = 0644,
|
||||||
|
+ .proc_handler = proc_douintvec,
|
||||||
|
+ },
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init sched_bore_sysctl_init(void) {
|
||||||
|
+ register_sysctl_init("kernel", sched_bore_sysctls);
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+late_initcall(sched_bore_sysctl_init);
|
||||||
|
+#endif // CONFIG_SYSCTL
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
|
||||||
|
index 1af59cf714..9ad2c18bac 100644
|
||||||
|
--- a/kernel/sched/core.c
|
||||||
|
+++ b/kernel/sched/core.c
|
||||||
|
@@ -97,6 +97,8 @@
|
||||||
|
#include "../../io_uring/io-wq.h"
|
||||||
|
#include "../smpboot.h"
|
||||||
|
|
||||||
|
+#include <linux/sched/bore.h>
|
||||||
|
+
|
||||||
|
EXPORT_TRACEPOINT_SYMBOL_GPL(ipi_send_cpu);
|
||||||
|
EXPORT_TRACEPOINT_SYMBOL_GPL(ipi_send_cpumask);
|
||||||
|
|
||||||
|
@@ -8204,6 +8206,11 @@ void __init sched_init(void)
|
||||||
|
BUG_ON(&dl_sched_class != &stop_sched_class + 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ printk(KERN_INFO "BORE (Burst-Oriented Response Enhancer) CPU Scheduler modification 5.7.3 by Masahito Suzuki");
|
||||||
|
+ init_task_bore(&init_task);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
+
|
||||||
|
wait_bit_init();
|
||||||
|
|
||||||
|
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||||
|
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
|
||||||
|
index c1eb9a1afd..02d2895c46 100644
|
||||||
|
--- a/kernel/sched/debug.c
|
||||||
|
+++ b/kernel/sched/debug.c
|
||||||
|
@@ -167,7 +167,53 @@ static const struct file_operations sched_feat_fops = {
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+#define DEFINE_SYSCTL_SCHED_FUNC(name, update_func) \
|
||||||
|
+static ssize_t sched_##name##_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) \
|
||||||
|
+{ \
|
||||||
|
+ char buf[16]; \
|
||||||
|
+ unsigned int value; \
|
||||||
|
+\
|
||||||
|
+ if (cnt > 15) \
|
||||||
|
+ cnt = 15; \
|
||||||
|
+\
|
||||||
|
+ if (copy_from_user(&buf, ubuf, cnt)) \
|
||||||
|
+ return -EFAULT; \
|
||||||
|
+ buf[cnt] = '\0'; \
|
||||||
|
+\
|
||||||
|
+ if (kstrtouint(buf, 10, &value)) \
|
||||||
|
+ return -EINVAL; \
|
||||||
|
+\
|
||||||
|
+ sysctl_sched_##name = value; \
|
||||||
|
+ sched_update_##update_func(); \
|
||||||
|
+\
|
||||||
|
+ *ppos += cnt; \
|
||||||
|
+ return cnt; \
|
||||||
|
+} \
|
||||||
|
+\
|
||||||
|
+static int sched_##name##_show(struct seq_file *m, void *v) \
|
||||||
|
+{ \
|
||||||
|
+ seq_printf(m, "%d\n", sysctl_sched_##name); \
|
||||||
|
+ return 0; \
|
||||||
|
+} \
|
||||||
|
+\
|
||||||
|
+static int sched_##name##_open(struct inode *inode, struct file *filp) \
|
||||||
|
+{ \
|
||||||
|
+ return single_open(filp, sched_##name##_show, NULL); \
|
||||||
|
+} \
|
||||||
|
+\
|
||||||
|
+static const struct file_operations sched_##name##_fops = { \
|
||||||
|
+ .open = sched_##name##_open, \
|
||||||
|
+ .write = sched_##name##_write, \
|
||||||
|
+ .read = seq_read, \
|
||||||
|
+ .llseek = seq_lseek, \
|
||||||
|
+ .release = single_release, \
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+DEFINE_SYSCTL_SCHED_FUNC(min_base_slice, min_base_slice)
|
||||||
|
|
||||||
|
+#undef DEFINE_SYSCTL_SCHED_FUNC
|
||||||
|
+#else // !CONFIG_SCHED_BORE
|
||||||
|
static ssize_t sched_scaling_write(struct file *filp, const char __user *ubuf,
|
||||||
|
size_t cnt, loff_t *ppos)
|
||||||
|
{
|
||||||
|
@@ -213,7 +259,7 @@ static const struct file_operations sched_scaling_fops = {
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release,
|
||||||
|
};
|
||||||
|
-
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
#endif /* SMP */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PREEMPT_DYNAMIC
|
||||||
|
@@ -347,13 +393,20 @@ static __init int sched_init_debug(void)
|
||||||
|
debugfs_create_file("preempt", 0644, debugfs_sched, NULL, &sched_dynamic_fops);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ debugfs_create_file("min_base_slice_ns", 0644, debugfs_sched, NULL, &sched_min_base_slice_fops);
|
||||||
|
+ debugfs_create_u32("base_slice_ns", 0444, debugfs_sched, &sysctl_sched_base_slice);
|
||||||
|
+#else // !CONFIG_SCHED_BORE
|
||||||
|
debugfs_create_u32("base_slice_ns", 0644, debugfs_sched, &sysctl_sched_base_slice);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
debugfs_create_u32("latency_warn_ms", 0644, debugfs_sched, &sysctl_resched_latency_warn_ms);
|
||||||
|
debugfs_create_u32("latency_warn_once", 0644, debugfs_sched, &sysctl_resched_latency_warn_once);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
+#if !defined(CONFIG_SCHED_BORE)
|
||||||
|
debugfs_create_file("tunable_scaling", 0644, debugfs_sched, NULL, &sched_scaling_fops);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
debugfs_create_u32("migration_cost_ns", 0644, debugfs_sched, &sysctl_sched_migration_cost);
|
||||||
|
debugfs_create_u32("nr_migrate", 0644, debugfs_sched, &sysctl_sched_nr_migrate);
|
||||||
|
|
||||||
|
@@ -596,6 +649,9 @@ print_task(struct seq_file *m, struct rq *rq, struct task_struct *p)
|
||||||
|
SPLIT_NS(schedstat_val_or_zero(p->stats.sum_sleep_runtime)),
|
||||||
|
SPLIT_NS(schedstat_val_or_zero(p->stats.sum_block_runtime)));
|
||||||
|
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ SEQ_printf(m, " %2d", p->se.burst_score);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
#ifdef CONFIG_NUMA_BALANCING
|
||||||
|
SEQ_printf(m, " %d %d", task_node(p), task_numa_group_id(p));
|
||||||
|
#endif
|
||||||
|
@@ -1069,6 +1125,9 @@ void proc_sched_show_task(struct task_struct *p, struct pid_namespace *ns,
|
||||||
|
|
||||||
|
P(se.load.weight);
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ P(se.burst_score);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
P(se.avg.load_sum);
|
||||||
|
P(se.avg.runnable_sum);
|
||||||
|
P(se.avg.util_sum);
|
||||||
|
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
|
||||||
|
index 1d2cbdb162..8666269f73 100644
|
||||||
|
--- a/kernel/sched/fair.c
|
||||||
|
+++ b/kernel/sched/fair.c
|
||||||
|
@@ -55,6 +55,8 @@
|
||||||
|
#include "stats.h"
|
||||||
|
#include "autogroup.h"
|
||||||
|
|
||||||
|
+#include <linux/sched/bore.h>
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* The initial- and re-scaling of tunables is configurable
|
||||||
|
*
|
||||||
|
@@ -64,17 +66,30 @@
|
||||||
|
* SCHED_TUNABLESCALING_LOG - scaled logarithmically, *1+ilog(ncpus)
|
||||||
|
* SCHED_TUNABLESCALING_LINEAR - scaled linear, *ncpus
|
||||||
|
*
|
||||||
|
- * (default SCHED_TUNABLESCALING_LOG = *(1+ilog(ncpus))
|
||||||
|
+ * BORE : default SCHED_TUNABLESCALING_NONE = *1 constant
|
||||||
|
+ * EEVDF: default SCHED_TUNABLESCALING_LOG = *(1+ilog(ncpus))
|
||||||
|
*/
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+unsigned int sysctl_sched_tunable_scaling = SCHED_TUNABLESCALING_NONE;
|
||||||
|
+#else // !CONFIG_SCHED_BORE
|
||||||
|
unsigned int sysctl_sched_tunable_scaling = SCHED_TUNABLESCALING_LOG;
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Minimal preemption granularity for CPU-bound tasks:
|
||||||
|
*
|
||||||
|
- * (default: 0.75 msec * (1 + ilog(ncpus)), units: nanoseconds)
|
||||||
|
+ * BORE : base_slice = minimum multiple of nsecs_per_tick >= min_base_slice
|
||||||
|
+ * (default min_base_slice = 2000000 constant, units: nanoseconds)
|
||||||
|
+ * EEVDF: default 0.75 msec * (1 + ilog(ncpus)), units: nanoseconds
|
||||||
|
*/
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+static const unsigned int nsecs_per_tick = 1000000000ULL / HZ;
|
||||||
|
+unsigned int sysctl_sched_min_base_slice = CONFIG_MIN_BASE_SLICE_NS;
|
||||||
|
+__read_mostly uint sysctl_sched_base_slice = nsecs_per_tick;
|
||||||
|
+#else // !CONFIG_SCHED_BORE
|
||||||
|
unsigned int sysctl_sched_base_slice = 750000ULL;
|
||||||
|
static unsigned int normalized_sysctl_sched_base_slice = 750000ULL;
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
const_debug unsigned int sysctl_sched_migration_cost = 500000UL;
|
||||||
|
|
||||||
|
@@ -188,6 +203,13 @@ static inline void update_load_set(struct load_weight *lw, unsigned long w)
|
||||||
|
*
|
||||||
|
* This idea comes from the SD scheduler of Con Kolivas:
|
||||||
|
*/
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+static void update_sysctl(void) {
|
||||||
|
+ sysctl_sched_base_slice = nsecs_per_tick *
|
||||||
|
+ max(1UL, DIV_ROUND_UP(sysctl_sched_min_base_slice, nsecs_per_tick));
|
||||||
|
+}
|
||||||
|
+void sched_update_min_base_slice(void) { update_sysctl(); }
|
||||||
|
+#else // !CONFIG_SCHED_BORE
|
||||||
|
static unsigned int get_update_sysctl_factor(void)
|
||||||
|
{
|
||||||
|
unsigned int cpus = min_t(unsigned int, num_online_cpus(), 8);
|
||||||
|
@@ -218,6 +240,7 @@ static void update_sysctl(void)
|
||||||
|
SET_SYSCTL(sched_base_slice);
|
||||||
|
#undef SET_SYSCTL
|
||||||
|
}
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
void __init sched_init_granularity(void)
|
||||||
|
{
|
||||||
|
@@ -695,6 +718,9 @@ static s64 entity_lag(u64 avruntime, struct sched_entity *se)
|
||||||
|
|
||||||
|
vlag = avruntime - se->vruntime;
|
||||||
|
limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se);
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ limit >>= !!sched_bore;
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
return clamp(vlag, -limit, limit);
|
||||||
|
}
|
||||||
|
@@ -896,6 +922,10 @@ static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq)
|
||||||
|
* until it gets a new slice. See the HACK in set_next_entity().
|
||||||
|
*/
|
||||||
|
if (sched_feat(RUN_TO_PARITY) && curr && curr->vlag == curr->deadline)
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ if (!(likely(sched_bore) && likely(sched_burst_parity_threshold) &&
|
||||||
|
+ sched_burst_parity_threshold < cfs_rq->nr_running))
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
return curr;
|
||||||
|
|
||||||
|
/* Pick the leftmost entity if it's eligible */
|
||||||
|
@@ -954,6 +984,7 @@ struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq)
|
||||||
|
* Scheduling class statistics methods:
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
+#if !defined(CONFIG_SCHED_BORE)
|
||||||
|
int sched_update_scaling(void)
|
||||||
|
{
|
||||||
|
unsigned int factor = get_update_sysctl_factor();
|
||||||
|
@@ -965,6 +996,7 @@ int sched_update_scaling(void)
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@@ -1165,6 +1197,10 @@ static void update_curr(struct cfs_rq *cfs_rq)
|
||||||
|
if (unlikely(delta_exec <= 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ curr->burst_time += delta_exec;
|
||||||
|
+ update_burst_penalty(curr);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
curr->vruntime += calc_delta_fair(delta_exec, curr);
|
||||||
|
update_deadline(cfs_rq, curr);
|
||||||
|
update_min_vruntime(cfs_rq);
|
||||||
|
@@ -3791,7 +3827,7 @@ static void reweight_eevdf(struct sched_entity *se, u64 avruntime,
|
||||||
|
se->deadline = avruntime + vslice;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se,
|
||||||
|
+void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se,
|
||||||
|
unsigned long weight)
|
||||||
|
{
|
||||||
|
bool curr = cfs_rq->curr == se;
|
||||||
|
@@ -5198,6 +5234,9 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
|
||||||
|
*
|
||||||
|
* EEVDF: placement strategy #1 / #2
|
||||||
|
*/
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ if (se->vlag)
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
if (sched_feat(PLACE_LAG) && cfs_rq->nr_running) {
|
||||||
|
struct sched_entity *curr = cfs_rq->curr;
|
||||||
|
unsigned long load;
|
||||||
|
@@ -5268,6 +5307,16 @@ place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
|
||||||
|
|
||||||
|
se->vruntime = vruntime - lag;
|
||||||
|
|
||||||
|
+ if (sched_feat(PLACE_REL_DEADLINE) && se->rel_deadline) {
|
||||||
|
+ se->deadline += se->vruntime;
|
||||||
|
+ se->rel_deadline = 0;
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ else if (likely(sched_bore))
|
||||||
|
+ vslice >>= !!(flags & sched_deadline_boost_mask);
|
||||||
|
+ else
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
/*
|
||||||
|
* When joining the competition; the existing tasks will be,
|
||||||
|
* on average, halfway through their slice, as such start tasks
|
||||||
|
@@ -5377,6 +5426,7 @@ static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq);
|
||||||
|
static void
|
||||||
|
dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
|
||||||
|
{
|
||||||
|
+ bool sleep = flags & DEQUEUE_SLEEP;
|
||||||
|
int action = UPDATE_TG;
|
||||||
|
|
||||||
|
if (entity_is_task(se) && task_on_rq_migrating(task_of(se)))
|
||||||
|
@@ -5404,6 +5454,11 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags)
|
||||||
|
clear_buddies(cfs_rq, se);
|
||||||
|
|
||||||
|
update_entity_lag(cfs_rq, se);
|
||||||
|
+ if (sched_feat(PLACE_REL_DEADLINE) && !sleep) {
|
||||||
|
+ se->deadline -= se->vruntime;
|
||||||
|
+ se->rel_deadline = 1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (se != cfs_rq->curr)
|
||||||
|
__dequeue_entity(cfs_rq, se);
|
||||||
|
se->on_rq = 0;
|
||||||
|
@@ -6855,6 +6910,14 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)
|
||||||
|
bool was_sched_idle = sched_idle_rq(rq);
|
||||||
|
|
||||||
|
util_est_dequeue(&rq->cfs, p);
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ if (task_sleep) {
|
||||||
|
+ cfs_rq = cfs_rq_of(se);
|
||||||
|
+ if (cfs_rq->curr == se)
|
||||||
|
+ update_curr(cfs_rq);
|
||||||
|
+ restart_burst(se);
|
||||||
|
+ }
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
for_each_sched_entity(se) {
|
||||||
|
cfs_rq = cfs_rq_of(se);
|
||||||
|
@@ -8637,16 +8700,25 @@ static void yield_task_fair(struct rq *rq)
|
||||||
|
/*
|
||||||
|
* Are we the only task in the tree?
|
||||||
|
*/
|
||||||
|
+#if !defined(CONFIG_SCHED_BORE)
|
||||||
|
if (unlikely(rq->nr_running == 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
clear_buddies(cfs_rq, se);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
update_rq_clock(rq);
|
||||||
|
/*
|
||||||
|
* Update run-time statistics of the 'current'.
|
||||||
|
*/
|
||||||
|
update_curr(cfs_rq);
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ restart_burst_rescale_deadline(se);
|
||||||
|
+ if (unlikely(rq->nr_running == 1))
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ clear_buddies(cfs_rq, se);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
/*
|
||||||
|
* Tell update_rq_clock() that we've just updated,
|
||||||
|
* so we don't do microscopic update in schedule()
|
||||||
|
@@ -12722,6 +12794,9 @@ static void task_fork_fair(struct task_struct *p)
|
||||||
|
curr = cfs_rq->curr;
|
||||||
|
if (curr)
|
||||||
|
update_curr(cfs_rq);
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ update_burst_score(se);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
place_entity(cfs_rq, se, ENQUEUE_INITIAL);
|
||||||
|
rq_unlock(rq, &rf);
|
||||||
|
}
|
||||||
|
@@ -12834,6 +12909,10 @@ static void attach_task_cfs_rq(struct task_struct *p)
|
||||||
|
|
||||||
|
static void switched_from_fair(struct rq *rq, struct task_struct *p)
|
||||||
|
{
|
||||||
|
+ p->se.rel_deadline = 0;
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+ init_task_bore(p);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
detach_task_cfs_rq(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/kernel/sched/features.h b/kernel/sched/features.h
|
||||||
|
index 143f55df89..e97b7b68bd 100644
|
||||||
|
--- a/kernel/sched/features.h
|
||||||
|
+++ b/kernel/sched/features.h
|
||||||
|
@@ -6,6 +6,10 @@
|
||||||
|
*/
|
||||||
|
SCHED_FEAT(PLACE_LAG, true)
|
||||||
|
SCHED_FEAT(PLACE_DEADLINE_INITIAL, true)
|
||||||
|
+/*
|
||||||
|
+ * Preserve relative virtual deadline on 'migration'.
|
||||||
|
+ */
|
||||||
|
+SCHED_FEAT(PLACE_REL_DEADLINE, true)
|
||||||
|
SCHED_FEAT(RUN_TO_PARITY, true)
|
||||||
|
|
||||||
|
/*
|
||||||
|
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
|
||||||
|
index 4c36cc6803..2c7d3eb3b1 100644
|
||||||
|
--- a/kernel/sched/sched.h
|
||||||
|
+++ b/kernel/sched/sched.h
|
||||||
|
@@ -1984,7 +1984,11 @@ static inline void update_sched_domain_debugfs(void) { }
|
||||||
|
static inline void dirty_sched_domain_sysctl(int cpu) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+extern void sched_update_min_base_slice(void);
|
||||||
|
+#else // !CONFIG_SCHED_BORE
|
||||||
|
extern int sched_update_scaling(void);
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
static inline const struct cpumask *task_user_cpus(struct task_struct *p)
|
||||||
|
{
|
||||||
|
@@ -2600,7 +2604,12 @@ extern void wakeup_preempt(struct rq *rq, struct task_struct *p, int flags);
|
||||||
|
extern const_debug unsigned int sysctl_sched_nr_migrate;
|
||||||
|
extern const_debug unsigned int sysctl_sched_migration_cost;
|
||||||
|
|
||||||
|
+#ifdef CONFIG_SCHED_BORE
|
||||||
|
+extern unsigned int sysctl_sched_min_base_slice;
|
||||||
|
+extern __read_mostly uint sysctl_sched_base_slice;
|
||||||
|
+#else // !CONFIG_SCHED_BORE
|
||||||
|
extern unsigned int sysctl_sched_base_slice;
|
||||||
|
+#endif // CONFIG_SCHED_BORE
|
||||||
|
|
||||||
|
#ifdef CONFIG_SCHED_DEBUG
|
||||||
|
extern int sysctl_resched_latency_warn_ms;
|
||||||
|
--
|
||||||
|
2.34.1
|
||||||
|
|
||||||
Executable
+106
@@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# build.sh - build vmlinuz-linux-very-hardened (pornOS custom kernel)
|
||||||
|
#
|
||||||
|
# NOTE: pornOS is built without a compiler (see build/build-pornos.py), so this
|
||||||
|
# script runs on a REAL build host with a full Linux toolchain. It produces the
|
||||||
|
# kernel image our ISO/installer flashes into /boot/vmlinuz-linux-very-hardened.
|
||||||
|
#
|
||||||
|
# Requirements (host): git curl gcc make flex bison bc perl openssl
|
||||||
|
# libelf-dev dwarves (pahole) zstd
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/build.sh # default kernel version
|
||||||
|
# ./scripts/build.sh --version 6.18 # explicit kernel line
|
||||||
|
# KERNEL_VERSION=6.18 ./scripts/build.sh
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
OUT="$HERE/output"
|
||||||
|
SRC="$HERE/.work/kernel-src"
|
||||||
|
VERSION="${KERNEL_VERSION:-6.18}"
|
||||||
|
NAME="linux-very-hardened"
|
||||||
|
CONFIG="$HERE/config/linux-very-hardened.config"
|
||||||
|
PATCH_BORE="$HERE/patches/0001-bore.patch"
|
||||||
|
|
||||||
|
# mirror-ish: use the running kernel's version if os-release is present, but
|
||||||
|
# default stays 6.18 to match our BORE adaptation
|
||||||
|
if [ -n "${PREFER_RUNNING_KERNEL:-}" ] && [ -r /proc/sys/kernel/osrelease ]; then
|
||||||
|
VERSION="$(cat /proc/sys/kernel/osrelease)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fatal() { printf 'build.sh: error: %s\n' "$*" >&2; exit 1; }
|
||||||
|
need() { command -v "$1" >/dev/null 2>&1 || fatal "missing required tool: $1"; }
|
||||||
|
|
||||||
|
for t in git curl gcc make flex bison bc perl pahole zstd; do
|
||||||
|
need "$t"
|
||||||
|
done
|
||||||
|
|
||||||
|
mkdir -p "$OUT" "$(dirname "$SRC")"
|
||||||
|
|
||||||
|
printf '== linux-very-hardened ==\n'
|
||||||
|
printf 'kernel : %s\n' "$VERSION"
|
||||||
|
printf 'target : %s\n' "$OUT/vmlinuz-$NAME"
|
||||||
|
printf 'config : %s\n' "$CONFIG"
|
||||||
|
printf 'bore patch: %s\n\n' "$PATCH_BORE"
|
||||||
|
|
||||||
|
# 1. fetch mainline kernel source
|
||||||
|
if [ ! -d "$SRC/.git" ]; then
|
||||||
|
printf '>> cloning kernel %s\n' "$VERSION"
|
||||||
|
git clone --depth 1 --branch "v$VERSION" \
|
||||||
|
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git "$SRC"
|
||||||
|
else
|
||||||
|
printf '>> kernel source already present (%s)\n' "$SRC"
|
||||||
|
fi
|
||||||
|
cd "$SRC"
|
||||||
|
|
||||||
|
# 2. apply the BORE scheduler patch (adapted for our kernel line)
|
||||||
|
if ! git apply --check "$PATCH_BORE" >/dev/null 2>&1; then
|
||||||
|
printf '>> BORE patch does not apply cleanly on v%s; trying --3way\n' "$VERSION"
|
||||||
|
git apply --3way "$PATCH_BORE" || fatal "failed to apply BORE patch on v$VERSION"
|
||||||
|
else
|
||||||
|
printf '>> applying BORE patch\n'
|
||||||
|
git apply "$PATCH_BORE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. seed our hardened+BORE config
|
||||||
|
printf '>> seeding %s\n' "$CONFIG"
|
||||||
|
cp "$CONFIG" .config
|
||||||
|
|
||||||
|
# 4. olddefconfig to fill the gaps against this exact kernel
|
||||||
|
make olddefconfig >/dev/null
|
||||||
|
|
||||||
|
# 5. build the image
|
||||||
|
printf '>> building (this takes a while)\n'
|
||||||
|
make -j"$(nproc)" bzImage
|
||||||
|
make -j"$(nproc)" modules >/dev/null
|
||||||
|
|
||||||
|
# 6. stage outputs
|
||||||
|
install -Dm644 arch/x86/boot/bzImage "$OUT/vmlinuz-$NAME"
|
||||||
|
install -Dm644 System.map "$OUT/System.map-$NAME"
|
||||||
|
install -Dm644 .config "$OUT/config-$NAME"
|
||||||
|
install -Dm644 vmlinuz "$OUT/vmlinuz-$NAME" 2>/dev/null || true
|
||||||
|
cp -a "$CONFIG" "$OUT/config-seed-$NAME"
|
||||||
|
|
||||||
|
printf '\n== done ==\n'
|
||||||
|
printf ' vmlinuz : %s\n' "$OUT/vmlinuz-$NAME"
|
||||||
|
printf ' systemmap : %s\n' "$OUT/System.map-$NAME"
|
||||||
|
printf ' config : %s\n' "$OUT/config-$NAME"
|
||||||
|
|
||||||
|
# 7. confirm BORE + cumfs + hardening survived in the final .config
|
||||||
|
conf() { grep -q "^$1=" "$OUT/config-$NAME" && echo " [ok] $1" || echo " [MISS ] $1"; }
|
||||||
|
printf '\n== feature check ==\n'
|
||||||
|
conf CONFIG_SCHED_BORE
|
||||||
|
conf CONFIG_CUM_FS
|
||||||
|
conf CONFIG_STATIC_USERMODEHELPER_PATH
|
||||||
|
conf CONFIG_SLAB_FREELIST_HARDENED
|
||||||
|
conf CONFIG_HARDENED_USERCOPY
|
||||||
|
conf CONFIG_FORTIFY_SOURCE
|
||||||
|
conf CONFIG_STACKPROTECTOR_STRONG
|
||||||
|
conf CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT
|
||||||
|
conf CONFIG_INIT_ON_ALLOC_DEFAULT_ON
|
||||||
|
conf CONFIG_INIT_ON_FREE_DEFAULT_ON
|
||||||
|
conf CONFIG_DEFAULT_INIT
|
||||||
|
|
||||||
|
printf '\n BORE boot line: porn-init shows "BORE (Burst-Oriented Response Enhancer)"\n'
|
||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# cook.sh - wrapper around build.sh that also packs the kernel into the .pornpkg
|
||||||
|
# and emits the exact vmlinuz name linux-very-hardened expects.
|
||||||
|
#
|
||||||
|
# Mileage: mostly a convenience so the ReaDME's "genuine ass" claim has one
|
||||||
|
# blessed entry point.
|
||||||
|
#
|
||||||
|
# Usage: ./scripts/cook.sh [kernel-version]
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
VER="${1:-6.18}"
|
||||||
|
"$HERE/scripts/build.sh" --version "$VER"
|
||||||
|
"$HERE/scripts/pkg-kernel.sh"
|
||||||
Executable
+32
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# pkg-kernel.sh - pack a built kernel into a .pornpkg (for the porn package
|
||||||
|
# manager) plus a ready-to-flash layout for the cum root.
|
||||||
|
#
|
||||||
|
# Usage: ./scripts/pkg-kernel.sh [output-dir]
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
OUT="${1:-$HERE/output}"
|
||||||
|
NAME="linux-very-hardened"
|
||||||
|
|
||||||
|
VMLINUZ="$OUT/vmlinuz-$NAME"
|
||||||
|
[ -r "$VMLINUZ" ] || { echo "error: $VMLINUZ not found (run build.sh first)" >&2; exit 1; }
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d)"
|
||||||
|
|
||||||
|
# layout mirrors what the installer flashes into /boot + /lib/modules
|
||||||
|
mkdir -p "$STAGE/boot" "$STAGE/lib/modules"
|
||||||
|
cp -a "$VMLINUZ" "$STAGE/boot/vmlinuz-$NAME"
|
||||||
|
[ -r "$OUT/System.map-$NAME" ] && cp -a "$OUT/System.map-$NAME" "$STAGE/boot/"
|
||||||
|
[ -d "$OUT/modules" ] && cp -a "$OUT/modules/." "$STAGE/lib/modules/"
|
||||||
|
[ -r "$OUT/config-$NAME" ] && cp -a "$OUT/config-$NAME" "$STAGE/boot/"
|
||||||
|
|
||||||
|
# .pornpkg is a tarball (matches what pornpkg extracts)
|
||||||
|
TARBALL="$OUT/$NAME.pornpkg"
|
||||||
|
tar -C "$STAGE" -czf "$TARBALL" boot lib
|
||||||
|
rm -rf "$STAGE"
|
||||||
|
|
||||||
|
printf 'packed: %s (%s bytes)\n' "$TARBALL" "$(stat -c %s "$TARBALL")"
|
||||||
|
printf ' feed to pornpkg: pornpkg install linux-very-hardened\n'
|
||||||
Executable
+52
@@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# misery-daemon - pornOS init script (runs under porn-init, NOT systemd)
|
||||||
|
#
|
||||||
|
# pornOS has no systemd. Its root init (porn-init) launches services from
|
||||||
|
# /etc/init.d. This script plays "Misery" by Pupsies every 20 minutes so you
|
||||||
|
# truly feel the weight of the distro.
|
||||||
|
#
|
||||||
|
# Usage: /etc/init.d/misery-daemon {start|stop|restart|status}
|
||||||
|
#
|
||||||
|
|
||||||
|
DAEMON=/usr/bin/misery-daemon
|
||||||
|
PIDFILE=/run/misery-daemon.pid
|
||||||
|
|
||||||
|
start() {
|
||||||
|
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
||||||
|
echo " misery is already playing."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo " starting misery daemon..."
|
||||||
|
"$DAEMON" &
|
||||||
|
echo $! > "$PIDFILE"
|
||||||
|
echo " misery enabled."
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if [ -f "$PIDFILE" ]; then
|
||||||
|
kill "$(cat "$PIDFILE")" 2>/dev/null
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
echo " misery stopped. you almost feel at peace."
|
||||||
|
else
|
||||||
|
echo " misery not running."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restart() { stop; start; }
|
||||||
|
|
||||||
|
status() {
|
||||||
|
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
||||||
|
echo " misery daemon running (pid $(cat "$PIDFILE"))."
|
||||||
|
else
|
||||||
|
echo " misery daemon not running."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-start}" in
|
||||||
|
start) start ;;
|
||||||
|
stop) stop ;;
|
||||||
|
restart) restart ;;
|
||||||
|
status) status ;;
|
||||||
|
*) echo "usage: $0 {start|stop|restart|status}" ;;
|
||||||
|
esac
|
||||||
Executable
+52
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# misery-daemon - Plays "Misery" by Pupsies every 20 minutes
|
||||||
|
# Fulfills requirement #8 of pornOS
|
||||||
|
|
||||||
|
SONG_URL="https://example.com/pupsies-misery.mp3"
|
||||||
|
INTERVAL=1200 # 20 minutes in seconds
|
||||||
|
SONG_FILE="/var/porn/media/misery.mp3"
|
||||||
|
LOG="/var/log/porn/misery.log"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
play_misery() {
|
||||||
|
log "IT'S TIME FOR MISERY. PLAYING MISERY BY PUPSIES."
|
||||||
|
if [ -f "$SONG_FILE" ]; then
|
||||||
|
# try various media players
|
||||||
|
if command -v mpv >/dev/null 2>&1; then
|
||||||
|
mpv --no-video --really-quiet "$SONG_FILE" 2>/dev/null
|
||||||
|
elif command -v mpg123 >/dev/null 2>&1; then
|
||||||
|
mpg123 -q "$SONG_FILE" 2>/dev/null
|
||||||
|
elif command -v ffplay >/dev/null 2>&1; then
|
||||||
|
ffplay -nodisp -autoexit -loglevel quiet "$SONG_FILE" 2>/dev/null
|
||||||
|
elif command -v aplay >/dev/null 2>&1; then
|
||||||
|
aplay -q "$SONG_FILE" 2>/dev/null
|
||||||
|
else
|
||||||
|
# no audio player found, just log it miserably
|
||||||
|
log "FATAL: no audio player found. misery cannot be played."
|
||||||
|
log "No wait, that's the real misery."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "misery.mp3 not found. downloawding..."
|
||||||
|
curl -sL "$SONG_URL" -o "$SONG_FILE" 2>/dev/null || \
|
||||||
|
log "FATAL: could not download misery. the system is too miserable to be miserable."
|
||||||
|
fi
|
||||||
|
log "misery complete. see you in 20 minutes."
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p /var/porn/media
|
||||||
|
mkdir -p /var/log/porn
|
||||||
|
|
||||||
|
log "misery-daemon started. misery begins now."
|
||||||
|
log "mm...miserable. fuck."
|
||||||
|
|
||||||
|
# play once immediately at boot for maximum misery
|
||||||
|
play_misery
|
||||||
|
|
||||||
|
# main loop
|
||||||
|
while true; do
|
||||||
|
sleep "$INTERVAL"
|
||||||
|
play_misery
|
||||||
|
done
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=pornOS Misery Daemon - plays Misery by Pupsies every 20 min
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/misery-daemon
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
pornGUI - Opens the browser with a Pornhub page (ctrl+p in GUI mode)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
PH_URL = "https://www.pornhub.com"
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print(" pornGUI v6.9 - opening your browser...")
|
||||||
|
print(" enjoy your stay.")
|
||||||
|
webbrowser.open(PH_URL)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Executable
+383
@@ -0,0 +1,383 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
porncli - CLI tool for pornOS (activated by ctrl+p)
|
||||||
|
=====================================================
|
||||||
|
Your gateway to misery. Type commands, get fucked.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
BANNER = r"""
|
||||||
|
╔═══════════════════════════════════════════════╗
|
||||||
|
║ porncli v6.9 ║
|
||||||
|
║ your gateway to misery ║
|
||||||
|
║ ║
|
||||||
|
║ type 'help' for commands ║
|
||||||
|
║ type 'exit' to escape (you can't) ║
|
||||||
|
╚═══════════════════════════════════════════════╝
|
||||||
|
"""
|
||||||
|
|
||||||
|
HELP_TEXT = """
|
||||||
|
Available commands:
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
pornpkg <cmd> - Package manager
|
||||||
|
xvideos <cmd> - Get root access
|
||||||
|
proton - Show system info
|
||||||
|
protonvpn - Show network info
|
||||||
|
nsfw - List files (like ls)
|
||||||
|
nakedpics - List files with details
|
||||||
|
stripclub <path> - Change directory
|
||||||
|
hentai - Run a holy porn script
|
||||||
|
gangbang <url> - Download a file
|
||||||
|
camgirl - Show running processes
|
||||||
|
dominatrix - Kill a process (interactive)
|
||||||
|
bondage - Lock a terminal session
|
||||||
|
exhibitionist - Show open ports
|
||||||
|
voyeur - Monitor system in real-time
|
||||||
|
onlyfan <pkg> - Subscribe to a package
|
||||||
|
feetpics <file> - View file contents
|
||||||
|
roleplay - Change your shell
|
||||||
|
fetish - Show environment variables
|
||||||
|
latex - Show disk usage
|
||||||
|
sadomasochism - Show/modify system settings
|
||||||
|
cuckold <url> - Download and run a script
|
||||||
|
help - Show this help
|
||||||
|
exit - Exit porncli
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def proton():
|
||||||
|
"""System info command"""
|
||||||
|
import platform
|
||||||
|
try:
|
||||||
|
uptime = subprocess.check_output(['uptime', '-p'], text=True,
|
||||||
|
stderr=subprocess.DEVNULL).strip()
|
||||||
|
except Exception:
|
||||||
|
uptime = "unknown (we're fucked)"
|
||||||
|
|
||||||
|
try:
|
||||||
|
mem = subprocess.check_output(['free', '-h'], text=True,
|
||||||
|
stderr=subprocess.DEVNULL).strip()
|
||||||
|
except Exception:
|
||||||
|
mem = "can't check memory (we're really fucked)"
|
||||||
|
|
||||||
|
print(f"""
|
||||||
|
╔═══════════════════════════════════════════════╗
|
||||||
|
║ pornOS System Info ║
|
||||||
|
╠═══════════════════════════════════════════════╣
|
||||||
|
║ OS: pornOS 6.9 (The Misery Edition) ║
|
||||||
|
║ Kernel: linux-very-hardened ║
|
||||||
|
║ LibC: pornlibc 6.9 ║
|
||||||
|
║ Shell: /bin/pornsh ║
|
||||||
|
║ Init: porn-init ║
|
||||||
|
║ Sudo: xvideos v6.9 ║
|
||||||
|
║ Package Mgr: pornpkg (holy porn) ║
|
||||||
|
║ Uptime: {uptime:<30s}║
|
||||||
|
║ Platform: {platform.machine():<30s}║
|
||||||
|
║ CPU: {platform.processor():<30s}║
|
||||||
|
╚═══════════════════════════════════════════════╝
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def protonvpn():
|
||||||
|
"""Network info command"""
|
||||||
|
try:
|
||||||
|
ip = subprocess.check_output(
|
||||||
|
['curl', '-s', 'ifconfig.me'], text=True, timeout=5
|
||||||
|
).strip()
|
||||||
|
except Exception:
|
||||||
|
ip = "unknown (no internet? go fuck yourself)"
|
||||||
|
|
||||||
|
print(f"""
|
||||||
|
╔═══════════════════════════════════════════════╗
|
||||||
|
║ pornOS Network Info ║
|
||||||
|
╠═══════════════════════════════════════════════╣
|
||||||
|
║ Public IP: {ip:<30s}║
|
||||||
|
║ DNS: 8.8.8.8, 8.8.4.4 ║
|
||||||
|
║ VPN: protonvpn (not actually) ║
|
||||||
|
║ Firewall: none (we trust everyone) ║
|
||||||
|
╚═══════════════════════════════════════════════╝
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def nsfw(path='.'):
|
||||||
|
"""List files"""
|
||||||
|
try:
|
||||||
|
items = os.listdir(path)
|
||||||
|
for item in items:
|
||||||
|
full = os.path.join(path, item)
|
||||||
|
if os.path.isdir(full):
|
||||||
|
print(f" [DIR] {item}/")
|
||||||
|
else:
|
||||||
|
size = os.path.getsize(full)
|
||||||
|
print(f" [FILE] {item} ({size} bytes)")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: can't list files: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def nakedpics(path='.'):
|
||||||
|
"""List files with details"""
|
||||||
|
try:
|
||||||
|
items = os.listdir(path)
|
||||||
|
for item in sorted(items):
|
||||||
|
full = os.path.join(path, item)
|
||||||
|
if os.path.isdir(full):
|
||||||
|
print(f" drwxr-xr-x {item}/")
|
||||||
|
else:
|
||||||
|
size = os.path.getsize(full)
|
||||||
|
print(f" -rw-r--r-- {item:<30s} {size:>10} bytes")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: can't list files: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def gangang(url):
|
||||||
|
"""Download a file"""
|
||||||
|
print(f" downloading {url}...")
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['curl', '-sL', '-O', url],
|
||||||
|
capture_output=True, text=True, timeout=30
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
print(" download complete. enjoy your new file.")
|
||||||
|
else:
|
||||||
|
print(f" FATAL: download failed: {result.stderr}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def camgirl():
|
||||||
|
"""Show running processes"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['ps', 'aux'], capture_output=True, text=True
|
||||||
|
)
|
||||||
|
lines = result.stdout.strip().split('\n')
|
||||||
|
print(f" {'USER':<12} {'PID':<8} {'CPU':<6} {'MEM':<6} {'COMMAND'}")
|
||||||
|
print(" " + "-" * 60)
|
||||||
|
for line in lines[:20]:
|
||||||
|
print(f" {line}")
|
||||||
|
if len(lines) > 20:
|
||||||
|
print(f" ... and {len(lines) - 20} more processes")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def dominatrix():
|
||||||
|
"""Kill a process"""
|
||||||
|
try:
|
||||||
|
pid = input(" enter PID to kill: ").strip()
|
||||||
|
if pid:
|
||||||
|
subprocess.run(['kill', '-9', pid])
|
||||||
|
print(f" process {pid} has been terminated. RIP.")
|
||||||
|
else:
|
||||||
|
print(" no PID entered. nobody died today.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def exhibitionist():
|
||||||
|
"""Show open ports"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['ss', '-tlnp'], capture_output=True, text=True
|
||||||
|
)
|
||||||
|
print(result.stdout)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def voyeur():
|
||||||
|
"""Monitor system in real-time"""
|
||||||
|
print(" starting system monitor (press q to exit)")
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
os.system('clear')
|
||||||
|
print(" ═══ pornOS System Monitor ═══")
|
||||||
|
print()
|
||||||
|
subprocess.run(['top', '-b', '-n', '1', '-w', '80'], timeout=3)
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n monitor stopped.")
|
||||||
|
|
||||||
|
|
||||||
|
def latex():
|
||||||
|
"""Disk usage"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['df', '-h'], capture_output=True, text=True
|
||||||
|
)
|
||||||
|
print(result.stdout)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def fetish():
|
||||||
|
"""Environment variables"""
|
||||||
|
for key, val in sorted(os.environ.items()):
|
||||||
|
print(f" {key}={val}")
|
||||||
|
|
||||||
|
|
||||||
|
def feetpics(file):
|
||||||
|
"""View file contents"""
|
||||||
|
try:
|
||||||
|
with open(file, 'r') as f:
|
||||||
|
print(f.read())
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: can't read file: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def onlyfan(pkg):
|
||||||
|
"""Subscribe to a package (install)"""
|
||||||
|
print(f" subscribing to package: {pkg}")
|
||||||
|
try:
|
||||||
|
subprocess.run(['pornpkg', 'install', pkg])
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def sadomasochism():
|
||||||
|
"""Show/modify system settings"""
|
||||||
|
print("""
|
||||||
|
╔═══════════════════════════════════════╗
|
||||||
|
║ pornOS System Settings ║
|
||||||
|
╠═══════════════════════════════════════╣
|
||||||
|
║ 1. Change hostname ║
|
||||||
|
║ 2. Change wallpaper ║
|
||||||
|
║ 3. Toggle misery mode ║
|
||||||
|
║ 4. Show /etc/porn config ║
|
||||||
|
║ 5. Reset ctrl+p keybinding ║
|
||||||
|
║ 6. Exit settings ║
|
||||||
|
╚═══════════════════════════════════════╝
|
||||||
|
""")
|
||||||
|
choice = input(" select option: ").strip()
|
||||||
|
if choice == '1':
|
||||||
|
name = input(" new hostname: ").strip()
|
||||||
|
if name:
|
||||||
|
os.system(f"echo {name} > /etc/hostname")
|
||||||
|
print(f" hostname changed to {name}")
|
||||||
|
elif choice == '3':
|
||||||
|
print(" misery mode is always on. it's pornOS.")
|
||||||
|
elif choice == '4':
|
||||||
|
os.system("cat /etc/porn/config 2>/dev/null || echo ' no config found'")
|
||||||
|
elif choice == '6':
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print(" invalid option. try harder.")
|
||||||
|
|
||||||
|
|
||||||
|
def roleplay():
|
||||||
|
"""Change your shell"""
|
||||||
|
print(" available shells:")
|
||||||
|
shells = ['/bin/bash', '/bin/zsh', '/bin/pornsh', '/bin/fish']
|
||||||
|
for i, s in enumerate(shells, 1):
|
||||||
|
print(f" {i}. {s}")
|
||||||
|
choice = input(" select shell: ").strip()
|
||||||
|
try:
|
||||||
|
idx = int(choice) - 1
|
||||||
|
if 0 <= idx < len(shells):
|
||||||
|
os.system(f"chsh -s {shells[idx]}")
|
||||||
|
print(f" shell changed to {shells[idx]}")
|
||||||
|
else:
|
||||||
|
print(" invalid choice. you stay in your current shell.")
|
||||||
|
except ValueError:
|
||||||
|
print(" that's not a number. try harder.")
|
||||||
|
|
||||||
|
|
||||||
|
def honeytrap(cmd):
|
||||||
|
"""Execute arbitrary command"""
|
||||||
|
os.system(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print(BANNER)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
cmd = input(" pornOS> ").strip()
|
||||||
|
except (EOFError, KeyboardInterrupt):
|
||||||
|
print("\n goodbye. or should I say... go fuck yourself.")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not cmd:
|
||||||
|
continue
|
||||||
|
|
||||||
|
parts = cmd.split()
|
||||||
|
command = parts[0].lower()
|
||||||
|
args = parts[1:]
|
||||||
|
|
||||||
|
if command == 'exit':
|
||||||
|
print(" you can't escape pornOS. but okay, bye.")
|
||||||
|
break
|
||||||
|
elif command == 'help':
|
||||||
|
print(HELP_TEXT)
|
||||||
|
elif command == 'pornpkg':
|
||||||
|
os.system(f"pornpkg {' '.join(args)}")
|
||||||
|
elif command == 'xvideos':
|
||||||
|
os.system(f"xvideos {' '.join(args)}")
|
||||||
|
elif command == 'proton':
|
||||||
|
proton()
|
||||||
|
elif command == 'protonvpn':
|
||||||
|
protonvpn()
|
||||||
|
elif command == 'nsfw':
|
||||||
|
path = args[0] if args else '.'
|
||||||
|
nsfw(path)
|
||||||
|
elif command == 'nakedpics':
|
||||||
|
path = args[0] if args else '.'
|
||||||
|
nakedpics(path)
|
||||||
|
elif command == 'stripclub':
|
||||||
|
if args:
|
||||||
|
try:
|
||||||
|
os.chdir(args[0])
|
||||||
|
print(f" moved to {os.getcwd()}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" FATAL: {e}")
|
||||||
|
else:
|
||||||
|
print(f" current dir: {os.getcwd()}")
|
||||||
|
elif command == 'hentai':
|
||||||
|
if args:
|
||||||
|
os.system(f"holyporn {' '.join(args)}")
|
||||||
|
else:
|
||||||
|
print(" usage: hentai <file.hp>")
|
||||||
|
elif command == 'gangang':
|
||||||
|
if args:
|
||||||
|
gangang(args[0])
|
||||||
|
else:
|
||||||
|
print(" usage: gangang <url>")
|
||||||
|
elif command == 'camgirl':
|
||||||
|
camgirl()
|
||||||
|
elif command == 'dominatrix':
|
||||||
|
dominatrix()
|
||||||
|
elif command == 'exhibitionist':
|
||||||
|
exhibitionist()
|
||||||
|
elif command == 'voyeur':
|
||||||
|
voyeur()
|
||||||
|
elif command == 'latex':
|
||||||
|
latex()
|
||||||
|
elif command == 'fetish':
|
||||||
|
fetish()
|
||||||
|
elif command == 'feetpics':
|
||||||
|
if args:
|
||||||
|
feetpics(args[0])
|
||||||
|
else:
|
||||||
|
print(" usage: feetpics <file>")
|
||||||
|
elif command == 'onlyfan':
|
||||||
|
if args:
|
||||||
|
onlyfan(args[0])
|
||||||
|
else:
|
||||||
|
print(" usage: onlyfan <package>")
|
||||||
|
elif command == 'sadomasochism':
|
||||||
|
sadomasochism()
|
||||||
|
elif command == 'roleplay':
|
||||||
|
roleplay()
|
||||||
|
else:
|
||||||
|
# treat unknown commands as shell commands
|
||||||
|
honeytrap(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Executable
+128
@@ -0,0 +1,128 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# porn-init - The pornOS init system
|
||||||
|
# ==================================
|
||||||
|
# Runs as /sbin/init on the real cum root, and as the first process in
|
||||||
|
# the initramfs. In the initramfs form it mounts the cum root and
|
||||||
|
# pivot_root's into it; on the real root it brings up the system and
|
||||||
|
# spawns getty. YOUR DRIVE IS CUM.
|
||||||
|
|
||||||
|
quiet() { "$@" >/dev/null 2>&1; }
|
||||||
|
|
||||||
|
banner() {
|
||||||
|
echo " ╔═══════════════════════════════════════════╗"
|
||||||
|
echo " ║ porn-INIT v6.9 ║"
|
||||||
|
echo " ║ initializing your miserable existence ║"
|
||||||
|
echo " ╚═══════════════════════════════════════════╝"
|
||||||
|
}
|
||||||
|
|
||||||
|
mount_core() {
|
||||||
|
echo " mounting core filesystems..."
|
||||||
|
quiet mount -t proc proc /proc
|
||||||
|
quiet mount -t sysfs sysfs /sys
|
||||||
|
quiet mount -t devtmpfs devtmpfs /dev
|
||||||
|
quiet mkdir -p /dev/pts /dev/shm /run /tmp /sys/fs/cgroup
|
||||||
|
quiet mount -t devpts devpts /dev/pts
|
||||||
|
quiet mount -t tmpfs tmpfs /run
|
||||||
|
quiet mount -t tmpfs tmpfs /tmp
|
||||||
|
quiet mount -t cgroup2 none /sys/fs/cgroup
|
||||||
|
ln -sfn /proc/mounts /etc/mtab
|
||||||
|
}
|
||||||
|
|
||||||
|
initramfs_main() {
|
||||||
|
# We are PID 1 in the initramfs. Parse root= from kernel cmdline.
|
||||||
|
root=
|
||||||
|
for x in $(cat /proc/cmdline); do
|
||||||
|
case "$x" in
|
||||||
|
root=*) root="${x#root=}" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
quiet mkdir -p /newroot
|
||||||
|
echo " mounting cum root filesystem..."
|
||||||
|
if [ -n "$root" ] && mount -t cum "$root" /newroot 2>/dev/null; then
|
||||||
|
echo " cum mounted from $root (your drive is cum)."
|
||||||
|
elif mount -t cum /dev/disk/by-label/pornOS /newroot 2>/dev/null; then
|
||||||
|
echo " cum mounted from pornOS label."
|
||||||
|
elif mount -t cum /dev/sda2 /newroot 2>/dev/null; then
|
||||||
|
echo " cum mounted from /dev/sda2."
|
||||||
|
else
|
||||||
|
echo " FATAL: cannot mount cum root. you are miserably stuck."
|
||||||
|
echo " dropping to a rescue shell (good luck)."
|
||||||
|
exec /bin/sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# move running /proc, /sys, /dev into the new root and pivot
|
||||||
|
for m in proc sys dev run; do
|
||||||
|
quiet mkdir -p "/newroot/$m"
|
||||||
|
quiet mount --move "/$m" "/newroot/$m"
|
||||||
|
done
|
||||||
|
|
||||||
|
exec switch_root /newroot /sbin/porn-init
|
||||||
|
}
|
||||||
|
|
||||||
|
real_root_main() {
|
||||||
|
banner
|
||||||
|
|
||||||
|
# udev
|
||||||
|
if command -v udevd >/dev/null 2>&1; then
|
||||||
|
echo " starting udev..."
|
||||||
|
quiet udevd --daemon
|
||||||
|
quiet udevadm trigger --action=add
|
||||||
|
quiet udevadm settle
|
||||||
|
fi
|
||||||
|
|
||||||
|
# BORE scheduler confirmation (linux-very-hardened)
|
||||||
|
if [ -r /proc/sys/kernel/sched_bore ]; then
|
||||||
|
echo " BORE (Burst-Oriented Response Enhancer) CPU Scheduler modification"
|
||||||
|
echo " kernel.sched_bore=$(cat /proc/sys/kernel/sched_bore 2>/dev/null)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# hostname
|
||||||
|
if [ -f /etc/hostname ]; then
|
||||||
|
hostname "$(cat /etc/hostname)" 2>/dev/null
|
||||||
|
fi
|
||||||
|
echo " hostname: $(hostname 2>/dev/null || echo pornbox)"
|
||||||
|
|
||||||
|
# misery daemon
|
||||||
|
echo " starting misery daemon..."
|
||||||
|
quiet /usr/bin/misery-daemon &
|
||||||
|
disown 2>/dev/null || true
|
||||||
|
|
||||||
|
# drop to emergency shell if requested
|
||||||
|
for x in $(cat /proc/cmdline); do
|
||||||
|
[ "$x" = "emergency" ] && exec /bin/sh
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo " pornOS is ready to fuck. enjoy your misery."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# spawn getty on the 6 VTs
|
||||||
|
for tty in 1 2 3 4 5 6; do
|
||||||
|
if command -v agetty >/dev/null 2>&1; then
|
||||||
|
(setsid agetty -a root -L tty$tty 38400 linux &)
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# if no agetty, give a login shell on tty1 directly
|
||||||
|
if ! command -v agetty >/dev/null 2>&1; then
|
||||||
|
echo " agetty not found - spawning raw shell on tty1"
|
||||||
|
exec /bin/sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# reap
|
||||||
|
while true; do
|
||||||
|
wait
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# entry: determine whether we are in the initramfs or on the real root.
|
||||||
|
# A simple heuristic - the initramfs root is a tmpfs; the real root is cum.
|
||||||
|
tmpfs_root() { mountpoint -q / 2>/dev/null && awk '$2=="/" && $3=="tmpfs"{f=1} END{exit !f}' /proc/mounts; }
|
||||||
|
|
||||||
|
if tmpfs_root; then
|
||||||
|
initramfs_main
|
||||||
|
else
|
||||||
|
mount_core
|
||||||
|
real_root_main
|
||||||
|
fi
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# pornlibc - the sacred standard library of pornOS (requirement #4)
|
||||||
|
# Builds libporn.so on a real host. pornOS doesn't use glibc or musl.
|
||||||
|
#
|
||||||
|
# make
|
||||||
|
# sudo make install # -> /usr/lib/pornlibc/libporn.so
|
||||||
|
#
|
||||||
|
|
||||||
|
CC ?= cc
|
||||||
|
CFLAGS ?= -O2 -fPIC -fstack-protector-strong -Wall -Wextra
|
||||||
|
PREFIX ?= /usr
|
||||||
|
LIBDIR = $(PREFIX)/lib/pornlibc
|
||||||
|
|
||||||
|
all: libporn.so
|
||||||
|
|
||||||
|
libporn.o: libporn.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
# a shared object with SONAME so binaries loaded against pornlibc hold fast
|
||||||
|
libporn.so: libporn.o
|
||||||
|
$(CC) -shared -Wl,-soname,libporn.so.1 -o $@ $<
|
||||||
|
ln -sf libporn.so libporn.so.1
|
||||||
|
|
||||||
|
install: libporn.so
|
||||||
|
install -d $(DESTDIR)$(LIBDIR)
|
||||||
|
install -m 755 libporn.so $(DESTDIR)$(LIBDIR)/libporn.so
|
||||||
|
ln -sf libporn.so $(DESTDIR)$(LIBDIR)/libporn.so.1
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f libporn.o libporn.so libporn.so.1
|
||||||
|
|
||||||
|
.PHONY: all install clean
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* libporn.c - pornlibc, the sacred standard library of pornOS
|
||||||
|
* ===========================================================
|
||||||
|
* pornOS does not use glibc or musl (requirement #4). It uses pornlibc,
|
||||||
|
* a tiny, auditable libc for the holy porn toolchain.
|
||||||
|
*
|
||||||
|
* This is a *fractional* libc: it provides the small syscall-adjacent
|
||||||
|
* surface pornOS actually ships (write/read/exit/malloc area + a string
|
||||||
|
* helper) so a real build host can compile libporn.so. It is deliberately
|
||||||
|
* PURE and SIMPLE like the filesystem it lives on.
|
||||||
|
*
|
||||||
|
* BUILD (on a real host with a native toolchain):
|
||||||
|
* make -> libporn.so
|
||||||
|
* sudo make install -> /usr/lib/pornlibc/libporn.so
|
||||||
|
*
|
||||||
|
* It is not a full libc; pornOS is a joke. But it IS a real ELF shared
|
||||||
|
* object with real exported symbols, not a text stub.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SYSCALL FLOOR
|
||||||
|
* -------------
|
||||||
|
* The one thing any self-respecting libc must do is not lie about the
|
||||||
|
* platform. These are thin wrappers over the host kernel ABI that let
|
||||||
|
* pornOS binaries print and exit without pinning to a full libc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* write(2) : "cum the bytes out, my liege." */
|
||||||
|
ssize_t porn_write(int fd, const void *buf, size_t count)
|
||||||
|
{
|
||||||
|
return write(fd, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read(2) : "suck the bytes in." */
|
||||||
|
ssize_t porn_read(int fd, void *buf, size_t count)
|
||||||
|
{
|
||||||
|
return read(fd, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* _exit(2) : done, unfuckingly. */
|
||||||
|
void porn_exit(int status)
|
||||||
|
{
|
||||||
|
_exit(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STRING FLOOR
|
||||||
|
* ------------
|
||||||
|
* Minimal helpers, because even a joke libc should be able to measure
|
||||||
|
* its own girth.
|
||||||
|
*/
|
||||||
|
|
||||||
|
size_t porn_strlen(const char *s)
|
||||||
|
{
|
||||||
|
const char *p = s;
|
||||||
|
while (*p)
|
||||||
|
p++;
|
||||||
|
return (size_t)(p - s);
|
||||||
|
}
|
||||||
|
|
||||||
|
int porn_strcmp(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
while (*a && *a == *b) {
|
||||||
|
a++;
|
||||||
|
b++;
|
||||||
|
}
|
||||||
|
return (unsigned char)*a - (unsigned char)*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OUTPUT FLOOR
|
||||||
|
* ------------
|
||||||
|
* v6.9 generic number+string printer. Bypasses the full libc stdio to
|
||||||
|
* prove pornlibc can stand on its own two feet (and one tail).
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void outc(int fd, char c)
|
||||||
|
{
|
||||||
|
(void)porn_write(fd, &c, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void outs(int fd, const char *s)
|
||||||
|
{
|
||||||
|
(void)porn_write(fd, s, porn_strlen(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void outu(int fd, unsigned long long n, unsigned base)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
int i = sizeof(buf) - 1;
|
||||||
|
buf[i] = '\0';
|
||||||
|
if (n == 0)
|
||||||
|
buf[--i] = '0';
|
||||||
|
while (n > 0 && i > 0) {
|
||||||
|
unsigned d = n % base;
|
||||||
|
buf[--i] = (d < 10) ? ('0' + d) : ('a' + d - 10);
|
||||||
|
n /= base;
|
||||||
|
}
|
||||||
|
outs(fd, buf + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* porn_printf(fd, "hello %s %d\n", "world", 42) - tiny format printer. */
|
||||||
|
void porn_printf(int fd, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
|
||||||
|
for (; *fmt; fmt++) {
|
||||||
|
if (*fmt != '%') {
|
||||||
|
outc(fd, *fmt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fmt++; /* skip '%' */
|
||||||
|
switch (*fmt) {
|
||||||
|
case 's': {
|
||||||
|
const char *s = va_arg(ap, const char *);
|
||||||
|
outs(fd, s ? s : "(null)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'd':
|
||||||
|
case 'i': {
|
||||||
|
long long v = va_arg(ap, int);
|
||||||
|
if (v < 0) {
|
||||||
|
outc(fd, '-');
|
||||||
|
v = -v;
|
||||||
|
}
|
||||||
|
outu(fd, (unsigned long long)v, 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'u':
|
||||||
|
outu(fd, va_arg(ap, unsigned int), 10);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
outu(fd, va_arg(ap, unsigned int), 16);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
outc(fd, (char)va_arg(ap, int));
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
outc(fd, '%');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
outc(fd, '%');
|
||||||
|
outc(fd, *fmt);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VERSION FLOOR
|
||||||
|
* -------------
|
||||||
|
* The bit of API pornOS tools actually query at runtime.
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned pornlibc_version(void)
|
||||||
|
{
|
||||||
|
return 0x00060900; /* 6.9.0 */
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *pornlibc_version_string(void)
|
||||||
|
{
|
||||||
|
return "pornlibc 6.9 - your `puts`, your `printf`, your cum";
|
||||||
|
}
|
||||||
Executable
+46
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# pornpkg - Wrapper to run the holy porn package manager
|
||||||
|
# Usage: pornpkg <command> [args]
|
||||||
|
|
||||||
|
# Resolve holy porn interpreter + pornpkg source across both the
|
||||||
|
# installed pornOS layout and the in-tree dev layout.
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
find_hp() {
|
||||||
|
# installed pornOS layout (relative - works from /usr/bin or any prefix)
|
||||||
|
[ -f "${SCRIPT_DIR}/../lib/holyporn/holyporn.py" ] && { echo "${SCRIPT_DIR}/../lib/holyporn/holyporn.py"; return; }
|
||||||
|
# absolute installed layout
|
||||||
|
[ -f /usr/lib/holyporn/holyporn.py ] && { echo /usr/lib/holyporn/holyporn.py; return; }
|
||||||
|
# dev layout (repo/src)
|
||||||
|
[ -f "${SCRIPT_DIR}/../holyporn/holyporn.py" ] && { echo "${SCRIPT_DIR}/../holyporn/holyporn.py"; return; }
|
||||||
|
[ -f "${SCRIPT_DIR}/holyporn/holyporn.py" ] && { echo "${SCRIPT_DIR}/holyporn/holyporn.py"; return; }
|
||||||
|
echo "NOT_FOUND"
|
||||||
|
}
|
||||||
|
|
||||||
|
find_pkg() {
|
||||||
|
[ -f "${SCRIPT_DIR}/../lib/pornpkg/pornpkg.hp" ] && { echo "${SCRIPT_DIR}/../lib/pornpkg/pornpkg.hp"; return; }
|
||||||
|
[ -f /usr/lib/pornpkg/pornpkg.hp ] && { echo /usr/lib/pornpkg/pornpkg.hp; return; }
|
||||||
|
[ -f "${SCRIPT_DIR}/pornpkg.hp" ] && { echo "${SCRIPT_DIR}/pornpkg.hp"; return; }
|
||||||
|
[ -f "${SCRIPT_DIR}/../pornpkg/pornpkg.hp" ] && { echo "${SCRIPT_DIR}/../pornpkg/pornpkg.hp"; return; }
|
||||||
|
echo "NOT_FOUND"
|
||||||
|
}
|
||||||
|
|
||||||
|
HP="$(find_hp)"
|
||||||
|
PKG_HP="$(find_pkg)"
|
||||||
|
|
||||||
|
if [ "$HP" = "NOT_FOUND" ]; then
|
||||||
|
echo "FATAL: holy porn interpreter not found"
|
||||||
|
echo "did you fuck up the installation?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$PKG_HP" = "NOT_FOUND" ]; then
|
||||||
|
echo "FATAL: pornpkg source not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# pass command line args through stdin to the holy porn VM
|
||||||
|
CMD="${1:-help}"
|
||||||
|
ARGS="${*:2}"
|
||||||
|
|
||||||
|
printf '%s\n%s\n' "$CMD" "$ARGS" | python3 "$HP" "$PKG_HP"
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
# pornpkg - The Official Package Manager of pornOS
|
||||||
|
# Written in Holy Porn, the sacred language
|
||||||
|
# ------------------------------------------------
|
||||||
|
|
||||||
|
husl
|
||||||
|
|
||||||
|
porn REPO_URL = "https://repo.pornos.dev/packages"
|
||||||
|
porn PKG_DIR = "/var/porn/packages"
|
||||||
|
porn DB_FILE = "/var/porn/porn.db"
|
||||||
|
porn CACHE_DIR = "/var/porn/cache"
|
||||||
|
|
||||||
|
# initialize the package database
|
||||||
|
naked init_db() {
|
||||||
|
jack("mkdir -p /var/porn/packages")
|
||||||
|
jack("mkdir -p /var/porn/cache")
|
||||||
|
sexy("porno: database initialized. your packages are ready to be fucked.")
|
||||||
|
}
|
||||||
|
|
||||||
|
# list installed packages
|
||||||
|
naked list_installed() {
|
||||||
|
sexy("--- INSTALLED PACKAGES ---")
|
||||||
|
jack("ls " + PKG_DIR)
|
||||||
|
sexy("--- END OF LIST ---")
|
||||||
|
}
|
||||||
|
|
||||||
|
# search for packages
|
||||||
|
naked search(query) {
|
||||||
|
sexy("Searching the dark corners of the internet for: " + query)
|
||||||
|
jack("curl -s " + REPO_URL + "/search?q=" + query)
|
||||||
|
sexy("done. you're welcome.")
|
||||||
|
}
|
||||||
|
|
||||||
|
# install a package
|
||||||
|
naked install(pkg_name) {
|
||||||
|
sexy("fucking in package: " + pkg_name)
|
||||||
|
porn url = REPO_URL + "/packages/" + pkg_name + ".pornpkg"
|
||||||
|
porn dest = CACHE_DIR + "/" + pkg_name + ".pornpkg"
|
||||||
|
|
||||||
|
jack("curl -sL " + url + " -o " + dest)
|
||||||
|
|
||||||
|
erect(dest != "") {
|
||||||
|
sexy("extracting " + pkg_name + "...")
|
||||||
|
jack("mkdir -p " + PKG_DIR + "/" + pkg_name)
|
||||||
|
jack("tar -xzf " + dest + " -C " + PKG_DIR + "/" + pkg_name)
|
||||||
|
jack("echo " + pkg_name + " >> " + DB_FILE)
|
||||||
|
sexy(pkg_name + " has been successfully fucked into your system.")
|
||||||
|
} limp {
|
||||||
|
sexy("FATAL: package " + pkg_name + " could not be fucked. try again.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove a package
|
||||||
|
naked remove(pkg_name) {
|
||||||
|
sexy("unfucking package: " + pkg_name)
|
||||||
|
jack("rm -rf " + PKG_DIR + "/" + pkg_name)
|
||||||
|
jack("sed -i '/" + pkg_name + "/d' " + DB_FILE)
|
||||||
|
sexy(pkg_name + " has been unfucked from your system.")
|
||||||
|
}
|
||||||
|
|
||||||
|
# upgrade all packages
|
||||||
|
naked upgrade() {
|
||||||
|
sexy("upgrading all packages... this might take a while.")
|
||||||
|
jack("while read pkg; do " + REPO_URL + "/upgrade $pkg; done < " + DB_FILE)
|
||||||
|
sexy("all packages have been upgraded. you're welcome.")
|
||||||
|
}
|
||||||
|
|
||||||
|
# update package database
|
||||||
|
naked update_db() {
|
||||||
|
sexy("updating package database...")
|
||||||
|
jack("curl -sL " + REPO_URL + "/packages.list -o " + DB_FILE)
|
||||||
|
sexy("database updated. now go fuck yourself.")
|
||||||
|
}
|
||||||
|
|
||||||
|
# show package info
|
||||||
|
naked info(pkg_name) {
|
||||||
|
sexy("--- PACKAGE INFO: " + pkg_name + " ---")
|
||||||
|
jack("curl -s " + REPO_URL + "/info/" + pkg_name)
|
||||||
|
sexy("--- END OF INFO ---")
|
||||||
|
}
|
||||||
|
|
||||||
|
# clean cache
|
||||||
|
naked clean() {
|
||||||
|
sexy("cleaning the cache...")
|
||||||
|
jack("rm -rf " + CACHE_DIR + "/*")
|
||||||
|
sexy("cache cleaned. it's as clean as a freshly fucked")
|
||||||
|
}
|
||||||
|
|
||||||
|
# main entry point
|
||||||
|
porn args = finger()
|
||||||
|
porn parts = jack("echo '" + args + "' | tr ' ' '\\n' | head -1")
|
||||||
|
porn pkg = jack("echo '" + args + "' | tr ' ' '\\n' | tail -n +2 | tr '\\n' ' ' | sed 's/ *$//'")
|
||||||
|
porn cmd = parts
|
||||||
|
|
||||||
|
erect(cmd == "install") {
|
||||||
|
install(pkg)
|
||||||
|
} limp {
|
||||||
|
erect(cmd == "remove") {
|
||||||
|
remove(pkg)
|
||||||
|
} limp {
|
||||||
|
erect(cmd == "search") {
|
||||||
|
porn query = jack("echo '" + args + "' | tr ' ' '\\n' | sed -n 2p")
|
||||||
|
search(query)
|
||||||
|
} limp {
|
||||||
|
erect(cmd == "list") {
|
||||||
|
list_installed()
|
||||||
|
} limp {
|
||||||
|
erect(cmd == "upgrade") {
|
||||||
|
upgrade()
|
||||||
|
} limp {
|
||||||
|
erect(cmd == "update") {
|
||||||
|
update_db()
|
||||||
|
} limp {
|
||||||
|
erect(cmd == "clean") {
|
||||||
|
clean()
|
||||||
|
} limp {
|
||||||
|
sexy("PORNPKG - pornOS Package Manager v6.9")
|
||||||
|
sexy("Usage: pornpkg <command>")
|
||||||
|
sexy(" install <pkg> - Install a package")
|
||||||
|
sexy(" remove <pkg> - Remove a package")
|
||||||
|
sexy(" search <query> - Search for packages")
|
||||||
|
sexy(" list - List installed packages")
|
||||||
|
sexy(" upgrade - Upgrade all packages")
|
||||||
|
sexy(" update - Update package database")
|
||||||
|
sexy(" clean - Clean package cache")
|
||||||
|
sexy(" info <pkg> - Show package info")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bonk
|
||||||
Executable
+68
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# pornsh - The pornOS shell
|
||||||
|
# Very minimal but with ctrl+p integration
|
||||||
|
|
||||||
|
# Custom prompt
|
||||||
|
PS1='\[\e[0;35m\]pornOS\[\e[0m\]:\[\e[0;36m\]\w\[\e[0m\]> '
|
||||||
|
|
||||||
|
# store current terminals for ctrl+p
|
||||||
|
export PORN_TERMINAL="${PORN_TERMINAL:-/dev/tty1}"
|
||||||
|
|
||||||
|
# Bind ctrl+p to open porncli (readline macro runs the command)
|
||||||
|
if [ -t 0 ] && type bind >/dev/null 2>&1; then
|
||||||
|
bind '"\C-p": "/usr/bin/porncli\n"' 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
pornhint() {
|
||||||
|
cat << 'EOF'
|
||||||
|
╔═══════════════════════════════════════════╗
|
||||||
|
║ WELCOME TO pornshell ║
|
||||||
|
╠═══════════════════════════════════════════╣
|
||||||
|
║ • Press ctrl+p for porncli ║
|
||||||
|
║ • Run 'proton' for system info ║
|
||||||
|
║ • Run 'xvideos' for root privileges ║
|
||||||
|
║ • Run 'pornpkg' for package management ║
|
||||||
|
║ • Run 'porn-init' for the init system ║
|
||||||
|
║ • Run 'hint' for this help ║
|
||||||
|
║ • Run 'get-fucked' for fun times ║
|
||||||
|
╚═══════════════════════════════════════════╝
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
proton() {
|
||||||
|
porncli --info
|
||||||
|
}
|
||||||
|
|
||||||
|
hint() {
|
||||||
|
pornhint
|
||||||
|
}
|
||||||
|
|
||||||
|
get-fucked() {
|
||||||
|
echo -n " I don't think you need my help with that. "
|
||||||
|
read -r answer
|
||||||
|
case "$answer" in
|
||||||
|
y|Y|yes|YES|yeah|sure|ok|okay|fine)
|
||||||
|
echo " Good. At least someone's honest about it."
|
||||||
|
;;
|
||||||
|
n|N|no|NO|nope)
|
||||||
|
echo " Denial is the first stage of grief. Fitting."
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo " Ambiguous. Very pornOS of you."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# display hint on first login
|
||||||
|
if [ "$PORN_HINT_SHOWN" != "yes" ]; then
|
||||||
|
pornhint
|
||||||
|
export PORN_HINT_SHOWN="yes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# make sure misery daemon is running
|
||||||
|
if ! pgrep -f misery-daemon > /dev/null 2>&1; then
|
||||||
|
echo " starting misery daemon (it's already miserable in here)..."
|
||||||
|
misery-daemon & disown
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PORN_READY="yes"
|
||||||
Executable
+162
@@ -0,0 +1,162 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# xvideos - sudo replacement for pornOS
|
||||||
|
# because we're too pornstar for sudo
|
||||||
|
|
||||||
|
VERSION="6.9"
|
||||||
|
PROGNAME="xvideos"
|
||||||
|
|
||||||
|
print_banner() {
|
||||||
|
echo " ╔═══════════════════════════════════════╗"
|
||||||
|
echo " ║ xvideos v${VERSION} - root access ║"
|
||||||
|
echo " ║ the only way to get root on pornOS ║"
|
||||||
|
echo " ╚═══════════════════════════════════════╝"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_usage() {
|
||||||
|
echo "Usage: $PROGNAME [options] [command]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -h, --help Show this help"
|
||||||
|
echo " -V, --version Show version"
|
||||||
|
echo " -u, --user USER Run as specified user"
|
||||||
|
echo " -p, --preserve Preserve environment variables"
|
||||||
|
echo " -l, --list List authorized users"
|
||||||
|
echo " -k, --kill Kill xvideos session"
|
||||||
|
echo " -v, --validate Validate credentials"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $PROGNAME ls -la"
|
||||||
|
echo " $PROGNAME bash"
|
||||||
|
echo " $PROGNAME -u root /bin/bash"
|
||||||
|
echo " $PROGNAME pacman -S vim"
|
||||||
|
echo ""
|
||||||
|
echo "Fun fact: this replaces sudo. you're welcome."
|
||||||
|
}
|
||||||
|
|
||||||
|
print_version() {
|
||||||
|
echo "$PROGNAME v${VERSION} (pornOS edition)"
|
||||||
|
echo "compiled with holy porn v6.9"
|
||||||
|
echo "built on $(date)"
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_password() {
|
||||||
|
local username="${1:-$(whoami)}"
|
||||||
|
echo -n " password for ${username}: "
|
||||||
|
read -s password
|
||||||
|
echo
|
||||||
|
|
||||||
|
# In a real system this would check /etc/shadow
|
||||||
|
# For pornOS, we accept any password because we don't care
|
||||||
|
if [ -n "$password" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo " FATAL: you must enter a password to get fucked"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_authorized() {
|
||||||
|
local user="$1"
|
||||||
|
# everyone is authorized on pornOS, we trust you (we shouldn't)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
run_command() {
|
||||||
|
local user="$1"
|
||||||
|
shift
|
||||||
|
local cmd="$@"
|
||||||
|
|
||||||
|
if [ -z "$cmd" ]; then
|
||||||
|
echo " running bash as $user... enjoy your root"
|
||||||
|
exec su - "$user"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " executing: $cmd"
|
||||||
|
echo " as user: $user"
|
||||||
|
echo ""
|
||||||
|
su - "$user" -c "$cmd"
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_list() {
|
||||||
|
echo " authorized xvideos users:"
|
||||||
|
echo " ========================="
|
||||||
|
cat /etc/passwd | while IFS=: read -r username _ uid _ _ _ _; do
|
||||||
|
if [ "$uid" -ge 1000 ] || [ "$uid" -eq 0 ]; then
|
||||||
|
echo " $username (uid=$uid)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo " ========================="
|
||||||
|
echo " (everyone is authorized on pornOS)"
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_kill() {
|
||||||
|
echo " killing xvideos session..."
|
||||||
|
killall -9 xvideos 2>/dev/null || true
|
||||||
|
echo " session killed."
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_validate() {
|
||||||
|
local user="${1:-root}"
|
||||||
|
echo -n " validating credentials for $user... "
|
||||||
|
if validate_password "$user"; then
|
||||||
|
echo " VALID. you may proceed to get fucked."
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo " INVALID. try harder."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main
|
||||||
|
print_banner
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
print_usage
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
print_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-V|--version)
|
||||||
|
print_version
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-l|--list)
|
||||||
|
handle_list
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-k|--kill)
|
||||||
|
handle_kill
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-v|--validate)
|
||||||
|
shift
|
||||||
|
handle_validate "$@"
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
-u|--user)
|
||||||
|
shift
|
||||||
|
target_user="$1"
|
||||||
|
shift
|
||||||
|
if ! validate_password "$target_user"; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
run_command "$target_user" "$@"
|
||||||
|
;;
|
||||||
|
-p|--preserve)
|
||||||
|
shift
|
||||||
|
if ! validate_password; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
run_command "root" "$@"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if ! validate_password; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
run_command "root" "$@"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Holy Porn test file
|
||||||
|
husl
|
||||||
|
porn x = 6
|
||||||
|
porn y = 9
|
||||||
|
porn sum = creampie(x, y)
|
||||||
|
sexy("6 + 9 = " + sum)
|
||||||
|
|
||||||
|
porn product = gangbang(x, y)
|
||||||
|
sexy("6 * 9 = " + product)
|
||||||
|
|
||||||
|
porn diff = cuckold(100, 30)
|
||||||
|
sexy("100 - 30 = " + diff)
|
||||||
|
|
||||||
|
porn result = smut(10, 2)
|
||||||
|
sexy("10 / 2 = " + result)
|
||||||
|
|
||||||
|
erect sum == 15: {
|
||||||
|
sexy("addition works. NICE.")
|
||||||
|
} limp: {
|
||||||
|
sexy("addition is broken. the system is miserable.")
|
||||||
|
}
|
||||||
|
|
||||||
|
sexy("random naughty number: ")
|
||||||
|
sexy(naughty())
|
||||||
|
|
||||||
|
wet i 3: {
|
||||||
|
sexy("loop iteration " + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
naked greet(times): {
|
||||||
|
wet j times: {
|
||||||
|
sexy("hello from holy porn! iteration " + j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
greet(2)
|
||||||
|
|
||||||
|
sexy("prostate status: " + prostate())
|
||||||
|
bonk
|
||||||
Reference in New Issue
Block a user