nx_torrent_clean.nx source
↩ module page · 113 lines · 6753 B
1// nx_torrent_clean.nx -- SOVEREIGN disk purge of leftover torrent folders. A folder is DELETED only
2// if it is NOT in the live torrents.idx (so the operator's real/active downloads are always kept) AND
3// its control is not "active" (defensive). Reports every action. Frees the dead/removed/duplicate/test
4// clutter the doctor surfaced. Uses raw unlinkat/rmdir (nx_syscalls has no wrapper). Run: nx_torrent_clean
5import "nx_syscalls.nx"
6const K_MAGIC_2048: i64 = 2048
7const K_MAGIC_65536: i64 = 65536
8const K_MAGIC_1024: i64 = 1024
9
10const DBUF: i64 = 65536
11const O: *u8 = "/mnt/c/Users/elder/Downloads/nishi-torrents" as *u8
12const IDX: *u8 = "/mnt/c/Users/elder/Downloads/nishi-torrents/torrents.idx" as *u8
13
14func cl_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
15func cl_n(v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m} var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} let b: *u8=sys_mmap(28); var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
16func cl_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
17func cl_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){dst[off+i]=s[i];i=i+1} return off+cl_len(s) }
18// raw unlinkat: file (flags=0) / dir (flags=0x200 AT_REMOVEDIR). x86_64 nr 263. returns 0 on success.
19func cl_unlink(path: *u8) -> i64 { return __syscall(263, AT_FDCWD, path as i64, 0, 0, 0, 0) }
20func cl_rmdir(path: *u8) -> i64 { return __syscall(263, AT_FDCWD, path as i64, 0x200, 0, 0, 0) }
21func cl_read(path: *u8, buf: *u8, cap: i64) -> i64 {
22 let fd: i64 = sys_openat_rd(path); if fd<0 { return 0-1 }
23 var off: i64=0; var go: i64=1
24 while go==1 { if off>=cap {go=0} else { let r: i64=sys_read(fd, ((buf as i64)+off) as *u8, cap-off); if r<=0 {go=0} else {off=off+r} } }
25 sys_close(fd); return off
26}
27// is `name` (len nl) a whole line in the idx buffer? (the keep-list)
28func cl_idx_has(idx: *u8, n: i64, name: *u8, nl: i64) -> i64 {
29 var i: i64 = 0
30 while i < n {
31 var ls: i64 = 0; if i==0 {ls=1} else { if (idx[i-1] as i64)==10 {ls=1} }
32 if ls==1 {
33 var m: i64=1; var j: i64=0
34 while j<nl { if i+j>=n {m=0;j=nl} else { if idx[i+j]!=name[j] {m=0;j=nl} else {j=j+1} } }
35 if m==1 { let e: i64=i+nl; var term: i64=0; if e>=n {term=1} else { if (idx[e] as i64)==10 {term=1} } if term==1 { return 1 } }
36 }
37 i=i+1
38 }
39 return 0
40}
41// flat-delete a torrent folder: unlink its files, then rmdir. returns 1 if the dir was removed.
42func cl_rmtree_flat(dir: *u8) -> i64 {
43 let fd: i64 = sys_openat_rd(dir); if fd<0 { return 0 }
44 let gbuf: *u8 = sys_mmap(DBUF); let child: *u8 = sys_mmap(K_MAGIC_2048); let dlen: i64 = cl_len(dir)
45 var nested: i64 = 0; var go: i64 = 1
46 while go == 1 {
47 let n: i64 = sys_getdents64(fd, gbuf, DBUF)
48 if n <= 0 { go = 0 } else {
49 var off: i64 = 0
50 while off < n {
51 let rec: *u8 = ((gbuf as i64) + off) as *u8
52 let reclen: i64 = dirent_reclen(rec)
53 let typ: i64 = dirent_type(rec)
54 let name: *u8 = dirent_name(rec)
55 if name[0]!=(46 as u8) {
56 var co: i64=0; while co<dlen {child[co]=dir[co];co=co+1} child[co]=47 as u8; co=co+1
57 var ni: i64=0; while name[ni]!=(0 as u8){child[co]=name[ni];co=co+1;ni=ni+1} child[co]=0 as u8
58 if typ == DT_DIR { nested = 1 } else { cl_unlink(child) }
59 }
60 if reclen <= 0 { off = n } else { off = off + reclen }
61 }
62 }
63 }
64 sys_close(fd)
65 if nested == 1 { return 0 } // a nested subdir remains (multi-file extract) -> leave the folder
66 if cl_rmdir(dir) == 0 { return 1 }
67 return 0
68}
69
70func main() -> i64 {
71 let idx: *u8 = sys_mmap(K_MAGIC_65536); let idxn: i64 = cl_read(IDX, idx, K_MAGIC_65536)
72 let fd: i64 = sys_openat_rd(O); if fd<0 { cl_p("CLEAN FAIL: no outdir\n" as *u8); sys_exit(1); return 1 }
73 let gbuf: *u8 = sys_mmap(DBUF); let sub: *u8 = sys_mmap(K_MAGIC_1024); let probe: *u8 = sys_mmap(K_MAGIC_1024); let ctl: *u8 = sys_mmap(64)
74 var kept: i64=0; var deleted: i64=0; var skipped: i64=0
75 cl_p("=== NISHI TORRENT CLEAN (folders NOT in the live index, control!=active) ===\n" as *u8)
76 var go: i64 = 1
77 while go == 1 {
78 let n: i64 = sys_getdents64(fd, gbuf, DBUF)
79 if n <= 0 { go = 0 } else {
80 var off: i64 = 0
81 while off < n {
82 let rec: *u8 = ((gbuf as i64) + off) as *u8
83 let reclen: i64 = dirent_reclen(rec)
84 let name: *u8 = dirent_name(rec)
85 if name[0]!=(46 as u8) {
86 var so: i64=cl_cat(sub,0,O); sub[so]=47 as u8; so=so+1; var ni: i64=0; while name[ni]!=(0 as u8){sub[so]=name[ni];so=so+1;ni=ni+1} sub[so]=0 as u8
87 // is this a torrent folder? (has download.status)
88 var po: i64=cl_cat(probe,0,sub); po=cl_cat(probe,po,"/download.status" as *u8); probe[po]=0 as u8
89 let stfd: i64 = sys_openat_rd(probe)
90 if stfd >= 0 {
91 sys_close(stfd)
92 let nl: i64 = cl_len(name)
93 if cl_idx_has(idx, idxn, name, nl) == 1 { kept = kept + 1; cl_p(" KEEP " as *u8); cl_p(name); cl_p(" (in index)\n" as *u8) }
94 else {
95 var cp: i64=cl_cat(probe,0,sub); cp=cl_cat(probe,cp,"/download.control" as *u8); probe[cp]=0 as u8
96 let cn: i64 = cl_read(probe, ctl, 63)
97 var active: i64 = 0; if cn>0 { let c0: i64=ctl[0] as i64; if c0==97 { active=1 } if c0==112 { active=1 } } // 'a'ctive or 'p'aused -> defensive skip
98 if active == 1 { skipped = skipped + 1; cl_p(" SKIP " as *u8); cl_p(name); cl_p(" (control=active/paused, not in index)\n" as *u8) }
99 else {
100 if cl_rmtree_flat(sub) == 1 { deleted = deleted + 1; cl_p(" DELETE " as *u8); cl_p(name); cl_p("\n" as *u8) }
101 else { skipped = skipped + 1; cl_p(" SKIP " as *u8); cl_p(name); cl_p(" (nested files remain)\n" as *u8) }
102 }
103 }
104 }
105 }
106 if reclen <= 0 { off = n } else { off = off + reclen }
107 }
108 }
109 }
110 sys_close(fd)
111 cl_p("--- summary --- kept=" as *u8); cl_n(kept); cl_p(" deleted=" as *u8); cl_n(deleted); cl_p(" skipped=" as *u8); cl_n(skipped); cl_p("\n" as *u8)
112 sys_exit(0); return 0
113}