From 3df24b0419a22e7b0ce4fcf60b3603575317db69 Mon Sep 17 00:00:00 2001 From: William Stein Date: Sat, 18 Nov 2023 18:19:44 +0000 Subject: [PATCH] improve read tracking -- add excludes --- websocketfs/lib/mount.ts | 18 +++++++++++++---- websocketfs/lib/read-tracking.ts | 34 +++++++++++++++++++++++++++++--- websocketfs/lib/sftp-fuse.ts | 7 ++++++- websocketfs/package.json | 2 +- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/websocketfs/lib/mount.ts b/websocketfs/lib/mount.ts index 1d67a69..6b8daed 100644 --- a/websocketfs/lib/mount.ts +++ b/websocketfs/lib/mount.ts @@ -20,11 +20,19 @@ interface Options { cacheLinkTimeout?: number; // Read Tracking - // write out filenames of files that were explicitly read to this file - // separated by nulls (and it is null terminated). A file is added no more - // than once. Delete the file to reset things. The leading slash is - // removed from the filenames, so they are relative to the mount point. + // Write out filenames of files that were explicitly read to readTrackingFile + // terminated by NULL, like the --null option to tar expects: + // readTrackingFile + // A file is added no more than once until readTrackingFile is deleted. Delete + // readTrackingFile to reset the processs. + // The leading slash is removed from the filenames, so they are relative to + // the mount point. readTrackingFile?: string; + // exclude given top level directories from tracking, e.g., + // ['scratch', 'tmp'] + // would exclude scratch and tmp. We also allow ['.*'] to mean "exclude + // all top level hidden directories". + readTrackingExclude?: string[]; // Metadata // If the metadataFile file path is given, we poll it for modification every few seconds. @@ -68,6 +76,7 @@ export default async function mount( cacheDirTimeout, cacheLinkTimeout, readTrackingFile, + readTrackingExclude, metadataFile, hidePath, } = opts; @@ -79,6 +88,7 @@ export default async function mount( cacheDirTimeout, cacheLinkTimeout, readTrackingFile, + readTrackingExclude, metadataFile, hidePath, }); diff --git a/websocketfs/lib/read-tracking.ts b/websocketfs/lib/read-tracking.ts index 2492dd2..7d0eb50 100644 --- a/websocketfs/lib/read-tracking.ts +++ b/websocketfs/lib/read-tracking.ts @@ -15,9 +15,21 @@ const log = debug("websocketfs:read-tracking"); export default class ReadTracking { private readTrackingFile: string; private history = new Set(); + private excludeHidden: boolean; + private excludePaths: string[]; - constructor(readTrackingFile) { + constructor(readTrackingFile: string, readTrackingExclude: string[]) { this.readTrackingFile = readTrackingFile; + this.excludeHidden = readTrackingExclude.includes(".*"); + this.excludePaths = readTrackingExclude + .filter((pattern) => pattern != ".*") + .map((pattern) => { + if (!pattern.endsWith("/")) { + return pattern + "/"; + } else { + return pattern; + } + }); this.init(); } @@ -30,7 +42,11 @@ export default class ReadTracking { }; trackRead = async (filename: string) => { - log(`fileWasRead`, { filename }); + filename = filename.slice(1); + if (this.isExcluded(filename)) { + return; + } + log(`trackRead`, { filename }); try { await stat(this.readTrackingFile); } catch (_) { @@ -40,7 +56,19 @@ export default class ReadTracking { if (this.history.has(filename)) { return; } - await appendFile(this.readTrackingFile, `${filename.slice(1)}\0`); + await appendFile(this.readTrackingFile, `${filename}\0`); this.history.add(filename); }; + + private isExcluded = (filename: string) => { + if (this.excludeHidden && filename.startsWith(".")) { + return true; + } + for (const pattern of this.excludePaths) { + if (filename.startsWith(pattern)) { + return true; + } + } + return false; + }; } diff --git a/websocketfs/lib/sftp-fuse.ts b/websocketfs/lib/sftp-fuse.ts index 332ee7a..1554747 100644 --- a/websocketfs/lib/sftp-fuse.ts +++ b/websocketfs/lib/sftp-fuse.ts @@ -44,6 +44,7 @@ interface Options { cacheDirTimeout?: number; cacheLinkTimeout?: number; readTrackingFile?: string; + readTrackingExclude?: string[]; metadataFile?: string; // reconnect -- defaults to true; if true, automatically reconnects // to server when connection breaks. @@ -76,6 +77,7 @@ export default class SftpFuse { cacheLinkTimeout, reconnect = true, readTrackingFile, + readTrackingExclude = [], metadataFile, hidePath, } = options; @@ -111,7 +113,10 @@ export default class SftpFuse { }); } if (readTrackingFile) { - this.readTracking = new ReadTracking(readTrackingFile); + this.readTracking = new ReadTracking( + readTrackingFile, + readTrackingExclude, + ); } if (metadataFile) { if (this.attrCache != null && this.dirCache != null && cacheTimeout) { diff --git a/websocketfs/package.json b/websocketfs/package.json index b145546..eab0d83 100644 --- a/websocketfs/package.json +++ b/websocketfs/package.json @@ -1,6 +1,6 @@ { "name": "websocketfs", - "version": "0.16.0", + "version": "0.17.0", "description": "Like sshfs, but over a WebSocket", "main": "./dist/lib/index.js", "scripts": {