ajhahn.de
← FlashOS
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()
}