Flash 78 lines
// mv — move/rename OLD to NEW for /bin/mv.
//
// mv OLD NEW
//
// First tries the kernel's same-directory rename (SYS_RENAME), an in-place
// 8.3 name rewrite with no data move — the fast path for `mv A.FL B.FL`. The
// kernel rejects a cross-directory rename (it cannot move bytes between
// directories), so on failure mv falls back to copy-then-unlink: create NEW,
// stream OLD into it, remove OLD. NEW must not already exist and its name
// must fit 8.3 (the rename / create syscalls reject otherwise).
//
// Same coreutil recipe as cp (which owns the same copy loop); the fallback is
// inlined rather than exec'ing /bin/cp to keep mv a single self-contained
// program. flibc _start shim, flibc_mem, stack buffer only.
use flibc
link "flibc_start"
link "flibc_mem"
const BUF_LEN usize = 512
fn diag(msg []u8) {
_ = flibc.sys.write_fd(2, msg.ptr, msg.len)
}
// Copy src -> dst (dst created fresh). Returns true on a clean copy.
fn copyFile(src cstr, dst cstr) bool {
const sfd = flibc.sys.open(src)
if sfd < 0 {
return false
}
const dfd = flibc.sys.create(dst)
if dfd < 0 {
_ = flibc.sys.close(sfd)
return false
}
var buf [BUF_LEN]u8 = undefined
var ok bool = true
while true {
n := flibc.sys.read(sfd, &buf, buf.len)
if n <= 0 {
break
}
if flibc.sys.write_fd(dfd, &buf, #intCast(n)) != n {
ok = false
break
}
}
_ = flibc.sys.close(sfd)
_ = flibc.sys.close(dfd)
return ok
}
export fn main(argc usize, argv argv) noreturn {
if argc < 3 {
diag("usage: mv OLD NEW\n")
flibc.exit()
}
const old = argv[1] orelse flibc.exit()
const new = argv[2] orelse flibc.exit()
// Fast path: same-directory in-place rename.
if flibc.sys.rename(old, new) == 0 {
flibc.exit()
}
// Fallback: cross-directory move via copy + unlink.
if !copyFile(old, new) {
diag("mv: cannot move\n")
flibc.exit()
}
if flibc.sys.unlink(old) < 0 {
diag("mv: moved but could not remove source\n")
}
flibc.exit()
}