From bb4a786b4143bd9e5df724c99fdaff2858626649 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Fri, 27 Nov 2020 12:15:11 -0600 Subject: [PATCH] Add support for extended attributes --- app/models/fs/Node.model.js | 30 ++++++++++++++++++++++++++++++ app/ws/routes/fs.xattr.get.js | 17 +++++++++++++++++ app/ws/routes/fs.xattr.list.js | 19 +++++++++++++++++++ app/ws/routes/fs.xattr.remove.js | 20 ++++++++++++++++++++ app/ws/routes/fs.xattr.set.js | 20 ++++++++++++++++++++ 5 files changed, 106 insertions(+) create mode 100644 app/ws/routes/fs.xattr.get.js create mode 100644 app/ws/routes/fs.xattr.list.js create mode 100644 app/ws/routes/fs.xattr.remove.js create mode 100644 app/ws/routes/fs.xattr.set.js diff --git a/app/models/fs/Node.model.js b/app/models/fs/Node.model.js index 58b0530..184de06 100644 --- a/app/models/fs/Node.model.js +++ b/app/models/fs/Node.model.js @@ -22,6 +22,12 @@ class Node extends Model { descriptor_type: { type: String, default: NodeDescriptorType.File }, uploaded_file_id: String, + extended_attributes: [{ + name: String, + value: String, // base64 encoded binary buffer value + // There's position, but it's legacy for macOS, so going to exclude it for now. + }], + deleted: { type: Boolean, default: false }, root: { type: Boolean, default: false }, } @@ -56,6 +62,8 @@ class Node extends Model { } static async get_path(path, workspace = 'mainline') { + if ( path === '/' ) return this.get_root() + const [pied_parent_path, pied_name] = this.path_parts(path) const nodes = await this.find({ @@ -142,6 +150,28 @@ class Node extends Model { return [...nodes, ...mainline_nodes].filter(x => !x.deleted) } + set_xattr(name, value) { + this.guarantee_xattrs() + this.remove_xattr(name) + this.extended_attributes.push({ name, value }) + } + + get_xattr(name) { + this.guarantee_xattrs() + return this.extended_attributes.find(attr => attr.name === name) + } + + remove_xattr(name) { + this.guarantee_xattrs() + this.extended_attributes = this.extended_attributes.filter(attr => attr.name !== name) + } + + guarantee_xattrs() { + if ( !Array.isArray(this.extended_attributes) ) { + this.extended_attributes = [] + } + } + to_api() { return { pied_name: this.pied_name, diff --git a/app/ws/routes/fs.xattr.get.js b/app/ws/routes/fs.xattr.get.js new file mode 100644 index 0000000..83278aa --- /dev/null +++ b/app/ws/routes/fs.xattr.get.js @@ -0,0 +1,17 @@ +const { Errors } = require('../../shared') + +module.exports = exports = async (message, di) => { + const Node = di.models.get('fs:Node') + const { path, name } = message.data() + + const node = await Node.get_path(path, message.socket.session.overlay_name || 'mainline') + if ( !node ) { + return message.send_response( + message.fresh().error(Errors.NodeDoesNotExist) + ) + } + + message.send_response( + message.fresh().data({ node: node.to_api(), value: node.get_xattr(name)?.value }) + ) +} diff --git a/app/ws/routes/fs.xattr.list.js b/app/ws/routes/fs.xattr.list.js new file mode 100644 index 0000000..21fe056 --- /dev/null +++ b/app/ws/routes/fs.xattr.list.js @@ -0,0 +1,19 @@ +const { Errors } = require('../../shared') + +module.exports = exports = async (message, di) => { + const Node = di.models.get('fs:Node') + const { path } = message.data() + + const node = await Node.get_path(path, message.socket.session.overlay_name || 'mainline') + if ( !node ) { + return message.send_response( + message.fresh().error(Errors.NodeDoesNotExist) + ) + } + + node.guarantee_xattrs() + + message.send_response( + message.fresh().data({ node: node.to_api(), names: node.extended_attributes.map(x => x.name) }) + ) +} diff --git a/app/ws/routes/fs.xattr.remove.js b/app/ws/routes/fs.xattr.remove.js new file mode 100644 index 0000000..df0145a --- /dev/null +++ b/app/ws/routes/fs.xattr.remove.js @@ -0,0 +1,20 @@ +const { Errors } = require('../../shared') + +module.exports = exports = async (message, di) => { + const Node = di.models.get('fs:Node') + const { path, name } = message.data() + + const node = await Node.get_path(path, message.socket.session.overlay_name || 'mainline') + if ( !node ) { + return message.send_response( + message.fresh().error(Errors.NodeDoesNotExist) + ) + } + + node.remove_xattr(name) + await node.save() + + message.send_response( + message.fresh().data({ node: node.to_api() }) + ) +} diff --git a/app/ws/routes/fs.xattr.set.js b/app/ws/routes/fs.xattr.set.js new file mode 100644 index 0000000..5017454 --- /dev/null +++ b/app/ws/routes/fs.xattr.set.js @@ -0,0 +1,20 @@ +const { Errors } = require('../../shared') + +module.exports = exports = async (message, di) => { + const Node = di.models.get('fs:Node') + const { path, name, value } = message.data() + + const node = await Node.get_path(path, message.socket.session.overlay_name || 'mainline') + if ( !node ) { + return message.send_response( + message.fresh().error(Errors.NodeDoesNotExist) + ) + } + + node.set_xattr(name, value) + await node.save() + + message.send_response( + message.fresh().data({ node: node.to_api() }) + ) +}