code wiki / _hdl_build / nx_file_install_session_20260910.nx

nx_file_install_session_20260910.nx source

↩ module page · 413 lines · 21537 B

1// Recoverable file installation. An immutable, checksummed intent identifies 2// every pathname and both artifact digests. No process-local pointer is persisted. 3// Runtime/service health is a later acceptance step, not implied by file install. 4import "nx_fio.nx" 5 6// Versioned wire schema widths, independent of host struct layout or I/O policy. 7const FI_MAGIC: *u8="NXFINST1" 8const FI_MAGIC_BYTES: i64=8 9const FI_DIGEST_BYTES: i64=32 10const FI_WORD_BYTES: i64=8 11const FI_PATH_FIELDS: i64=4 12const FI_HEADER_BYTES: i64=FI_MAGIC_BYTES + FI_DIGEST_BYTES*2 + FI_WORD_BYTES*(1+FI_PATH_FIELDS) 13const FI_ENOENT: i64=0-2 14const FI_EACCES: i64=0-13 // Linux errno: recorded target is outside the supplied authorization. 15 16struct NxFileInstallPlan { 17 bytes: *u8, 18 length: i64, 19 source: *u8, 20 live: *u8, 21 candidate: *u8, 22 backup: *u8, 23 expected_new: *u8, 24 expected_old: *u8, 25 mode: i64, 26} 27struct NxFileInstallResult { 28 stage: *u8, 29 code: i64, 30 already_published: i64, 31 io: NxFileWriteResult, 32 replacement: NxFileReplaceResult, 33} 34func fi_len(s: *u8) -> i64 { 35 if (s as i64)==0 { return 0 } 36 var n: i64=0;while s[n]!=(0 as u8) { n=n+1 };return n 37} 38func fi_same(a: *u8,b: *u8,n: i64) -> i64 { 39 var different: i64=0;var i: i64=0 40 while i<n { different=different | ((a[i] as i64) ^ (b[i] as i64));i=i+1 } 41 return (different==0) as i64 42} 43func fi_copy(dst: *u8,src: *u8,n: i64) -> i64 { 44 var i: i64=0;while i<n { dst[i]=src[i];i=i+1 };return n 45} 46// Durable plans cannot depend on the caller's working directory. Registry path 47// resolution must additionally ensure owned parents and no symlink aliases. 48func fi_path_valid(path: *u8) -> i64 { 49 if (path as i64)==0 { return 0 } 50 if path[0]!=(47 as u8) { return 0 } 51 var start: i64=1;var i: i64=1 52 while 1 { 53 if path[i]==(47 as u8) || path[i]==(0 as u8) { 54 let n: i64=i-start 55 if n==0 { return 0 } 56 if n==1 && path[start]==(46 as u8) { return 0 } 57 if n==2 && path[start]==(46 as u8) && path[start+1]==(46 as u8) { return 0 } 58 if path[i]==(0 as u8) { return 1 } 59 start=i+1 60 } 61 i=i+1 62 } 63 return 0 64} 65func fi_paths_distinct(names: *i64) -> i64 { 66 var i: i64=0 67 while i<FI_PATH_FIELDS { 68 let a: *u8=names[i] as *u8;if fi_path_valid(a)==0 { return 0 } 69 var j: i64=i+1 70 while j<FI_PATH_FIELDS { 71 let b: *u8=names[j] as *u8 72 let n: i64=fi_len(a) 73 if n==fi_len(b) && fi_same(a,b,n)==1 { return 0 } 74 j=j+1 75 } 76 i=i+1 77 } 78 return 1 79} 80func fi_word_put(p: *u8,v: i64) -> i64 { 81 var i: i64=0;while i<FI_WORD_BYTES { p[i]=((v>>(i*8))&255) as u8;i=i+1 };return 0 82} 83func fi_word_get(p: *u8) -> i64 { 84 var v: i64=0;var i: i64=0 85 while i<FI_WORD_BYTES { v=v | ((p[i] as i64)<<(i*8));i=i+1 };return v 86} 87func fi_plan_init(p: *NxFileInstallPlan) -> i64 { 88 let raw: *u8=p as *u8;var i: i64=0 89 while i<__size_of(NxFileInstallPlan) { raw[i]=0 as u8;i=i+1 };return 0 90} 91func fi_plan_close(p: *NxFileInstallPlan) -> i64 { 92 var rc: i64=0 93 if (p.bytes as i64)!=0 { rc=sys_munmap(p.bytes,p.length) } 94 fi_plan_init(p);return rc 95} 96// Validation is over a bounded blob. Path pointers are published only after 97// checksum, length, termination and mode validation have all succeeded. 98func fi_plan_decode(bytes: *u8,n: i64,p: *NxFileInstallPlan) -> i64 { 99 if n<FI_HEADER_BYTES+FI_PATH_FIELDS+FI_DIGEST_BYTES { return FIO_EBADMSG } 100 if fi_same(bytes,FI_MAGIC,FI_MAGIC_BYTES)==0 { return FIO_EBADMSG } 101 let digest: *u8=sys_mmap(FI_DIGEST_BYTES) 102 sha256_digest(bytes,n-FI_DIGEST_BYTES,digest) 103 let checked: i64=fi_same(digest,bytes+n-FI_DIGEST_BYTES,FI_DIGEST_BYTES) 104 sys_munmap(digest,FI_DIGEST_BYTES) 105 if checked==0 { return FIO_EBADMSG } 106 let mode_at: i64=FI_MAGIC_BYTES+FI_DIGEST_BYTES*2 107 let mode: i64=fi_word_get(bytes+mode_at) 108 if mode<0 || mode>0x1ff { return FIO_EBADMSG } 109 let sizes: *u8=bytes+mode_at+FI_WORD_BYTES 110 let end: i64=n-FI_DIGEST_BYTES 111 var off: i64=FI_HEADER_BYTES;var i: i64=0 112 while i<FI_PATH_FIELDS { 113 let length: i64=fi_word_get(sizes+i*FI_WORD_BYTES) 114 if length<=1 || length>end-off { return FIO_EBADMSG } 115 var j: i64=0 116 while j<length-1 { if bytes[off+j]==(0 as u8) { return FIO_EBADMSG };j=j+1 } 117 if bytes[off+length-1]!=(0 as u8) { return FIO_EBADMSG } 118 off=off+length;i=i+1 119 } 120 if off!=end { return FIO_EBADMSG } 121 let names: *i64=sys_mmap(FI_PATH_FIELDS*__size_of(i64)) as *i64 122 off=FI_HEADER_BYTES;i=0 123 while i<FI_PATH_FIELDS { names[i]=(bytes+off) as i64;off=off+fi_word_get(sizes+i*FI_WORD_BYTES);i=i+1 } 124 let distinct: i64=fi_paths_distinct(names) 125 sys_munmap(names as *u8,FI_PATH_FIELDS*__size_of(i64)) 126 if distinct==0 { return FIO_EBADMSG } 127 p.bytes=bytes;p.length=n;p.mode=mode 128 p.expected_new=bytes+FI_MAGIC_BYTES;p.expected_old=p.expected_new+FI_DIGEST_BYTES 129 off=FI_HEADER_BYTES;p.source=bytes+off 130 off=off+fi_word_get(sizes);p.live=bytes+off 131 off=off+fi_word_get(sizes+FI_WORD_BYTES);p.candidate=bytes+off 132 off=off+fi_word_get(sizes+FI_WORD_BYTES*2);p.backup=bytes+off 133 return 0 134} 135// Callers supply the admitted record budget. It is not inferred from untrusted 136// file lengths. The file region owns descriptor cleanup on every read failure. 137func fi_plan_read(path: *u8,budget: i64,p: *NxFileInstallPlan) -> i64 { 138 fi_plan_init(p) 139 let r: *NxFileReadRegion=sys_mmap(__size_of(NxFileReadRegion)) as *NxFileReadRegion 140 fio_region_init(r);var rc: i64=fio_region_open(path,r) 141 var bytes: *u8=0 as *u8 142 let n: i64=r.total 143 if rc==0 && (n<=0 || n>budget) { rc=FIO_EINVAL } 144 if rc==0 { 145 bytes=sys_mmap(n) 146 if (bytes as i64)<0 { rc=bytes as i64;bytes=0 as *u8 } 147 } 148 if rc==0 { let got: i64=fio_region_next(r,bytes,n);if got!=n { rc=got;if rc>=0 { rc=FIO_EIO } } } 149 fio_region_close(r) 150 if rc==0 { rc=r.code } 151 sys_munmap(r as *u8,__size_of(NxFileReadRegion)) 152 if rc==0 { rc=fi_plan_decode(bytes,n,p) } 153 if rc!=0 && (bytes as i64)!=0 { sys_munmap(bytes,n) } 154 return rc 155} 156// Exclusive creation preserves operation identity. An interrupted partial record 157// fails checksum validation; it is retained, never silently overwritten on retry. 158func fi_plan_create(path: *u8,source: *u8,live: *u8,candidate: *u8,backup: *u8,mode: i64,new_digest: *u8,old_digest: *u8,budget: i64,out: *NxFileWriteResult) -> i64 { 159 out.stage="intent-input";out.code=FIO_EINVAL;out.written=0;out.close_code=0 160 if (new_digest as i64)==0 || (old_digest as i64)==0 || mode<0 || mode>0x1ff { return out.code } 161 let names: *i64=sys_mmap(FI_PATH_FIELDS*__size_of(i64)) as *i64 162 names[0]=source as i64;names[1]=live as i64;names[2]=candidate as i64;names[3]=backup as i64 163 if fi_paths_distinct(names)==0 { sys_munmap(names as *u8,FI_PATH_FIELDS*__size_of(i64));return out.code } 164 var n: i64=FI_HEADER_BYTES+FI_DIGEST_BYTES;var i: i64=0;var valid: i64=1 165 while i<FI_PATH_FIELDS { 166 let length: i64=fi_len(names[i] as *u8) 167 if length<=0 || n>budget || length>=budget-n { valid=0;break } 168 n=n+length+1;i=i+1 169 } 170 if valid==0 { sys_munmap(names as *u8,FI_PATH_FIELDS*__size_of(i64));return out.code } 171 let b: *u8=sys_mmap(n) 172 fi_copy(b,FI_MAGIC,FI_MAGIC_BYTES) 173 fi_copy(b+FI_MAGIC_BYTES,new_digest,FI_DIGEST_BYTES) 174 fi_copy(b+FI_MAGIC_BYTES+FI_DIGEST_BYTES,old_digest,FI_DIGEST_BYTES) 175 let mode_at: i64=FI_MAGIC_BYTES+FI_DIGEST_BYTES*2 176 fi_word_put(b+mode_at,mode) 177 var off: i64=FI_HEADER_BYTES;i=0 178 while i<FI_PATH_FIELDS { 179 let name: *u8=names[i] as *u8;let length: i64=fi_len(name)+1 180 fi_word_put(b+mode_at+FI_WORD_BYTES*(i+1),length) 181 fi_copy(b+off,name,length);off=off+length;i=i+1 182 } 183 sys_munmap(names as *u8,FI_PATH_FIELDS*__size_of(i64)) 184 sha256_digest(b,n-FI_DIGEST_BYTES,b+n-FI_DIGEST_BYTES) 185 out.stage="intent-create" 186 let fd: i64=sys_openat_exclusive(path,MODE_0600) 187 if fd<0 { out.code=fd } else { 188 out.code=fio_write_sync_fd(fd,b,n,out) 189 if out.code==0 { 190 let directory: *NxFileWriteResult=sys_mmap(__size_of(NxFileWriteResult)) as *NxFileWriteResult 191 out.code=fio_sync_parent(path,directory) 192 if out.code!=0 { out.stage=directory.stage;out.close_code=directory.close_code } 193 sys_munmap(directory as *u8,__size_of(NxFileWriteResult)) 194 } 195 } 196 sys_munmap(b,n) 197 if out.code==0 { out.stage="intent-durable" } 198 return out.code 199} 200func fi_sync_existing(path: *u8,out: *NxFileWriteResult) -> i64 { 201 out.stage="sync-open";out.code=0;out.written=0;out.close_code=0 202 let fd: i64=sys_openat_rd(path) 203 if fd<0 { out.code=fd;return fd } 204 return fio_write_sync_fd(fd,0 as *u8,0,out) 205} 206// Existing prepared files are reused only after digest verification. Partial or 207// conflicting files are retained and reported; there is no automatic truncation. 208func fi_prepare_expected(source: *u8,path: *u8,digest: *u8,mode: i64,buffer: *u8,capacity: i64,out: *NxFilePrepareResult) -> i64 { 209 out.stage="prepared-identity" 210 var rc: i64=fio_verify_sha256(path,digest,buffer,capacity) 211 if rc==FI_ENOENT { 212 rc=fio_prepare_copy(source,path,mode,buffer,capacity,out) 213 if rc==0 { out.stage="prepared-identity";rc=fio_verify_sha256(path,digest,buffer,capacity) } 214 } 215 if rc==0 { out.stage="prepared-mode";rc=nx_chmod(path,mode) } 216 if rc==0 { 217 let sync: *NxFileWriteResult=sys_mmap(__size_of(NxFileWriteResult)) as *NxFileWriteResult 218 rc=fi_sync_existing(path,sync);out.stage=sync.stage;out.destination_close=sync.close_code 219 sys_munmap(sync as *u8,__size_of(NxFileWriteResult)) 220 } 221 out.code=rc 222 if rc!=0 { return rc } 223 out.stage="prepared";out.durable=1;return 0 224} 225func fi_install_owned(p: *NxFileInstallPlan,buffer: *u8,capacity: i64,out: *NxFileInstallResult) -> i64 { 226 out.stage="observe-live" 227 var current: i64=fio_verify_sha256(p.live,p.expected_new,buffer,capacity) 228 if current==0 { 229 out.already_published=1;out.replacement.publication.visible=1 230 out.stage="verify-existing-backup" 231 out.code=fio_verify_sha256(p.backup,p.expected_old,buffer,capacity) 232 if out.code!=0 { return out.code } 233 out.stage="reconcile-durability" 234 out.code=fi_sync_existing(p.live,&out.io) 235 if out.code==0 { out.stage="live-directory-sync";out.code=fio_sync_parent(p.live,&out.io) } 236 if out.code==0 { out.stage="candidate-directory-sync";out.code=fio_sync_parent(p.candidate,&out.io) } 237 if out.code==0 { out.stage="backup-file-sync";out.code=fi_sync_existing(p.backup,&out.io) } 238 if out.code==0 { out.stage="backup-directory-sync";out.code=fio_sync_parent(p.backup,&out.io) } 239 if out.code==0 { out.stage="published-reconciled";out.replacement.publication.durable=1 } 240 return out.code 241 } 242 if current!=FIO_EBADMSG { out.code=current;return current } 243 out.stage="verify-original-live";out.code=fio_verify_sha256(p.live,p.expected_old,buffer,capacity) 244 if out.code!=0 { return out.code } 245 out.stage="prepare-candidate" 246 out.code=fi_prepare_expected(p.source,p.candidate,p.expected_new,p.mode,buffer,capacity,&out.replacement.candidate) 247 if out.code!=0 { return out.code } 248 out.stage="prepare-backup" 249 out.code=fi_prepare_expected(p.live,p.backup,p.expected_old,p.mode,buffer,capacity,&out.replacement.backup) 250 if out.code!=0 { return out.code } 251 out.stage="backup-directory-sync";out.code=fio_sync_parent(p.backup,&out.io) 252 if out.code!=0 { return out.code } 253 out.stage="publish";out.code=fio_publish_candidate(p.candidate,p.live,&out.replacement.publication) 254 if out.code==0 { out.stage="published" };return out.code 255} 256// plan_path is the operation handle. Every retry reopens its recorded inputs; 257// it never infers success from existence or repeats a copy into the live file. 258func fi_install_core(plan_path: *u8,record_budget: i64,buffer: *u8,capacity: i64,allowed_live: *u8,expected_intent: *u8,out: *NxFileInstallResult) -> i64 { 259 out.stage="intent-read";out.code=0;out.already_published=0;fio_replace_init(&out.replacement) 260 out.io.stage="not-started";out.io.code=0;out.io.written=0;out.io.close_code=0 261 let p: *NxFileInstallPlan=sys_mmap(__size_of(NxFileInstallPlan)) as *NxFileInstallPlan 262 fi_plan_init(p) 263 out.code=fi_plan_read(plan_path,record_budget,p) 264 if out.code==0 && (expected_intent as i64)!=0 { 265 // Bind authorization to the exact decoded bytes, not a second pathname 266 // read that could observe a different intent between check and use. 267 out.stage="intent-identity" 268 let digest: *u8=sys_mmap(FI_DIGEST_BYTES) 269 sha256_digest(p.bytes,p.length,digest) 270 if fi_same(digest,expected_intent,FI_DIGEST_BYTES)==0 { out.code=FIO_EBADMSG } 271 sys_munmap(digest,FI_DIGEST_BYTES) 272 if out.code==0 { 273 out.stage="target-binding" 274 let n: i64=fi_len(allowed_live) 275 if n!=fi_len(p.live) { out.code=FI_EACCES } 276 else { if fi_same(allowed_live,p.live,n)==0 { out.code=FI_EACCES } } 277 } 278 } 279 if out.code==0 { 280 out.code=fio_target_lock_acquire(p.live,&out.replacement.lock) 281 if out.code!=0 { out.stage=out.replacement.lock.stage } 282 else { 283 // The intent must be durable even if its creator lost the response 284 // or exited between file fsync and directory fsync. 285 out.stage="intent-durability";out.code=fi_sync_existing(plan_path,&out.io) 286 if out.code==0 { out.stage="intent-directory-sync";out.code=fio_sync_parent(plan_path,&out.io) } 287 if out.code==0 { fi_install_owned(p,buffer,capacity,out) } 288 let released: i64=fio_target_lock_release(&out.replacement.lock) 289 if out.code==0 && released!=0 { out.code=released;out.stage=out.replacement.lock.stage } 290 } 291 } 292 fi_plan_close(p);sys_munmap(p as *u8,__size_of(NxFileInstallPlan)) 293 return out.code 294} 295 296// Privileged local compatibility entry point. Management-facing execution must 297// use fi_install_bound with a target resolved from its authorized registry. 298func fi_install(plan_path: *u8,record_budget: i64,buffer: *u8,capacity: i64,out: *NxFileInstallResult) -> i64 { 299 return fi_install_core(plan_path,record_budget,buffer,capacity,0 as *u8,0 as *u8,out) 300} 301func fi_install_bound(plan_path: *u8,record_budget: i64,buffer: *u8,capacity: i64,allowed_live: *u8,expected_intent: *u8,out: *NxFileInstallResult) -> i64 { 302 if fi_path_valid(allowed_live)==0 || (expected_intent as i64)==0 { 303 out.stage="binding-input";out.code=FIO_EINVAL;out.already_published=0 304 fio_replace_init(&out.replacement) 305 out.io.stage="not-started";out.io.code=0;out.io.written=0;out.io.close_code=0 306 return out.code 307 } 308 return fi_install_core(plan_path,record_budget,buffer,capacity,allowed_live,expected_intent,out) 309} 310 311// A caller may keep this session across runtime acceptance and rollback. Only 312// cooperating writers using the same registry-resolved canonical live path are 313// excluded; this is not a filesystem sandbox or protection from arbitrary writers. 314struct NxFileInstallSession { 315 plan: NxFileInstallPlan, 316 lock: NxFileTargetLock, 317 stage: *u8, 318 code: i64, 319 held: i64, 320} 321func fi_session_init(s:*NxFileInstallSession)->i64 { 322 fi_plan_init(&s.plan);fio_target_lock_init(&s.lock) 323 s.stage="session-input";s.code=FIO_EINVAL;s.held=0;return 0 324} 325func fi_session_bind(p:*NxFileInstallPlan,allowed_live:*u8,expected_intent:*u8)->i64 { 326 if fi_path_valid(allowed_live)==0 || (expected_intent as i64)==0 { return FIO_EINVAL } 327 let digest:*u8=sys_mmap(FI_DIGEST_BYTES) 328 sha256_digest(p.bytes,p.length,digest) 329 let same:i64=fi_same(digest,expected_intent,FI_DIGEST_BYTES) 330 sys_munmap(digest,FI_DIGEST_BYTES) 331 if same==0 { return FIO_EBADMSG } 332 let n:i64=fi_len(allowed_live) 333 if n!=fi_len(p.live) { return FI_EACCES } 334 if fi_same(allowed_live,p.live,n)==0 { return FI_EACCES };return 0 335} 336func fi_session_close(s:*NxFileInstallSession)->i64 { 337 let rc:i64=fio_target_lock_release(&s.lock);s.held=0 338 let closed:i64=fi_plan_close(&s.plan) 339 if rc!=0 { s.code=rc;s.stage="session-unlock";return rc } 340 if closed!=0 { s.code=closed;s.stage="session-plan-close";return closed } 341 s.stage="session-closed";s.code=0;return 0 342} 343// Initialize once before first begin. Reopening an active session refuses without 344// destroying its lock or plan. On failure close is still safe and required. 345func fi_session_begin(path:*u8,budget:i64,allowed_live:*u8,expected_intent:*u8,s:*NxFileInstallSession)->i64 { 346 if s.held!=0 || s.lock.fd>=0 || (s.plan.bytes as i64)!=0 { return FIO_EEXIST } 347 s.stage="session-input";s.code=FIO_EINVAL 348 if fi_path_valid(path)==0 || budget<=0 || fi_path_valid(allowed_live)==0 || (expected_intent as i64)==0 { return s.code } 349 s.stage="intent-read";s.code=fi_plan_read(path,budget,&s.plan) 350 if s.code!=0 { return s.code } 351 s.stage="intent-binding";s.code=fi_session_bind(&s.plan,allowed_live,expected_intent) 352 if s.code!=0 { return s.code } 353 s.stage="session-lock";s.code=fio_target_lock_acquire(s.plan.live,&s.lock) 354 if s.code!=0 { return s.code };s.held=1 355 let io:*NxFileWriteResult=sys_mmap(__size_of(NxFileWriteResult)) as *NxFileWriteResult 356 s.stage="intent-durability";s.code=fi_sync_existing(path,io) 357 if s.code==0 { s.code=fio_sync_parent(path,io) } 358 sys_munmap(io as *u8,__size_of(NxFileWriteResult)) 359 if s.code==0 { s.stage="session-ready" };return s.code 360} 361func fi_session_result_init(out:*NxFileInstallResult)->i64 { 362 out.stage="session-input";out.code=FIO_EINVAL;out.already_published=0 363 fio_replace_init(&out.replacement) 364 out.io.stage="not-started";out.io.code=0;out.io.written=0;out.io.close_code=0;return 0 365} 366func fi_session_publish(s:*NxFileInstallSession,buffer:*u8,capacity:i64,out:*NxFileInstallResult)->i64 { 367 fi_session_result_init(out) 368 if s.held!=1 || s.lock.fd<0 || s.code!=0 || (buffer as i64)==0 || capacity<=0 { return out.code } 369 return fi_install_owned(&s.plan,buffer,capacity,out) 370} 371// Rollback is another approved immutable intent, preserving the rejected artifact 372// in its own backup. It must invert this session's digests and keep the same target. 373func fi_session_reverse(s:*NxFileInstallSession,path:*u8,budget:i64,expected_intent:*u8,buffer:*u8,capacity:i64,out:*NxFileInstallResult)->i64 { 374 fi_session_result_init(out) 375 if fi_path_valid(path)==0 || budget<=0 || (expected_intent as i64)==0 { return out.code } 376 if s.held!=1 || s.lock.fd<0 || s.code!=0 || (buffer as i64)==0 || capacity<=0 { return out.code } 377 let reverse:*NxFileInstallPlan=sys_mmap(__size_of(NxFileInstallPlan)) as *NxFileInstallPlan 378 fi_plan_init(reverse);out.stage="reverse-intent-read";out.code=fi_plan_read(path,budget,reverse) 379 if out.code==0 { out.stage="reverse-intent-binding";out.code=fi_session_bind(reverse,s.plan.live,expected_intent) } 380 if out.code==0 { 381 out.stage="reverse-digest-pair" 382 if fi_same(reverse.expected_new,s.plan.expected_old,FI_DIGEST_BYTES)==0 || fi_same(reverse.expected_old,s.plan.expected_new,FI_DIGEST_BYTES)==0 { out.code=FIO_EBADMSG } 383 } 384 if out.code==0 { out.stage="reverse-intent-durability";out.code=fi_sync_existing(path,&out.io) } 385 if out.code==0 { out.code=fio_sync_parent(path,&out.io) } 386 if out.code==0 { fi_install_owned(reverse,buffer,capacity,out) } 387 fi_plan_close(reverse);sys_munmap(reverse as *u8,__size_of(NxFileInstallPlan));return out.code 388} 389 390// Prepare the approved rollback inputs before publishing NEW. This verifies the 391// inverse identity pair while OLD is still live; no live pathname is replaced. 392func fi_session_prepare_reverse(s:*NxFileInstallSession,path:*u8,budget:i64,expected_intent:*u8,buffer:*u8,capacity:i64,out:*NxFileInstallResult)->i64 { 393 fi_session_result_init(out) 394 if s.held!=1 || s.lock.fd<0 || s.code!=0 || fi_path_valid(path)==0 || budget<=0 || (expected_intent as i64)==0 || (buffer as i64)==0 || capacity<=0 { return out.code } 395 let p:*NxFileInstallPlan=sys_mmap(__size_of(NxFileInstallPlan)) as *NxFileInstallPlan 396 if (p as i64)<0 { out.stage="reverse-admission-allocation";out.code=p as i64;return out.code } 397 fi_plan_init(p);out.stage="reverse-admission-read";out.code=fi_plan_read(path,budget,p) 398 if out.code==0 { out.stage="reverse-admission-binding";out.code=fi_session_bind(p,s.plan.live,expected_intent) } 399 if out.code==0 { 400 out.stage="reverse-admission-identities" 401 if fi_same(p.expected_new,s.plan.expected_old,FI_DIGEST_BYTES)==0 || fi_same(p.expected_old,s.plan.expected_new,FI_DIGEST_BYTES)==0 { out.code=FIO_EBADMSG } 402 if fi_len(p.source)!=fi_len(s.plan.backup) { out.code=FI_EACCES } 403 else { if fi_same(p.source,s.plan.backup,fi_len(p.source))==0 { out.code=FI_EACCES } } 404 } 405 if out.code==0 { out.stage="reverse-intent-sync";out.code=fi_sync_existing(path,&out.io) } 406 if out.code==0 { out.code=fio_sync_parent(path,&out.io) } 407 if out.code==0 { out.stage="reverse-candidate-prepare";out.code=fi_prepare_expected(s.plan.live,p.candidate,p.expected_new,p.mode,buffer,capacity,&out.replacement.candidate) } 408 if out.code==0 { out.code=fio_sync_parent(p.candidate,&out.io) } 409 if out.code==0 { out.stage="reverse-history-prepare";out.code=fi_prepare_expected(s.plan.source,p.backup,p.expected_old,p.mode,buffer,capacity,&out.replacement.backup) } 410 if out.code==0 { out.code=fio_sync_parent(p.backup,&out.io) } 411 if out.code==0 { out.stage="reverse-ready" } 412 fi_plan_close(p);sys_munmap(p as *u8,__size_of(NxFileInstallPlan));return out.code 413}