feat(volumes): read only mount mode

This commit is contained in:
Nicolas Meienberger
2025-11-08 10:01:54 +01:00
parent 418369c4ad
commit f5339d3708
9 changed files with 137 additions and 51 deletions

View File

@@ -22,7 +22,7 @@ const mount = async (config: BackendConfig, path: string) => {
return { status: BACKEND_STATUS.error, error: "NFS mounting is only supported on Linux hosts." };
}
const { status } = await checkHealth(path);
const { status } = await checkHealth(path, config.readOnly);
if (status === "mounted") {
return { status: BACKEND_STATUS.mounted };
}
@@ -35,6 +35,9 @@ const mount = async (config: BackendConfig, path: string) => {
const source = `${config.server}:${config.exportPath}`;
const options = [`vers=${config.version}`, `port=${config.port}`];
if (config.readOnly) {
options.push("ro");
}
const args = ["-t", "nfs", "-o", options.join(","), source, path];
logger.debug(`Mounting volume ${path}...`);
@@ -84,7 +87,7 @@ const unmount = async (path: string) => {
}
};
const checkHealth = async (path: string) => {
const checkHealth = async (path: string, readOnly: boolean) => {
const run = async () => {
logger.debug(`Checking health of NFS volume at ${path}...`);
await fs.access(path);
@@ -95,7 +98,9 @@ const checkHealth = async (path: string) => {
throw new Error(`Path ${path} is not mounted as NFS.`);
}
await createTestFile(path);
if (!readOnly) {
await createTestFile(path);
}
logger.debug(`NFS volume at ${path} is healthy and mounted.`);
return { status: BACKEND_STATUS.mounted };
@@ -112,5 +117,5 @@ const checkHealth = async (path: string) => {
export const makeNfsBackend = (config: BackendConfig, path: string): VolumeBackend => ({
mount: () => mount(config, path),
unmount: () => unmount(path),
checkHealth: () => checkHealth(path),
checkHealth: () => checkHealth(path, config.readOnly),
});

View File

@@ -22,7 +22,7 @@ const mount = async (config: BackendConfig, path: string) => {
return { status: BACKEND_STATUS.error, error: "SMB mounting is only supported on Linux hosts." };
}
const { status } = await checkHealth(path);
const { status } = await checkHealth(path, config.readOnly);
if (status === "mounted") {
return { status: BACKEND_STATUS.mounted };
}
@@ -47,6 +47,10 @@ const mount = async (config: BackendConfig, path: string) => {
options.push(`domain=${config.domain}`);
}
if (config.readOnly) {
options.push("ro");
}
const args = ["-t", "cifs", "-o", options.join(","), source, path];
logger.debug(`Mounting SMB volume ${path}...`);
@@ -96,7 +100,7 @@ const unmount = async (path: string) => {
}
};
const checkHealth = async (path: string) => {
const checkHealth = async (path: string, readOnly: boolean) => {
const run = async () => {
logger.debug(`Checking health of SMB volume at ${path}...`);
await fs.access(path);
@@ -107,7 +111,9 @@ const checkHealth = async (path: string) => {
throw new Error(`Path ${path} is not mounted as CIFS/SMB.`);
}
await createTestFile(path);
if (!readOnly) {
await createTestFile(path);
}
logger.debug(`SMB volume at ${path} is healthy and mounted.`);
return { status: BACKEND_STATUS.mounted };
@@ -124,5 +130,5 @@ const checkHealth = async (path: string) => {
export const makeSmbBackend = (config: BackendConfig, path: string): VolumeBackend => ({
mount: () => mount(config, path),
unmount: () => unmount(path),
checkHealth: () => checkHealth(path),
checkHealth: () => checkHealth(path, config.readOnly),
});

View File

@@ -26,7 +26,7 @@ const mount = async (config: BackendConfig, path: string) => {
return { status: BACKEND_STATUS.error, error: "WebDAV mounting is only supported on Linux hosts." };
}
const { status } = await checkHealth(path);
const { status } = await checkHealth(path, config.readOnly);
if (status === "mounted") {
return { status: BACKEND_STATUS.mounted };
}
@@ -44,7 +44,9 @@ const mount = async (config: BackendConfig, path: string) => {
const port = config.port !== defaultPort ? `:${config.port}` : "";
const source = `${protocol}://${config.server}${port}${config.path}`;
const options = ["uid=1000", "gid=1000", "file_mode=0664", "dir_mode=0775"];
const options = config.readOnly
? ["uid=1000", "gid=1000", "file_mode=0444", "dir_mode=0555", "ro"]
: ["uid=1000", "gid=1000", "file_mode=0664", "dir_mode=0775"];
if (config.username && config.password) {
const secretsFile = "/etc/davfs2/secrets";
@@ -132,7 +134,7 @@ const unmount = async (path: string) => {
}
};
const checkHealth = async (path: string) => {
const checkHealth = async (path: string, readOnly: boolean) => {
const run = async () => {
logger.debug(`Checking health of WebDAV volume at ${path}...`);
await fs.access(path);
@@ -143,7 +145,9 @@ const checkHealth = async (path: string) => {
throw new Error(`Path ${path} is not mounted as WebDAV.`);
}
await createTestFile(path);
if (!readOnly) {
await createTestFile(path);
}
logger.debug(`WebDAV volume at ${path} is healthy and mounted.`);
return { status: BACKEND_STATUS.mounted };
@@ -160,5 +164,5 @@ const checkHealth = async (path: string) => {
export const makeWebdavBackend = (config: BackendConfig, path: string): VolumeBackend => ({
mount: () => mount(config, path),
unmount: () => unmount(path),
checkHealth: () => checkHealth(path),
checkHealth: () => checkHealth(path, config.readOnly),
});