You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
116 lines
3.9 KiB
JavaScript
116 lines
3.9 KiB
JavaScript
1 year ago
|
"use strict";
|
||
|
|
||
|
const fs = require("fs").promises;
|
||
|
const memoizee = require("memoizee");
|
||
|
const DataLoader = require("dataloader");
|
||
|
|
||
|
const findmnt = require("../exec-findmnt");
|
||
|
const dlayerSource = require("../dlayer-source");
|
||
|
const treeFilterFlatAsync = require("../tree-filter-flat-async");
|
||
|
const treeMapAsync = require("../tree-map-async");
|
||
|
const treeFind = require("../tree-find");
|
||
|
const All = require("../graphql-interface/symbols/all");
|
||
|
|
||
|
module.exports = {
|
||
|
name: "sysquery.mounts",
|
||
|
makeContext: function () {
|
||
|
let findmntOnce = memoizee(async () => {
|
||
|
return treeMapAsync(await findmnt(), async (mount) => {
|
||
|
return {
|
||
|
... mount,
|
||
|
sourceDevice: (mount.sourceDevice?.startsWith("/"))
|
||
|
? await fs.realpath(mount.sourceDevice)
|
||
|
: mount.sourceDevice
|
||
|
};
|
||
|
|
||
|
});
|
||
|
});
|
||
|
|
||
|
return {
|
||
|
findmnt: new DataLoader(async function (mountpoints) {
|
||
|
let mounts = await findmntOnce();
|
||
|
|
||
|
// TODO: It's kind of strange that it sometimes returns a tree and sometimes a list, this can probably be improved?
|
||
|
let matches = mountpoints.map((mountpoint) => {
|
||
|
if (mountpoint === All) {
|
||
|
return mounts;
|
||
|
} else {
|
||
|
return treeFind(mounts, (mount) => mount.mountpoint === mountpoint);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return matches;
|
||
|
})
|
||
|
};
|
||
|
},
|
||
|
types: {
|
||
|
"sysquery.mounts.Mount": function ({ mountpoint }) {
|
||
|
return dlayerSource.withSources({
|
||
|
mountpoint: mountpoint,
|
||
|
$sources: {
|
||
|
findmnt: {
|
||
|
[dlayerSource.ID]: mountpoint,
|
||
|
id: "id",
|
||
|
// FIXME: Aren't we inferring the below somewhere else in the code, using the square brackets?
|
||
|
type: (mount) => (mount.rootPath === "/")
|
||
|
? "ROOT_MOUNT"
|
||
|
: "SUBMOUNT",
|
||
|
filesystem: "filesystem",
|
||
|
options: "options",
|
||
|
label: "label",
|
||
|
uuid: "uuid",
|
||
|
partitionLabel: "partitionLabel",
|
||
|
partitionUUID: "partitionUUID",
|
||
|
deviceNumber: "deviceNumber",
|
||
|
sourceDevicePath: "sourceDevice",
|
||
|
totalSpace: "totalSpace",
|
||
|
freeSpace: "freeSpace",
|
||
|
usedSpace: "usedSpace",
|
||
|
rootPath: "rootPath",
|
||
|
taskID: "taskID",
|
||
|
optionalFields: "optionalFields",
|
||
|
propagationFlags: "propagationFlags",
|
||
|
children: (mount, { $make }) => mount.children.map((child) => {
|
||
|
return $make("sysquery.mounts.Mount", { mountpoint: child.mountpoint });
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
},
|
||
|
extensions: {
|
||
|
"sysquery.blockDevices.BlockDevice": {
|
||
|
// TODO: Eventually make this produce a (filtered) tree instead?
|
||
|
mounts: async function ({ type }, { findmnt, $make, $getProperty, $getPropertyPath }) {
|
||
|
let mountTree = await findmnt.load(All);
|
||
|
|
||
|
let relevantMounts = await treeFilterFlatAsync(mountTree, async (mount) => {
|
||
|
let mountObject = $make("sysquery.mounts.Mount", { mountpoint: mount.mountpoint });
|
||
|
// console.log({ sourceDevice: await $getProperty(mountObject, "sourceDevice") });
|
||
|
let sourcePath = await $getPropertyPath(mountObject, "sourceDevice.path");
|
||
|
let sourceName = await $getPropertyPath(mountObject, "sourceDevice.name");
|
||
|
|
||
|
// TODO: This logic looks strange. Is it actually correct, even when only one of name/path is specified upon BlockDevice construction?
|
||
|
let matchesDevice = (
|
||
|
(sourcePath != null && sourcePath === await $getProperty(this, "path"))
|
||
|
|| (sourceName != null && sourceName === await $getProperty(this, "name"))
|
||
|
);
|
||
|
|
||
|
let matchesType = (
|
||
|
type == null
|
||
|
|| await $getProperty(mountObject, "type" === type)
|
||
|
);
|
||
|
|
||
|
return matchesDevice && matchesType;
|
||
|
}, { recurseFilteredSubtrees: true });
|
||
|
|
||
|
// This is a bit hacky; this approach should probably be replaced by a map-filter instead. But for now, this will do - as this all happens in the same request context, there's no real penalty to re-creating the mount objects a second time.
|
||
|
return relevantMounts.map((mount) => {
|
||
|
return $make("sysquery.mounts.Mount", { mountpoint: mount.mountpoint });
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
root: {} // FIXME: Expose root mounts endpoint
|
||
|
};
|