Skip to content

Commit 29635de

Browse files
committed
fs/memory-provider: stat, mkdir
1 parent 1e720a7 commit 29635de

File tree

20 files changed

+691
-68
lines changed

20 files changed

+691
-68
lines changed

src/backend/src/api/APIError.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
*/
1919
const { URLSearchParams } = require("node:url");
20+
const config = require("../config");
2021
const { quot } = require('@heyputer/putility').libs.string;
2122

2223
/**
@@ -518,14 +519,19 @@ module.exports = class APIError {
518519
* is set to null. The first argument is used as the status code.
519520
*
520521
* @static
521-
* @param {number} status
522-
* @param {string|Error} message_or_source one of the following:
522+
* @param {number|string} status
523+
* @param {object} source
524+
* @param {string|Error|object} fields one of the following:
523525
* - a string to use as the error message
524526
* - an Error object to use as the source of the error
525527
* - an object with a message property to use as the error message
526528
* @returns
527529
*/
528530
static create (status, source, fields = {}) {
531+
if ( config.env === 'dev' ) {
532+
console.trace('APIError.create', status, source, fields);
533+
}
534+
529535
// Just the error code
530536
if ( typeof status === 'string' ) {
531537
const code = this.codes[status];

src/backend/src/filesystem/FSNodeContext.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ module.exports = class FSNodeContext {
121121
return result;
122122
}
123123
}
124+
125+
if ( (! this.path) && (! this.uid) ) {
126+
console.log('no path or uid');
127+
selector.setPropertiesKnownBySelector(this);
128+
console.log('no path or uid');
129+
}
124130
}
125131

126132
set selector (new_selector) {
@@ -288,10 +294,18 @@ module.exports = class FSNodeContext {
288294
controls,
289295
});
290296

297+
if ( entry === undefined ) {
298+
console.error('entry is undefined');
299+
this.found = false;
300+
this.entry = false;
301+
return;
302+
}
303+
291304
if ( entry === null ) {
292305
this.found = false;
293306
this.entry = false;
294307
} else {
308+
295309
this.found = true;
296310

297311
if ( ! this.uid && entry.uuid ) {

src/backend/src/filesystem/hl_operations/hl_mkdir.js

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const { HLFilesystemOperation } = require('./definitions');
3131
const { is_valid_path } = require('../validation');
3232
const { HLRemove } = require('./hl_remove');
3333
const { LLMkdir } = require('../ll_operations/ll_mkdir');
34+
const { MemoryFSService } = require('../../modules/puterfs/customfs/MemoryFSService');
3435

3536
class MkTree extends HLFilesystemOperation {
3637
static DESCRIPTION = `
@@ -287,7 +288,7 @@ class HLMkdir extends HLFilesystemOperation {
287288
// "top_parent" is the immediate parent of the target directory
288289
// (e.g: /home/foo/bar -> /home/foo)
289290
const top_parent = values.create_missing_parents
290-
? await this._create_top_parent({ top_parent: parent_node })
291+
? await this._create_dir(parent_node)
291292
: await this._get_existing_top_parent({ top_parent: parent_node })
292293
;
293294

@@ -299,6 +300,10 @@ class HLMkdir extends HLFilesystemOperation {
299300
});
300301
}
301302

303+
if ( values.dedupe_name ) {
304+
console.log('DEDUPE NAME', values.dedupe_name);
305+
}
306+
302307
// `parent_node` becomes the parent of the last directory name
303308
// specified under `path`.
304309
parent_node = await this._create_parents({
@@ -317,6 +322,10 @@ class HLMkdir extends HLFilesystemOperation {
317322

318323
await existing.fetchEntry();
319324

325+
if ( values.dedupe_name ) {
326+
console.log('DEDUPE NAME', values.dedupe_name);
327+
}
328+
320329
if ( existing.found ) {
321330
const { overwrite, dedupe_name, create_missing_parents } = values;
322331
if ( overwrite ) {
@@ -331,13 +340,17 @@ class HLMkdir extends HLFilesystemOperation {
331340
});
332341
}
333342
else if ( dedupe_name ) {
334-
const fsEntryFetcher = context.get('services').get('fsEntryFetcher');
343+
if ( parent_node.provider instanceof MemoryFSService ) {
344+
console.log('MEMORYFS DEDUPE');
345+
}
346+
347+
const fs = context.get('services').get('filesystem');
348+
const parent_selector = parent_node.selector;
335349
for ( let i=1 ;; i++ ) {
336350
let try_new_name = `${target_basename} (${i})`;
337-
const exists = await fsEntryFetcher.nameExistsUnderParent(
338-
existing.entry.parent_uid, try_new_name
339-
);
340-
if ( ! exists ) {
351+
const selector = new NodeChildSelector(parent_selector, try_new_name);
352+
const node = await fs.node(selector);
353+
if ( ! await node.exists() ) {
341354
target_basename = try_new_name;
342355
break;
343356
}
@@ -468,28 +481,44 @@ class HLMkdir extends HLFilesystemOperation {
468481
return node;
469482
}
470483

471-
async _create_top_parent ({ top_parent }) {
472-
if ( await top_parent.exists() ) {
473-
if ( ! top_parent.entry.is_dir ) {
484+
/**
485+
* Creates a directory and all its ancestors.
486+
*
487+
* @param {FSNodeContext} dir - The directory to create.
488+
* @returns {Promise<FSNodeContext>} The created directory.
489+
*/
490+
async _create_dir (dir) {
491+
console.log('CREATING DIR', dir.selector.describe());
492+
493+
if ( await dir.exists() ) {
494+
if ( ! dir.entry.is_dir ) {
474495
throw APIError.create('dest_is_not_a_directory');
475496
}
476-
return top_parent;
497+
return dir;
477498
}
478499

479500
const maybe_path_selector =
480-
top_parent.get_selector_of_type(NodePathSelector);
501+
dir.get_selector_of_type(NodePathSelector);
481502

482503
if ( ! maybe_path_selector ) {
483504
throw APIError.create('dest_does_not_exist');
484505
}
485506

486-
const path = maybe_path_selector.value;
507+
let path = maybe_path_selector.value;
487508

488509
const fs = this.context.get('services').get('filesystem');
489510

511+
let parent = await fs.node(new RootNodeSelector());
512+
// if ( mountpoint && mountpoint !== '/' ) {
513+
// parent = await fs.node(new NodePathSelector(mountpoint));
514+
515+
// // remove the mountpoint from the path
516+
// path = path.replace(mountpoint, '');
517+
// }
518+
490519
const tree_op = new MkTree();
491520
await tree_op.run({
492-
parent: await fs.node(new RootNodeSelector()),
521+
parent: parent,
493522
tree: [path],
494523
});
495524

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

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,45 @@ class NodeChildSelector {
8787
this.name = name;
8888
}
8989

90+
/**
91+
* Try to infer the absolute path of the node.
92+
*
93+
* @returns {string|null} the absolute path of the node, or null if the path cannot be inferred
94+
*/
95+
try_infer_path () {
96+
// try to get the full path recursively
97+
//
98+
// TODO (xiaochen): this is a hack to get the absolute path and should be removed once
99+
// we have a better solution. We need the absolute path so that `MountpointService`
100+
// can determine the mountpoint and provider for the node.
101+
const stack = [this.name];
102+
let current = this.parent;
103+
while ( current ) {
104+
if ( current instanceof NodeChildSelector ) {
105+
stack.push(current.name);
106+
current = current.parent;
107+
} else if ( current instanceof NodePathSelector ) {
108+
stack.push(current.value);
109+
current = null;
110+
} else if ( current instanceof RootNodeSelector ) {
111+
current = null;
112+
} else {
113+
// for other selectors, we can't determine the absolute path
114+
return null;
115+
}
116+
}
117+
118+
let path = '/';
119+
stack.reverse().forEach(item => {
120+
path = _path.join(path, item);
121+
});
122+
123+
return path;
124+
}
125+
90126
setPropertiesKnownBySelector (node) {
91127
node.name = this.name;
92-
// no properties known
128+
node.path = this.try_infer_path();
93129
}
94130

95131
describe () {
@@ -145,6 +181,30 @@ class NodeRawEntrySelector {
145181
}
146182
}
147183

184+
/**
185+
* Try to infer following attributes for a selector:
186+
* - path
187+
* - uid
188+
*
189+
* @param {NodePathSelector|NodeUIDSelector|NodeChildSelector|RootNodeSelector} selector
190+
*/
191+
function try_infer_attributes (selector) {
192+
if ( selector instanceof NodePathSelector ) {
193+
selector.path = selector.value;
194+
} else if ( selector instanceof NodeUIDSelector ) {
195+
selector.uid = selector.value;
196+
} else if ( selector instanceof NodeChildSelector ) {
197+
try_infer_attributes(selector.parent);
198+
if ( selector.parent.path ) {
199+
selector.path = _path.join(selector.parent.path, selector.name);
200+
}
201+
} else if ( selector instanceof RootNodeSelector ) {
202+
selector.path = '/';
203+
} else {
204+
throw new Error('invalid selector');
205+
}
206+
}
207+
148208
const relativeSelector = (parent, path) => {
149209
if ( path === '.' ) return parent;
150210
if ( path.startsWith('..') ) {
@@ -169,4 +229,5 @@ module.exports = {
169229
RootNodeSelector,
170230
NodeRawEntrySelector,
171231
relativeSelector,
232+
try_infer_attributes,
172233
};

src/backend/src/helpers.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ async function is_user_signup_disabled() {
9797
}
9898

9999
const chkperm = spanify('chkperm', async (target_fsentry, requester_user_id, action) => {
100+
// TODO (xiaochen): remove this branch after the related ACL permission logic is implemented for "MemoryFSProvider".
101+
if (target_fsentry.is_public) {
102+
console.log('TRUE BECAUSE IS_PUBLIC')
103+
// return true;
104+
}
105+
100106
// basic cases where false is the default response
101107
if(!target_fsentry)
102108
return false;

src/backend/src/modules/puterfs/MountpointService.js

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
*/
2020
// const Mountpoint = o => ({ ...o });
2121

22-
const { RootNodeSelector, NodeUIDSelector } = require("../../filesystem/node/selectors");
22+
const { RootNodeSelector, NodeUIDSelector, NodeChildSelector, NodePathSelector, NodeInternalIDSelector } = require("../../filesystem/node/selectors");
2323
const BaseService = require("../../services/BaseService");
2424

2525
/**
@@ -87,12 +87,39 @@ class MountpointService extends BaseService {
8787
}
8888

8989
async get_provider (selector) {
90+
// type check
91+
if ( ! (selector instanceof RootNodeSelector) &&
92+
! (selector instanceof NodeUIDSelector) &&
93+
! (selector instanceof NodeChildSelector) &&
94+
! (selector instanceof NodePathSelector) &&
95+
! (selector instanceof NodeInternalIDSelector)
96+
) {
97+
throw new Error('Invalid selector type');
98+
}
99+
90100
if ( selector instanceof RootNodeSelector ) {
91101
return this.mountpoints_['/'].provider;
92102
}
93103

94104
if ( selector instanceof NodeUIDSelector ) {
95-
return this.mountpoints_['/'].provider;
105+
for ( const [path, { provider }] of Object.entries(this.mountpoints_) ) {
106+
const result = await provider._stat_by_uid({
107+
uid: selector.value,
108+
});
109+
if ( result ) {
110+
return provider;
111+
}
112+
}
113+
console.log('no provider found');
114+
}
115+
116+
if ( selector instanceof NodeChildSelector ) {
117+
const path = selector.try_infer_path();
118+
if ( path ) {
119+
return this.get_provider(new NodePathSelector(path));
120+
} else {
121+
return this.get_provider(selector.parent);
122+
}
96123
}
97124

98125
const probe = {};

src/backend/src/modules/puterfs/PuterFSModule.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ class PuterFSModule extends AdvancedBase {
4040

4141
const DatabaseFSEntryFetcher = require("./DatabaseFSEntryFetcher");
4242
services.registerService('fsEntryFetcher', DatabaseFSEntryFetcher);
43+
44+
const { MemoryFSService } = require('./customfs/MemoryFSService');
45+
services.registerService('memoryfs', MemoryFSService);
4346
}
4447
}
4548

0 commit comments

Comments
 (0)