filed

Job queue using FUSE

git clone git://mccd.space/filed

commit ebd1b1342664e2d3f08d47a07558c6b479282af0
parent 1fbbb1875bc27b42e8f4608e46991c8932469479
Author: Marc Coquand <marc@coquand.email>
Date:   Mon, 15 Dec 2025 10:55:15 +0100

Add the start of file metadata

Diffstat:
Asetfattr.go | 38++++++++++++++++++++++++++++++++++++++
Astore/filemeta.go | 44++++++++++++++++++++++++++++++++++++++++++++
Mstore/jobs.go | 8+++++++-
3 files changed, 89 insertions(+), 1 deletion(-)
diff --git a/setfattr.go b/setfattr.go
@@ -0,0 +1,38 @@
+package main
+
+import (
+	"context"
+	"log/slog"
+	"os"
+	"qj/store"
+	"syscall"
+
+	"bazil.org/fuse"
+)
+
+func Setattr(store *store.Store, meta *store.FileMeta, content []byte, inode uint64, ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
+	if req.Valid.Mode() {
+		meta.Mode = uint32(req.Mode)
+	}
+
+	if req.Valid.Uid() {
+		meta.UID = req.Uid
+	}
+
+	if req.Valid.Gid() {
+		meta.GID = req.Gid
+	}
+
+	if err := store.UpsertFileMeta(*meta); err != nil {
+		slog.Error("STORE: Failed to save metadata", "error", err)
+		return syscall.EIO
+	}
+
+	resp.Attr.Inode = meta.Inode
+	resp.Attr.Mode = os.FileMode(meta.Mode)
+	resp.Attr.Uid = meta.UID
+	resp.Attr.Gid = meta.GID
+	resp.Attr.Size = uint64(len(content))
+
+	return nil
+}
diff --git a/store/filemeta.go b/store/filemeta.go
@@ -0,0 +1,44 @@
+package store
+
+import (
+	"database/sql"
+)
+
+// FileMeta holds standard POSIX file attributes
+type FileMeta struct {
+	Inode uint64
+	Mode  uint32
+	UID   uint32
+	GID   uint32
+}
+
+// Returns file metadata, if it exists.
+func (st *Store) GetFileMeta(inode uint64) (m *FileMeta, err error) {
+	query := `
+		SELECT inode, mode, uid, gid 
+		FROM file_meta 
+		WHERE inode = ?
+	`
+	err = st.db.QueryRow(query, inode).Scan(&m.Inode, &m.Mode, &m.UID, &m.GID)
+	if err == sql.ErrNoRows {
+		// No metadata exists, that's ok
+		return nil, nil
+	}
+	if err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+func (st *Store) UpsertFileMeta(meta FileMeta) error {
+	query := `
+		INSERT INTO file_meta (inode, mode, uid, gid)
+		VALUES (?, ?, ?, ?)
+		ON CONFLICT(inode) DO UPDATE SET
+			mode = excluded.mode,
+			uid = excluded.uid,
+			gid = excluded.gid
+	`
+	_, err := st.db.Exec(query, meta.Inode, meta.Mode, meta.UID, meta.GID)
+	return err
+}
diff --git a/store/jobs.go b/store/jobs.go
@@ -65,6 +65,7 @@ func (s *Store) initSchema() error {
 		created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
 		updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
 	);
+	CREATE INDEX IF NOT EXISTS idx_state ON jobs(state);
 	CREATE TABLE IF NOT EXISTS config (
 		id INTEGER PRIMARY KEY CHECK (id = 0),
 		max_attempts INTEGER DEFAULT 3,
@@ -80,8 +81,13 @@ func (s *Store) initSchema() error {
 			SET inode = (SELECT IFNULL(MAX(inode), 0) + 1 FROM jobs)
 			WHERE id = new.id;
 	END;
+	CREATE TABLE IF NOT EXISTS file_meta (
+		inode INTEGER PRIMARY KEY,
+		mode INTEGER,
+		uid INTEGER,
+		gid INTEGER
+	);
 	
-	CREATE INDEX IF NOT EXISTS idx_state ON jobs(state);
 	`
 	_, err := s.db.Exec(query)
 	return err