use std::os::unix::ffi::OsStringExt;
use std::{ffi::OsString, time::SystemTime};
use super::exclusive::ExclusiveFileId;
use super::store::SqlStore;
use crate::util::system_time_to_nanos;
use crate::{
RootId,
block::FileId as StoreFileId,
errors::InternalError,
file_metadata::{FileMode, Gid, RawMetadata, Uid, Xattrs},
user::UserMetadata,
};
impl<'conn> SqlStore<'conn> {
pub fn get_file_metadata_by_id(&self, file_id: StoreFileId) -> crate::Result<RawMetadata> {
let mut stmt = self.db.prepare_cached(
r#"
SELECT
liteboxfs_files.kind, liteboxfs_files.mode, liteboxfs_files.atime, liteboxfs_files.mtime,
liteboxfs_files.ctime, liteboxfs_files.btime, liteboxfs_files.uid, liteboxfs_files.gid
FROM
liteboxfs_files
WHERE
liteboxfs_files.id = ?;
"#,
)?;
let result = stmt.query_row(rusqlite::params![file_id], |row| {
Ok(RawMetadata {
discriminant: row.get(0)?,
mode: FileMode::from_bits_truncate(row.get(1)?),
atime: row.get(2)?,
mtime: row.get(3)?,
ctime: row.get(4)?,
btime: row.get(5)?,
uid: Uid::from_raw(row.get(6)?),
gid: Gid::from_raw(row.get(7)?),
})
});
match result {
Ok(data) => Ok(data),
Err(rusqlite::Error::QueryReturnedNoRows) => {
Err(InternalError::FileNotFound { id: file_id }.into())
}
Err(err) => Err(err.into()),
}
}
pub fn update_file_mode(&self, file_id: ExclusiveFileId, mode: FileMode) -> crate::Result<()> {
let rows_updated = self.db.execute(
r#"
UPDATE
liteboxfs_files
SET
mode = ?
WHERE
id = ?;
"#,
rusqlite::params![mode.bits(), file_id],
)?;
if rows_updated == 0 {
return Err(InternalError::FileNotFound {
id: file_id.file_id(),
}
.into());
}
Ok(())
}
pub fn update_file_uid(&self, file_id: ExclusiveFileId, uid: Uid) -> crate::Result<()> {
let rows_updated = self.db.execute(
r#"
UPDATE
liteboxfs_files
SET
uid = ?
WHERE
id = ?;
"#,
rusqlite::params![uid.as_raw(), file_id],
)?;
if rows_updated == 0 {
return Err(InternalError::FileNotFound {
id: file_id.file_id(),
}
.into());
}
Ok(())
}
pub fn update_file_gid(&self, file_id: ExclusiveFileId, gid: Gid) -> crate::Result<()> {
let rows_updated = self.db.execute(
r#"
UPDATE
liteboxfs_files
SET
gid = ?
WHERE
id = ?;
"#,
rusqlite::params![gid.as_raw(), file_id],
)?;
if rows_updated == 0 {
return Err(InternalError::FileNotFound {
id: file_id.file_id(),
}
.into());
}
Ok(())
}
pub fn update_file_atime(
&self,
file_id: ExclusiveFileId,
atime: SystemTime,
) -> crate::Result<()> {
let rows_updated = self.db.execute(
r#"
UPDATE
liteboxfs_files
SET
atime = ?
WHERE
id = ?;
"#,
rusqlite::params![system_time_to_nanos(atime)?, file_id],
)?;
if rows_updated == 0 {
return Err(InternalError::FileNotFound {
id: file_id.file_id(),
}
.into());
}
Ok(())
}
pub fn update_file_mtime(
&self,
file_id: ExclusiveFileId,
mtime: SystemTime,
) -> crate::Result<()> {
let rows_updated = self.db.execute(
r#"
UPDATE
liteboxfs_files
SET
mtime = ?
WHERE
id = ?;
"#,
rusqlite::params![system_time_to_nanos(mtime)?, file_id],
)?;
if rows_updated == 0 {
return Err(InternalError::FileNotFound {
id: file_id.file_id(),
}
.into());
}
Ok(())
}
pub fn update_file_ctime(
&self,
file_id: ExclusiveFileId,
ctime: SystemTime,
) -> crate::Result<()> {
let rows_updated = self.db.execute(
r#"
UPDATE
liteboxfs_files
SET
ctime = ?
WHERE
id = ?;
"#,
rusqlite::params![system_time_to_nanos(ctime)?, file_id],
)?;
if rows_updated == 0 {
return Err(InternalError::FileNotFound {
id: file_id.file_id(),
}
.into());
}
Ok(())
}
pub fn update_file_btime(
&self,
file_id: ExclusiveFileId,
btime: Option<SystemTime>,
) -> crate::Result<()> {
let rows_updated = self.db.execute(
r#"
UPDATE
liteboxfs_files
SET
btime = ?
WHERE
id = ?;
"#,
rusqlite::params![btime.map(system_time_to_nanos).transpose()?, file_id],
)?;
if rows_updated == 0 {
return Err(InternalError::FileNotFound {
id: file_id.file_id(),
}
.into());
}
Ok(())
}
pub fn get_file_xattrs(&self, file_id: StoreFileId) -> crate::Result<Xattrs> {
let mut stmt = self.db.prepare_cached(
r#"
SELECT
name,
value
FROM
liteboxfs_xattrs
WHERE
file = ?;
"#,
)?;
let rows = stmt.query_map(rusqlite::params![file_id], |row| {
Ok((row.get::<_, Vec<u8>>(0)?, row.get::<_, Vec<u8>>(1)?))
})?;
let mut xattrs = Xattrs::new();
for row in rows {
let (name, value) = row?;
let name = OsString::from_vec(name);
xattrs.set(name, value);
}
Ok(xattrs)
}
pub fn set_file_xattrs(&self, file_id: ExclusiveFileId, xattrs: &Xattrs) -> crate::Result<()> {
self.db.execute(
r#"
DELETE FROM
liteboxfs_xattrs
WHERE
file = ?;
"#,
rusqlite::params![file_id],
)?;
let mut stmt = self.db.prepare_cached(
r#"
INSERT INTO
liteboxfs_xattrs (file, name, value)
VALUES
(?, ?, ?);
"#,
)?;
for (name, value) in xattrs {
stmt.execute(rusqlite::params![file_id, name.as_encoded_bytes(), value])?;
}
Ok(())
}
pub fn get_file_xattr(
&self,
file_id: StoreFileId,
name: &[u8],
) -> crate::Result<Option<Vec<u8>>> {
let mut stmt = self.db.prepare_cached(
r#"
SELECT
value
FROM
liteboxfs_xattrs
WHERE
file = ?
AND name = ?;
"#,
)?;
let result = stmt.query_row(rusqlite::params![file_id, name], |row| {
row.get::<_, Vec<u8>>(0)
});
match result {
Ok(value) => Ok(Some(value)),
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(err) => Err(err.into()),
}
}
pub fn set_file_xattr(
&self,
file_id: ExclusiveFileId,
name: &[u8],
value: &[u8],
) -> crate::Result<()> {
self.db.execute(
r#"
INSERT OR REPLACE INTO
liteboxfs_xattrs (file, name, value)
VALUES
(?, ?, ?);
"#,
rusqlite::params![file_id, name, value],
)?;
Ok(())
}
pub fn get_filesystem_metadata(&self) -> crate::Result<UserMetadata> {
let mut stmt = self.db.prepare_cached(
r#"
SELECT
key,
value
FROM
liteboxfs_metadata;
"#,
)?;
let rows = stmt.query_map([], |row| {
Ok((row.get::<_, String>(0)?, row.get::<_, Vec<u8>>(1)?))
})?;
let mut metadata = UserMetadata::new();
for row in rows {
let (key, value) = row?;
metadata.set(key, value)?;
}
Ok(metadata)
}
pub fn set_filesystem_metadata(&self, metadata: &UserMetadata) -> crate::Result<()> {
self.db.execute(
r#"
DELETE FROM
liteboxfs_metadata;
"#,
[],
)?;
let mut stmt = self.db.prepare_cached(
r#"
INSERT INTO
liteboxfs_metadata (key, value)
VALUES
(?, ?);
"#,
)?;
for (key, value) in metadata {
stmt.execute(rusqlite::params![key, value])?;
}
Ok(())
}
pub fn get_root_metadata(&self, root_id: RootId) -> crate::Result<UserMetadata> {
let mut stmt = self.db.prepare_cached(
r#"
SELECT
key,
value
FROM
liteboxfs_root_metadata
WHERE
root = (
SELECT
id
FROM
liteboxfs_roots
WHERE
uuid = ?
);
"#,
)?;
let rows = stmt.query_map(rusqlite::params![root_id.to_string()], |row| {
Ok((row.get::<_, String>(0)?, row.get::<_, Vec<u8>>(1)?))
})?;
let mut metadata = UserMetadata::new();
for row in rows {
let (key, value) = row?;
metadata.set(key, value)?;
}
Ok(metadata)
}
pub fn set_root_metadata(&self, root_id: RootId, metadata: &UserMetadata) -> crate::Result<()> {
self.db.execute(
r#"
DELETE FROM
liteboxfs_root_metadata
WHERE
root = (
SELECT
id
FROM
liteboxfs_roots
WHERE
uuid = ?
);
"#,
rusqlite::params![root_id.to_string()],
)?;
let mut stmt = self.db.prepare_cached(
r#"
INSERT INTO
liteboxfs_root_metadata (root, key, value)
VALUES
(
(
SELECT
id
FROM
liteboxfs_roots
WHERE
uuid = ?
),
?,
?
);
"#,
)?;
for (key, value) in metadata {
stmt.execute(rusqlite::params![root_id.to_string(), key, value])?;
}
Ok(())
}
}