diff --git a/src/App.vue b/src/App.vue index 54a70212..a3600cd5 100644 --- a/src/App.vue +++ b/src/App.vue @@ -36,7 +36,7 @@ import { Walk } from './utils/driveWalker.js' import GlobalSearchWorker from 'worker-loader!./workers/globalSearchWorker.js' import DirWatcherWorker from 'worker-loader!./workers/dirWatcherWorker.js' import TimeUtils from './utils/timeUtils.js' -import getStorageDevices from './utils/storageInfo.js' +import {getStorageDevices} from './utils/storageInfo.js' import idleJs from 'idle-js' const electron = require('electron') const PATH = require('path') diff --git a/src/utils/fsManager.js b/src/utils/fsManager.js index d2cf3ed5..817137dc 100644 --- a/src/utils/fsManager.js +++ b/src/utils/fsManager.js @@ -44,25 +44,65 @@ async function initCliProcess () { params.shell = '/bin/sh' params.args = [] } - + reusableCli.init( params, (spawnedCliProcess) => { state.cliProcess = spawnedCliProcess + if (process.platform === 'win32') { + setUTF8encoding() + } } ) } function getCommand (params) { if (process.platform === 'win32') { + // Format path + if (params.path) { + params.path = params.path.replace(/\//g, '\\') + } + // Get command if (params.command === 'sudo') { - if (params.adminPrompt === 'built-in') { - // return `echo -e "${params.sudoPassword}\n" | sudo -S` - return `echo ${params.sudoPassword} | sudo -S` - } - else if (params.adminPrompt === 'pkexec') { - return 'pkexec' - } + return '-Verb RunAs' + } + if (params.command === 'set-utf-8-encoding') { + return '[System.Console]::OutputEncoding = [System.Console]::InputEncoding = [System.Text.Encoding]::UTF8' + } + if (params.command === 'drive-info') { + return [ + 'get-ciminstance -ClassName Win32_LogicalDisk', + '| convertTo-csv | convertFrom-csv | convertTo-json' + ].join(' ') + } + if (params.command === 'extra-drive-info') { + return `(Get-CimInstance Win32_Diskdrive -Filter "Partitions>0" | ForEach-Object { + $disk = Get-CimInstance + -ClassName MSFT_PhysicalDisk + -Namespace root\\Microsoft\\Windows\\Storage + -Filter "SerialNumber='$($_.SerialNumber.trim())'"; + + foreach ($partition in $_ | Get-CimAssociatedInstance -ResultClassName Win32_DiskPartition) { + foreach ($logicaldisk in $partition | Get-CimAssociatedInstance -ResultClassName Win32_LogicalDisk) { + [PSCustomObject]@{ + Disk = $_.DeviceID; + DiskModel = $_.Model; + DiskSize = $_.Size; + HealthStatus = $disk.HealthStatus; + BusType = $disk.BusType; + DiskType = $_.MediaType; + MediaType = $disk.MediaType; + Partition = $partition.Name; + PartitionSize = $partition.Size; + VolumeName = $logicaldisk.VolumeName; + DriveLetter = $logicaldisk.DeviceID; + VolumeSize = $logicaldisk.Size; + FreeSpace = $logicaldisk.FreeSpace; + } + } + } + }) | convertTo-csv | convertFrom-csv | convertTo-json + `.replace(/\n/g, ' ') } // Note: all commands on win32 are executed with powershell. // it doesn't have support for operators like &&. @@ -235,6 +275,35 @@ function execCommand (params) { }) } +/** +* @returns void +*/ +async function setUTF8encoding () { + execCommand({ + command: getCommand({command: 'set-utf-8-encoding'}) + }) +} + +/** +* @returns {array} +*/ +async function getDriveInfo () { + let data = await execCommand({ + command: getCommand({command: 'drive-info'}) + }) + return JSON.parse(data.join('')) +} + +/** +* @returns {array} +*/ +async function getExtraDriveInfo () { + let data = await execCommand({ + command: getCommand({command: 'extra-drive-info'}) + }) + return JSON.parse(data.join('')) +} + /** * @param {string} params.path * @returns void @@ -522,6 +591,8 @@ function changeMode (params) { export { initCliProcess, changeMode, + getDriveInfo, + getExtraDriveInfo, resetPermissions, getDirItemOwner, isDirItemImmutable, diff --git a/src/utils/storageInfo.js b/src/utils/storageInfo.js index 5f5b3545..e60dc591 100644 --- a/src/utils/storageInfo.js +++ b/src/utils/storageInfo.js @@ -3,42 +3,189 @@ // Copyright © 2021 - present Aleksey Hoffman. All rights reserved. // TODO: (?) Move to the main process -// TODO: If systeminformation.fsSize() ever becomes faster, -// replace "node-diskuage" with the "sysInfo.fsSize()"" -// and then remove "app.allowRendererProcessReuse = false" from the main. -// Currently it's 1000 times slower (0.1 ms vs 100 ms) import utils from './utils' +import * as fsManager from './fsManager.js' + +const PATH = require('path') const sysInfo = require('systeminformation') const diskusage = require('diskusage') -const PATH = require('path') -export default async function getStorageDevices () { - let drives = await sysInfo.blockDevices() - drives = formatDrivesData(drives) - drives = processDrivesData(drives) - drives = await addPropertiesToDrivesData(drives) - return drives +let state = { + extraDeviceData: [], + driveList: [], + previousDriveList: [], + driveTypes: { + '1': '', + '2': 'removable', + '3': 'fixed', + '4': 'network', + '5': 'cd', + '6': 'RAM' + }, + driveMemoryTypes: { + '1': '', + '3': 'HDD', + '4': 'SSD', + '5': 'SCM' + } +} + +export async function getStorageDevices () { + try { + let blockDevices = await getBlockDevices() + let storageDevices = [ + ...blockDevices, + ] + state.previousDriveList = [...state.driveList] + state.driveList = blockDevices + handleDriveListChange() + return storageDevices + } + catch (error) { + console.error(error) + return [] + } +} + +function handleDriveListChange () { + let driveListChanged = state.previousDriveList.length !== state.driveList.length + if (driveListChanged) { + fetchBlockDevicesExtraData() + } +} + +export async function fetchBlockDevicesExtraData () { + try { + getBlockDevicesExtraData() + .then((data) => { + const extraDeviceDataFormatted = data + .map(drive => { + drive.MediaType = state.driveMemoryTypes[drive.MediaType] + return drive + }) + state.extraDeviceData = extraDeviceDataFormatted + }) + } + catch (error) {} +} + +export async function getBlockDevicesExtraData () { + return new Promise((resolve, reject) => { + if (process.platform === 'win32') { + fsManager.getExtraDriveInfo().then((data) => resolve(data)) + } + else { + return [] + } + }) +} + +async function getBlockDevicesData () { + try { + if (process.platform === 'win32') { + return await fsManager.getDriveInfo() + } + else if (process.platform === 'linux') { + return await sysInfo.blockDevices() + } + else if (process.platform === 'darwin') { + return await sysInfo.blockDevices() + } + } + catch (error) { + return [] + } +} + +async function getBlockDevices () { + let blockDevices = await getBlockDevicesData() + blockDevices = formatBlockDevicesProperties(blockDevices) + blockDevices = formatDrivesData(blockDevices) + blockDevices = processDrivesData(blockDevices) + blockDevices = await addPropertiesToDrivesData(blockDevices) + return blockDevices +} + +function formatBlockDevicesProperties (blockDevices) { + if (process.platform === 'win32') { + blockDevices.forEach(device => { + device.mount = device.Caption + device.path = device.Caption + device.name = device.Caption + device.label = device.ProviderName ? device.ProviderName : device.VolumeName + device.fsType = device.FileSystem + device.type = getDriveType(device) + device.memoryType = getDriveMemoryType(device) + }) + } + else if (process.platform === 'linux') { + blockDevices.forEach(device => { + device.type = getDriveType(device) + }) + } + else if (process.platform === 'darwin') { + blockDevices.forEach(device => { + device.type = getDriveType(device) + }) + } + return blockDevices +} + +function getDriveType (device) { + if (process.platform === 'win32') { + return state.driveTypes[device.driveType] || '' + } + else if (process.platform === 'linux') { + if (device.removable && device.type === 'rom') { + return 'rom' + } + else { + return device.removable ? 'removable' : device.type + } + } + else if (process.platform === 'darwin') { + if (device.removable && device.type === 'rom') { + return 'rom' + } + else { + return device.removable ? 'removable' : device.type + } + } + else { + return '' + } +} + +function getDriveMemoryType (device) { + if (process.platform === 'win32') { + let extraData = state.extraDeviceData.find(drive => drive.DriveLetter === device.Caption) + if (extraData) { + return extraData.MediaType + } + else { + return '' + } + } + else { + return '' + } } async function addPropertiesToDrivesData (drives) { for (let index = 0; index < drives.length; index++) { const drive = drives[index] - - let size - let percentUsed + let size = {available: 0, free: 0, total: 0} + let percentUsed = 0 try { size = await diskusage.check(drive.mount) - percentUsed = Math.floor((1 - (size.free / size.total)) * 100) - } - catch (e) { - // Drive couldn't be checked by diskusage, likely because it isn't readable - size = { available: 0, free: 0, total: 0 } - percentUsed = 0 + percentUsed = Math.floor((1 - (size.free / size.total)) * 100) || 0 } + catch (error) {} drive.size = size + drive.path = drive.mount drive.percentUsed = percentUsed drive.titleSummary = getDriveTitleSummary(drive) drive.infoSummary = getDriveInfoSummary(drive, percentUsed) @@ -48,17 +195,26 @@ async function addPropertiesToDrivesData (drives) { } function processDrivesData (drives) { - if (process.platform === 'linux') { - // Filter out unneeded mount points + if (process.platform === 'win32') { drives = drives.filter(drive => { - const allowedTypes = ['part', 'rom'].includes(drive.type) + return drive.fsType !== '' + }) + } + else if (process.platform === 'linux') { + drives = drives.filter(drive => { + const incudesAllowedTypes = ['disk', 'part', 'rom', 'removable'].includes(drive.type) + const includesDisallowedMounts = ['/boot/efi/'].includes(drive.mount) const isSwap = drive.fsType === 'swap' - return allowedTypes && !isSwap + return drive.fsType !== '' && incudesAllowedTypes && !includesDisallowedMounts && !isSwap }) } - else if (process.platform === 'win32') { - // Filter out drives with empty 'fsType' property (likely RAW drives or CD/DVD Drives) - drives = drives.filter(drive => drive.fsType !== '') + else if (process.platform === 'darwin') { + drives = drives.filter(drive => { + const disallowedMountEntryPoints = ['/System'] + const isRootEntryPoint = drive.mount === '/' + const isDisallowedEntryPoint = disallowedMountEntryPoints.some(entryPoint => drive.mount.startsWith(entryPoint)) + return drive.fsType !== '' && !isDisallowedEntryPoint && !isRootEntryPoint + }) } return drives } @@ -73,10 +229,21 @@ function formatDrivesData (drives) { function getDriveTitleSummary (drive) { if (process.platform === 'win32') { - return `${drive.mount.replace(':/', ':')} • ${drive.label}` + let mount = drive.mount.replace(':/', ':') + if (drive.memoryType) { + return `${mount} • ${drive.label} • ${drive.memoryType}` + } + else { + return `${mount} • ${drive.label}` + } } else { - return `${drive.name} • ${drive.mount}` + if (drive.label) { + return `${drive.label}` + } + else { + return `${drive.mount}` + } } }