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