All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oshi.software.os.mac.MacFileSystem Maven / Gradle / Ivy

There is a newer version: 6.6.4
Show newest version
/**
 * MIT License
 *
 * Copyright (c) 2010 - 2020 The OSHI Project Contributors: https://github.com/oshi/oshi/graphs/contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package oshi.software.os.mac;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jna.Pointer; // NOSONAR squid:S1191
import com.sun.jna.platform.mac.CoreFoundation;
import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef;
import com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef;
import com.sun.jna.platform.mac.CoreFoundation.CFStringRef;
import com.sun.jna.platform.mac.DiskArbitration;
import com.sun.jna.platform.mac.DiskArbitration.DADiskRef;
import com.sun.jna.platform.mac.DiskArbitration.DASessionRef;
import com.sun.jna.platform.mac.IOKit.IOIterator;
import com.sun.jna.platform.mac.IOKit.IORegistryEntry;
import com.sun.jna.platform.mac.IOKitUtil;
import com.sun.jna.platform.mac.SystemB;
import com.sun.jna.platform.mac.SystemB.Statfs;

import oshi.annotation.concurrent.ThreadSafe;
import oshi.software.common.AbstractFileSystem;
import oshi.software.os.OSFileStore;
import oshi.util.Constants;
import oshi.util.platform.mac.SysctlUtil;

/**
 * The Mac File System contains {@link oshi.software.os.OSFileStore}s which are
 * a storage pool, device, partition, volume, concrete file system or other
 * implementation specific means of file storage. In macOS, these are found in
 * the /Volumes directory.
 */
@ThreadSafe
public class MacFileSystem extends AbstractFileSystem {

    private static final Logger LOG = LoggerFactory.getLogger(MacFileSystem.class);

    // Regexp matcher for /dev/disk1 etc.
    private static final Pattern LOCAL_DISK = Pattern.compile("/dev/disk\\d");

    // User specifiable flags.
    private static final int MNT_RDONLY = 0x00000001;
    private static final int MNT_SYNCHRONOUS = 0x00000002;
    private static final int MNT_NOEXEC = 0x00000004;
    private static final int MNT_NOSUID = 0x00000008;
    private static final int MNT_NODEV = 0x00000010;
    private static final int MNT_UNION = 0x00000020;
    private static final int MNT_ASYNC = 0x00000040;
    private static final int MNT_CPROTECT = 0x00000080;
    private static final int MNT_EXPORTED = 0x00000100;
    private static final int MNT_QUARANTINE = 0x00000400;
    private static final int MNT_LOCAL = 0x00001000;
    private static final int MNT_QUOTA = 0x00002000;
    private static final int MNT_ROOTFS = 0x00004000;
    private static final int MNT_DOVOLFS = 0x00008000;
    private static final int MNT_DONTBROWSE = 0x00100000;
    private static final int MNT_IGNORE_OWNERSHIP = 0x00200000;
    private static final int MNT_AUTOMOUNTED = 0x00400000;
    private static final int MNT_JOURNALED = 0x00800000;
    private static final int MNT_NOUSERXATTR = 0x01000000;
    private static final int MNT_DEFWRITE = 0x02000000;
    private static final int MNT_MULTILABEL = 0x04000000;
    private static final int MNT_NOATIME = 0x10000000;

    private static final Map OPTIONS_MAP = new HashMap<>();
    static {
        OPTIONS_MAP.put(MNT_SYNCHRONOUS, "synchronous");
        OPTIONS_MAP.put(MNT_NOEXEC, "noexec");
        OPTIONS_MAP.put(MNT_NOSUID, "nosuid");
        OPTIONS_MAP.put(MNT_NODEV, "nodev");
        OPTIONS_MAP.put(MNT_UNION, "union");
        OPTIONS_MAP.put(MNT_ASYNC, "asynchronous");
        OPTIONS_MAP.put(MNT_CPROTECT, "content-protection");
        OPTIONS_MAP.put(MNT_EXPORTED, "exported");
        OPTIONS_MAP.put(MNT_QUARANTINE, "quarantined");
        OPTIONS_MAP.put(MNT_LOCAL, "local");
        OPTIONS_MAP.put(MNT_QUOTA, "quotas");
        OPTIONS_MAP.put(MNT_ROOTFS, "rootfs");
        OPTIONS_MAP.put(MNT_DOVOLFS, "volfs");
        OPTIONS_MAP.put(MNT_DONTBROWSE, "nobrowse");
        OPTIONS_MAP.put(MNT_IGNORE_OWNERSHIP, "noowners");
        OPTIONS_MAP.put(MNT_AUTOMOUNTED, "automounted");
        OPTIONS_MAP.put(MNT_JOURNALED, "journaled");
        OPTIONS_MAP.put(MNT_NOUSERXATTR, "nouserxattr");
        OPTIONS_MAP.put(MNT_DEFWRITE, "defwrite");
        OPTIONS_MAP.put(MNT_MULTILABEL, "multilabel");
        OPTIONS_MAP.put(MNT_NOATIME, "noatime");
    }

    @Override
    public List getFileStores(boolean localOnly) {
        // List of file systems
        return getFileStoreMatching(null, localOnly);
    }

    // Called by MacOSFileStore
    static List getFileStoreMatching(String nameToMatch) {
        return getFileStoreMatching(nameToMatch, false);
    }

    private static List getFileStoreMatching(String nameToMatch, boolean localOnly) {
        List fsList = new ArrayList<>();

        // Use getfsstat to find fileSystems
        // Query with null to get total # required
        int numfs = SystemB.INSTANCE.getfsstat64(null, 0, 0);
        if (numfs > 0) {
            // Open a DiskArbitration session to get VolumeName of file systems
            // with bsd names
            DASessionRef session = DiskArbitration.INSTANCE
                    .DASessionCreate(CoreFoundation.INSTANCE.CFAllocatorGetDefault());
            if (session == null) {
                LOG.error("Unable to open session to DiskArbitration framework.");
            } else {
                CFStringRef daVolumeNameKey = CFStringRef.createCFString("DAVolumeName");

                // Create array to hold results
                Statfs[] fs = new Statfs[numfs];
                // Fill array with results
                numfs = SystemB.INSTANCE.getfsstat64(fs, numfs * new Statfs().size(), SystemB.MNT_NOWAIT);
                for (int f = 0; f < numfs; f++) {
                    // Mount on name will match mounted path, e.g. /Volumes/foo
                    // Mount to name will match canonical path., e.g., /dev/disk0s2
                    // Byte arrays are null-terminated strings

                    // Get volume name
                    String volume = new String(fs[f].f_mntfromname, StandardCharsets.UTF_8).trim();
                    // Skip non-local drives if requested, skip system types
                    final int flags = fs[f].f_flags;
                    if ((localOnly && (flags & MNT_LOCAL) == 0) || volume.equals("devfs")
                            || volume.startsWith("map ")) {
                        continue;
                    }

                    String type = new String(fs[f].f_fstypename, StandardCharsets.UTF_8).trim();
                    String description = "Volume";
                    if (LOCAL_DISK.matcher(volume).matches()) {
                        description = "Local Disk";
                    } else if (volume.startsWith("localhost:") || volume.startsWith("//") || volume.startsWith("smb://")
                            || NETWORK_FS_TYPES.contains(type)) {
                        description = "Network Drive";
                    }
                    String path = new String(fs[f].f_mntonname, StandardCharsets.UTF_8).trim();
                    String name = "";
                    File file = new File(path);
                    if (name.isEmpty()) {
                        name = file.getName();
                        // getName() for / is still blank, so:
                        if (name.isEmpty()) {
                            name = file.getPath();
                        }
                    }
                    if (nameToMatch != null && !nameToMatch.equals(name)) {
                        continue;
                    }

                    StringBuilder options = new StringBuilder((MNT_RDONLY & flags) == 0 ? "rw" : "ro");
                    String moreOptions = OPTIONS_MAP.entrySet().stream().filter(e -> (e.getKey() & flags) > 0)
                            .map(Map.Entry::getValue).collect(Collectors.joining(","));
                    if (!moreOptions.isEmpty()) {
                        options.append(',').append(moreOptions);
                    }

                    String uuid = "";
                    // Use volume to find DiskArbitration volume name and search for
                    // the registry entry for UUID
                    String bsdName = volume.replace("/dev/disk", "disk");
                    if (bsdName.startsWith("disk")) {
                        // Get the DiskArbitration dictionary for this disk,
                        // which has volumename
                        DADiskRef disk = DiskArbitration.INSTANCE.DADiskCreateFromBSDName(
                                CoreFoundation.INSTANCE.CFAllocatorGetDefault(), session, volume);
                        if (disk != null) {
                            CFDictionaryRef diskInfo = DiskArbitration.INSTANCE.DADiskCopyDescription(disk);
                            if (diskInfo != null) {
                                // get volume name from its key
                                Pointer result = diskInfo.getValue(daVolumeNameKey);
                                CFStringRef volumePtr = new CFStringRef(result);
                                name = volumePtr.stringValue();
                                if (name == null) {
                                    name = Constants.UNKNOWN;
                                }
                                diskInfo.release();
                            }
                            disk.release();
                        }
                        // Search for bsd name in IOKit registry for UUID
                        CFMutableDictionaryRef matchingDict = IOKitUtil.getBSDNameMatchingDict(bsdName);
                        if (matchingDict != null) {
                            // search for all IOservices that match the bsd name
                            IOIterator fsIter = IOKitUtil.getMatchingServices(matchingDict);
                            if (fsIter != null) {
                                // getMatchingServices releases matchingDict
                                // Should only match one logical drive
                                IORegistryEntry fsEntry = fsIter.next();
                                if (fsEntry != null && fsEntry.conformsTo("IOMedia")) {
                                    // Now get the UUID
                                    uuid = fsEntry.getStringProperty("UUID");
                                    if (uuid != null) {
                                        uuid = uuid.toLowerCase();
                                    }
                                    fsEntry.release();
                                }
                                fsIter.release();
                            }
                        }
                    }

                    fsList.add(new MacOSFileStore(name, volume, name, path, options.toString(),
                            uuid == null ? "" : uuid, "", description, type, file.getFreeSpace(), file.getUsableSpace(),
                            file.getTotalSpace(), fs[f].f_ffree, fs[f].f_files));
                }
                daVolumeNameKey.release();
                // Close DA session
                session.release();
            }
        }
        return fsList;
    }

    @Override
    public long getOpenFileDescriptors() {
        return SysctlUtil.sysctl("kern.num_files", 0);
    }

    @Override
    public long getMaxFileDescriptors() {
        return SysctlUtil.sysctl("kern.maxfiles", 0);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy