|
|
|
@ -4,6 +4,7 @@ const DataLoader = require("dataloader");
|
|
|
|
|
const fs = require("fs").promises;
|
|
|
|
|
const matchValue = require("match-value");
|
|
|
|
|
const memoizee = require("memoizee");
|
|
|
|
|
const { InvalidObject } = require("dlayer");
|
|
|
|
|
const unreachable = require("@joepie91/unreachable")("@sysquery/block-devices");
|
|
|
|
|
|
|
|
|
|
const dlayerSource = require("../dlayer-source");
|
|
|
|
@ -13,6 +14,7 @@ const mapFromSource = require("../map-from-source");
|
|
|
|
|
const treeMapAsync = require("../tree-map-async");
|
|
|
|
|
const treeFind = require("../tree-find");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function makePredicate({ path, name }) {
|
|
|
|
|
if (path != null) {
|
|
|
|
|
return (device) => device.path === path;
|
|
|
|
@ -36,6 +38,7 @@ module.exports = {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
// FIXME: This is probably broken; {path,name} selectors are always unique keys!
|
|
|
|
|
lsblk: new DataLoader(async function (selectors) {
|
|
|
|
|
let blockDeviceTree = await lsblkOnce();
|
|
|
|
|
|
|
|
|
@ -50,46 +53,47 @@ module.exports = {
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
types: {
|
|
|
|
|
"sysquery.blockDevices.BlockDevice": function ({ name, path }) {
|
|
|
|
|
return {
|
|
|
|
|
... dlayerSource("lsblk", {
|
|
|
|
|
[dlayerSource.ID]: { name, path },
|
|
|
|
|
name: "name",
|
|
|
|
|
path: (device) => fs.realpath(device.path),
|
|
|
|
|
type: (device) => matchValue(device.type, {
|
|
|
|
|
partition: "PARTITION",
|
|
|
|
|
disk: "DISK",
|
|
|
|
|
loopDevice: "LOOP_DEVICE"
|
|
|
|
|
}),
|
|
|
|
|
size: "size",
|
|
|
|
|
mountpoint: "mountpoint", // FIXME: Isn't this obsoleted by `mounts`?
|
|
|
|
|
deviceNumber: "deviceNumber",
|
|
|
|
|
removable: "removable",
|
|
|
|
|
readOnly: "readOnly",
|
|
|
|
|
children: (device, { $make }) => device.children.map((child) => {
|
|
|
|
|
return $make("sysquery.blockDevices.BlockDevice", { name: child.name });
|
|
|
|
|
"sysquery.blockDevices.BlockDevice": async function ({ name, path }, { lsblk }) {
|
|
|
|
|
let entry = await lsblk.load({ name, path });
|
|
|
|
|
|
|
|
|
|
if (entry != null) {
|
|
|
|
|
return {
|
|
|
|
|
... dlayerSource("lsblk", {
|
|
|
|
|
[dlayerSource.ID]: { name, path },
|
|
|
|
|
name: "name",
|
|
|
|
|
path: (device) => fs.realpath(device.path),
|
|
|
|
|
type: (device) => matchValue(device.type, {
|
|
|
|
|
partition: "PARTITION",
|
|
|
|
|
disk: "DISK",
|
|
|
|
|
loopDevice: "LOOP_DEVICE"
|
|
|
|
|
}),
|
|
|
|
|
size: "size",
|
|
|
|
|
mountpoint: "mountpoint", // FIXME: Isn't this obsoleted by `mounts`?
|
|
|
|
|
deviceNumber: "deviceNumber",
|
|
|
|
|
removable: "removable",
|
|
|
|
|
readOnly: "readOnly",
|
|
|
|
|
children: (device, { $make }) => device.children.map((child) => {
|
|
|
|
|
return $make("sysquery.blockDevices.BlockDevice", { name: child.name });
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
return InvalidObject;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
extensions: {
|
|
|
|
|
"sysquery.mounts.Mount": {
|
|
|
|
|
sourceDevice: async function (_, { lsblk, $make, $getProperty }) {
|
|
|
|
|
sourceDevice: async function (_, { $make, $getProperty }) {
|
|
|
|
|
let sourceDevicePath = await $getProperty(this, "sourceDevicePath");
|
|
|
|
|
|
|
|
|
|
if (sourceDevicePath == null) {
|
|
|
|
|
// This occurs when the mount is not backed by a device, eg. an sshfs FUSE mount
|
|
|
|
|
return null;
|
|
|
|
|
} else {
|
|
|
|
|
let realSourcePath = await fs.realpath(sourceDevicePath);
|
|
|
|
|
|
|
|
|
|
if (await lsblk.load({ path: realSourcePath }) != null) {
|
|
|
|
|
return $make("sysquery.blockDevices.BlockDevice", { path: realSourcePath });
|
|
|
|
|
} else {
|
|
|
|
|
// This occurs when the `sourceDevice` is a valid device, but it is not a *block* device, eg. `/dev/fuse`
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return $make("sysquery.blockDevices.BlockDevice", {
|
|
|
|
|
path: await fs.realpath(sourceDevicePath)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|