Skip to content

Commit

Permalink
chore: merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
divyenduz committed Oct 15, 2023
2 parents a8afd85 + bf2a0e2 commit 9533c7a
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 64 deletions.
3 changes: 2 additions & 1 deletion packages/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ type Result<T> =
status: "not_found";
};

// TODO: bump this based on the latest state of the actual backend!

export interface Backend {
getFiles: (dir: string) => Promise<File[]>;
getFile: (filepath: string) => Promise<Result<File>>;
Expand All @@ -26,7 +28,6 @@ export interface Backend {

writeFile: (
filepath: string,
content: Buffer,
uid: number,
gid: number
) => Promise<Result<File>>;
Expand Down
8 changes: 5 additions & 3 deletions packages/fuse-client/syscalls/ftruncate.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { SQLiteBackend } from "@zoid-fs/sqlite-backend";
import fuse, { MountOptions } from "@zoid-fs/node-fuse-bindings";
import { match } from "ts-pattern";
import { MountOptions } from "@zoid-fs/node-fuse-bindings";
import { truncate } from "./truncate";

export const ftruncate: (
backend: SQLiteBackend
) => MountOptions["ftruncate"] = (backend) => {
return async (path, fd, size, cb) => {
console.log("ftruncate(%s, %d, %d)", path, fd, size);
cb(0);
//@ts-expect-error fix types
// TODO: implement ftruncate properly
truncate(backend)(path, size, cb);
};
};
13 changes: 10 additions & 3 deletions packages/fuse-client/syscalls/getattr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,22 @@ export const getattr: (backend: SQLiteBackend) => MountOptions["getattr"] = (
}

const r = await backend.getFile(path);
match(r)
.with({ status: "ok" }, (r) => {

await match(r)
.with({ status: "ok" }, async (r) => {
const rSize = await backend.getFileSize(path);
if (rSize.status !== "ok") {
cb(fuse.ENOENT);
return;
}

const { mtime, atime, ctime, mode } = r.file;
cb(0, {
mtime,
atime,
ctime,
nlink: 1,
size: r.file.content.length,
size: rSize.size,
mode: mode,
uid: process.getuid ? process.getuid() : 0,
gid: process.getgid ? process.getgid() : 0,
Expand Down
17 changes: 10 additions & 7 deletions packages/fuse-client/syscalls/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ export const read: (backend: SQLiteBackend) => MountOptions["read"] = (
backend
) => {
return async (path, fd, buf, len, pos, cb) => {
console.log("read(%s, %d, %o, %d, %d)", path, fd, buf, len, pos);
const r = await backend.getFile(path);
match(r)
.with({ status: "ok" }, (r) => {
const bufChunk = Buffer.copyBytesFrom(r.file.content, pos, len);
if (!bufChunk) return cb(0);
console.log("read(%s, %d, %d, %d)", path, fd, len, pos);
const r = await backend.getFileChunks(fd, pos, len);
await match(r)
.with({ status: "ok" }, async (r) => {
if (r.chunks.length === 0) {
cb(0);
return;
}
const bufChunk = Buffer.concat(r.chunks.map((chunk) => chunk.content));
buf.write(bufChunk.toString("binary"), "binary");
return cb(Buffer.byteLength(bufChunk));
cb(Buffer.byteLength(bufChunk));
})
.with({ status: "not_found" }, () => {
cb(fuse.ENOENT);
Expand Down
25 changes: 3 additions & 22 deletions packages/fuse-client/syscalls/truncate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,10 @@ export const truncate: (backend: SQLiteBackend) => MountOptions["truncate"] = (
) => {
return async (path, size, cb) => {
console.log("truncate(%s, %d)", path, size);
const r = await backend.getFile(path);
match(r)
const r = await backend.truncateFile(path, size);
await match(r)
.with({ status: "ok" }, async (r) => {
const truncatedContent = r.file.content.slice(0, size);

//@ts-expect-error fix types
const context = fuse.context();
const { uid, gid } = context;

const writeResult = await backend.writeFile(
path,
truncatedContent,
uid,
gid
);
match(writeResult)
.with({ status: "ok" }, () => {
cb(0);
})
.with({ status: "not_found" }, () => {
cb(fuse.ENOENT);
})
.exhaustive();
cb(0);
})
.with({ status: "not_found" }, () => {
cb(fuse.ENOENT);
Expand Down
19 changes: 4 additions & 15 deletions packages/fuse-client/syscalls/write.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,12 @@ export const write: (backend: SQLiteBackend) => MountOptions["write"] = (
backend
) => {
return async (path, fd, buf, len, pos, cb) => {
console.log("write(%s, %d, %o, %d, %d)", path, fd, buf, len, pos);
console.log("write(%s, %d, %d, %d)", path, fd, len, pos);
const chunk = Buffer.from(buf, pos, len);

const oldContentResult = await backend.getFile(path);
const oldContent = match(oldContentResult)
.with({ status: "ok" }, (r) => r.file.content)
.otherwise(() => Buffer.from(""));

const newContent = Buffer.concat([oldContent, chunk]);

//@ts-expect-error fix types
const context = fuse.context();
const { uid, gid } = context;

const r = await backend.writeFile(path, newContent, uid, gid);
match(r)
.with({ status: "ok" }, (r) => {
const rChunk = await backend.writeFileChunk(path, chunk, pos, len);
match(rChunk)
.with({ status: "ok" }, () => {
cb(chunk.length);
})
.with({ status: "not_found" }, () => {
Expand Down
118 changes: 112 additions & 6 deletions packages/sqlite-backend/SQLiteBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,61 @@ export class SQLiteBackend implements Backend {
}
}

async getFileChunks(fileId: number, offset: number, size: number) {
try {
const chunks = await this.prisma.content.findMany({
where: {
fileId,
AND: [
{
offset: {
gte: offset,
},
},
{
offset: {
lt: offset + size,
},
},
],
},
orderBy: {
offset: "asc",
},
});

return {
status: "ok" as const,
chunks,
};
} catch (e) {
return {
status: "not_found" as const,
};
}
}

async getFileSize(filepath: string) {
try {
const chunks = await this.prisma.content.findMany({
where: {
file: {
path: filepath,
},
},
});
const bufChunk = Buffer.concat(chunks.map((chunk) => chunk.content));
return {
status: "ok" as const,
size: Buffer.byteLength(bufChunk),
};
} catch (e) {
return {
status: "not_found" as const,
};
}
}

async createFile(
filepath: string,
type = "file",
Expand All @@ -82,7 +137,6 @@ export class SQLiteBackend implements Backend {
dir: parsedPath.dir,
path: filepath,
type,
content: Buffer.from([]),
mode: type === "dir" ? 16877 : mode,
atime: new Date(),
mtime: new Date(),
Expand All @@ -103,22 +157,19 @@ export class SQLiteBackend implements Backend {
}
}

async writeFile(filepath: string, content: Buffer, uid: number, gid: number) {
async writeFile(filepath: string, uid: number, gid: number) {
try {
const parsedPath = path.parse(filepath);
const file = await this.prisma.file.upsert({
where: {
path: filepath,
},
update: {
content,
},
update: {},
create: {
type: "file",
name: parsedPath.base,
dir: parsedPath.dir,
path: filepath,
content,
mode: 755,
atime: new Date(),
mtime: new Date(),
Expand All @@ -138,13 +189,68 @@ export class SQLiteBackend implements Backend {
}
}

async writeFileChunk(
filepath: string,
content: Buffer,
offset: number,
size: number
) {
try {
const contentChunk = await this.prisma.content.create({
data: {
offset,
size,
content,
file: {
connect: {
path: filepath,
},
},
},
});

return {
status: "ok" as const,
chunk: contentChunk,
};
} catch (e) {
return {
status: "not_found" as const,
};
}
}

async truncateFile(filepath: string, size: number) {
try {
const file = await this.prisma.content.deleteMany({
where: {
file: {
path: filepath,
},
offset: {
gte: size,
},
},
});
return {
status: "ok" as const,
file: file,
};
} catch (e) {
return {
status: "not_found" as const,
};
}
}

async deleteFile(filepath: string) {
try {
const file = await this.prisma.file.delete({
where: {
path: filepath,
},
});

return {
status: "ok" as const,
file: file,
Expand Down
21 changes: 15 additions & 6 deletions packages/sqlite-backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,27 @@ datasource db {
}

model File {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
type String // dir or file or symlink
targetId Int @default(0) // only relevant for symlink otherwise 0
targetId Int @default(0) // only relevant for symlink otherwise 0
mode Int
name String
dir String
path String @unique
path String @unique
// fileType String // text or binary
content Bytes // only relevant for file
uid Int
gid Int
atime DateTime
mtime DateTime @updatedAt
ctime DateTime @default(now())
mtime DateTime @updatedAt
ctime DateTime @default(now())
Content Content[]
}

model Content {
id Int @id @default(autoincrement())
content Bytes
offset Int
size Int
file File @relation(fields: [fileId], references: [id], onDelete: Cascade)
fileId Int
}
2 changes: 1 addition & 1 deletion packages/zoid-fs-client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SQLiteBackend } from "@zoid-fs/sqlite-backend";
import { FuseClient } from "@zoid-fs/fuse-client";
import arg from "arg";
import { match } from "ts-pattern";
import { TursoBackend } from "../turso-backend";
import { TursoBackend } from "@zoid-fs/turso-backend";

type BackendType = "sqlite" | "turso";
const args = arg({
Expand Down

0 comments on commit 9533c7a

Please sign in to comment.