• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* df.c - report free disk space.
2  *
3  * Copyright 2006 Rob Landley <rob@landley.net>
4  *
5  * See http://opengroup.org/onlinepubs/9699919799/utilities/df.html
6 
7 USE_DF(NEWTOY(df, "HPkhit*a[-HPh]", TOYFLAG_SBIN))
8 
9 config DF
10   bool "df"
11   default y
12   help
13     usage: df [-HPkhi] [-t type] [FILE...]
14 
15     The "disk free" command shows total/used/available disk space for
16     each filesystem listed on the command line, or all currently mounted
17     filesystems.
18 
19     -a	Show all (including /proc and friends)
20     -P	The SUSv3 "Pedantic" option
21     -k	Sets units back to 1024 bytes (the default without -P)
22     -h	Human readable (K=1024)
23     -H	Human readable (k=1000)
24     -i	Show inodes instead of blocks
25     -t type	Display only filesystems of this type
26 
27     Pedantic provides a slightly less useful output format dictated by POSIX,
28     and sets the units to 512 bytes instead of the default 1024 bytes.
29 */
30 
31 #define FOR_df
32 #include "toys.h"
33 
GLOBALS(struct arg_list * t;int units,width[6];)34 GLOBALS(
35   struct arg_list *t;
36 
37   int units, width[6];
38 )
39 
40 static void measure_columns(char *s[])
41 {
42   int i;
43 
44   for (i = 0; i<5; i++) TT.width[i] = maxof(TT.width[i], strlen(s[i]));
45 }
46 
print_columns(char ** dsuapm)47 static void print_columns(char **dsuapm)
48 {
49   int i;
50 
51   for (i = 0; i<6; i++) printf(!i ? "%-*s" : " %*s", TT.width[i], dsuapm[i]);
52   xputc('\n');
53 }
54 
print_header()55 static void print_header()
56 {
57   char *dsuapm[] = {"Filesystem", "Size", "Used", "Avail", "Use%","Mounted on"};
58 
59   // The filesystem column is always at least this wide.
60   TT.width[0] = maxof(TT.width[0], 14+(FLAG(H)||FLAG(h)));
61 
62   if (FLAG(i)) memcpy(dsuapm+1, (char *[]){"Inodes", "IUsed", "IFree", "IUse%"},
63                       sizeof(char *)*4);
64   else {
65     if (!(FLAG(H)||FLAG(h))) {
66       dsuapm[1] = TT.units == 512 ? "512-blocks" :
67         FLAG(P) ? "1024-blocks" : "1K-blocks";
68       dsuapm[3] = "Available";
69       if (FLAG(P)) dsuapm[4] = "Capacity";
70     }
71   }
72 
73   measure_columns(dsuapm);
74   TT.width[5] = -1;
75   print_columns(dsuapm);
76 }
77 
show_mt(struct mtab_list * mt,int measuring)78 static void show_mt(struct mtab_list *mt, int measuring)
79 {
80   unsigned long long suap[4], block = 1, ll;
81   char *dsuapm[6]; // device, size, used, avail, percent, mount
82   int i;
83 
84   // If we don't have -a, skip overmounted and synthetic filesystems.
85   if (!mt || (!FLAG(a) && (!mt->stat.st_dev || !mt->statvfs.f_blocks))) return;
86 
87   // If we have -t, skip other filesystem types
88   if (TT.t) {
89     struct arg_list *al;
90 
91     for (al = TT.t; al; al = al->next) if (!strcmp(mt->type, al->arg)) break;
92 
93     if (!al) return;
94   }
95 
96   // Prepare filesystem display fields
97   *dsuapm = *mt->device == '/' ? xabspath(mt->device, 0) : 0;
98   if (!*dsuapm) *dsuapm = mt->device;
99   if (!mt->stat.st_dev) for (i = 1; i<5; i++) dsuapm[i] = "-";
100   else {
101     if (FLAG(i)) {
102       suap[0] = mt->statvfs.f_files;
103       suap[1] = mt->statvfs.f_files - mt->statvfs.f_ffree;
104       suap[2] = getuid() ? mt->statvfs.f_favail : mt->statvfs.f_ffree;
105     } else {
106       block = maxof(mt->statvfs.f_frsize, 1);
107       suap[0] = mt->statvfs.f_blocks;
108       suap[1] = mt->statvfs.f_blocks - mt->statvfs.f_bfree;
109       suap[2] = getuid() ? mt->statvfs.f_bavail : mt->statvfs.f_bfree;
110     }
111 
112     // Scale and convert to strings
113     dsuapm[1] = toybuf;
114     for (i = 0; i<3; i++) {
115       suap[i] = (block*suap[i])/TT.units;
116 
117       if (FLAG(H)||FLAG(h))
118         human_readable(dsuapm[i+1], suap[i], FLAG(H) ? HR_1000 : 0);
119       else sprintf(dsuapm[i+1], "%llu", suap[i]);
120       dsuapm[i+2] = strchr(dsuapm[i+1], 0)+1;
121     }
122 
123     // percent
124     if ((suap[3] = ll = suap[1]+suap[2])) {
125       suap[3] = (block = suap[1]*100)/ll;
126       if (block != suap[3]*ll) suap[3]++;
127     }
128     sprintf(dsuapm[4], "%llu%%", suap[3]);
129   }
130   dsuapm[5] = mt->dir;
131 
132   if (measuring) measure_columns(dsuapm);
133   else print_columns(dsuapm);
134 
135   if (*dsuapm != mt->device) free(*dsuapm);
136 }
137 
df_main(void)138 void df_main(void)
139 {
140   struct mtab_list *mt, *mtstart, *mtend, *mt2, *mt3;
141   int measuring;
142   char **next;
143 
144   // Units are 512 bytes if you select "pedantic" without "kilobytes".
145   if (FLAG(H)||FLAG(h)||FLAG(i)) TT.units = 1;
146   else TT.units = FLAG(P) && !FLAG(k) ? 512 : 1024;
147 
148   if (!(mtstart = xgetmountlist(0))) return;
149   mtend = dlist_terminate(mtstart);
150 
151   // If we have a list of filesystems on the command line, loop through them.
152   if (*toys.optargs) {
153     // Measure the names then output the table.
154     for (measuring = 1;;) {
155       for (next = toys.optargs; *next; next++) {
156         struct stat st;
157 
158         // Stat it (complain if we can't).
159         if (stat(*next, &st)) {
160           if (!measuring) perror_msg("'%s'", *next);
161         } else {
162           // Find and display this filesystem.  Use _last_ hit in case of
163           // overmounts (which is first hit in the reversed list).
164           for (mt = mtend, mt2 = 0; mt; mt = mt->prev) {
165             if (!mt2 && st.st_dev == mt->stat.st_dev) mt2 = mt;
166             if (st.st_rdev && (st.st_rdev == mt->stat.st_dev)) break;
167           }
168           show_mt(mt ? : mt2, measuring);
169         }
170       }
171       if (!measuring--) break;
172       print_header();
173     }
174   } else {
175     // Loop through mount list to filter out overmounts.
176     for (mt = mtend; mt; mt = mt->prev) {
177       for (mt3 = mt, mt2 = mt->prev; mt2; mt2 = mt2->prev) {
178         if (mt->stat.st_dev == mt2->stat.st_dev) {
179           // For --bind mounts, show earliest mount
180           if (!strcmp(mt->device, mt2->device)) {
181             mt3->stat.st_dev = 0;
182             mt3 = mt2;
183           } else mt2->stat.st_dev = 0;
184         }
185       }
186     }
187 
188     // Measure the names then output the table (in filesystem creation order).
189     for (measuring = 1;;) {
190       for (mt = mtstart; mt; mt = mt->next) show_mt(mt, measuring);
191       if (!measuring--) break;
192       print_header();
193     }
194   }
195 
196   if (CFG_TOYBOX_FREE) llist_traverse(mtstart, free);
197 }
198