CS360 Midterm -- October 18, 2000. Question 2:
Answer and grading
This is a straightforward recursive directory traversal.
You need to traverse each directory and do the following
things:
- Before traversing the directory, chmod the directory
so that you have rwx permission on it -- that way you
can delete files in the directory.
- For each regular file or soft link in the directory, delete it
with unlink or remove.
Again, before you make the unlink or remove call,
you should call chmod so that you have permission to delete
the file.
- For each directory that is not . or .., put the directory
on a list.
- When you are done traversing the directory, call closedir, then
traverse the list and make your recursive calls.
- When you are done with the recursive calls, call rmdir to
delete the directory itself.
As in the prsize lecture notes, you have to build file names
with strcat() or sprintf().
You also have to pay attention to order -- you cannot delete a directory
until all of its files are gone.
Finally, you must call lstat instead of stat, becuase you
want to delete symbolic links too. If you don't call lstat, your
code is going to fail on links to other people's directories.
Below is my code:
#include "dllist.h"
#include < stdio.h >
#include < sys/types.h >
#include < sys/stat.h >
#include < dirent.h >
#include < string.h >
bye_bye(char *s)
{
struct stat buf;
struct dirent *de;
DIR *d;
char *fns;
Dllist directories, tmp;
/* Allocate a string to hold all the file names */
fns = (char *) malloc(sizeof(char)*(strlen(s)+257));
if (fns == NULL) { perror("Malloc"); exit(1); }
/* Allocate a dlist to hold the directories */
directories = new_dllist();
/* Open the directory */
d = opendir(s);
if (d == NULL) { perror(s); exit(1); }
/* Traverse the directory and ignore . and ..
For each file, call chmod on it and then either remove it,
or put it on the dlist */
for (de = readdir(d); de != NULL; de = readdir(d)) {
if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) {
sprintf(fns, "%s/%s", s, de->d_name);
if (lstat(fns, &buf) != 0) {
perror(fns);
exit(1);
}
chmod(fns, 0700);
if (S_ISDIR(buf.st_mode)) {
dll_append(directories, new_jval_s(strdup(fns)));
} else {
remove(fns);
}
}
}
/* Close the directory, and make all of your recursive calls */
closedir(d);
dll_traverse(tmp, directories) {
bye_bye(tmp->val.s);
free(tmp->val.s);
}
/* Get rid of the dlist, and finally call remove on the directory */
free_dllist(directories);
free(fns);
remove(s);
}
/* The initial call calls chmod on /home/boss and then nukes it */
main()
{
char *firstdir = "/home/boss";
chmod(firstdir, 0700);
bye_bye(firstdir);
}
Grading
As always, the grading is broken into individual pieces, each worth
one point.
- You have a recursive subroutine, and a main() that
calls it on /home/boss: 1 point
- The rest pertains to the recursive subroutine. You allocate
a string big enough to hold "directory/file": 1 point
- You make the proper opendir call: 1 point
- You initialize your dlist: 1 point
- You have a directory traversal based on readdir: 1 point
- In the directory traversal, you test for "." and "..": 1 point
- You create the proper file name for each file: 1 point
- You call lstat on the file: 1 point
- You check for directories and treat them differently: 1 point
- You put directories on the list: 1 point
- You call chmod on regular files before calling remove/unlink:
1 point
- You call remove/unlink: 1 point
- You make the proper recursive calls: 1 point
- You call closedir: 1 point
- You free all memory that you allocate with malloc,
strdup, and the dlist routines.
- You call chmod on directories before trying to delete any files
in them: 1 point
- You do error checking on the opendir call: 1 point
- You do error checking on the lstat call: 1 point
And as usual, there are deductions:
- If your code doesn't have the right form, even though it has pieces
that somehow match the above, I will take off up to 8 points.
- If there is extraneous stuff that is detrimental to your code,
I will take off up to 2 points.
- If the syntax is really, really bad, I will take off up to two
points.
- Some of you created a list, but made recursive calls during
the directory traversal -- this was a 1 point deduction, since,
if you are going through the trouble of creating a list, you
may as well move the recursion outside of the closedir statement
(as in the prsize lecture notes).
- Some you you created a nice name using strcat or
sprintf, and then went ahead and used de->d_name
anyway. That was another deduction typically denoted
``Inconsistent use of the filename vs the path name.''