1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <dirent.h> 6#include <errno.h> 7#include <fcntl.h> 8#include <stdalign.h> 9#include <stdarg.h> 10#include <stdbool.h> 11#include <stdio.h> 12#include <stdlib.h> 13#include <string.h> 14#include <sys/stat.h> 15#include <unistd.h> 16 17#include <fbl/unique_fd.h> 18#include <fuchsia/io/c/fidl.h> 19#include <lib/fzl/fdio.h> 20#include <zircon/device/vfs.h> 21 22int usage(void) { 23 fprintf(stderr, "usage: df [ <option>* ] [paths]\n"); 24 fprintf(stderr, "df displays the mounted filesystems for a list of paths\n"); 25 fprintf(stderr, " -i : List inode information instead of block usage\n"); 26 fprintf(stderr, " -h : Show sizes in human readable format (e.g., 1K 2M 3G)\n"); 27 fprintf(stderr, " --help : Show this help message\n"); 28 return -1; 29} 30 31typedef struct { 32 bool node_usage; 33 bool human_readable; 34} df_options_t; 35 36const char* root = "/"; 37 38int parse_args(int argc, const char** argv, df_options_t* options, const char*** dirs, size_t* count) { 39 while (argc > 1) { 40 if (!strcmp(argv[1], "-i")) { 41 options->node_usage = true; 42 } else if (!strcmp(argv[1], "-h")) { 43 options->human_readable = true; 44 } else if (!strcmp(argv[1], "--help")) { 45 return usage(); 46 } else { 47 break; 48 } 49 argc--; 50 argv++; 51 } 52 if (argc >= 2) { 53 *dirs = &argv[1]; 54 *count = argc - 1; 55 } else { 56 *dirs = &root; 57 *count = 1; 58 } 59 return 0; 60} 61 62// Format for the header 63const char* hfmt = "%-10s %10s %10s %10s %3s%% %-10s %-10s\n"; 64// Format for the human-readable header 65const char* hrfmt = "%-10s %5s %5s %5s %5s%% %-10s %-10s\n"; 66// Format for the individual filesystems queried 67const char* ffmt = "%-10s %10zu %10zu %10zu %3zu%% %-10s %-10s\n"; 68 69#define KB (1lu << 10) 70#define MB (1lu << 20) 71#define GB (1lu << 30) 72#define TB (1lu << 40) 73#define PB (1lu << 50) 74#define EB (1lu << 60) 75 76// Conditionally print the size if it falls within the range of the magnitude. 77// [1.0XX, 999XX] 78bool print_magnitude(int padding, size_t size, size_t magnitude, const char* mag_string) { 79 if (size < 10 * magnitude) { 80 printf("%*zu.%zu%s ", padding - 4, size / magnitude, 81 size / (magnitude / 10) % 10, mag_string); 82 return true; 83 } else if (size < magnitude << 10) { 84 printf("%*zu%s ", padding - 2, size / magnitude, mag_string); 85 return true; 86 } 87 return false; 88} 89 90void print_human_readable(int padding, size_t size) { 91 if (size < KB) { 92 printf("%*s ", padding, "0"); 93 } else if (print_magnitude(padding, size, KB, "KB")) { 94 } else if (print_magnitude(padding, size, MB, "MB")) { 95 } else if (print_magnitude(padding, size, GB, "GB")) { 96 } else if (print_magnitude(padding, size, TB, "TB")) { 97 } else if (print_magnitude(padding, size, PB, "PB")) { 98 } else { 99 printf("%*zu ", padding, size); 100 } 101} 102 103void print_fs_type(const char* name, const df_options_t* options, 104 const fuchsia_io_FilesystemInfo* info, const char* device_path) { 105 if (options->node_usage) { 106 size_t nodes_total = info ? info->total_nodes : 0; 107 size_t nodes_used = info ? info->used_nodes : 0; 108 size_t nodes_available = nodes_total - nodes_used; 109 size_t use_percentage = nodes_total ? nodes_used * 100 / nodes_total : 0; 110 printf(ffmt, 111 info != nullptr ? reinterpret_cast<const char*>(info->name) : "?", 112 nodes_total, 113 nodes_used, 114 nodes_available, 115 use_percentage, 116 name, 117 device_path ? device_path : "none"); 118 } else { 119 // Block Usage 120 if (options->human_readable) { 121 size_t bytes_total = info ? info->total_bytes: 0; 122 size_t bytes_used = info ? info->used_bytes : 0; 123 size_t bytes_available = bytes_total - bytes_used; 124 size_t use_percentage = bytes_total ? bytes_used * 100 / bytes_total : 0; 125 printf("%-10s ", info != nullptr ? reinterpret_cast<const char*>(info->name) : "?"); 126 print_human_readable(5, bytes_total); 127 print_human_readable(5, bytes_used); 128 print_human_readable(5, bytes_available); 129 printf("%5zu%% ", use_percentage); 130 printf("%-10s ", name); 131 printf("%-10s\n", device_path ? device_path : "none"); 132 } else { 133 size_t blocks_total = info ? info->total_bytes >> 10 : 0; 134 size_t blocks_used = info ? info->used_bytes >> 10 : 0; 135 size_t blocks_available = blocks_total - blocks_used; 136 size_t use_percentage = blocks_total ? blocks_used * 100 / blocks_total : 0; 137 printf(ffmt, 138 info != nullptr ? reinterpret_cast<const char*>(info->name) : "?", 139 blocks_total, 140 blocks_used, 141 blocks_available, 142 use_percentage, 143 name, 144 device_path ? device_path : "none"); 145 } 146 } 147 148} 149 150int main(int argc, const char** argv) { 151 const char** dirs; 152 size_t dircount; 153 df_options_t options; 154 memset(&options, 0, sizeof(df_options_t)); 155 int r; 156 if ((r = parse_args(argc, argv, &options, &dirs, &dircount))) { 157 return r; 158 } 159 160 if (options.node_usage) { 161 printf(hfmt, "Filesystem", "Inodes", "IUsed", "IFree", "IUse", 162 "Path", "Device"); 163 } else { 164 if (options.human_readable) { 165 printf(hrfmt, "Filesystem", "Size", "Used", "Avail", "Use", 166 "Path", "Device"); 167 } else { 168 printf(hfmt, "Filesystem", "1K-Blocks", "Used", "Available", "Use", 169 "Path", "Device"); 170 } 171 } 172 173 // Try to open path with O_ADMIN so we can query for underlying block devices. 174 // If we fail, open directory without O_ADMIN. Block devices will not be returned. 175 for (size_t i = 0; i < dircount; i++) { 176 fbl::unique_fd fd; 177 bool admin = true; 178 fd.reset(open(dirs[i], O_RDONLY | O_ADMIN)); 179 if (!fd) { 180 fd.reset(open(dirs[i], O_RDONLY)); 181 if (!fd) { 182 fprintf(stderr, "df: Could not open target: %s\n", dirs[i]); 183 continue; 184 } 185 admin = false; 186 } 187 188 fuchsia_io_FilesystemInfo info; 189 zx_status_t status; 190 fzl::FdioCaller caller(fbl::move(fd)); 191 zx_status_t io_status = fuchsia_io_DirectoryAdminQueryFilesystem(caller.borrow_channel(), 192 &status, &info); 193 if (io_status != ZX_OK || status != ZX_OK) { 194 print_fs_type(dirs[i], &options, nullptr, "Unknown; cannot query filesystem"); 195 continue; 196 } 197 info.name[fuchsia_io_MAX_FS_NAME_BUFFER - 1] = '\0'; 198 199 char device_buffer[1024]; 200 char* device_path = static_cast<char*>(device_buffer); 201 size_t path_len; 202 io_status = fuchsia_io_DirectoryAdminGetDevicePath(caller.borrow_channel(), &status, 203 device_path, sizeof(device_buffer) - 1, 204 &path_len); 205 const char* path = nullptr; 206 if (io_status == ZX_OK) { 207 if (status == ZX_OK) { 208 device_buffer[path_len] = '\0'; 209 path = device_path; 210 } else if (!admin && status == ZX_ERR_ACCESS_DENIED) { 211 path = "Unknown; missing O_ADMIN"; 212 } 213 } 214 215 print_fs_type(dirs[i], &options, &info, path); 216 } 217 218 return 0; 219} 220