nx_fileop.nx source
↩ module page · 77 lines · 5897 B
1// nx_fileop.nx -- sovereign file-op CLI (retires Remove-Item / mv). Composes the canonical nx_fio + nx_syscalls's
2// sys_renameat. Usage:
3// nx_fileop rm <path> delete a file (refuses empty / "/" )
4// nx_fileop mv <src> <dst> atomic rename (sys_renameat)
5// nx_fileop exists <path> existence check (exit 0 if present, 3 if absent)
6// nx_fileop prepare-install-intent INTENT SOURCE LIVE CANDIDATE BACKUP NEW_SHA256 OLD_SHA256 MODE_DECIMAL BUDGET_BYTES
7// Creates or verifies an identical immutable intent; never publishes an artifact.
8// No shell. license_tier: ORIGINAL
9import "nx_file_install.nx"
10import "nx_gate.nx"
11
12func fo_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if a[i]!=b[i] { return 0 } i=i+1 } if b[i]!=(0 as u8) { return 0 } return 1 }
13func fo_bad_path(p: *u8) -> i64 { if p[0]==(0 as u8) { return 1 } if p[0]==(0x2f as u8) { if p[1]==(0 as u8) { return 1 } } return 0 }
14
15func main(argc: i64, argv: *i64) -> i64 {
16 if argc>=2 && fo_streq(argv[1] as *u8,"prepare-install-intent")==1 { return fo_prepare_intent(argc,argv) }
17 if argc < 3 { gw("usage: nx_fileop rm <path> | mv <src> <dst> | exists <path> | prepare-install-intent INTENT SOURCE LIVE CANDIDATE BACKUP NEW_SHA256 OLD_SHA256 MODE_DECIMAL BUDGET_BYTES\n" as *u8); sys_exit(1); return 1 }
18 let op: *u8 = argv[1] as *u8
19 let a2: *u8 = argv[2] as *u8
20
21 if fo_streq(op, "rm" as *u8)==1 {
22 if fo_bad_path(a2)==1 { gw("nx_fileop rm: refusing empty / root path\n" as *u8); sys_exit(1); return 1 }
23 if fio_exists(a2)==0 { gw("nx_fileop rm: not found " as *u8); gw(a2); gw("\n" as *u8); sys_exit(2); return 2 }
24 let r: i64 = fio_unlink(a2)
25 if r==0 { gw("nx_fileop rm OK " as *u8); gw(a2); gw("\n" as *u8); sys_exit(0); return 0 }
26 gw("nx_fileop rm FAIL " as *u8); gw(a2); gw(" rc=" as *u8); gn(r); gw("\n" as *u8); sys_exit(3); return 3
27 }
28 if fo_streq(op, "mv" as *u8)==1 {
29 if argc < 4 { gw("usage: nx_fileop mv <src> <dst>\n" as *u8); sys_exit(1); return 1 }
30 let dst: *u8 = argv[3] as *u8
31 if fo_bad_path(a2)==1 { gw("nx_fileop mv: refusing empty / root src\n" as *u8); sys_exit(1); return 1 }
32 let r: i64 = sys_renameat(a2, dst)
33 if r==0 { gw("nx_fileop mv OK " as *u8); gw(a2); gw(" -> " as *u8); gw(dst); gw("\n" as *u8); sys_exit(0); return 0 }
34 gw("nx_fileop mv FAIL rc=" as *u8); gn(r); gw("\n" as *u8); sys_exit(3); return 3
35 }
36 if fo_streq(op, "exists" as *u8)==1 {
37 if fio_exists(a2)==1 { gw("nx_fileop exists YES " as *u8); gw(a2); gw("\n" as *u8); sys_exit(0); return 0 }
38 gw("nx_fileop exists NO " as *u8); gw(a2); gw("\n" as *u8); sys_exit(3); return 3
39 }
40 gw("nx_fileop: unknown op " as *u8); gw(op); gw("\n" as *u8); sys_exit(1); return 1
41}
42
43// Preparation records intent only; publication remains a separately authorized operation.
44func fo_intent_error(stage:*u8,code:i64)->i64 {
45 gw("{\"schema\":\"nishi.install-intent.v1\",\"status\":\"refused\",\"published\":false,\"stage\":\"");gw(stage);gw("\",\"code\":");gn(code);gw("}\n");return 3
46}
47func fo_decimal(s:*u8,max:i64,out:*i64)->i64 {
48 if s[0]==0 as u8 { return 0 };var i:i64=0;var v:i64=0
49 while s[i]!=0 as u8 { let d:i64=(s[i] as i64)-48;if d<0 || d>9 || v>(max-d)/10 { return 0 };v=v*10+d;i=i+1 }
50 out[0]=v;return 1
51}
52func fo_digest(s:*u8,out:*u8)->i64 {
53 if fi_len(s)!=64 { return 0 };var i:i64=0
54 while i<64 { let c:i64=s[i] as i64;var v:i64=0-1
55 if c>=48 && c<=57 { v=c-48 };if c>=97 && c<=102 { v=c-87 };if c>=65 && c<=70 { v=c-55 };if v<0 { return 0 }
56 if i%2==0 { out[i/2]=(v*16) as u8 } else { out[i/2]=((out[i/2] as i64)+v) as u8 };i=i+1 };return 1
57}
58func fo_plan_matches(p:*NxFileInstallPlan,argv:*i64,newhash:*u8,oldhash:*u8,mode:i64)->i64 {
59 return fo_streq(p.source,argv[3] as *u8)==1 && fo_streq(p.live,argv[4] as *u8)==1 && fo_streq(p.candidate,argv[5] as *u8)==1 && fo_streq(p.backup,argv[6] as *u8)==1 && p.mode==mode && fi_same(p.expected_new,newhash,FI_DIGEST_BYTES)==1 && fi_same(p.expected_old,oldhash,FI_DIGEST_BYTES)==1
60}
61func fo_prepare_intent(argc:i64,argv:*i64)->i64 {
62 if argc!=11 { return fo_intent_error("arguments",FIO_EINVAL) }
63 let capacity:i64=FI_DIGEST_BYTES*2+__size_of(i64)*2+__size_of(NxFileInstallPlan)+__size_of(NxFileWriteResult)
64 let mem:*u8=sys_mmap(capacity);if (mem as i64)<0 { return fo_intent_error("allocation",mem as i64) }
65 let newhash:*u8=mem;let oldhash:*u8=mem+FI_DIGEST_BYTES;let values:*i64=(mem+FI_DIGEST_BYTES*2) as *i64
66 let p:*NxFileInstallPlan=(mem+FI_DIGEST_BYTES*2+__size_of(i64)*2) as *NxFileInstallPlan
67 let w:*NxFileWriteResult=((p as *u8)+__size_of(NxFileInstallPlan)) as *NxFileWriteResult
68 fi_plan_init(p)
69 var rc:i64=FIO_EINVAL;var stage:*u8="input";var reused:i64=0
70 if fi_path_valid(argv[2] as *u8)==1 && fo_streq(argv[2] as *u8,argv[3] as *u8)==0 && fo_streq(argv[2] as *u8,argv[4] as *u8)==0 && fo_streq(argv[2] as *u8,argv[5] as *u8)==0 && fo_streq(argv[2] as *u8,argv[6] as *u8)==0 && fo_digest(argv[7] as *u8,newhash)==1 && fo_digest(argv[8] as *u8,oldhash)==1 && fo_decimal(argv[9] as *u8,0x1ff,values)==1 && fo_decimal(argv[10] as *u8,9223372036854775807,((values as *u8)+__size_of(i64)) as *i64)==1 && values[1]>0 {
71 stage="intent-create";rc=fi_plan_create(argv[2] as *u8,argv[3] as *u8,argv[4] as *u8,argv[5] as *u8,argv[6] as *u8,values[0],newhash,oldhash,values[1],w)
72 if rc==FIO_EEXIST { stage="existing-intent";rc=fi_plan_read(argv[2] as *u8,values[1],p);if rc==0 { if fo_plan_matches(p,argv,newhash,oldhash,values[0])==1 { reused=1 } else { rc=FIO_EBADMSG } };fi_plan_close(p) }
73 }
74 sys_munmap(mem,capacity)
75 if rc!=0 { return fo_intent_error(stage,rc) }
76 if reused==1 { gw("{\"schema\":\"nishi.install-intent.v1\",\"status\":\"existing-identical\",\"published\":false}\n") } else { gw("{\"schema\":\"nishi.install-intent.v1\",\"status\":\"created-durable\",\"published\":false}\n") };return 0
77}