| git.druid.rocks | index | druid520 | kaboom | src/ | user/ | mv.nsc |
src/user/mv.nsc
include "syscalls.nsh";
include "lib.nsh";
include "string.nsh";
include "stdio.nsh";
/* mv src dst -- files only, not directories (kfs has no directory
* move/rename primitive, and moving a whole subtree is a bigger
* feature than "rename this file"). not a real rename either: kfs
* has no rename-in-place at the directory-entry level yet, so this
* is genuinely copy-then-delete (read src fully, sys_writefile it to
* dst, sys_rm the old name) -- functionally a move (src is gone
* afterward, dst has its content), just not atomic and not O(1) the
* way a real rename would be. good enough until something actually
* needs better.
*
* because the source is destroyed at the end, the order below is the
* whole point:
* (0) if src and dst already name the same file -- same inode, by
* sys_ino, e.g. `mv /usr/k /usr/k`, or `cd /usr; mv k /usr/k`
* -- do nothing at all and succeed. copy-then-delete onto
* itself would rewrite the file and then rm the only copy.
* (1) read ALL of src (read_all returns 0 rather than a partial
* buffer).
* (2) write ALL of it to dst and check sys_writefile actually
* succeeded (kfs_save refuses a dst that's a directory rather
* than overwriting it).
* (3) only then sys_rm src.
* any failure before (3) returns with src untouched. this used to
* read at most 63488 bytes (kfs's old max file size), so moving
* anything bigger wrote a truncated dst and then deleted the only full
* copy. */
global i32
main(i32 argc, ptr argv)
{
ptr src;
ptr dst;
u64 src_len;
u64 dst_len;
i32 fd;
i64 n;
i64 src_ino;
ptr buf;
if(argc < 3)
{
fputs(2, "usage: mv src dst\n");
return 1;
}
src = argv_get(argv, 1);
src_len = strlen(src);
dst = argv_get(argv, 2);
dst_len = strlen(dst);
src_ino = sys_ino(src, src_len);
if(src_ino >= 0 && sys_ino(dst, dst_len) == src_ino)
{
return 0; /* same file: moving it onto itself is a no-op */
}
fd = sys_open(src, src_len);
if(fd < 0)
{
print_err(2, "mv", "source not found");
return 1;
}
buf = read_all(fd, &n);
sys_close(fd);
if(buf == (ptr)0)
{
print_err(2, "mv", "read failed, source left in place");
return 1;
}
if(sys_writefile(dst, dst_len, buf, (u64)n) == 0)
{
print_err(2, "mv", "write failed, source left in place");
return 1;
}
if(sys_rm(src, src_len) == 0)
{
print_err(2, "mv", "warning: could not remove source");
return 1;
}
return 0;
}