filebox: add path-based I/O and exists
This commit is contained in:
committed by
Selfhosting Dev
parent
7fc5fef5cc
commit
3babd8f69c
@ -22,9 +22,10 @@ type_id = 6
|
|||||||
[libraries."libnyash_filebox_plugin".FileBox.methods]
|
[libraries."libnyash_filebox_plugin".FileBox.methods]
|
||||||
birth = { method_id = 0 }
|
birth = { method_id = 0 }
|
||||||
open = { method_id = 1, args = ["path", "mode"] }
|
open = { method_id = 1, args = ["path", "mode"] }
|
||||||
read = { method_id = 2 }
|
read = { method_id = 2, args = ["path?"] }
|
||||||
write = { method_id = 3, args = ["data"] }
|
write = { method_id = 3, args = ["path?", "data"] }
|
||||||
close = { method_id = 4 }
|
close = { method_id = 4 }
|
||||||
|
exists = { method_id = 5, args = ["path"] }
|
||||||
fini = { method_id = 4294967295 }
|
fini = { method_id = 4294967295 }
|
||||||
copyFrom = { method_id = 7, args = [ { kind = "box", category = "plugin" } ] }
|
copyFrom = { method_id = 7, args = [ { kind = "box", category = "plugin" } ] }
|
||||||
cloneSelf = { method_id = 8 }
|
cloneSelf = { method_id = 8 }
|
||||||
|
|||||||
@ -21,9 +21,19 @@ returns = { type = "void", error = "string" }
|
|||||||
|
|
||||||
[FileBox.methods.read]
|
[FileBox.methods.read]
|
||||||
id = 2
|
id = 2
|
||||||
args = []
|
args = [ { name = "path", type = "string", optional = true } ]
|
||||||
returns = { type = "string" }
|
returns = { type = "string" }
|
||||||
|
|
||||||
|
[FileBox.methods.write]
|
||||||
|
id = 3
|
||||||
|
args = [ { name = "path", type = "string", optional = true }, { name = "data", type = "bytes" } ]
|
||||||
|
returns = { type = "i32" }
|
||||||
|
|
||||||
|
[FileBox.methods.exists]
|
||||||
|
id = 5
|
||||||
|
args = [ { name = "path", type = "string" } ]
|
||||||
|
returns = { type = "bool" }
|
||||||
|
|
||||||
[implementation]
|
[implementation]
|
||||||
ffi_version = 1
|
ffi_version = 1
|
||||||
thread_safe = true
|
thread_safe = true
|
||||||
|
|||||||
@ -42,6 +42,7 @@ const METHOD_OPEN: u32 = 1;
|
|||||||
const METHOD_READ: u32 = 2;
|
const METHOD_READ: u32 = 2;
|
||||||
const METHOD_WRITE: u32 = 3;
|
const METHOD_WRITE: u32 = 3;
|
||||||
const METHOD_CLOSE: u32 = 4;
|
const METHOD_CLOSE: u32 = 4;
|
||||||
|
const METHOD_EXISTS: u32 = 5;
|
||||||
const METHOD_COPY_FROM: u32 = 7; // New: copyFrom(other: Handle)
|
const METHOD_COPY_FROM: u32 = 7; // New: copyFrom(other: Handle)
|
||||||
const METHOD_CLONE_SELF: u32 = 8; // New: cloneSelf() -> Handle
|
const METHOD_CLONE_SELF: u32 = 8; // New: cloneSelf() -> Handle
|
||||||
const METHOD_FINI: u32 = u32::MAX; // Destructor
|
const METHOD_FINI: u32 = u32::MAX; // Destructor
|
||||||
@ -167,46 +168,68 @@ pub extern "C" fn nyash_plugin_invoke(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
METHOD_READ => {
|
METHOD_READ => {
|
||||||
// args: None (Nyash spec: read() has no arguments)
|
// args: optional String path
|
||||||
// Read entire file content
|
let args = std::slice::from_raw_parts(_args, _args_len);
|
||||||
if let Ok(mut map) = INSTANCES.lock() {
|
if _args_len > 0 {
|
||||||
if let Some(inst) = map.get_mut(&_instance_id) {
|
match tlv_parse_string(args) {
|
||||||
if let Some(file) = inst.file.as_mut() {
|
Ok(path) => {
|
||||||
// Read entire file from beginning
|
match open_file("r", &path) {
|
||||||
let _ = file.seek(SeekFrom::Start(0));
|
Ok(mut file) => {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
match file.read_to_end(&mut buf) {
|
if let Err(_) = file.read_to_end(&mut buf) { return NYB_E_PLUGIN_ERROR; }
|
||||||
Ok(n) => {
|
|
||||||
log_info(&format!("READ {} bytes (entire file)", n));
|
|
||||||
// Preflight for Bytes TLV: header(4) + entry(4) + content
|
|
||||||
let need = 8usize.saturating_add(buf.len());
|
let need = 8usize.saturating_add(buf.len());
|
||||||
if preflight(_result, _result_len, need) { return NYB_E_SHORT_BUFFER; }
|
if preflight(_result, _result_len, need) { return NYB_E_SHORT_BUFFER; }
|
||||||
return write_tlv_bytes(&buf, _result, _result_len);
|
return write_tlv_bytes(&buf, _result, _result_len);
|
||||||
}
|
}
|
||||||
Err(_) => return NYB_E_PLUGIN_ERROR,
|
Err(_) => return NYB_E_PLUGIN_ERROR,
|
||||||
}
|
}
|
||||||
} else { return NYB_E_INVALID_HANDLE; }
|
}
|
||||||
|
Err(_) => return NYB_E_INVALID_ARGS,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Ok(mut map) = INSTANCES.lock() {
|
||||||
|
if let Some(inst) = map.get_mut(&_instance_id) {
|
||||||
|
if let Some(file) = inst.file.as_mut() {
|
||||||
|
let _ = file.seek(SeekFrom::Start(0));
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
match file.read_to_end(&mut buf) {
|
||||||
|
Ok(n) => {
|
||||||
|
log_info(&format!("READ {} bytes (entire file)", n));
|
||||||
|
let need = 8usize.saturating_add(buf.len());
|
||||||
|
if preflight(_result, _result_len, need) { return NYB_E_SHORT_BUFFER; }
|
||||||
|
return write_tlv_bytes(&buf, _result, _result_len);
|
||||||
|
}
|
||||||
|
Err(_) => return NYB_E_PLUGIN_ERROR,
|
||||||
|
}
|
||||||
|
} else { return NYB_E_INVALID_HANDLE; }
|
||||||
|
} else { return NYB_E_PLUGIN_ERROR; }
|
||||||
} else { return NYB_E_PLUGIN_ERROR; }
|
} else { return NYB_E_PLUGIN_ERROR; }
|
||||||
} else { return NYB_E_PLUGIN_ERROR; }
|
}
|
||||||
}
|
}
|
||||||
METHOD_WRITE => {
|
METHOD_WRITE => {
|
||||||
// args: TLV { Bytes data }
|
// args: TLV { [String path]? Bytes data }
|
||||||
let args = std::slice::from_raw_parts(_args, _args_len);
|
let args = std::slice::from_raw_parts(_args, _args_len);
|
||||||
match tlv_parse_bytes(args) {
|
match tlv_parse_optional_string_and_bytes(args) {
|
||||||
Ok(data) => {
|
Ok((Some(path), data)) => {
|
||||||
// Preflight for I32 TLV: header(4) + entry(4) + 4
|
if preflight(_result, _result_len, 12) { return NYB_E_SHORT_BUFFER; }
|
||||||
|
match open_file("w", &path) {
|
||||||
|
Ok(mut file) => {
|
||||||
|
if let Err(_) = file.write_all(&data) { return NYB_E_PLUGIN_ERROR; }
|
||||||
|
if let Err(_) = file.flush() { return NYB_E_PLUGIN_ERROR; }
|
||||||
|
return write_tlv_i32(data.len() as i32, _result, _result_len);
|
||||||
|
}
|
||||||
|
Err(_) => return NYB_E_PLUGIN_ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((None, data)) => {
|
||||||
if preflight(_result, _result_len, 12) { return NYB_E_SHORT_BUFFER; }
|
if preflight(_result, _result_len, 12) { return NYB_E_SHORT_BUFFER; }
|
||||||
if let Ok(mut map) = INSTANCES.lock() {
|
if let Ok(mut map) = INSTANCES.lock() {
|
||||||
if let Some(inst) = map.get_mut(&_instance_id) {
|
if let Some(inst) = map.get_mut(&_instance_id) {
|
||||||
if let Some(file) = inst.file.as_mut() {
|
if let Some(file) = inst.file.as_mut() {
|
||||||
match file.write(&data) {
|
match file.write(&data) {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
// ファイルバッファをフラッシュ(重要!)
|
if let Err(_) = file.flush() { return NYB_E_PLUGIN_ERROR; }
|
||||||
if let Err(_) = file.flush() {
|
|
||||||
return NYB_E_PLUGIN_ERROR;
|
|
||||||
}
|
|
||||||
log_info(&format!("WRITE {} bytes", n));
|
log_info(&format!("WRITE {} bytes", n));
|
||||||
// バッファも更新(copyFromなどのため)
|
|
||||||
inst.buffer = Some(data.clone());
|
inst.buffer = Some(data.clone());
|
||||||
return write_tlv_i32(n as i32, _result, _result_len);
|
return write_tlv_i32(n as i32, _result, _result_len);
|
||||||
}
|
}
|
||||||
@ -291,6 +314,18 @@ pub extern "C" fn nyash_plugin_invoke(
|
|||||||
payload[4..8].copy_from_slice(&new_id.to_le_bytes());
|
payload[4..8].copy_from_slice(&new_id.to_le_bytes());
|
||||||
return write_tlv_result(&[(8u8, &payload)], _result, _result_len);
|
return write_tlv_result(&[(8u8, &payload)], _result, _result_len);
|
||||||
}
|
}
|
||||||
|
METHOD_EXISTS => {
|
||||||
|
// args: TLV { String path }
|
||||||
|
let args = std::slice::from_raw_parts(_args, _args_len);
|
||||||
|
match tlv_parse_string(args) {
|
||||||
|
Ok(path) => {
|
||||||
|
let exists = std::path::Path::new(&path).exists();
|
||||||
|
if preflight(_result, _result_len, 9) { return NYB_E_SHORT_BUFFER; }
|
||||||
|
return write_tlv_bool(exists, _result, _result_len);
|
||||||
|
}
|
||||||
|
Err(_) => NYB_E_INVALID_ARGS,
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => NYB_SUCCESS
|
_ => NYB_SUCCESS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,6 +379,11 @@ fn write_tlv_i32(v: i32, result: *mut u8, result_len: *mut usize) -> i32 {
|
|||||||
write_tlv_result(&[(2u8, &v.to_le_bytes())], result, result_len)
|
write_tlv_result(&[(2u8, &v.to_le_bytes())], result, result_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_tlv_bool(v: bool, result: *mut u8, result_len: *mut usize) -> i32 {
|
||||||
|
let b = [if v {1u8} else {0u8}];
|
||||||
|
write_tlv_result(&[(1u8, &b)], result, result_len)
|
||||||
|
}
|
||||||
|
|
||||||
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
fn preflight(result: *mut u8, result_len: *mut usize, needed: usize) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
if result_len.is_null() { return false; }
|
if result_len.is_null() { return false; }
|
||||||
@ -404,6 +444,33 @@ fn tlv_parse_bytes(data: &[u8]) -> Result<Vec<u8>, ()> {
|
|||||||
Ok(data[pos..pos+size].to_vec())
|
Ok(data[pos..pos+size].to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tlv_parse_string(data: &[u8]) -> Result<String, ()> {
|
||||||
|
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||||
|
if argc < 1 { return Err(()); }
|
||||||
|
tlv_parse_string_at(data, &mut pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tlv_parse_optional_string_and_bytes(data: &[u8]) -> Result<(Option<String>, Vec<u8>), ()> {
|
||||||
|
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||||
|
if argc == 1 {
|
||||||
|
// only bytes
|
||||||
|
if pos + 4 > data.len() { return Err(()); }
|
||||||
|
let tag = data[pos]; let _res = data[pos+1];
|
||||||
|
let size = u16::from_le_bytes([data[pos+2], data[pos+3]]) as usize; pos += 4;
|
||||||
|
if (tag != 6 && tag != 7) || pos + size > data.len() { return Err(()); }
|
||||||
|
return Ok((None, data[pos..pos+size].to_vec()));
|
||||||
|
} else if argc >= 2 {
|
||||||
|
let s = tlv_parse_string_at(data, &mut pos)?;
|
||||||
|
if pos + 4 > data.len() { return Err(()); }
|
||||||
|
let tag = data[pos]; let _res = data[pos+1];
|
||||||
|
let size = u16::from_le_bytes([data[pos+2], data[pos+3]]) as usize; pos += 4;
|
||||||
|
if (tag != 6 && tag != 7) || pos + size > data.len() { return Err(()); }
|
||||||
|
Ok((Some(s), data[pos..pos+size].to_vec()))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn tlv_parse_handle(data: &[u8]) -> Result<(u32, u32), ()> {
|
fn tlv_parse_handle(data: &[u8]) -> Result<(u32, u32), ()> {
|
||||||
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||||
if argc < 1 { return Err(()); }
|
if argc < 1 { return Err(()); }
|
||||||
|
|||||||
Reference in New Issue
Block a user