; ------------------------------------------------------------------------
; HeavyThing x86_64 assembly language library and showcase programs
; Copyright © 2015-2018 2 Ton Digital
; Homepage: https://2ton.com.au/
; Author: Jeff Marrison <jeff@2ton.com.au>
;
; This file is part of the HeavyThing library.
;
; HeavyThing is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License, or
; (at your option) any later version.
;
; HeavyThing is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License along
; with the HeavyThing library. If not, see <http://www.gnu.org/licenses/>.
; ------------------------------------------------------------------------
; fileshadow.inc: "read/modify" either mmap-backed or plain memory
; backed "file", but keep modifications separate, either also in
; memory or file-based as well
;
; Burning Purpose: without actually modifying a read-only source, our
; interface allows for insert, delete, overwrite as a "shadow", such that
; post-modification calls to seek/read gives the "new version". In addition
; we allow "commits" to then overwrite the original.
;
if used fileshadow$new | used fileshadow$new_file | defined include_everything
fileshadow_base_ofs = 0 ; our read-only source
fileshadow_size_ofs = 8 ; our read-only source size
fileshadow_privmapped_ofs = 16 ; if file backed, this is set.
fileshadow_destroycb_ofs = 24 ; function to call (if nonzero) before destroy completes
fileshadow_destroycbarg_ofs = 32
fileshadow_permitmods_ofs = 40 ; bool
fileshadow_pos_ofs = 48 ; current position (not used for _offset functions)
fileshadow_mtree_ofs = 56 ; mtree for modifications, null if !permitmods
fileshadow_filesize_ofs = 64 ; computed filesize (either read-only source size, or modmap based)
fileshadow_size = 72
fileshadow_modmap_size_ofs = 0
fileshadow_modmap_offset_ofs = 8
fileshadow_modmap_inheap_ofs = 16 ; (dword)
fileshadow_modmap_size = 20
end if
if used fileshadow$new | defined include_everything
; three arguments: rdi == buffer, rsi == length of same, edx == bool allow modifications
falign
fileshadow$new:
prolog fileshadow$new
push rdi rsi rdx
mov edi, fileshadow_size
call heap$alloc_clear
pop rdx rsi rdi
mov [rax+fileshadow_base_ofs], rdi
mov [rax+fileshadow_size_ofs], rsi
mov [rax+fileshadow_permitmods_ofs], edx
mov [rax+fileshadow_filesize_ofs], rsi
test edx, edx
jnz .modmap
epilog
calign
.modmap:
push rax
xor edi, edi
call mtree$new_anon
mov rdx, rax
pop rax
mov [rax+fileshadow_mtree_ofs], rdx
epilog
end if
if used fileshadow$new_file | defined include_everything
; two arguments: rdi == string filename, esi == bool allow modifications
falign
fileshadow$new_file:
prolog fileshadow$new_file
push rsi
xor esi, esi
call privmapped$new
push rax
mov edi, fileshadow_size
call heap$alloc_clear
pop rdi rsi
mov [rax+fileshadow_permitmods_ofs], esi
mov [rax+fileshadow_privmapped_ofs], rdi
test rdi, rdi
jz .nofile
mov rdx, [rdi+privmapped_base_ofs]
mov rcx, [rdi+privmapped_size_ofs]
mov [rax+fileshadow_base_ofs], rdx
mov [rax+fileshadow_size_ofs], rcx
mov [rax+fileshadow_filesize_ofs], rcx
; if allow mods, then create our modmap
cmp dword [rax+fileshadow_permitmods_ofs], 0
jne .modmap
.nofile:
epilog
cleartext .shadow, '.shadow'
calign
.modmap:
push rbx
mov rbx, rax
; rdi is the privmapped object, which also has the filename:
mov rdi, [rdi+privmapped_filename_ofs]
mov rsi, .shadow
call string$concat
push rax
xor edi, edi
mov rsi, rax
call mtree$new
mov [rbx+fileshadow_mtree_ofs], rax
pop rdi
call heap$free
; recompute our size
mov rdi, rbx
call fileshadow$sizecalc
mov [rbx+fileshadow_filesize_ofs], rax
mov rax, rbx
pop rbx
epilog
end if
if used fileshadow$destroy_callback | defined include_everything
; three arguments: rdi == fileshadow object, rsi == function to call, rdx == argument to pass to it in rdi
; placeholder only really, set it yourself
falign
fileshadow$destroy_callback:
prolog fileshadow$destroy_callback
mov [rdi+fileshadow_destroycb_ofs], rsi
mov [rdi+fileshadow_destroycbarg_ofs], rdx
epilog
end if
if used fileshadow$destroy | defined include_everything
; single argument in rdi: fileshadow object
falign
fileshadow$destroy:
prolog fileshadow$destroy
push rdi
mov rsi, rdi
cmp qword [rdi+fileshadow_destroycb_ofs], 0
je .nocallback
mov rdi, [rdi+fileshadow_destroycbarg_ofs]
call qword [rsi+fileshadow_destroycb_ofs]
.nocallback:
mov rcx, [rsp]
cmp qword [rcx+fileshadow_privmapped_ofs], 0
je .noprivmapped
mov rdi, [rcx+fileshadow_privmapped_ofs]
call privmapped$destroy
.noprivmapped:
mov rcx, [rsp]
cmp qword [rcx+fileshadow_mtree_ofs], 0
je .nomtree
mov rdi, [rcx+fileshadow_mtree_ofs]
call mtree$destroy
.nomtree:
pop rdi
epilog
end if
if used fileshadow$sync | defined include_everything
; two arguments: rdi == fileshadow object, esi == bool as to whether to async it or not
; NOTES: this is a wrapper o'er top of mtree's sync, does nothing if no mtree object
; exists or the mapping is anonymous (not filebacked)
falign
fileshadow$sync:
prolog fileshadow$sync
mov rdi, [rdi+fileshadow_mtree_ofs]
test rdi, rdi
jz .ret
call mtree$sync
.ret:
epilog
end if
if used fileshadow$sizecalc | defined include_everything
; single argument in rdi: fileshadow object
; if the modmap is populated, this returns its end offset+size
; otehrwise, returns our read-only source size
; this is not normally needed, as we maintain fileshadow_filesize_ofs for mods as we go
; but initially (on new_file) this is called to figure out the size initially.
falign
fileshadow$sizecalc:
prolog fileshadow$sizecalc
mov rsi, [rdi+fileshadow_mtree_ofs]
mov rax, [rdi+fileshadow_size_ofs]
test rsi, rsi
jnz .maybe_modmap
epilog
.maybe_modmap:
push rsi rax
mov rdi, rsi
call mtree$empty
pop rcx rdi
test eax, eax
cmovnz rax, rcx
jz .modmap
epilog
.modmap:
; rdi == mtree, we need its end size
; we need its maximum key/val
sub rsp, mtree_iter_size + 32
mov rsi, rsp
push rdi
call mtree$maximum
lea rdi, [rsp+8]
call mtree_iter$key_value
pop rdi
; the offset to the modmap entry is sitting in rdx
mov [rsp+mtree_iter_size+24], rax ; save the effective offset rel to the fileshadow
mov rsi, rdx ; offset of the modmap entry
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$read
mov rax, [rsp+mtree_iter_size+24]
add rax, [rsp+mtree_iter_size+fileshadow_modmap_size_ofs]
add rsp, mtree_iter_size + 32
epilog
end if
if used fileshadow$seek | defined include_everything
; two arguments: rdi == fileshadow object, rsi == position
; returns bounded position
falign
fileshadow$seek:
prolog fileshadow$seek
mov rdx, [rdi+fileshadow_filesize_ofs]
mov rax, rsi
cmp rsi, rdx
cmova rax, rdx
mov [rdi+fileshadow_pos_ofs], rax
epilog
end if
if used fileshadow$read | defined include_everything
; three arguments: rdi == fileshadow object, rsi == buffer, rdx == bytes to read
; reads from the current file position, updates current file position, returns # bytes read
; (a wrapper o'er top of the offset version)
falign
fileshadow$read:
prolog fileshadow$read
push rdi
mov rcx, [rdi+fileshadow_pos_ofs]
call fileshadow$read_offset
pop rdi
add [rdi+fileshadow_pos_ofs], rax
epilog
end if
if used fileshadow$read_offset | defined include_everything
; four arguments: rdi == fileshadow object, rsi == buffer, rdx == bytes to read, rcx == offset/position
; returns # of bytes read in rax
falign
fileshadow$read_offset:
prolog fileshadow$read_offset
xor eax, eax
test rdx, rdx
jz .ret
cmp rcx, [rdi+fileshadow_filesize_ofs]
ja .ret
; sanity checks complete
push rbx r12 r13
mov rbx, rdi
mov r12, rsi
mov r13, rdx
push r14 r15
mov r14, rcx
mov r15, [rbx+fileshadow_mtree_ofs]
; if there is no mtree, straight from the base underlying it is
test r15, r15
jz .modmap_empty
; first case: mtree is empty
mov rdi, r15
call mtree$empty
test eax, eax
jnz .modmap_empty
sub rsp, mtree_iter_size + 96
; accumulate # of bytes we have read so far at +32
mov qword [rsp+mtree_iter_size+32], 0
calign
.readloop:
; if the offset is at the end (possible with a loop jump back up to here)
; all done
cmp r14, [rbx+fileshadow_filesize_ofs]
je .readloop_done
; otherwise, get the lower_bound or maximum
mov rdi, r15
mov rsi, r14
mov rdx, rsp
call mtree$lower_bound
; if we didn't get a lower_bound, there is no modmap entry >= the one we are after, and since we know it is
; not out of bounds (.e.g offset of read request is not past the end), then we can safely use the maximum
test eax, eax
jz .modmap_need_maximum
; if the key of the iterator != our offset, go back one
mov rdi, rsp
call mtree_iter$key
cmp rax, r14
je .modmap_iter_ready
mov rdi, rsp
call mtree_iter$prev
.modmap_iter_ready:
; so our iterator is either the last iterator (in which case our offset is >it, but not past the end)
; or it is the first one >= our offset
mov rdi, rsp
call mtree_iter$key_value
; hangon to hte key/val
mov [rsp+mtree_iter_size+48], rax ; offset of this modmap block (effective offset of fileshadow proper rel r14)
mov [rsp+mtree_iter_size+56], rdx ; heap offset of the modmap record
; read the modmap record
mov rdi, r15
mov rsi, rdx
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$read
; calculate our effective offset into this modmap block (where we ultimately are going to read relative to its modmap_offset)
; calculate our effective size (which is modmap's size - effective_offset), aka how many bytes remain in this modmap block
; calculate our to_read (which is min(bytecount, effective_size))
mov rax, r14 ; our offset/position for this read
mov rcx, [rsp+mtree_iter_size+fileshadow_modmap_size_ofs] ; the size of this modmap block
sub rax, [rsp+mtree_iter_size+48] ; our offset/position - the effective offset of this modmap block == effective offset into this modmap block of our read
sub rcx, rax ; effective size == how many bytes remain in this modmap block for our write
mov rdx, r13
cmp rcx, r13
cmovb rdx, rcx ; rdx == min(bytecount to read, effective size remaining in this modmap block)
; hangon to those values
mov [rsp+mtree_iter_size+64], rax ; == effective offset into this modmap block of our read
mov [rsp+mtree_iter_size+72], rcx ; effective size remaining in this modmap block for our read
mov [rsp+mtree_iter_size+80], rdx ; to_read == how many bytes we can read, aka min(bytecount, effective size remaining)
; only two possibles: inheap or not.
cmp dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1
je .readloop_inheap
; otherwise, this modmap entry is from the underlying source
mov rdi, r12
mov rsi, [rbx+fileshadow_base_ofs]
; rdx is already set to a valid to_read
add rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs] ; + the offset
add rsi, rax ; + effective offset into this modmap
call memcpy
; update our position and keep going
mov rax, [rsp+mtree_iter_size+80] ; to_read
add r12, rax
add r14, rax
add [rsp+mtree_iter_size+32], rax ; cumulative total of how many we've read
sub r13, rax
jnz .readloop
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 r12 rbx
epilog
calign
.readloop_inheap:
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs] ; offset to read from
mov rcx, rdx ; rcx == to_read
mov rdx, r12 ; destination buffer
add rsi, rax ; offset to read from += effective_offset
call mtree$read
; update our position and keep going
mov rax, [rsp+mtree_iter_size+80] ; to_read
add r12, rax
add r14, rax
add [rsp+mtree_iter_size+32], rax ; cumulative total of how many we've read
sub r13, rax
jnz .readloop
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 r12 rbx
epilog
calign
.modmap_need_maximum:
mov rdi, r15
mov rsi, rsp
call mtree$maximum
jmp .modmap_iter_ready
calign
.readloop_done:
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 r12 rbx
epilog
calign
.modmap_empty:
; simple case, no modmap/mtree to traverse... dest == r12, source == base+r14, for min(r13, filesize-r14) return for
mov rax, [rbx+fileshadow_filesize_ofs]
mov rdi, r12
mov rsi, [rbx+fileshadow_base_ofs]
sub rax, r14
mov rdx, r13
add rsi, r14 ; source = base+r14
cmp rdx, rax
cmova rdx, rax
mov r15, rdx
call memcpy
mov rax, r15
pop r15 r14 r13 r12 rbx
.ret:
epilog
end if
if used fileshadow$write | defined include_everything
; three arguments: rdi == fileshadow object, rsi == buffer, rdx == bytes to write
; writes to the current file position, updates current file position, returns # bytes written
; (a wrapper o'er top of the offset version)
falign
fileshadow$write:
prolog fileshadow$write
push rdi
mov rcx, [rdi+fileshadow_pos_ofs]
call fileshadow$write_offset
pop rdi
add [rdi+fileshadow_pos_ofs], rax
epilog
end if
if used fileshadow$write_offset | defined include_everything
; four arguments: rdi == fileshadow object, rsi == buffer, rdx == bytes to write, rcx == offset/position
; returns # of bytes written in rax
falign
fileshadow$write_offset:
prolog fileshadow$write_offset
; if modifications are not permitted, return 0
xor eax, eax
cmp dword [rdi+fileshadow_permitmods_ofs], 0
je .ret
; if for some reason there is no mtree, also bailout
cmp qword [rdi+fileshadow_mtree_ofs], 0
je .ret
; if the offset > filesize, return 0 (offset == filesize is okay, then we'll append/expand)
cmp rcx, [rdi+fileshadow_filesize_ofs]
ja .ret
test rdx, rdx ; zero byte write == nothing to do
jz .ret
; sanity checks complete...
push rbx r12 r13
mov rbx, rdi
mov r12, rsi
mov r13, rdx
push r14 r15
mov r14, rcx
mov r15, [rbx+fileshadow_mtree_ofs]
; first case: mtree is empty
mov rdi, r15
call mtree$empty
test eax, eax
jnz .modmap_empty
sub rsp, mtree_iter_size + 96
; save our total # of bytes we are writing for our final return
mov [rsp+mtree_iter_size+32], r13
calign
.writeloop:
; simple case: offset is at the end (aka append)
cmp r14, [rbx+fileshadow_filesize_ofs]
je .modmap_append
; otherwise, we have five possibilities to deal with, all of which require our lower_bound or maximum
mov rdi, r15
mov rsi, r14
mov rdx, rsp
call mtree$lower_bound
; if we didn't get a lower bound, there is no modmap entry >= the one we are after, and since we know it is
; not out of bounds (e.g. offset of write request is not past the end of the write buffer), then we can safely
; use the maximum bounds of the modmap...
test eax, eax
jz .modmap_need_maximum
; if the key of the iterator != our offset, go back one
mov rdi, rsp
call mtree_iter$key
cmp rax, r14
je .modmap_iter_ready
mov rdi, rsp
call mtree_iter$prev
.modmap_iter_ready:
; so our iterator is either the last iterator (in which case our offset is >it, but not past the end)
; or it is the first one >= our offset
mov rdi, rsp
call mtree_iter$key_value
; hangon to the key/val
mov [rsp+mtree_iter_size+48], rax ; offset of this modmap block (effective offset of fileshadow proper)
mov [rsp+mtree_iter_size+56], rdx ; heap offset of the modmap record (where in the heap this modmap record actually lives)
; read the modmap record
mov rdi, r15 ; our mtree
mov rsi, rdx ; our offset
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$read
; calculate our effective offset into this modmap (where we ultimately are going to write relative to its modmap_offset)
; calculate our effective_size (which is modmap's size - effective_offset), aka how many bytes remain in this modmap block
; calculate our to_write (which is min(bytecount, effective_size))
mov rax, r14 ; our offset/position for this write block
mov rcx, [rsp+mtree_iter_size+fileshadow_modmap_size_ofs] ; the size of this modmap block
sub rax, [rsp+mtree_iter_size+48] ; our offset/position - the effective offset of this modmap block == effective offset into this modmap block of our write
sub rcx, rax ; effective size == how many bytes remain in this modmap block for our write
mov rdx, r13
cmp rcx, r13
cmovb rdx, rcx ; rdx == min(bytecount to write, effective size remaining in this modmap block)
; hangon to those values
mov [rsp+mtree_iter_size+64], rax ; == effective offset into this modmap block of our write
mov [rsp+mtree_iter_size+72], rcx ; effective size remaining in this modmap block for our write
mov [rsp+mtree_iter_size+80], rdx ; to_write == how many bytes we CAN write, aka min(bytecount, effective size remaining)
; case 1
; if this modmap block is already in the heap, we can just overwrite it:
cmp dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1
je .writeloop_inheap
; if effective offset is zero, and our bytecount is >= our effective size, special case where we can flip the modmap
test rax, rax
jnz .writeloop_check_case4 ; skip to checking case 4, since case 3 is effective offset == 0
cmp r13, rcx
jae .writeloop_flip_modmap
; otherwise, effective offset is zero, but our bytecount is < our effective size
; which means we need to modify the offset and size of the modmap, and increase the lowerbound iterator's key
; and then create a new one for this write block
; we know we are not violating the tree order so we can safely move the key forward
sub [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13
add [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], r13
mov rsi, [rsp+mtree_iter_size+48] ; offset of this modmap block (effective offset of fileshadow proper)
mov rdi, rsp
add rsi, r13
call mtree_iter$set_key
; save our modmap changes
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; now create a new entries for our block
mov rdi, r15
mov rsi, r13
call mtree$alloc
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13 ; new modmap entry size == bytecount
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax ; offset into the heap of our block
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1 ; inheap = true
; write our block to the heap first
mov rdi, r15
mov rsi, rax
mov rdx, r12
mov rcx, r13
call mtree$write
; allocate a new modmap
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that offset:
mov [rsp+mtree_iter_size+88], rax
; write our new modmap there
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; add a new mtree entry for it at offset r14
mov rdi, r15
mov rsi, r14
mov rdx, [rsp+mtree_iter_size+88]
call mtree$insert
; bytecount was < effective_size, which means we have no more to write, done, dusted.
mov rax, [rsp+mtree_iter_size+32] ; our initial write bytecount
add rsp, mtree_iter_size + 96
pop r15 r14 r13 r12 rbx
epilog
.writeloop_check_case4:
; if bytecount < effective_size, then worst case of 3 way split
cmp r13, rcx
jb .writeloop_threeway
; our final case, effective offset >0, and our bytecount >= the effective size of the remainder of this modmap entry
; so we have to reduce the size of hte current modmap entry by the effective_size
; and then add a new entry at our offset for effective_size bytes
sub [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rcx
; write that modmap entry back out
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; allocate space for our new one
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+72] ; effective size remaining (past tense)
call mtree$alloc
mov rcx, [rsp+mtree_iter_size+72] ; effective size remaining (past tense)
; configure our new modmap record
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rcx
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax ; offset of our new digs
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1 ; inheap
; write our newly allocate goods
mov rdi, r15
mov rsi, rax ; offset we are writing to
mov rdx, r12
; rcx already set
call mtree$write
; allocate a new modmap record
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that location:
mov [rsp+mtree_iter_size+88], rax
; write our modmap record
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; last but not least, insert a new mtree key/val for it
mov rdi, r15
mov rsi, r14
mov rdx, [rsp+mtree_iter_size+88]
call mtree$insert
; update our position and continue or exit
mov rax, [rsp+mtree_iter_size+72] ; effective size (how much we wrote)
add r12, rax ; buffer pointer += how many we wrote
add r14, rax ; offset/position += how many we wrote
sub r13, rax ; bytecount to write -= how many we wrote
jnz .writeloop
mov rax, [rsp+mtree_iter_size+32] ; our initial write bytecount
add rsp, mtree_iter_size + 96
pop r15 r14 r13 r12 rbx
epilog
calign
.modmap_need_maximum:
mov rdi, r15
mov rsi, rsp
call mtree$maximum
jmp .modmap_iter_ready
calign
.writeloop_inheap:
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
mov rdx, r12
mov rcx, [rsp+mtree_iter_size+80]
add rsi, [rsp+mtree_iter_size+64] ; effective offset of our write
call mtree$write
; no new modmap entry is required... so just update our position and be done
mov rax, [rsp+mtree_iter_size+80]
add r12, rax ; buffer pointer += how many we wrote
add r14, rax ; offset/position += how many we wrote
sub r13, rax ; bytecount to write -= how many we wrote
jnz .writeloop
mov rax, [rsp+mtree_iter_size+32] ; our initial write bytecount
add rsp, mtree_iter_size + 96
pop r15 r14 r13 r12 rbx
epilog
calign
.writeloop_flip_modmap:
; allocate space for our to_write, since it is min(bytecount, effectivesize) and we knwo bytecount >= effectivesize, this
; is same as effective size
mov rdi, r15
mov rsi, rcx
call mtree$alloc
; save that offset into the modmap and flip its inheap status
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1 ; set this modmap to inheap
; write our buffer
mov rdi, r15
mov rsi, rax
mov rdx, r12
mov rcx, [rsp+mtree_iter_size+72]
call mtree$write
; write our modified modmap block
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; no new modmap entry is required... so just update our position and be done
mov rax, [rsp+mtree_iter_size+80]
add r12, rax ; buffer pointer += how many we wrote
add r14, rax ; offset/position += how many we wrote
sub r13, rax ; bytecount to write -= how many we wrote
jnz .writeloop
mov rax, [rsp+mtree_iter_size+32] ; our initial write bytecount
add rsp, mtree_iter_size + 96
pop r15 r14 r13 r12 rbx
epilog
calign
.writeloop_threeway:
; this is the worst case scenario:
; the effective offset of our write into this block is >0
; and the bytecount of our write is less than the remaining bytes in this block after our offset
;
; which means we need three modmap entries from this single one and our own.
;
; so first up, create the last segment along with a new mtree key/val for it
; size = modmap.size - effective_offset - bytecount
; offset = modmap.offset + effective_offset + bytecount
; the fileshadow outer offset is r14+bytecount
sub [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax ; size -= effective_offset
sub [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13 ; size -= bytecount
add [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax ; offset += effective_offset
add [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], r13 ; offset += bytecount
; its inheap is already set to 0, so leave it
; allocate a new modmap spot for that
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+88], rax
; write our modified new modmap
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; create our new mtree key/val for it
mov rdi, r15
mov rsi, r14 ; our actual offset
mov rdx, [rsp+mtree_iter_size+88]
add rsi, r13 ; + bytecount is the offset of this one
call mtree$insert
mov rax, [rsp+mtree_iter_size+64] ; original effective offset into the block
; undo our changes to the modmap's offset
sub [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], r13 ; offset -= bytecount
sub [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax ; offset -= effective_offset
; set its size now to the effective_offset
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax ; size = effective_offset
; write that one out
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; so now we can create a new spot for our write, set inheap to true
mov rdi, r15
mov rsi, r13
call mtree$alloc
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13 ; size == our bytecount
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax ; offset == our new spot on the heap
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1
; write our block
mov rdi, r15
mov rsi, rax
mov rdx, r12
mov rcx, r13
call mtree$write
; allocate a new spot for our modmap record
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+88], rax
; write our new modmap
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; create our new mtree key/val for it
mov rdi, r15
mov rsi, r14
mov rdx, [rsp+mtree_iter_size+88]
call mtree$insert
; done, dusted.
mov rax, [rsp+mtree_iter_size+32] ; our initial write bytecount
add rsp, mtree_iter_size + 96
pop r15 r14 r13 r12 rbx
epilog
calign
.modmap_append:
; special case here where we are only appending (offset == filesize)
; create a modmap entry for us, !inheap, key(effective offset) is filesize,
; inheap, for r13 bytes.
; first up, allocate heap space for this buffer:
mov rdi, r15
mov rsi, r13 ; # of bytes of this write
call mtree$alloc
mov rdi, r15
mov rsi, rax
mov rdx, r12
mov rcx, r13
mov r12, rax ; overwrite buffer passed w/ our new heap offset
call mtree$write
; now we need a modmap entry, inheap for this one
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
mov [rsp+mtree_iter_size+24], rax ; hangon to it
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], r12 ; our inheap alloc offset
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1 ; inheap = true
; now write that
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; now we insert
mov rdi, r15
mov rsi, r14 ; this is same as [rbx+fileshadow_filesize_ofs]
mov rdx, [rsp+mtree_iter_size+24] ; our modmap offset
call mtree$insert
; increase our filesize by r13 bytes
add [rbx+fileshadow_filesize_ofs], r13
; done, dusted
add rsp, mtree_iter_size + 96
mov rax, r13 ; # of bytes we wrote
pop r15 r14 r13 r12 rbx
epilog
calign
.modmap_empty:
; special case here where we have no modmap entries whatsoever, this is our first mod
; if the offset of our write is >0, we need a not-in-heap modmap entry at 0 for r14 bytes
sub rsp, mtree_iter_size + 32
test r14, r14
jz .modmap_empty_nofirst
; allocate a modmap entry in the mtree first up
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
mov [rsp+mtree_iter_size+24], rax ; hangon to it
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r14
mov qword [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], 0 ; real offset (!inheap)
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 0 ; !inheap
; now write that
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; now insert our effective offset (0) + that modmap offset to our actual mtree
mov rdi, r15
xor esi, esi
mov rdx, [rsp+mtree_iter_size+24] ; offset of our modmap entry
call mtree$insert
.modmap_empty_nofirst:
; now we insert another modmap entry for this write contents, first we alloc a spot for
; this write's buffer
mov rdi, r15
mov rsi, r13 ; # of bytes of this write
call mtree$alloc
mov rdi, r15
mov rsi, rax
mov rdx, r12
mov rcx, r13
mov r12, rax
call mtree$write
; now we need a modmap entry, inheap for this one
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
mov [rsp+mtree_iter_size+24], rax ; hangon to it
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], r12 ; our inheap alloc offset
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1 ; inheap = true
; now write that
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; now insert our effective offset (r14) + that modmap offset to our actual mtree
mov rdi, r15
mov rsi, r14
mov rdx, [rsp+mtree_iter_size+24] ; offset of our modmap entry
call mtree$insert
; so, if r14+r13 > the previous filesize, set new filesize, and we are done.
lea rdx, [r14+r13]
cmp rdx, [rbx+fileshadow_filesize_ofs]
jae .modmap_empty_filesize_exit
; otherwise, there is space left after our write that is !inheap, so we need one final modmap entry
; its offset is r14+r13, and its length is filesize - that
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
lea rcx, [r14+r13]
mov rdx, [rbx+fileshadow_filesize_ofs]
sub rdx, rcx
mov [rsp+mtree_iter_size+24], rax ; hangon to it
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rdx ; remaining bytes after our write
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rcx ; offset into the underlying !inheap buffer/file
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 0 ; inheap = false
; now write that
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; now insert our effective offset (r14+r13) + that modmap offset to our actual mtree
mov rdi, r15
lea rsi, [r14+r13]
mov rdx, [rsp+mtree_iter_size+24] ; offset of our modmap entry in the heap
call mtree$insert
add rsp, mtree_iter_size + 32
mov rax, r13 ; # of bytes we wrote
pop r15 r14 r13 r12 rbx
epilog
calign
.modmap_empty_filesize_exit:
add rsp, mtree_iter_size + 32
mov [rbx+fileshadow_filesize_ofs], rdx
mov rax, r13 ; # of bytes we wrote
pop r15 r14 r13 r12 rbx
.ret:
epilog
end if
if used fileshadow$insert | defined include_everything
; three arguments: rdi == fileshadow object, rsi == buffer, rdx == bytes to insert
; inserts at the current file position, does not update the current file position, returns # bytes inserted
; (a wrapper o'er top of the offset version)
falign
fileshadow$insert:
prolog fileshadow$insert
mov rcx, [rdi+fileshadow_pos_ofs]
call fileshadow$insert_offset
epilog
end if
if used fileshadow$insert_offset | defined include_everything
; four arguments: rdi == fileshadow object, rsi == buffer, rdx == bytes to insert, rcx == offset/position
; returns # of bytes inserted in rax
falign
fileshadow$insert_offset:
prolog fileshadow$insert_offset
; if modifications are not permitted, or if for some reason there is no mtree, bailout
xor eax, eax
cmp dword [rdi+fileshadow_permitmods_ofs], 0
je .ret
cmp qword [rdi+fileshadow_mtree_ofs], 0
je .ret
; if the offset/position is > filesize, also 0
cmp rcx, [rdi+fileshadow_filesize_ofs]
ja .ret
; zero byte insert == also 0
test rdx, rdx
jz .ret
; sanity checks complete
push rbx r12 r13
mov rbx, rdi
mov r12, rsi
mov r13, rdx
push r14 r15
mov r14, rcx
mov r15, [rbx+fileshadow_mtree_ofs]
mov rdi, r15
call mtree$empty
test eax, eax
jnz .modmap_empty
; if the offset is at the end, aka append, special case as its much simpler
cmp r14, [rbx+fileshadow_filesize_ofs]
je .modmap_append
sub rsp, mtree_iter_size + 96
.insert_again: ; jumped to from below if other cases are formed
; otherwise, we need either the lower_bound of our offset or maximum
mov rdi, r15
mov rsi, r14
mov rdx, rsp
call mtree$lower_bound
test eax, eax
jz .modmap_need_maximum
; if the key of the iterator != our offset, go back one
mov rdi, rsp
call mtree_iter$key
cmp rax, r14
je .modmap_iter_ready
mov rdi, rsp
call mtree_iter$prev
.modmap_iter_ready:
; so our iterator is either the last iterator (in which case our offset is >it, but not past the end)
; or it is the first one >= our offset
mov rdi, rsp
call mtree_iter$key_value
; hangon to the key/val
mov [rsp+mtree_iter_size+48], rax ; offset of this modmap block (relative to the fileshadow proper)
mov [rsp+mtree_iter_size+56], rdx ; heap offset of the modmap record
; read the modmap record
mov rdi, r15
mov rsi, rdx
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$read
; calculate the effective offset, effective size
mov rax, r14
mov rcx, [rsp+mtree_iter_size+fileshadow_modmap_size_ofs]
sub rax, [rsp+mtree_iter_size+48] ; our offset/pos - effective offset of this modmap block (relative to fileshadow)
sub rcx, rax ; effective size == how many bytes remain in this modmap block after our effective offset
; hangon to those values
mov [rsp+mtree_iter_size+64], rax
mov [rsp+mtree_iter_size+72], rcx
; three possibilities:
; case 1: effective offset is zero (the simple case)
test rax, rax
jz .insert_case1
; case 2: this modmap block is not in the heap
cmp dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 0
je .insert_case2
; case 3: this modmap block is in the heap, which means we have to do the same amount of tree manip
; and basic actions as insert_case2, but we have to _physically_ split the modmap inheap block
; and create two separate ones from it
; save the original size of our modmap, and its original offset
mov rdx, [rsp+mtree_iter_size+fileshadow_modmap_size_ofs]
mov r8, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
mov [rsp+mtree_iter_size+80], rdx
mov [rsp+mtree_iter_size+88], r8
; reduce the size of the current modmap to effective_offset
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax
; we need a reduced copy of the first segment of our inheap modmap
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
mov rdx, rax
call mtree$alloc_clone
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
; save the modified modmap
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; create a new modmap with size == effective_size, offset == old offset + old size
mov rax, [rsp+mtree_iter_size+64] ; effective offset is the new size of the first segment
mov rsi, [rsp+mtree_iter_size+88] ; original inheap offset
mov rdx, [rsp+mtree_iter_size+72] ; effective size is what was leftover in the block
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rdx
add rsi, rax
mov rdi, r15
call mtree$alloc_clone
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
; free our original offset
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+88]
call mtree$free
; create a spot for our new modmap
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+24], rax
; write our new modmap
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; and finally, we need a new mtree key/val for it
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+48]
mov rdx, [rsp+mtree_iter_size+24]
add rsi, [rsp+mtree_iter_size+64]
call mtree$insert
; go back to the start, which will result in a fallthrough to case1
jmp .insert_again
calign
.insert_case2:
; effective offset is nonzero, and our modmap record says it is not in the heap
; save the original size of our modmap
mov rdx, [rsp+mtree_iter_size+fileshadow_modmap_size_ofs]
mov [rsp+mtree_iter_size+80], rdx
; first, reduce the size of our current modmap to effective_offset
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax
; save the modified modmap
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; second, create a new modmap with size == effective_size, offset == old offset + old size
mov rax, [rsp+mtree_iter_size+64] ; effective offset is the new size of the first segment
mov rcx, [rsp+mtree_iter_size+72] ; effective size is what was leftover in the block
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rcx ; size of last segment == effective_size
add [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax ; offset += size of first segment
; create a spot for our new modmap
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+24], rax
; write our new modmap
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; and finally, we need a new mtree key/val for it
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+48]
mov rdx, [rsp+mtree_iter_size+24]
add rsi, [rsp+mtree_iter_size+64]
call mtree$insert
; go back to the start, which will result in a fallthrough to case1
; TODO: someday when I am bored, redo the case so it doesn't have to jump back, do the fallthrough instead
jmp .insert_again
calign
.insert_case1:
; simple case, our effective offset for the insert is zero, which means we just bump all entries
; forward by our bytecount, and then insert a new one
mov rdi, rsp ; our iterator loc
call mtree_iter$key
mov rdi, rsp
lea rsi, [rax+r13] ; its new effective offset
call mtree_iter$set_key
mov rdi, rsp
call mtree_iter$next
test eax, eax
jnz .insert_case1
; now one for our insert block
mov rdi, r15
mov rsi, r13
call mtree$alloc
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1
; write our insert block first
mov rdi, r15
mov rsi, rax
mov rdx, r12
mov rcx, r13
call mtree$write
; make room for our modmap record
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+24], rax
; write it
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; add an mtree key/val
mov rdi, r15
mov rsi, r14
mov rdx, [rsp+mtree_iter_size+24]
call mtree$insert
; increase our filesize and return
add [rbx+fileshadow_filesize_ofs], r13
mov rax, r13
add rsp, mtree_iter_size + 96
pop r15 r14 r13 r12 rbx
epilog
calign
.modmap_need_maximum:
mov rdi, r15
mov rsi, rsp
call mtree$maximum
jmp .modmap_iter_ready
calign
.modmap_append:
sub rsp, mtree_iter_size + 32
mov rdi, r15
mov rsi, r13
call mtree$alloc
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1
; write our goods
mov rdi, r15
mov rsi, rax
mov rdx, r12
mov rcx, r13
call mtree$write
; make room for our modmap record
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+24], rax
; write it
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; create a new mtree key/val for it
mov rdi, r15
mov rsi, [rbx+fileshadow_filesize_ofs]
mov rdx, [rsp+mtree_iter_size+24]
call mtree$insert
; increase our filesize and return
add [rbx+fileshadow_filesize_ofs], r13
mov rax, r13
add rsp, mtree_iter_size + 32
pop r15 r14 r13 r12 rbx
epilog
calign
.modmap_empty:
sub rsp, mtree_iter_size + 32
; if we are inserting at offset 0, simpler still
test r14, r14
jz .modmap_empty_isfirst
; else, we have to make 2, possibly 3 modmap records
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r14 ; size is our insert offset
mov qword [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], 0 ; offset into base is 0
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 0 ; !inheap
; allocate a spot for our modmap record
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+24], rax
; write it
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; insert a new mtree key/val for it
mov rdi, r15
xor esi, esi ; effective offset 0
mov rdx, [rsp+mtree_iter_size+24] ; the modmap record offset
call mtree$insert
; now our insert block
mov rdi, r15
mov rsi, r13
call mtree$alloc
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1
; write our insert block
mov rdi, r15
mov rsi, rax
mov rdx, r12
mov rcx, r13
call mtree$write
; allocate a spot for our modmap record
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+24], rax
; write it
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; insert a new mtree key/val for it
mov rdi, r15
mov rsi, r14 ; effective offset is our insert position
mov rdx, [rsp+mtree_iter_size+24]
call mtree$insert
mov rax, [rbx+fileshadow_filesize_ofs]
; now, if r14 != old file size, then we have a third to insert
cmp r14, [rbx+fileshadow_filesize_ofs]
je .modmap_empty_notfirst_nolast
sub rax, r14 ; rax == filesize - our insert position, aka what is left
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], r14 ; offset is our insert position in the base
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 0 ; !inheap
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+24], rax
; write it
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; insert a new mtree key/val for it
mov rdi, r15
lea rsi, [r13+r14] ; effective offset is our insert pos + our bytecount
mov rdx, [rsp+mtree_iter_size+24]
call mtree$insert
; fallthrough to increase size and return
.modmap_empty_notfirst_nolast:
; increase our filesize and return
add [rbx+fileshadow_filesize_ofs], r13
mov rax, r13
add rsp, mtree_iter_size + 32
pop r15 r14 r13 r12 rbx
epilog
calign
.modmap_empty_isfirst:
; our insert block is first
mov rdi, r15
mov rsi, r13
call mtree$alloc
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1
; write our insert block
mov rdi, r15
mov rsi, rax
mov rdx, r12
mov rcx, r13
call mtree$write
; allocate a spot for our modmap record
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+24], rax
; write it
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; insert a new mtree key/val for it
mov rdi, r15
xor esi, esi ; effective offset 0
mov rdx, [rsp+mtree_iter_size+24] ; the modmap record offset
call mtree$insert
; now another insert for the rest of the file
mov rax, [rbx+fileshadow_filesize_ofs]
xor ecx, ecx
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rcx ; 0 offset
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 0 ; !inheap
; allocate a spot for the modmap record
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; save that spot
mov [rsp+mtree_iter_size+24], rax
; write it
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; insert a new mtree key/val for it
mov rdi, r15
mov rsi, r13 ; effective offset is our insert bytecount
mov rdx, [rsp+mtree_iter_size+24] ; the modmap record offset
call mtree$insert
; increase our filesize by our bytecount
add [rbx+fileshadow_filesize_ofs], r13
mov rax, r13
add rsp, mtree_iter_size + 32
pop r15 r14 r13 r12 rbx
.ret:
epilog
end if
if used fileshadow$delete | defined include_everything
; two arguments: rdi == fileshadow object, rsi == bytes to delete
; deletes from the current file position, does not update the current file position, returns # bytes deleted
; (a wrapper o'er top of the offset version)
falign
fileshadow$delete:
prolog fileshadow$delete
mov rdx, [rdi+fileshadow_pos_ofs]
call fileshadow$delete_offset
epilog
end if
if used fileshadow$delete_offset | defined include_everything
; three arguments: rdi == fileshadow object, rsi == bytes to delete, rdx == offset/position
; returns # of bytes deleted in rax
falign
fileshadow$delete_offset:
prolog fileshadow$delete_offset
; if modifications are not permitted, or if there is no mtree, return 0
xor eax, eax
mov rcx, [rdi+fileshadow_filesize_ofs]
cmp dword [rdi+fileshadow_permitmods_ofs], 0
je .ret
cmp qword [rdi+fileshadow_mtree_ofs], 0
je .ret
; if the offset >= filesize, return 0
cmp rdx, rcx
jae .ret
sub rcx, rdx ; max # of bytes we can delete (filesize - offset/position)
; zero bytes to delete == zero ret as well
test rsi, rsi
jz .ret
; sanity cap the maximum number of bytes we CAN delete (such that you could pass 0xffffffffffffffff in rsi and get returned
; the number of bytes it really deleted)
cmp rsi, rcx
cmova rsi, rcx
push rbx r13 r14 r15
mov rbx, rdi
mov r13, rsi
mov r14, rdx
mov r15, [rdi+fileshadow_mtree_ofs]
; first case: mtree is empty
mov rdi, r15
call mtree$empty
test eax, eax
jnz .modmap_empty
sub rsp, mtree_iter_size + 96
; save our total # of bytes we are deleting for our final return
mov [rsp+mtree_iter_size+32], r13
calign
.deleteloop:
; if the offset is at the end (possible from loop continuation), we are done.
; (filesize itself will have been modified as the loop progressed)
cmp r14, [rbx+fileshadow_filesize_ofs]
je .deleteloop_done
; otherwise, lower_bound or maximum
mov rdi, r15
mov rsi, r14
mov rdx, rsp
call mtree$lower_bound
; if we didn't get a lower bound, there is no modmap entry >= the one we are after, and since we know it is
; not out of bounds (e.g. offset of delete request is not past the end of the goods), then we can safely
; use the maximum bounds of the modmap...
test eax, eax
jz .modmap_need_maximum
; if the key of the iterator != our offset, go back one
mov rdi, rsp
call mtree_iter$key
cmp rax, r14
je .modmap_iter_ready
mov rdi, rsp
call mtree_iter$prev
.modmap_iter_ready:
; get our iterator's key/value, load up modmap record, and determine our effective offset and bytes remaining
mov rdi, rsp
call mtree_iter$key_value
; hangon to the key/val
mov [rsp+mtree_iter_size+48], rax ; effective offset of this modmap block (relative to fileshadow)
mov [rsp+mtree_iter_size+56], rdx ; heap offset of its modmap record
; read the modmap record
mov rdi, r15
mov rsi, rdx
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$read
; calculate our effective offset into this modmap (where we ultimately are going to delete relative to its modmap_offset)
; calculate our effective size (which is modmap's size - effective_offset), aka how many bytes remain in this modmap block
; calculate our to_delete (which is min(bytecount, effective_size))
mov rax, r14 ; our offset/position for this delete
mov rcx, [rsp+mtree_iter_size+fileshadow_modmap_size_ofs] ; the size of this modmap block
sub rax, [rsp+mtree_iter_size+48] ; our offset position - effective offset == effective offset into this modmap block
sub rcx, rax ; effective_size == how many bytes remain in this modmap block (from our effective_offset forward)
mov rdx, r13
cmp rcx, r13
cmovb rdx, rcx ; rdx == min(bytecount to delete, effective size remaining in this modmap block)
; hangon to those values
mov [rsp+mtree_iter_size+64], rax ; == effective offset into this modmap block
mov [rsp+mtree_iter_size+72], rcx ; effective size remaining in this modmap block
mov [rsp+mtree_iter_size+80], rdx ; to_delete == how many bytes we CAN delete, aka min(bytecount, effective_size)
xor r8d, r8d
mov r9d, 2
mov r10d, 1
test rax, rax
cmovnz r8d, r9d ; r8d == 0 if effective_offset == 0, else r8d == 2
xor r9d, r9d
cmp r13, rcx
cmovb r9d, r10d
or r8d, r9d ; r8d++ if bytecount < effective_size
mov r10d, 4
xor r9d, r9d
cmp dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 1
cmove r9d, r10d
or r8d, r9d ; r8d += 4 if inheap
jmp qword [r8*8+.delete_dispatch]
calign
.delete_base_full:
; !inheap, effective_offset == 0, bytecount >= effective_size
; we can delete this entire modmap entry, and reduce all entries from here forward by effective_size
; first, free the modmap block
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
call mtree$free
; now blast our iterator key/val altogether
mov rdi, rsp
call mtree_iter$delete
; get its lower bound again, which will be the next one after the one we just deleted
mov rdi, r15
mov rsi, r14
mov rdx, rsp
call mtree$lower_bound
; if that returns false, then there is no additional
test eax, eax
jz .delete_base_full_skipmod
; otherwise, walk the remaining spots and decrease by effective_size
calign
.delete_base_full_walk:
mov rdi, rsp
call mtree_iter$key
mov rdi, rsp
mov rsi, rax
sub rsi, [rsp+mtree_iter_size+72]
call mtree_iter$set_key
mov rdi, rsp
call mtree_iter$next
test eax, eax
jnz .delete_base_full_walk
.delete_base_full_skipmod:
mov rax, [rsp+mtree_iter_size+72]
sub [rbx+fileshadow_filesize_ofs], rax
sub r13, rax
jnz .deleteloop
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 rbx
epilog
calign
.delete_base_seg1:
; !inheap, effective_offset == 0, bytecount < effective_size
; modify and write our modmap record first, bytecount bytes
sub [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13 ; size of this block -= bytecount
add [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], r13 ; offset of the !inheap offset += bytecount
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; decrement our filesize by bytecount
sub [rbx+fileshadow_filesize_ofs], r13
; walk the remaining tree decrementing each key by r13
calign
.delete_base_seg1_walk:
mov rdi, rsp
call mtree_iter$next
test eax, eax
jz .delete_base_seg1_done
mov rdi, rsp
call mtree_iter$key
mov rdi, rsp
mov rsi, rax
sub rsi, r13
call mtree_iter$set_key
jmp .delete_base_seg1_walk
.delete_base_seg1_done:
; since bytecount was less than effective_size, we know there is no more to be done
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 rbx
epilog
calign
.delete_base_seg2:
; !inheap, effective_offset >0, bytecount >= effective_size
; modify and write our modmap record first, effective_size bytes
mov rax, [rsp+mtree_iter_size+64] ; effective_offset
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
mov rax, [rsp+mtree_iter_size+72] ; effective_size
; decrement filesize by bytecount
sub [rbx+fileshadow_filesize_ofs], rax
; walk the remaining tree decrementing each key by effective_size
calign
.delete_base_seg2_walk:
mov rdi, rsp
call mtree_iter$next
test eax, eax
jz .delete_base_seg2_done
mov rdi, rsp
call mtree_iter$key
mov rcx, [rsp+mtree_iter_size+72] ; effective_size
mov rdi, rsp
mov rsi, rax
sub rsi, rcx
call mtree_iter$set_key
jmp .delete_base_seg2_walk
.delete_base_seg2_done:
sub r13, [rsp+mtree_iter_size+72] ; btyecount -= effective_size
jnz .deleteloop
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 rbx
epilog
calign
.delete_base_mid:
; !inheap, effective_offset >0, bytecount < effective_size
; modify and write our current modmap record first
; set the current one's size == effective_offset, add a new one for the remainder
mov rax, [rsp+mtree_iter_size+64] ; effective_offset
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax
call mtree$write
; size of the new one is effective_size - bytecount
; its key/fileshadow offset is this one's key + effective_offset
mov rax, [rsp+mtree_iter_size+64] ; effective_offset
mov rcx, [rsp+mtree_iter_size+72] ; effective_size
add [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], r13 ; offset += bytecount
add [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax ; offset += effective_offset
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rcx ; size = effective_size
sub [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13 ; size -= bytecount
; allocate a new heap spot for our modmap
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
; hangon to that one
mov [rsp+mtree_iter_size+24], rax
; write our new modmap
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; get the key from our previous iterator
mov rdi, rsp
call mtree_iter$key
mov rcx, [rsp+mtree_iter_size+64] ; effective_offset
mov rdi, r15
lea rsi, [rax+rcx] ; new key is old key + effective_offset
mov rdx, [rsp+mtree_iter_size+24] ; modmap record location
call mtree$insert
; decrement filesize and bytecount, we know there is no more data to delete
; but we still have to walk remaining tree
sub [rbx+fileshadow_filesize_ofs], r13
; skip the one we just inserted
mov rdi, rsp
call mtree_iter$next
calign
.delete_base_mid_walk:
mov rdi, rsp
call mtree_iter$next
test eax, eax
jz .delete_base_mid_done
mov rdi, rsp
call mtree_iter$key
mov rdi, rsp
mov rsi, rax
sub rsi, r13
call mtree_iter$set_key
jmp .delete_base_mid_walk
.delete_base_mid_done:
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 rbx
epilog
calign
.delete_heap_full:
; inheap, effective_offset == 0, bytecount >= effective_size
; same as delete_base_full, only we have to mtree$free the offset first
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
call mtree$free
jmp .delete_base_full
calign
.delete_heap_seg1:
; inheap, effective_offset == 0, bytecount < effective_size
; since we can't modify heap based blocks inplace, we need to make a copy of
; the correct space
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
mov rdx, [rsp+mtree_iter_size+fileshadow_modmap_size_ofs]
add rsi, r13 ; offset of the new block += bytecount
sub rdx, r13 ; size of the new block -= bytecount
call mtree$alloc_clone
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
call mtree$free
; the rest is the same as heap based, only we don't modify the offset now
sub [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r13
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; decrement our filesize by bytecount
sub [rbx+fileshadow_filesize_ofs], r13
; walk the remaining tree decrementing each key by r13
calign
.delete_heap_seg1_walk:
mov rdi, rsp
call mtree_iter$next
test eax, eax
jz .delete_heap_seg1_done
mov rdi, rsp
call mtree_iter$key
mov rdi, rsp
mov rsi, rax
sub rsi, r13
call mtree_iter$set_key
jmp .delete_heap_seg1_walk
.delete_heap_seg1_done:
; since bytecount was less than effective_size, we know there is no more to be done
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 rbx
epilog
calign
.delete_heap_seg2:
; inheap, effective_offset >0, bytecount >= effective_size
; since we can't modify heap based blocks inplcae, we need to make a copy of
; the correct space
mov rax, [rsp+mtree_iter_size+64] ; effective_offset
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
mov rdx, rax ; new size == effective_offset
call mtree$alloc_clone
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
call mtree$free
; set the new size == effective_offset
mov rax, [rsp+mtree_iter_size+64] ; effective_offset
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; our overall size is decreasing by effective_size
mov rax, [rsp+mtree_iter_size+72] ; effective_size
sub [rbx+fileshadow_filesize_ofs], rax
calign
.delete_heap_seg2_walk:
mov rdi, rsp
call mtree_iter$next
test eax, eax
jz .delete_heap_seg2_done
mov rdi, rsp
call mtree_iter$key
mov rcx, [rsp+mtree_iter_size+72] ; effective_size
mov rdi, rsp
mov rsi, rax
sub rsi, rcx
call mtree_iter$set_key
jmp .delete_heap_seg2_walk
.delete_heap_seg2_done:
sub r13, [rsp+mtree_iter_size+72] ; bytecount -= effective_size
jnz .deleteloop
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size+96
pop r15 r14 r13 rbx
epilog
calign
.delete_heap_mid:
; inheap, effective_offset >0, bytecount < effective_size
; again, since we can't modify heap based blocks inplace, we need to make a
; copy of the correct space and free the prior, noting here we need TWO copies
mov rax, [rsp+mtree_iter_size+64] ; effective_offset == new size
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
mov rdx, rax
mov [rsp+mtree_iter_size+88], rsi ; hangon to the previous one
call mtree$alloc_clone
; set our new offset and size
mov rcx, [rsp+mtree_iter_size+64] ; effective_offset == new size
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rcx
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+56]
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; allocate another copy of the end segment
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+88] ; the original segment
mov rdx, [rsp+mtree_iter_size+72] ; effective_size
sub rdx, r13 ; - bytecount is new size
add rsi, [rsp+mtree_iter_size+64] ; offset += effective_offset
add rsi, r13 ; offset += bytecount
call mtree$alloc_clone
mov rdi, r15
mov rsi, [rsp+mtree_iter_size+88]
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rax
call mtree$free
mov rax, [rsp+mtree_iter_size+72] ; effective_size
sub rax, r13 ; - bytecount is new size
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rax
; now we need a new modmap entry
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
mov [rsp+mtree_iter_size+24], rax ; hangon to our new spot
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; new we need a new effective offset, whose key is old key + effective offset
mov rdi, rsp
call mtree_iter$key
mov rcx, [rsp+mtree_iter_size+64] ; effective_offset
mov rdi, r15
lea rsi, [rax+rcx] ; new key is old key + effective_offset
mov rdx, [rsp+mtree_iter_size+24]
call mtree$insert
; decrement filesize and bytecount, we know there is no more data to delete
; but we still have to walk remaining tree
sub [rbx+fileshadow_filesize_ofs], r13
; skip the one we just inserted
mov rdi, rsp
call mtree_iter$next
calign
.delete_heap_mid_walk:
mov rdi, rsp
call mtree_iter$next
test eax, eax
jz .delete_heap_mid_done
mov rdi, rsp
call mtree_iter$key
mov rdi, rsp
mov rsi, rax
sub rsi, r13
call mtree_iter$set_key
jmp .delete_heap_mid_walk
.delete_heap_mid_done:
mov rax, [rsp+mtree_iter_size+32]
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 rbx
epilog
dalign
.delete_dispatch:
dq .delete_base_full, .delete_base_seg1, .delete_base_seg2, .delete_base_mid
dq .delete_heap_full, .delete_heap_seg1, .delete_heap_seg2, .delete_heap_mid
calign
.deleteloop_done:
mov rax, [rsp+mtree_iter_size+32]
add rsp, mtree_iter_size + 96
pop r15 r14 r13 rbx
epilog
calign
.modmap_need_maximum:
mov rdi, r15
mov rsi, rsp
call mtree$maximum
jmp .modmap_iter_ready
calign
.modmap_empty:
; up to 2 modmap entries get created for the underlying base that exclude our "deleted" region
sub rsp, mtree_iter_size + 32
test r14, r14
jz .modmap_empty_nofirst
; otherwise, we need a !inheap modmap entry at 0 for r14 bytes
; allocate a modmap entry in the mtree first up
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
mov [rsp+mtree_iter_size+24], rax ; hangon to it
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], r14
mov qword [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], 0 ; real offset (!inheap)
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 0 ; !inheap
; write it
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; add an mtree key/val for effective offset 0 + that modmap record
mov rdi, r15
xor esi, esi
mov rdx, [rsp+mtree_iter_size+24]
call mtree$insert
.modmap_empty_nofirst:
; if r14+r13 > previous filesize, set new filesize and be done with it
lea rdx, [r14+r13]
cmp rdx, [rbx+fileshadow_filesize_ofs]
jae .modmap_empty_filesize_exit
; otherwise, there is space left after our deleted segment that is !inheap
; so we need one more modmap entry
; its offset is r14+r13, and its length is filesize - that
mov rdi, r15
mov esi, fileshadow_modmap_size
call mtree$alloc
lea rcx, [r14+r13]
mov rdx, [rbx+fileshadow_filesize_ofs]
sub rdx, rcx
mov [rsp+mtree_iter_size+24], rax ; hangon to it
mov [rsp+mtree_iter_size+fileshadow_modmap_size_ofs], rdx ; remaining bytes after our delete
mov [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs], rcx ; offset into the underlying !inheap buffer/file
mov dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 0 ; !inheap
; write that
mov rdi, r15
mov rsi, rax
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$write
; insert our delete offset + that modmap record into the mtree
mov rdi, r15
mov rsi, r14
mov rdx, [rsp+mtree_iter_size+24]
call mtree$insert
.modmap_empty_filesize_exit:
; reduce the filesize by our bytecount
add rsp, mtree_iter_size + 32
sub [rbx+fileshadow_filesize_ofs], r13
mov rax, r13
pop r15 r14 r13 rbx
.ret:
epilog
end if
if used fileshadow$commit | defined include_everything
; two arguments: rdi == fileshadow object, esi == history count
; NOTES: This does nothing if called on a non-file-backed fileshadow.
; What this does is write a new file of the shadowed contents, then
; eliminates the shadow, then renames original file to a version-numbered name
; and moves our new file back in place.
; This does not blast the shadow, but does revert it to an empty modmap.
; returns a bool in eax as to whether we did the deed or not
falign
fileshadow$commit:
prolog fileshadow$commit
cmp dword [rdi+fileshadow_permitmods_ofs], 0
je .nothingtodo
cmp qword [rdi+fileshadow_mtree_ofs], 0
je .nothingtodo
cmp qword [rdi+fileshadow_privmapped_ofs], 0
je .nothingtodo
push rbx r12 r13 r14 r15
mov rbx, rdi
mov r12d, esi
; we make use of mtree's get/setint index 5 to store our revision number
; get the current revision number + 1
mov rdi, [rdi+fileshadow_mtree_ofs]
mov esi, 5
call mtree$getint
lea rdi, [rax+1]
mov esi, 16
call string$from_unsigned
mov r14, rax
mov rdi, rax
mov esi, 8
mov edx, '0'
call string$lpad
mov rdi, r14
mov r14, rax
call heap$free
; r14 is now a zero padded 8 character long hex string
; create a new filename from that
mov rdi, [rbx+fileshadow_privmapped_ofs]
mov rdi, [rdi+privmapped_filename_ofs]
mov rsi, .dotsep
call string$concat
mov r13, rax
mov rdi, rax
mov rsi, r14
call string$concat
mov rdi, r13
mov r13, rax
call heap$free
mov rdi, r14
call heap$free
; r13 is our filename.xxxxxxxx, r14 got freed
; so now we have a new filename for it, create a mapped object
; with a reserve size that matches our computed filesize
mov rdi, [rbx+fileshadow_filesize_ofs]
mov rsi, r13
call mapped$new
test rax, rax
jz .mappedfail
mov r14, rax
; so now, we can read our entirety into the newly created mapped object
mov rdi, rbx
mov rsi, [rax+mapped_base_ofs]
mov rdx, [rbx+fileshadow_filesize_ofs]
xor ecx, ecx
call fileshadow$read_offset
; now we can close/destroy our mapped object
mov rdi, r14
call mapped$destroy
; now we can clear the existing modmap, leaving the shadow in tact
mov rdi, rbx
call fileshadow$revert
; we need a filename of our current revision to rename our file to
; get the current revision number
mov rdi, [rbx+fileshadow_mtree_ofs]
mov esi, 5
call mtree$getint
mov rdi, rax
mov esi, 16
call string$from_unsigned
mov r14, rax
mov rdi, rax
mov esi, 8
mov edx, '0'
call string$lpad
mov rdi, r14
mov r14, rax
call heap$free
; create a new filename from that
mov rdi, [rbx+fileshadow_privmapped_ofs]
mov rdi, [rdi+privmapped_filename_ofs]
mov rsi, .dotsep
call string$concat
mov r15, rax
mov rdi, rax
mov rsi, r14
call string$concat
mov rdi, r15
mov r15, rax
call heap$free
mov rdi, r14
call heap$free
; so now r13 has the next revision filename, r15 has current revision filename
; make a copy of the base privmapped filename and stick it in r14
mov rdi, [rbx+fileshadow_privmapped_ofs]
mov rdi, [rdi+privmapped_filename_ofs]
call string$copy
mov r14, rax
; destroy our privmapped object
mov rdi, [rbx+fileshadow_privmapped_ofs]
call privmapped$destroy
; rename its file to the old revision
sub rsp, 16
mov rdi, r14
call string$utf8_length
lea rdi, [rax+8]
call heap$alloc
mov [rsp], rax
mov rdi, r14
mov rsi, rax
call string$to_utf8
mov rcx, [rsp]
mov byte [rcx+rax], 0
mov rdi, r15
call string$utf8_length
lea rdi, [rax+8]
call heap$alloc
mov [rsp+8], rax
mov rdi, r15
mov rsi, rax
call string$to_utf8
mov rcx, [rsp+8]
mov byte [rcx+rax], 0
mov eax, syscall_rename
mov rdi, [rsp]
mov rsi, [rsp+8]
syscall
; rename next revision filename to r14 filename
mov rdi, [rsp+8]
call heap$free
mov rdi, r13
call string$utf8_length
lea rdi, [rax+8]
call heap$alloc
mov [rsp+8], rax
mov rdi, r13
mov rsi, rax
call string$to_utf8
mov rcx, [rsp+8]
mov byte [rcx+rax], 0
mov eax, syscall_rename
mov rdi, [rsp+8]
mov rsi, [rsp]
syscall
; done with both utf8 buffers:
pop rdi
call heap$free
pop rdi
call heap$free
; so now, we can re-initialize our privmapped object
mov rdi, r14
xor esi, esi
call privmapped$new
mov [rbx+fileshadow_privmapped_ofs], rax
mov rcx, [rax+privmapped_base_ofs]
mov rdx, [rax+privmapped_size_ofs]
mov [rbx+fileshadow_base_ofs], rcx
mov [rbx+fileshadow_size_ofs], rdx
mov rdi, rbx
call fileshadow$sizecalc
mov [rbx+fileshadow_filesize_ofs], rax
; we are done with our 2 strings r13 and r15
mov rdi, r13
call heap$free
mov rdi, r15
call heap$free
; we need to increment our revision number
mov rdi, [rbx+fileshadow_mtree_ofs]
mov esi, 5
call mtree$getint
mov rdi, [rbx+fileshadow_mtree_ofs]
mov esi, 5
lea rdx, [rax+1]
call mtree$setint
; last but not least, deal with our revision history
mov rdi, [rbx+fileshadow_mtree_ofs]
mov esi, 5
call mtree$getint
mov rdi, rax
sub rdi, r12
sub rdi, 1
mov esi, 16
call string$from_unsigned
mov r13, rax
mov rdi, rax
mov esi, 8
mov edx, '0'
call string$lpad
mov rdi, r13
mov r13, rax
call heap$free
; so r13 is now an 8 character long hex string
; r14 is still a copy of the original string
; create a new filename from that
mov rdi, r14
mov rsi, .dotsep
call string$concat
mov r15, rax
mov rdi, rax
mov rsi, r13
call string$concat
mov rdi, r15
mov r15, rax
call heap$free
mov rdi, r13
call heap$free
; so r15 now has our history count filename
; r14 is still a string$copy of the original filename
; r13 is clear, r12 is no longer needed
mov rdi, r15
call string$utf8_length
lea rdi, [rax+8]
call heap$alloc
mov r13, rax
mov rdi, r15
mov rsi, rax
call string$to_utf8
mov byte [r13+rax], 0
mov eax, syscall_unlink
mov rdi, r13
syscall
mov rdi, r13
call heap$free
mov rdi, r14
call heap$free
mov rdi, r15
call heap$free
pop r15 r14 r13 r12 rbx
.nothingtodo:
mov eax, 1 ; successful result even if we didn't do anything
epilog
calign
.mappedfail:
; free the string we created, and leave everything else alone
mov rdi, r13
call heap$free
xor eax, eax
pop r15 r14 r13 r12 rbx
epilog
cleartext .dotsep, '.'
end if
if used fileshadow$revert | defined include_everything
; single argument in rdi: fileshadow object
; NOTE: this function leaves the shadow in tact (useful if you are nesting other trees inside it, or whatever).
; but deletes one-by-one the modification mtree and all associated modmap records
; (unlike reset, which blasts it entirely)
falign
fileshadow$revert:
prolog fileshadow$revert
cmp dword [rdi+fileshadow_permitmods_ofs], 0
je .nothingtodo
cmp qword [rdi+fileshadow_mtree_ofs], 0
je .nothingtodo
push rbx r12 r13
mov rbx, rdi
sub rsp, mtree_iter_size+32
calign
.deleteloop:
mov rdi, [rbx+fileshadow_mtree_ofs]
call mtree$empty
test eax, eax
jnz .deleteloop_done
mov rdi, [rbx+fileshadow_mtree_ofs]
mov rsi, rsp
call mtree$minimum
; load its key/value
mov rdi, rsp
call mtree_iter$key_value
mov r12, rdx ; save its modmap record offset
mov rdi, [rbx+fileshadow_mtree_ofs]
mov rsi, rdx
lea rdx, [rsp+mtree_iter_size]
mov ecx, fileshadow_modmap_size
call mtree$read
; regardless of whether this one is in the heap or not, we can free it
mov rdi, [rbx+fileshadow_mtree_ofs]
mov rsi, r12
call mtree$free
; we can also go ahead and delete the iterator
mov rdi, rsp
call mtree_iter$delete
; if the modmap record is in the heap, we have more work to do
cmp dword [rsp+mtree_iter_size+fileshadow_modmap_inheap_ofs], 0
je .deleteloop
; otherwise, it is in hte heap, so free its offset
mov rdi, [rbx+fileshadow_mtree_ofs]
mov rsi, [rsp+mtree_iter_size+fileshadow_modmap_offset_ofs]
call mtree$free
jmp .deleteloop
calign
.deleteloop_done:
add rsp, mtree_iter_size+32
; recalculate our filesize before we are done
mov rdi, rbx
call fileshadow$sizecalc
mov [rbx+fileshadow_filesize_ofs], rax
pop r13 r12 rbx
.nothingtodo:
epilog
end if
if used fileshadow$reset | defined include_everything
; single argument in rdi: fileshadow object
; NOTE: this function _blasts_ the shadow object entirely (and as a result is much faster than revert)
; (unlike revert, which does it nicely and leaves the shadow object in tact)
falign
fileshadow$reset:
prolog fileshadow$reset
cmp dword [rdi+fileshadow_permitmods_ofs], 0
je .nothingtodo
cmp qword [rdi+fileshadow_mtree_ofs], 0
je .nothingtodo
push rbx r12 r13
mov rbx, rdi
mov rdi, [rdi+fileshadow_mtree_ofs]
call mtree$destroy
; reconstruct our filename
mov rdi, [rbx+fileshadow_privmapped_ofs]
mov rdi, [rdi+privmapped_filename_ofs]
mov rsi, .shadow
call string$concat
mov r12, rax
; unlink the physical filename
mov rdi, rax
call string$utf8_length
lea rdi, [rax+8]
call heap$alloc
mov r13, rax
mov rdi, r12
mov rsi, rax
call string$to_utf8
mov byte [r13+rax], 0
mov eax, syscall_unlink
mov rdi, r13
syscall
mov rdi, r13
call heap$free
; reconstruct a new mtree with the same filename
xor edi, edi
mov rsi, r12
call mtree$new
mov [rbx+fileshadow_mtree_ofs], rax
mov rdi, r12
call heap$free
; recompute our size
mov rdi, rbx
call fileshadow$sizecalc
mov [rbx+fileshadow_filesize_ofs], rax
; done, dusted.
pop r13 r12 rbx
.nothingtodo:
epilog
cleartext .shadow, '.shadow'
end if