From 21c69d4ba4ddaabbc43c82c040ecc8edd916e27b Mon Sep 17 00:00:00 2001 From: Stefan Plantikow Date: Wed, 16 Nov 2011 23:14:18 +0100 Subject: [PATCH] Added cross-platform fsync api to io; win32 impl needs to be refined No tests, need mktmpfile first --- src/lib/io.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++- src/lib/linux_os.rs | 12 ++++++ src/lib/macos_os.rs | 44 +++++++++++++++------ src/lib/win32_os.rs | 8 ++++ 4 files changed, 146 insertions(+), 12 deletions(-) diff --git a/src/lib/io.rs b/src/lib/io.rs index 99ae9bb670c..910ec8bb9ea 100644 --- a/src/lib/io.rs +++ b/src/lib/io.rs @@ -25,6 +25,9 @@ type buf_reader = fn eof() -> bool; fn seek(int, seek_style); fn tell() -> uint; + // Needed on readers in case one needs to flush metadata + // changes (atime) + fn fsync(level: fsync::level) -> int; }; @@ -58,7 +61,9 @@ fn convert_whence(whence: seek_style) -> i32 { }; } -resource FILE_res(f: os::libc::FILE) { os::libc::fclose(f); } +resource FILE_res(f: os::libc::FILE) { + os::libc::fclose(f); +} obj FILE_buf_reader(f: os::libc::FILE, res: option::t<@FILE_res>) { fn read(len: uint) -> [u8] unsafe { @@ -76,6 +81,9 @@ obj FILE_buf_reader(f: os::libc::FILE, res: option::t<@FILE_res>) { assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0i32); } fn tell() -> uint { ret os::libc::ftell(f) as uint; } + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(os::libc::fileno(f), level) as int; + } } @@ -219,6 +227,7 @@ obj byte_buf_reader(bbuf: byte_buf) { bbuf.pos = seek_in_buf(offset, pos, len, whence); } fn tell() -> uint { ret bbuf.pos; } + fn fsync(_level: fsync::level) -> int { ret 0; } } fn new_byte_buf_reader(buf: [u8]) -> buf_reader { @@ -242,6 +251,8 @@ type buf_writer = fn write([u8]); fn seek(int, seek_style); fn tell() -> uint; + fn flush() -> int; + fn fsync(level: fsync::level) -> int; }; obj FILE_writer(f: os::libc::FILE, res: option::t<@FILE_res>) { @@ -255,6 +266,10 @@ obj FILE_writer(f: os::libc::FILE, res: option::t<@FILE_res>) { assert (os::libc::fseek(f, offset, convert_whence(whence)) == 0i32); } fn tell() -> uint { ret os::libc::ftell(f) as uint; } + fn flush() -> int { ret os::libc::fflush(f) as int; } + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(os::libc::fileno(f), level) as int; + } } resource fd_res(fd: fd_t) { os::libc::close(fd); } @@ -283,6 +298,12 @@ obj fd_buf_writer(fd: fd_t, res: option::t<@fd_res>) { log_err "need 64-bit native calls for tell, sorry"; fail; } + + fn flush() -> int { ret 0; } + + fn fsync(level: fsync::level) -> int { + ret os::fsync_fd(fd, level) as int; + } } fn file_buf_writer(path: str, @@ -433,6 +454,8 @@ obj byte_buf_writer(buf: mutable_byte_buf) { buf.pos = seek_in_buf(offset, pos, len, whence); } fn tell() -> uint { ret buf.pos; } + fn flush() -> int { ret 0; } + fn fsync(_level: fsync::level) -> int { ret 0; } } fn string_writer() -> str_writer { @@ -477,6 +500,75 @@ fn read_whole_file(file: str) -> result::t<[u8], str> { }) } +// fsync related + +mod fsync { + + tag level { + // whatever fsync does on that platform + fsync; + + // fdatasync on linux, similiar or more on other platforms + fdatasync; + + // full fsync + // + // You must additionally sync the parent directory as well! + fullfsync; + } + + + // Resource of artifacts that need to fsync on destruction + resource res(arg: arg) { + alt arg.opt_level { + option::none::. { } + option::some::(level) { + // fail hard if not succesful + assert(arg.fsync_fn(arg.val, level) != -1); + } + } + } + + type arg = { + val: t, + opt_level: option::t, + fsync_fn: fn(t, level) -> int + }; + + // fsync file after executing blk + // FIXME find better way to create resources within lifetime of outer res + fn FILE_res_sync(&&file: FILE_res, opt_level: option::t, + blk: block(&&res)) { + blk(res({ + val: *file, opt_level: opt_level, + fsync_fn: fn(&&file: os::libc::FILE, l: level) -> int { + ret os::fsync_fd(os::libc::fileno(file), l) as int; + } + })); + } + + // fsync fd after executing blk + fn fd_res_sync(&&fd: fd_res, opt_level: option::t, + blk: block(&&res)) { + blk(res({ + val: *fd, opt_level: opt_level, + fsync_fn: fn(&&fd: fd_t, l: level) -> int { + ret os::fsync_fd(fd, l) as int; + } + })); + } + + // Type of objects that may want to fsync + type t = obj { fn fsync(l: level) -> int; }; + + // Call o.fsync after executing blk + fn obj_sync(&&o: t, opt_level: option::t, blk: block(&&res)) { + blk(res({ + val: o, opt_level: opt_level, + fsync_fn: fn(&&o: t, l: level) -> int { ret o.fsync(l); } + })); + } +} // diff --git a/src/lib/linux_os.rs b/src/lib/linux_os.rs index a2dc0e8632a..7e30088ee3c 100644 --- a/src/lib/linux_os.rs +++ b/src/lib/linux_os.rs @@ -18,6 +18,7 @@ export exec_suffix; export target_os; export dylib_filename; export get_exe_path; +export fsync_fd; // FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult // by https://github.com/graydon/rust/issues#issue/268 @@ -35,6 +36,10 @@ native mod libc { fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE; fn fclose(f: FILE); + fn fflush(f: FILE) -> c_int; + fn fsync(fd: fd_t) -> c_int; + fn fdatasync(fd: fd_t) -> c_int; + fn fileno(f: FILE) -> fd_t; fn fgetc(f: FILE) -> c_int; fn ungetc(c: c_int, f: FILE); fn feof(f: FILE) -> c_int; @@ -89,6 +94,13 @@ fn fclose(file: libc::FILE) { libc::fclose(file) } +fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { + alt level { + io::fsync::fsync. | io::fsync::fullfsync. { ret libc::fsync(fd); } + io::fsync::fdatasync. { ret libc::fdatasync(fd); } + } +} + fn waitpid(pid: pid_t) -> i32 { let status = 0i32; assert (os::libc::waitpid(pid, status, 0i32) != -1i32); diff --git a/src/lib/macos_os.rs b/src/lib/macos_os.rs index ad90fef489e..f23e82eaa63 100644 --- a/src/lib/macos_os.rs +++ b/src/lib/macos_os.rs @@ -12,6 +12,7 @@ export exec_suffix; export target_os; export dylib_filename; export get_exe_path; +export fsync_fd; // FIXME Refactor into unix_os module or some such. Doesn't // seem to work right now. @@ -28,6 +29,9 @@ native mod libc { type FILE; fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE; + fn fflush(f: FILE) -> c_int; + fn fsync(fd: fd_t) -> c_int; + fn fileno(f: FILE) -> fd_t; fn fclose(f: FILE); fn fgetc(f: FILE) -> c_int; fn ungetc(c: c_int, f: FILE); @@ -47,21 +51,26 @@ native mod libc { fn mkdir(s: str::sbuf, mode: c_int) -> c_int; fn rmdir(s: str::sbuf) -> c_int; fn chdir(s: str::sbuf) -> c_int; + + // FIXME: Needs varags + fn fcntl(fd: fd_t, cmd: c_int) -> c_int; } mod libc_constants { - const O_RDONLY: c_int = 0i32; - const O_WRONLY: c_int = 1i32; - const O_RDWR: c_int = 2i32; - const O_APPEND: c_int = 8i32; - const O_CREAT: c_int = 512i32; - const O_EXCL: c_int = 2048i32; - const O_TRUNC: c_int = 1024i32; - const O_TEXT: c_int = 0i32; // nonexistent in darwin libc - const O_BINARY: c_int = 0i32; // nonexistent in darwin libc + const O_RDONLY: c_int = 0i32; + const O_WRONLY: c_int = 1i32; + const O_RDWR: c_int = 2i32; + const O_APPEND: c_int = 8i32; + const O_CREAT: c_int = 512i32; + const O_EXCL: c_int = 2048i32; + const O_TRUNC: c_int = 1024i32; + const O_TEXT: c_int = 0i32; // nonexistent in darwin libc + const O_BINARY: c_int = 0i32; // nonexistent in darwin libc - const S_IRUSR: unsigned = 256u32; - const S_IWUSR: unsigned = 128u32; + const S_IRUSR: unsigned = 256u32; + const S_IWUSR: unsigned = 128u32; + + const F_FULLFSYNC: c_int = 51i32; } fn pipe() -> {in: fd_t, out: fd_t} { @@ -88,6 +97,19 @@ fn waitpid(pid: pid_t) -> i32 { ret status; } +fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { + alt level { + io::fsync::fsync. { ret libc::fsync(fd); } + _ { + // According to man fnctl, the ok retval is only specified to be !=-1 + if (libc::fcntl(libc_constants::F_FULLFSYNC, fd) == -1 as c_int) + { ret -1 as c_int; } + else + { ret 0 as c_int; } + } + } +} + #[abi = "cdecl"] native mod rustrt { fn rust_getcwd() -> str; diff --git a/src/lib/win32_os.rs b/src/lib/win32_os.rs index c926af31d51..e2c4583df18 100644 --- a/src/lib/win32_os.rs +++ b/src/lib/win32_os.rs @@ -15,6 +15,9 @@ native mod libc { fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; fn _fdopen(fd: fd_t, mode: str::sbuf) -> FILE; fn fclose(f: FILE); + fn fflush(f: FILE) -> c_int; + fn fsync(fd: fd_t) -> c_int; + fn fileno(f: FILE) -> fd_t; fn fgetc(f: FILE) -> c_int; fn ungetc(c: c_int, f: FILE); fn feof(f: FILE) -> c_int; @@ -93,6 +96,11 @@ fn fclose(file: libc::FILE) { libc::fclose(file) } +fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int { + // FIXME do something more apropriate + ret libc::fsync(fd); +} + #[abi = "cdecl"] native mod rustrt { fn rust_process_wait(handle: c_int) -> c_int;