nx_mgmt_private_response_candidate_t189.nx source
↩ module page · 137 lines · 7595 B
1// Private response handoff used by nx_mgmt_call; never emits upstream body bytes.
2// Trusted parent directory; exclusive pending name serializes cooperating callers.
3// Does not claim hostile-directory race resistance or crash-orphan recovery.
4import "nx_fio.nx"
5const MPR_NOENT:i64=0-2
6const MPR_BAD_RESPONSE:i64=0-74
7struct NxMgmtPrivateResponse{
8 path:*u8,tmp:*u8,parent:*u8,allocation:i64,fd:i64,dirfd:i64,
9 code:i64,status:i64,sent:i64,visible:i64,durable:i64,body_bytes:i64,
10 owned:i64,dev:i64,ino:i64,stage:*u8,
11 stat:*u8,write:*NxFileWriteResult,
12}
13func mpr_len(s:*u8)->i64{var n:i64=0;while s[n]!=(0 as u8){n=n+1};return n}
14func mpr_word(s:*u8,o:i64)->i64{return *((s+o) as *i64)}
15func mpr_init(r:*NxMgmtPrivateResponse)->i64{
16 r.path=0 as *u8;r.tmp=0 as *u8;r.parent=0 as *u8;r.allocation=0;r.fd=0-1;r.dirfd=0-1
17 r.code=0;r.status=0;r.sent=0;r.visible=0;r.durable=0;r.body_bytes=0;r.owned=0;r.dev=0;r.ino=0
18 r.stage="input";r.stat=0 as *u8;r.write=0 as *NxFileWriteResult;return 0
19}
20// Any existing final entry (including symlink/FIFO) is occupied. Nonblocking,
21// no-follow inspection cannot be trapped waiting on a FIFO or dangling symlink.
22func mpr_absent(path:*u8)->i64{
23 let fd:i64=__syscall(SYS_OPENAT,AT_FDCWD,path,O_RDONLY|O_NONBLOCK|O_NOFOLLOW|O_CLOEXEC,0,0,0)
24 if fd>=0{let closed:i64=sys_close(fd);if closed!=0{return closed};return FIO_EEXIST}
25 if fd==MPR_NOENT{return 0};return fd
26}
27func mpr_begin(path:*u8,r:*NxMgmtPrivateResponse)->i64{
28 mpr_init(r);r.path=path
29 if (path as i64)==0{return FIO_EINVAL}
30 let n:i64=mpr_len(path);if n<=0||n>(9223372036854775807-NX_STAT_X64_BYTES-__size_of(NxFileWriteResult))/2-17{return FIO_EINVAL}
31 r.code=mpr_absent(path);if r.code!=0{r.stage="destination-occupied-or-unreadable";return r.code}
32 let path_capacity:i64=((n+17)/8)*8
33 r.allocation=path_capacity*2+NX_STAT_X64_BYTES+__size_of(NxFileWriteResult)
34 r.tmp=sys_mmap_try(r.allocation)
35 if (r.tmp as i64)<=0{r.tmp=0 as *u8;r.code=0-12;r.stage="private-workspace";return r.code}
36 r.parent=r.tmp+path_capacity;r.stat=r.parent+path_capacity;r.write=(r.stat+NX_STAT_X64_BYTES) as *NxFileWriteResult
37 let suffix:*u8=".pending";var i:i64=0;var slash:i64=0-1
38 while i<n{r.tmp[i]=path[i];if path[i]==47 as u8{slash=i};i=i+1}
39 var j:i64=0;while suffix[j]!=(0 as u8){r.tmp[n+j]=suffix[j];j=j+1};r.tmp[n+j]=0 as u8
40 if slash<0{r.parent[0]=46 as u8;r.parent[1]=0 as u8}else{
41 var end:i64=slash;if end==0{end=1};i=0;while i<end{r.parent[i]=path[i];i=i+1};r.parent[end]=0 as u8
42 }
43 r.stage="private-parent";r.dirfd=sys_openat_directory(r.parent)
44 if r.dirfd<0{r.code=r.dirfd;return r.code}
45 r.stage="private-reserve";r.fd=sys_openat_exclusive(r.tmp,MODE_0600)
46 if r.fd<0{r.code=r.fd;return r.code};r.owned=1
47 r.code=sys_fstat_fd(r.fd,r.stat)
48 if r.code==0{
49 r.dev=mpr_word(r.stat,NX_STAT_X64_DEVICE_OFFSET);r.ino=mpr_word(r.stat,NX_STAT_X64_INODE_OFFSET)
50 if sys_stat_permissions(r.stat)!=MODE_0600{r.code=0-13}
51 }
52 return r.code
53}
54func mpr_cleanup(r:*NxMgmtPrivateResponse)->i64{
55 if r.fd>=0{let rc:i64=sys_close(r.fd);r.fd=0-1;if r.code==0&&rc!=0{r.code=rc}}
56 if r.owned==1&&r.visible==0{
57 let st:i64=sys_fstatat(r.tmp,r.stat)
58 if st==0&&mpr_word(r.stat,NX_STAT_X64_DEVICE_OFFSET)==r.dev&&mpr_word(r.stat,NX_STAT_X64_INODE_OFFSET)==r.ino{
59 let rc:i64=fio_unlink(r.tmp);if rc==0{r.owned=0}else{if r.code==0{r.code=rc}}
60 }
61 }
62 if r.dirfd>=0{let rc:i64=sys_close(r.dirfd);r.dirfd=0-1;if r.code==0&&rc!=0{r.code=rc}}
63 if (r.tmp as i64)>0{let rc:i64=sys_munmap_direct(r.tmp,r.allocation);r.tmp=0 as *u8;if r.code==0&&rc!=0{r.code=rc}}
64 return r.code
65}
66func mpr_eq(b:*u8,n:i64,s:*u8)->i64{
67 if n!=mpr_len(s){return 0};var i:i64=0
68 while i<n{var a:i64=b[i] as i64;var c:i64=s[i] as i64;if a>=65&&a<=90{a=a+32};if c>=65&&c<=90{c=c+32};if a!=c{return 0};i=i+1};return 1
69}
70func mpr_decimal(b:*u8,n:i64)->i64{
71 if n<=0{return 0-1};var v:i64=0;var i:i64=0
72 while i<n{let d:i64=(b[i] as i64)-48;if d<0||d>9||v>(9223372036854775807-d)/10{return 0-1};v=v*10+d;i=i+1};return v
73}
74// Exact Content-Length response only. Unsupported transfer encoding, duplicate
75// length, incomplete capture and malformed status fail without publishing a body.
76func mpr_body(b:*u8,n:i64,capacity:i64,r:*NxMgmtPrivateResponse)->i64{
77 r.stage="http-framing";r.code=MPR_BAD_RESPONSE
78 if n<13||n>=capacity{return r.code}
79 if mpr_eq(b,7,"HTTP/1.")!=1||(b[7]!=48 as u8&&b[7]!=49 as u8)||b[8]!=32 as u8||b[12]!=32 as u8{return r.code}
80 let status:i64=mpr_decimal(b+9,3);if status<100||status>599{return r.code};r.status=status
81 var p:i64=0
82 while p+1<n{if b[p]==13 as u8&&b[p+1]==10 as u8{break};p=p+1}
83 if p+1>=n{return r.code};p=p+2
84 var length:i64=0-1;var body:i64=0-1
85 while p+1<n{
86 if b[p]==13 as u8&&b[p+1]==10 as u8{body=p+2;break}
87 var end:i64=p;while end+1<n{if b[end]==13 as u8&&b[end+1]==10 as u8{break};end=end+1}
88 if end+1>=n{return r.code}
89 var colon:i64=p;while colon<end&&b[colon]!=58 as u8{colon=colon+1};if colon==end{return r.code}
90 var first:i64=colon+1;while first<end&&(b[first]==32 as u8||b[first]==9 as u8){first=first+1}
91 var last:i64=end;while last>first&&(b[last-1]==32 as u8||b[last-1]==9 as u8){last=last-1}
92 if mpr_eq(b+p,colon-p,"transfer-encoding")==1{return r.code}
93 if mpr_eq(b+p,colon-p,"content-length")==1{
94 if length>=0{return r.code};length=mpr_decimal(b+first,last-first);if length<0{return r.code}
95 }
96 p=end+2
97 }
98 if body<0||length<0||length!=n-body{return r.code}
99 r.body_bytes=length;r.code=0;return body
100}
101func mpr_publish(b:*u8,n:i64,capacity:i64,r:*NxMgmtPrivateResponse)->i64{
102 let body:i64=mpr_body(b,n,capacity,r);if body<0{return r.code}
103 if r.status<200||r.status>=300{r.code=0-71;r.stage="upstream-http-refusal";return r.code}
104 r.stage="private-write";r.code=fio_write_sync_fd(r.fd,b+body,r.body_bytes,r.write);r.fd=0-1
105 if r.code!=0{return r.code}
106 r.stage="private-publish-precondition";r.code=mpr_absent(r.path);if r.code!=0{return r.code}
107 r.code=sys_fstatat(r.tmp,r.stat)
108 if r.code!=0||mpr_word(r.stat,NX_STAT_X64_DEVICE_OFFSET)!=r.dev||mpr_word(r.stat,NX_STAT_X64_INODE_OFFSET)!=r.ino{r.code=0-17;return r.code}
109 r.stage="private-publish";r.code=sys_renameat(r.tmp,r.path);if r.code!=0{return r.code}
110 r.visible=1;r.owned=0;r.stage="private-directory-sync"
111 r.code=fio_write_sync_fd(r.dirfd,0 as *u8,0,r.write);r.dirfd=0-1
112 if r.code==0{r.durable=1;r.stage="private-response-retained"};return r.code
113}
114func mpr_put(s:*u8)->i64{
115 let n:i64=mpr_len(s);var at:i64=0;while at<n{let w:i64=sys_write(1,s+at,n-at);if w==FIO_EINTR{continue};if w<=0{return 0-1};at=at+w};return 0
116}
117func mpr_num(v:i64)->i64{
118 if v<0{if mpr_put("-")!=0{return 0-1}}
119 return mpr_digits(v)
120}
121func mpr_digits(v:i64)->i64{
122 if v<=0-10||v>=10{if mpr_digits(v/10)!=0{return 0-1}}
123 var digit:i64=v%10;if digit<0{digit=0-digit}
124 var w:i64=sys_write(1,"0123456789"+digit,1);while w==FIO_EINTR{w=sys_write(1,"0123456789"+digit,1)};if w!=1{return 0-1};return 0
125}
126func mpr_report(r:*NxMgmtPrivateResponse)->i64{
127 if mpr_put("{\"action\":\"PRIVATE-RESPONSE\",\"upstream_status\":")!=0||mpr_num(r.status)!=0{return 0-1}
128 if mpr_put(",\"request_sent\":")!=0{return 0-1}
129 if r.sent<0{if mpr_put("null")!=0{return 0-1}}else{if mpr_num(r.sent)!=0{return 0-1}}
130 if mpr_put(",\"visible\":")!=0||mpr_num(r.visible)!=0{return 0-1}
131 if mpr_put(",\"durable\":")!=0||mpr_num(r.durable)!=0{return 0-1}
132 if mpr_put(",\"temporary_owned\":")!=0||mpr_num(r.owned)!=0{return 0-1}
133 if mpr_put(",\"body_bytes\":")!=0||mpr_num(r.body_bytes)!=0{return 0-1}
134 if mpr_put(",\"code\":")!=0||mpr_num(r.code)!=0{return 0-1}
135 if mpr_put(",\"stage\":\"")!=0||mpr_put(r.stage)!=0||mpr_put("\"}\n")!=0{return 0-1}
136 return 0
137}