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
}