Skip to content

Commit 0bf1f5b

Browse files
committed
fs/memory-provider: move
1 parent 29635de commit 0bf1f5b

File tree

5 files changed

+137
-30
lines changed

5 files changed

+137
-30
lines changed

src/backend/src/filesystem/FSNodeContext.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,12 @@ module.exports = class FSNodeContext {
298298
console.error('entry is undefined');
299299
this.found = false;
300300
this.entry = false;
301+
let entry_1 = await this.provider.stat({
302+
selector: this.selector,
303+
options: fetch_entry_options,
304+
node: this,
305+
controls,
306+
});
301307
return;
302308
}
303309

@@ -377,6 +383,9 @@ module.exports = class FSNodeContext {
377383
async fetchOwner (force) {
378384
if ( this.isRoot ) return;
379385
const owner = await get_user({ id: this.entry.user_id });
386+
if ( ! owner ) {
387+
console.error('owner is undefined', this.entry);
388+
}
380389
this.entry.owner = {
381390
username: owner.username,
382391
email: owner.email,
@@ -746,9 +755,17 @@ module.exports = class FSNodeContext {
746755
}
747756
await this.fetchEntry(fetch_options);
748757

758+
if ( this.entry.id === 123 ) {
759+
console.log('this.entry', this.entry);
760+
}
761+
749762
const res = this.entry;
750763
const fsentry = {};
751764

765+
if ( this.entry === false ) {
766+
console.log('this.entry is false', this.entry);
767+
}
768+
752769
// This property will not be serialized, but it can be checked
753770
// by other code to verify that API calls do not send
754771
// unsanitized filsystem entries.
@@ -761,6 +778,10 @@ module.exports = class FSNodeContext {
761778
fsentry[k] = res[k];
762779
}
763780

781+
if ( this.entry === false ) {
782+
console.log('this.entry is false', this.entry);
783+
}
784+
764785
let actor; try {
765786
actor = Context.get('actor');
766787
} catch (e) {}

src/backend/src/filesystem/hl_operations/hl_move.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ class HLMove extends HLFilesystemOperation {
182182
const old_path = await source.get('path');
183183

184184
const ll_move = new LLMove();
185+
186+
console.log(`source: ${JSON.stringify(source.entry)}`);
187+
185188
const source_new = await ll_move.run({
186189
source,
187190
parent,
@@ -190,6 +193,8 @@ class HLMove extends HLFilesystemOperation {
190193
metadata: metadata,
191194
});
192195

196+
console.log(`source_new: ${JSON.stringify(source_new.entry)}`);
197+
193198
await source_new.awaitStableEntry();
194199
await source_new.fetchSuggestedApps();
195200
await source_new.fetchOwner();

src/backend/src/filesystem/node/selectors.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,14 @@
1919
const _path = require('path');
2020
const { PuterPath } = require('../lib/PuterPath');
2121

22-
class NodePathSelector {
22+
class NodeSelector {
23+
path = null;
24+
uid = null;
25+
}
26+
27+
class NodePathSelector extends NodeSelector {
2328
constructor (path) {
29+
super();
2430
this.value = path;
2531
}
2632

@@ -34,8 +40,9 @@ class NodePathSelector {
3440
}
3541
}
3642

37-
class NodeUIDSelector {
43+
class NodeUIDSelector extends NodeSelector {
3844
constructor (uid) {
45+
super();
3946
this.value = uid;
4047
}
4148

@@ -58,8 +65,9 @@ class NodeUIDSelector {
5865
}
5966
}
6067

61-
class NodeInternalIDSelector {
68+
class NodeInternalIDSelector extends NodeSelector {
6269
constructor (service, id, debugInfo) {
70+
super();
6371
this.service = service;
6472
this.id = id;
6573
this.debugInfo = debugInfo;
@@ -81,8 +89,9 @@ class NodeInternalIDSelector {
8189
}
8290
}
8391

84-
class NodeChildSelector {
92+
class NodeChildSelector extends NodeSelector {
8593
constructor (parent, name) {
94+
super();
8695
this.parent = parent;
8796
this.name = name;
8897
}
@@ -133,7 +142,7 @@ class NodeChildSelector {
133142
}
134143
}
135144

136-
class RootNodeSelector {
145+
class RootNodeSelector extends NodeSelector {
137146
static entry = {
138147
is_dir: true,
139148
is_root: true,
@@ -146,6 +155,7 @@ class RootNodeSelector {
146155
node.uid = PuterPath.NULL_UUID;
147156
}
148157
constructor () {
158+
super();
149159
this.entry = this.constructor.entry;
150160
}
151161

@@ -154,8 +164,9 @@ class RootNodeSelector {
154164
}
155165
}
156166

157-
class NodeRawEntrySelector {
167+
class NodeRawEntrySelector extends NodeSelector {
158168
constructor (entry) {
169+
super();
159170
// Fix entries from get_descendants
160171
if ( ! entry.uuid && entry.uid ) {
161172
entry.uuid = entry.uid;
@@ -186,7 +197,7 @@ class NodeRawEntrySelector {
186197
* - path
187198
* - uid
188199
*
189-
* @param {NodePathSelector|NodeUIDSelector|NodeChildSelector|RootNodeSelector} selector
200+
* @param {NodeSelector} selector
190201
*/
191202
function try_infer_attributes (selector) {
192203
if ( selector instanceof NodePathSelector ) {
@@ -222,6 +233,7 @@ const relativeSelector = (parent, path) => {
222233
}
223234

224235
module.exports = {
236+
NodeSelector,
225237
NodePathSelector,
226238
NodeUIDSelector,
227239
NodeInternalIDSelector,

src/backend/src/helpers.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,42 @@ const body_parser_error_handler = (err, req, res, next) => {
972972
next();
973973
}
974974

975+
/**
976+
* Given a uid, returns a file node.
977+
*
978+
* TODO (xiaochen): It only works for MemoryFSProvider currently.
979+
*
980+
* @param {string} uid - The uid of the file to get.
981+
* @returns {Promise<FileInfo|null>} The file node, or null if the file does not exist.
982+
*/
983+
async function get_entry(uid) {
984+
const svc_mountpoint = Context.get('services').get('mountpoint');
985+
const uid_selector = new NodeUIDSelector(uid);
986+
const provider = await svc_mountpoint.get_provider(uid_selector);
987+
988+
// NB: We cannot import MemoryFSProvider here because it will cause a circular dependency.
989+
if ( provider.constructor.name !== 'MemoryFSProvider' ) {
990+
return null;
991+
}
992+
993+
return provider.stat({
994+
selector: uid_selector,
995+
});
996+
}
997+
975998
async function is_ancestor_of(ancestor_uid, descendant_uid){
999+
console.log('is_ancestor_of', ancestor_uid, descendant_uid);
1000+
1001+
const ancestor = await get_entry(ancestor_uid);
1002+
const descendant = await get_entry(descendant_uid);
1003+
1004+
if ( ancestor && descendant ) {
1005+
console.log('ancestor', ancestor);
1006+
console.log('descendant', descendant);
1007+
return descendant.path.startsWith(ancestor.path);
1008+
}
1009+
1010+
9761011
/** @type BaseDatabaseAccessService */
9771012
const db = services.get('database').get(DB_READ, 'filesystem');
9781013

src/backend/src/modules/puterfs/customfs/MemoryFSProvider.js

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ const _path = require('path');
2222
const { Context } = require("../../../util/context");
2323
const { v4: uuidv4 } = require('uuid');
2424
const config = require("../../../config");
25-
const { try_infer_attributes, NodeChildSelector, NodePathSelector } = require("../../../filesystem/node/selectors");
25+
const { try_infer_attributes, NodeChildSelector, NodePathSelector, NodeSelector } = require("../../../filesystem/node/selectors");
2626

2727
const path = require('path');
2828
const APIError = require("../../../api/APIError");
2929

30-
class FileInfo {
30+
class MemoryFile {
3131
/**
3232
* @param {Object} param
3333
* @param {string} param.full_path
@@ -65,6 +65,8 @@ class FileInfo {
6565
this.created = 123;
6666
this.accessed = 123;
6767
this.modified = 123;
68+
69+
this.own
6870
}
6971
}
7072

@@ -83,7 +85,7 @@ class MemoryFSProvider {
8385
// We declare 2 maps to support 2 lookup apis: by-path/by-uuid.
8486
this.entriesByUUID = new Map();
8587

86-
const root = new FileInfo({
88+
const root = new MemoryFile({
8789
full_path: '/',
8890
is_dir: true,
8991
content: null,
@@ -117,28 +119,25 @@ class MemoryFSProvider {
117119
/**
118120
* Check the integrity of the whole memory filesystem and the input. Throws error if any violation is found.
119121
*
120-
* @param {FSNodeContext} node - The node to check.
122+
* @param {MemoryFile} entry - The entry to check.
121123
* @returns {Promise<void>}
122124
*/
123-
_integrity_check (node) {
125+
_integrity_check (entry) {
124126
if ( config.env !== 'dev' ) {
125127
// only check in debug mode since it's expensive
126128
return;
127129
}
128130

129-
// check the node's provider is "memory-fs"
130-
if ( node.provider !== this ) {
131-
throw new Error('Node provider mismatch');
132-
}
133-
134-
// check all directories along the path are valid
135-
const inner_path = this._inner_path(node.path);
136-
const path_components = inner_path.split('/');
137-
for ( let i = 2; i < path_components.length; i++ ) {
138-
const path_component = path_components.slice(0, i).join('/');
139-
const entry_uuid = this.entriesByPath.get(path_component);
140-
if ( ! entry_uuid ) {
141-
throw new Error(`Directory ${path_component} does not exist`);
131+
if ( entry ) {
132+
// check all directories along the path are valid
133+
const inner_path = this._inner_path(entry.path);
134+
const path_components = inner_path.split('/');
135+
for ( let i = 2; i < path_components.length; i++ ) {
136+
const path_component = path_components.slice(0, i).join('/');
137+
const entry_uuid = this.entriesByPath.get(path_component);
138+
if ( ! entry_uuid ) {
139+
throw new Error(`Directory ${path_component} does not exist`);
140+
}
142141
}
143142
}
144143

@@ -158,12 +157,11 @@ class MemoryFSProvider {
158157
* Performs a stat operation on the given FSNode.
159158
*
160159
* @param {Object} param
161-
* @param {FSNodeContext} param.node - The node to stat.
162-
* @returns {Promise<FileInfo|null>} - The result of the stat operation, or `null` if the node doesn't exist.
160+
* @param {NodeSelector} param.selector - The selector to stat.
161+
* @returns {Promise<MemoryFile|null>} - The result of the stat operation, or `null` if the node doesn't exist.
163162
*/
164163
async stat ({
165164
selector,
166-
node,
167165
}) {
168166
let entry_uuid = null;
169167

@@ -226,7 +224,7 @@ class MemoryFSProvider {
226224
const full_path = _path.join(parent.path, name);
227225
const inner_path = this._inner_path(full_path);
228226

229-
const entry = new FileInfo({
227+
const entry = new MemoryFile({
230228
full_path: full_path,
231229
is_dir: true,
232230
content: null,
@@ -284,6 +282,42 @@ class MemoryFSProvider {
284282
this.entriesByUUID.delete(node.uid);
285283
}
286284

285+
/**
286+
* Move a file.
287+
*
288+
* @param {Object} param
289+
* @param {Context} param.context
290+
* @param {FSNodeContext} param.node: The file to move.
291+
* @param {FSNodeContext} param.new_parent: The new parent directory of the file.
292+
* @param {string} param.new_name: The new name of the file.
293+
* @param {Object} param.metadata: The metadata of the file.
294+
* @returns {Promise<MemoryFile>}
295+
*/
296+
async move({ context, node, new_parent, new_name, metadata }) {
297+
this._integrity_check(null);
298+
299+
// create the new entry
300+
const new_full_path = _path.join(new_parent.path, new_name);
301+
const new_inner_path = this._inner_path(new_full_path);
302+
const entry = new MemoryFile({
303+
full_path: new_full_path,
304+
is_dir: false,
305+
content: node.entry.content,
306+
});
307+
entry.uuid = node.entry.uuid;
308+
this.entriesByPath.set(new_inner_path, entry.uuid);
309+
this.entriesByUUID.set(entry.uuid, entry);
310+
311+
// remove the old entry
312+
const inner_path = this._inner_path(node.path);
313+
this.entriesByPath.delete(inner_path);
314+
// should not delete the entry by uuid because it's the same
315+
316+
this._integrity_check(entry);
317+
318+
return entry;
319+
}
320+
287321
/**
288322
* Write a new file to the filesystem. Throws an error if the destination
289323
* already exists.
@@ -299,7 +333,7 @@ class MemoryFSProvider {
299333
const full_path = _path.join(parent.path, name);
300334
const inner_path = this._inner_path(full_path);
301335

302-
const entry = new FileInfo({
336+
const entry = new MemoryFile({
303337
full_path: full_path,
304338
is_dir: false,
305339
content: file.stream.readableBuffer,

0 commit comments

Comments
 (0)