fix: permissions not correctly applied with masks
Massimo Melina committed
Mar 16, 2023 at 17:54 UTC
6079b1372d2a3bf06606ed5030c6d03e2ac3ef58
3 files changed
+40
-28
src/api.vfs.ts
+4
-4
@@ -168,21 +168,21 @@ const apis: ApiHandlers = {
168
}
169
try {
170
path = isWindowsDrive(path) ? path + '\\' : resolve(path || '/')
171
- for await (const name of dirStream(path)) {
171
+ for await (const [name, isDir] of dirStream(path)) {
172
if (ctx.req.aborted)
173
return
174
try {
175
- const stats = await stat(join(path, name))
176
- if (stats.isFile())
175
+ if (!isDir)
176
if (!files || fileMask && !isMatch(name, fileMask))
177
continue
178
+ const stats = await stat(join(path, name))
179
yield {
180
add: {
181
n: name,
182
s: stats.size,
183
c: stats.ctime,
184
m: stats.mtime,
185
- k: stats.isDirectory() ? 'd' : undefined,
185
+ k: isDir ? 'd' : undefined,
186
}
187
}
188
}
src/util-files.ts
+3
-2
@@ -94,10 +94,11 @@ export async function* dirStream(path: string, deep?: number) {
94
const skip = await getItemsToSkip(path)
95
for await (const entry of dirStream) {
96
let { path, dirent } = entry as any
97
- if (!dirent.isDirectory() && !dirent.isFile()) continue
97
+ const isDir = dirent.isDirectory()
98
+ if (!isDir && !dirent.isFile()) continue
99
path = String(path)
100
if (!skip?.includes(path))
100
- yield path
101
+ yield [path, isDir]
102
}
103
104
async function getItemsToSkip(path: string) {
src/vfs.ts
+33
-22
@@ -1,7 +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 from 'fs/promises'
4
-import { basename, join, resolve } from 'path'
4
+import { basename, dirname, join, resolve } from 'path'
5
import { isMatch } from 'micromatch'
6
import { dirStream, dirTraversal, enforceFinal, getOrSet, isDirectory, typedKeys } from './misc'
7
import Koa from 'koa'
@@ -175,26 +175,44 @@ export async function* walkNode(parent:VfsNode, ctx?: Koa.Context, depth:number=
175
const took = prefixPath ? undefined : new Set()
176
if (children)
177
for (const child of children) {
178
- const name = prefixPath + getNodeName(child)
178
+ const nodeName = getNodeName(child)
179
+ const name = prefixPath + nodeName
180
took?.add(name)
180
- yield* workItem({
181
- ...child,
182
- name,
183
- }, depth > 0 && await nodeIsDirectory(child).catch(() => false))
181
+ const item = { ...child, name }
182
+ if (!canSee(item)) continue
183
+ yield item
184
+ if (!depth || !await nodeIsDirectory(child).catch(() => false)) continue
185
+ inheritMasks(item, parent, nodeName)
186
+ if (!ctx || hasPermission(item, 'can_list', ctx)) // check perm before recursion
187
+ yield* walkNode(item, ctx, depth - 1, name + '/')
188
}
189
if (!source)
190
return
191
try {
188
- for await (const path of dirStream(source, depth)) {
192
+ let lastDir = prefixPath.slice(0, -1) || '.'
193
+ const map = new Map()
194
+ map.set(lastDir, parent)
195
+ // it's important to keep using dirStream in deep-mode, as it is manyfold faster (it parallelizes)
196
+ for await (const [path, isDir] of dirStream(source, depth)) {
197
if (ctx?.req.aborted)
198
return
199
const name = prefixPath + (parent.rename?.[path] || path)
200
if (took?.has(name)) continue
193
- yield* workItem({
201
+ if (depth) {
202
+ const dir = dirname(name)
203
+ if (dir !== lastDir)
204
+ parent = map.get(lastDir = dir)
205
+ }
206
+
207
+ const item = {
208
name,
209
source: join(source, path),
210
rename: renameUnderPath(parent.rename, path),
197
- })
211
+ }
212
+ if (!canSee(item)) continue
213
+ if (isDir)
214
+ map.set(name, item)
215
+ yield item
216
}
217
}
218
catch(e) {
@@ -202,20 +220,13 @@ export async function* walkNode(parent:VfsNode, ctx?: Koa.Context, depth:number=
220
}
221
222
// item will be changed, so be sure to pass a temp node
205
- async function* workItem(item: VfsNode, recur=false) {
206
- const name = getNodeName(item)
207
- // we basename for depth>0 where we already have the rest of the path in the parent's url, and would be duplicated
208
- const virtualBasename = basename(name)
209
- item.isTemp = true
210
- applyMasks(item, parent, virtualBasename)
223
+ function canSee(item: VfsNode) {
224
+ // we basename for depth>0 where we already have the rest of the path in the parent's url, and would be duplicated
225
+ applyMasks(item, parent, basename(getNodeName(item)))
226
inheritFromParent(parent, item)
212
- if (ctx && !hasPermission(item, 'can_see', ctx))
213
- return
214
- yield item
215
- if (!recur) return
216
- inheritMasks(item, parent, virtualBasename)
217
- if (!ctx || hasPermission(item, 'can_list', ctx)) // check perm before recursion
218
- yield* walkNode(item, ctx, depth - 1, name + '/')
227
+ if (ctx && !hasPermission(item, 'can_see', ctx)) return
228
+ item.isTemp = true
229
+ return item
230
}
231
232
function masksCouldGivePermission(masks: Masks | undefined) {