-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwalk.c
More file actions
230 lines (195 loc) · 6.88 KB
/
walk.c
File metadata and controls
230 lines (195 loc) · 6.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/* dirStat.c
This program computes directory statistics. The usage is:
dirStat [-r] [<directory>],
where -r enables recursive statistics and <directory> is the directory
on which the statistics are computed (if not specified then cwd is used);
The following statistics are computed:
number of regular files/directories encountered and the amount of space
they use
total space & number of files in the tree (without counting the same file
twice if it has more than one link to it)
total space linked outside the directory structure
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
/* this is the typedef for the linked list */
/* we need a linked list in order to keep track of used inodes */
typedef struct node {
off_t size;
nlink_t links; /* no. of hard links -- we decrease it as we encounter
other files with the same inode */
ino_t inode; /* inode */
struct node *next; /* pointer to next node in the linked list */
} iinfo;
/* This function prints the appropriate error message depending on its
argument; it then stops the program returning code 1 */
void error(int eno) {
switch(eno) {
case 1:
printf("Incorrect parameters.\n");
printf("Usage: dirStat [-r] [<directory>]\n");
break;
case 2:
printf("Incorrect number of parameters.\n");
printf("Usage: dirStat [-r] [<directory>]\n");
break;
case 3:
perror("Memory allocation failed");
break;
}
exit(1);
}
/* Strips the slashes at the end of the string passed as an argument; */
/* we use this for "aesthetic" reasons; although /usr/bin///ls */
/* is correct, strictly speaking, /usr/bin/ls is much nicer */
void strip_trailing_slashes(char *path) {
char *last = path + strlen(path) - 1;
while ( last >= path && *last == '/')
*last-- = '\0';
}
/* this function returns true, or false, depending on whether "inode" is
already in the linked list or not; if it is, we decrease the "links"
field; after ending the directory traversal, "links" should be 1 for
every inode, if there are no "outside" links */
int inlist(ino_t inode, iinfo *In) {
iinfo *tmp;
tmp = In;
while (tmp != NULL) {
if ( tmp->inode == inode ) {
(tmp->links)--;
return(1);
}
tmp=tmp->next;
}
return 0;
}
/* this function adds a node with the information contained in "size",
"links" and "inode" to the linked list */
void addtolist(off_t size, nlink_t links, ino_t inode, iinfo **In) {
iinfo *tmp;
tmp=(iinfo *) malloc(sizeof(iinfo));
tmp->size=size;
tmp->links=links;
tmp->inode=inode;
tmp->next=*In;
*In=tmp;
}
/* this is the recursive traversal function - the most important part of the
program; the arguments are the "path" (directory) we're currently
processing, whether we should do this "rec"ursively or not (-r parameter
was given), total number of dirs, and of file links processed (these two
are transmitted by reference because we need their value in the main
function, and we don't want to use any global variables), and finally
a pointer to the inode list (to the first element in the linked list) */
int traverse(char *path, int rec, int *dirs, int *links, iinfo **In) {
DIR *cdir;
struct dirent *dirp;
char *filepath;
struct stat buf;
int length, filelinks=0, subdirs=0;
long int fspace=0, sbdspace=0;
/* opening the directory */
if ( (cdir = opendir(path)) == NULL ) {
perror(path);
return 1;
}
/* for every file in the directory */
while ( (dirp = readdir(cdir) ) != NULL) {
strip_trailing_slashes(path);
/* the next block puts in the "filepath" variable the path to the
currently processed file */
length = strlen(path) + strlen(dirp->d_name) + 2;
if ( ( filepath=(char *) malloc(sizeof(char)*length) ) == NULL ) error(3);
*filepath='\0';
strcat( strcat( strcat(filepath, path), "/"), dirp->d_name);
if ( lstat(filepath, &buf) == -1) {
perror(filepath);
continue;
}
/* processing the file appropriately depending of whether it is a regular
file or a directory; otherwise just ignore it */
if (S_ISREG(buf.st_mode)) {
filelinks++;
fspace+=buf.st_size;
/* if the inode for the current file is not already in the linked list
then add it */
if (!inlist(buf.st_ino, *In))
addtolist(buf.st_size, buf.st_nlink, buf.st_ino, In);
} else
if (S_ISDIR(buf.st_mode) && strcmp(dirp->d_name, ".") &&
strcmp( dirp->d_name, "..") ) {
subdirs++;
sbdspace+=buf.st_size;
/* if the file is a directory and recursive traversal is enabled
just call the function recursively for this subdirectory */
if (rec) traverse(filepath, 1, dirs, links, In);
}
free(filepath);
}
closedir(cdir);
*links+=filelinks;
(*dirs)++;
/* print statistics for current directory */
printf("Directory: %s\n", path);
printf(" Total file links: %d\n", filelinks);
printf(" Total file space: %ld\n", fspace);
printf(" Total sub-directories: %d\n", subdirs);
printf(" Total sub-directory file space: %ld\n", sbdspace);
return 0;
}
int main(int argc, char *argv[]) {
int rec, files=0, links=0, dirs=0, fouts=0;
long int filespc=0, spcouts=0;
iinfo *tmp, *In=NULL;
char *path;
/* depending on the arguments given to the program, call the traversal
function with the appropriate path and recursively or not */
switch(argc) {
case 1:
path="."; rec=0;
break;
case 2:
if ( !strcmp(argv[1], "-r") ) {
path="."; rec=1;
} else {
path=argv[1]; rec=0;
}
break;
case 3:
if ( strcmp(argv[1], "-r") ) error(1);
else {
path=argv[2]; rec=1;
}
break;
default: error(2);
}
/* print the total statistics */
if (!traverse(path, rec, &dirs, &links, &In)) {
printf("Total directories encountered: %d\n", dirs);
printf("Total file links: %d\n", links);
/* this is the tricky part: the actual number of files (and the disk
space they use) is counted using the linked list of inodes; if one
of the files has "links" > 1, it means there are actually some hard
links to the file outside the directory tree we examined */
tmp = In;
while (tmp != NULL) {
files++;
filespc+=tmp->size;
if (tmp->links > 1 ) {
spcouts+=tmp->size;
fouts++;
}
tmp = tmp->next;
}
printf("Total files: %d\n", files);
printf("Total file space: %ld\n", filespc);
printf("Files linked outside directory structure: %d\n", fouts);
printf("File Space linked outside directory structure: %ld\n", spcouts);
}
return 0;
}