@samitouri / QOSami-HFS / commits / 88dcc4c6

support for "uploader" information (and more) on fat32

Massimo Melina committed May 24, 2024 at 20:10 UTC 88dcc4c6f1a7921a742f6c8d37794722f86df4ab
5 files changed +49 -19
src/commands.ts
+5
@@ -9,6 +9,7 @@ import yaml from 'yaml'
9 import { argv, BUILD_TIMESTAMP, VERSION } from './const'
10 import { createInterface } from 'readline'
11 import { startPlugin, stopPlugin } from './plugins'
12 +import { purgeFileAttr } from './fileAttr'
13
14 if (!argv.updating)
15 try {
@@ -122,4 +123,8 @@ const commands = {
123 params: '<name>',
124 cb: stopPlugin,
125 },
126 + 'purge-file-attr': {
127 + params: '',
128 + cb: purgeFileAttr,
129 + }
130 }
src/fileAttr.ts new
+41
@@ -0,0 +1,41 @@
1 +import { KvStorage } from '@rejetto/kvstorage'
2 +import { existsSync } from 'fs'
3 +import { promisify } from 'util'
4 +import { access } from 'fs/promises'
5 +import { tryJson } from './cross'
6 +// @ts-ignore
7 +import fsx from 'fs-x-attributes'
8 +
9 +let fileAttrDb = new KvStorage({ defaultPutDelay: 1000, maxPutDelay: 5000 })
10 +const FN = 'file-attr.kv'
11 +if (existsSync(FN))
12 + fileAttrDb.open(FN)
13 +const FILE_ATTR_PREFIX = 'user.hfs.' // user. prefix to be linux compatible
14 +export function storeFileAttr(path: string, k: string, v: any) {
15 + return promisify(fsx.set)(path, FILE_ATTR_PREFIX + k, JSON.stringify(v))
16 + .catch(async () => { // fallback to our kv-storage
17 + if (!fileAttrDb.isOpen())
18 + await fileAttrDb.open(FN)
19 + return fileAttrDb.put(`${path}|${k}`, v) // pipe should be a safe separator
20 + }).then(() => true, (e: any) => {
21 + console.error("couldn't store metadata on", path, String(e.message || e))
22 + return false
23 + })
24 +}
25 +
26 +export async function loadFileAttr(path: string, k: string) {
27 + return await promisify(fsx.get)(path, FILE_ATTR_PREFIX + k)
28 + .then((x: any) => x && tryJson(String(x)), () => {})
29 + .then((x: any) => x ?? (fileAttrDb.isOpen() ? fileAttrDb.get(`${path}|${k}`) : null))
30 + ?? undefined // normalize, as we get null instead of undefined on windows
31 +}
32 +
33 +export async function purgeFileAttr() {
34 + let n = 0
35 + await Promise.all(Array.from(fileAttrDb.keys()).map(k =>
36 + access(k).catch(() =>
37 + n++ && void fileAttrDb.del(k) )))
38 + if (n)
39 + await fileAttrDb.rewrite()
40 + console.log(`removed ${n} entrie(s)`)
41 +}
\ No newline at end of file
src/misc.ts
+1
@@ -7,6 +7,7 @@ import { Connection } from './connections'
7 import assert from 'assert'
8 export * from './util-http'
9 export * from './util-files'
10 +export * from './fileAttr'
11 export * from './cross'
12 export * from './debounceAsync'
13 import { Readable } from 'stream'
src/util-files.ts
+1 -18
@@ -1,8 +1,7 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import fs, { readFile, stat } from 'fs/promises'
4 -import { Promisable, try_, tryJson, wait, isWindowsDrive } from './misc'
5 -import { promisify } from 'util'
4 +import { Promisable, try_, wait, isWindowsDrive } from './misc'
5 import { createWriteStream, mkdirSync, watch } from 'fs'
6 import { basename, dirname } from 'path'
7 import glob from 'fast-glob'
@@ -11,8 +10,6 @@ import { runCmd } from './util-os'
10 import { once, Readable } from 'stream'
11 // @ts-ignore
12 import unzipper from 'unzip-stream'
14 -// @ts-ignore
15 -import fsx from 'fs-x-attributes'
13
14 export async function isDirectory(path: string) {
15 try { return (await fs.stat(path)).isDirectory() }
@@ -156,20 +153,6 @@ export function isValidFileName(name: string) {
153 return !/[/*?<>|\\]/.test(name) && !dirTraversal(name)
154 }
155
159 -const FILE_ATTR_PREFIX = 'user.hfs.' // user. prefix to be linux compatible
160 -export function storeFileAttr(path: string, k: string, v: any) {
161 - return promisify(fsx.set)(path, FILE_ATTR_PREFIX + k, JSON.stringify(v))
162 - .then(() => true, (e: any) => {
163 - console.error("couldn't store metadata on", path, String(e.message || e))
164 - return false
165 - })
166 -}
167 -
168 -export async function loadFileAttr(path: string, k: string) {
169 - return tryJson(String(await promisify(fsx.get)(path, FILE_ATTR_PREFIX + k)))
170 - ?? undefined // normalize, as we get null instead of undefined on windows
171 -}
172 -
156 // read and parse a file, caching unless timestamp has changed
157 export const parseFileCache = new Map<string, { ts: Date, parsed: unknown }>()
158 export async function parseFile<T>(path: string, parse: (raw: Buffer) => T) {
src/vfs.ts
+1 -1
@@ -137,7 +137,7 @@ export async function getNodeByName(name: string, parent: VfsNode) {
137 }
138 ret.rename = renameUnderPath(parent.rename, name)
139 }
140 - ret.source = enforceFinal('/', parent.source) + onDisk
140 + ret.source = join(parent.source, onDisk)
141 ret.original = undefined // overwrite in applyParentToChild, so we know this is not part of the vfs
142 return ret
143 }