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