Individual Report
FILE SYSTEM EXERCISE REPORT
An Assignment for Operating System module
in the University of Central England
By: Harry Sufehmi, student id #98799231
INTRODUCTION
In this assignment, we are required to do a file system
exercise in a Unix system. Because I can't go to the
University very often, I use my existing computer running
Redhat Linux operating system to develop the program. Also
doing this assignment from my own computer gives me easy
access to the Internet, to find references I needed quickly.
Most Unix flavour are compatible to each other. Most
notably is the ones who adheres to the POSIX standard, it
will be compatible down to the system calls. RedHat Linux is
a POSIX-compliant Unix OS. It's a pleasant experience to
develop a program on it. Documentation and examples are very
easy to find, since I could use other Unix's
documentation/examples too - especially the other Linux
variants, System V and FreeBSD. No wonder Informix was able
to port their database engine by just doing a recompile.
System calls are provided by the kernel itself. Calling
a system call activates a part of the kernel. The equivalent
of system call in MS-DOS is perhaps the interrupt routines.
In Windows95, this perhaps would be the WindowsAPI
(Application Programming Interface).
Surprisingly, the number of system call available in
Linux is not much. Only about 64 of them. But they indeed
covers a lot of ground.
We will now continue to the discussion about the
program itself.
DISCUSSION
I designed my program to be modular. To accomplish
this, I dedicate a routine to every task. This way, it would
be easier to maintain and to debug.
At first, the code was very messy. But along the time I
found out better ways to do it in Unix. The final result is
a code that's clean and short.
I tried to make the source code to be as readable as
possible. I fully use indentations, especially in an if.else
statement. A lot of blanks/empty lines inserted, so the
actual program is even shorter than it looks. No fancy C
programming tricks, so even a beginner should find it easy
to read the source code.
The menu for the program is very easy. I tried to make
it as clean as possible, so user wouldn't get the
overwhelmed feeling. The menu is self-explanatory, so I
don't have to put further explanation for that in the
interface. Command given by entering a single key only,
concerns are given to functionality instead of eyecandy
features.
DEBUGGING LOG
Here is the debugging log, copied directly from the
source-code file.
/*
Revision History
Revision History
>>>> Some problem with gets(), it always makes core dump. Strange.
= I reverted to scanf(), but must not forget to do fflush(stdin) after that.
Also, scanf() won't accept *char variables it would core dump,
it must be defined as char[].
>>>> I can't find the equivalent of getchar(0) [press any key] from Borland C
in Unix.... I tried scanf("%*"), and getc() with no avail.
-> must use scanf("%s", var), so the user must enter an alphabet and
press Enter to continue. Not a clean solution. But still, it's a solution.
= Trying to figure out how to show access restriction info for the file,
...turns out the double-byte used to store the information is also used
to store other informations.
by the way; the field that store the information is named st_mode
in the stat struct, defined in .
Trying to do bitwise left/right-shift operation, but that doesn't seem to work.
-> the information is showed "as if",
copied directly from st_mode with no modification.
= Finally able to print the access restriction info,
by doing bitwise AND operation to st_mode with 0777.
The result is then printed with printf() using %o format specifier,
which prints it in octal notation:
First digit represent user's access rights,
- 4 for read access, 2 for write access, 1 for execute access, 7 for all.
Second digit represent user group's access rights,
- 4 for read access, 2 for write access, 1 for execute access, 7 for all.
Third digit represent other user's access rights,
- 4 for read access, 2 for write access, 1 for execute access, 7 for all.
So for a file to have all access opened for everybody,
the access restriction info would be printed as 777
>>>> Got core dump everytime I tried to create and remove directory.
But the directory is created/deleted already. Don't know why.
Will try to investigate further.
= Oops.
Core dumps in creating/deleting directory (above) is caused by this line:
printf("Directory %s has been successfully DELETED");
I forgot to put the variable to be printed in that line.
= Changed the method of clearing keyboard buffer,
from fflush(stdin) to while((temp=getchar()) != '\n')
It's a little bit more reliable now.
(code originated from Chris Noble's sample program)
= Finally found the (almost) equivalent of getchar(0) (in Borland C -> press any key)
in Unix,..... it's getchar().
= Finally found out how to list files in directory,
by using opendir, readdir and closedir function.
>>> The list_files() function always produces core-dump after printing
the last file in the directory. I think though, I know the culprit.
= Nailed that bug at last.
I just have to check whether the dir_ent pointer points to a valid
directory entries struct, by inserting code "if (dir_ent)" et al.
Now the list_files() functions correctly, even when given "." input
With "extra" feature: it will also listed _all_ hidden files
= After a long time rampaging through books in library and countless man
pages, finally find out how to change permission for future files -
use umask().
The new umask entered will be turning off the permission bits from 0666 octal.
So specifying 22 octal will actually turning off the second bits
(0666 & ~0022), so the actual result will be 0644 octal.
Find that out by hanging in IRC channel #unix.
>>> Working on the move_files() function. Due to constraint of time,
I have to cancel a feature that I'd like to see - creation of target directory
even if its parent haven't existed yet. Like you want to move files to
/home/my_dir/source/target/, but /home/my_dir/source/ have not existed yet.
The directory creation routine in this program will only work if the
/home/my_dir/source/ directory already existed.
Coding this feature is easy, but I wouldn't have time to _debug_ it,
and it's been awhile since the last time I did a recursive routine
(about... 5 years??) so it'd be easy to make a lot of (silly) mistakes.
Also due to time constraint, I'm forced to skip quite some error-checking.
I do only the ones that I expected to happen the most.
(for example, nothing about not enough file handle in the target dir,
circular symbolic link, etc)
A lot of time spend to learn the possibility of pitfalls in this operation
mv.c is a good source to learn it.
>>> rename() won't accept wildcards. Now looking into possible solutions.
= The solution to the problem above is even easier that I thought....
Just give the name of the directory, and rename() automatically will move
the entire directory. No need to append "*.*" to the end of the source_directory
Got it working now.
Example; set the source directory to be /home/guest/test/
and the target directory to be /home/test/
If the current directory is specified to be moved,
its content would be copied immediately,
but the directory itself won't be deleted _until_ you change to other directory
Make sense, but I was actually tried to delete the source (current) directory
myself because I thought Unix won't delete it (since we're in it),
only to be confused to find out that the directory is not there anymore
when I did "ls -l" to that directory again from another directory. :-)
>>> Funny that umask() seems to be in effect only within the program.
When I quit from the program, the umask goes back to its previous value
(22 octal).
>>> Got error message everytime trying to chmod a file,
it ALWAYS says "No such file or directory".
= Find out the cause.
Just eliminate the "&" in the scanf(), and it's gone.
Found by using parameter "-Wall" with gcc compiler,
as suggested by Nicholas Riley.
*/
IMPLEMENTATION
The source-code of the program is provided in the
diskette enclosed with this report. But for convenience,
it's also included in this report below.
/*
menu.c - menu-driven, file-system access program
An assignment for Operating System module for MSc System Design (Software Design) course at University of Central England
Author : Harry Sufehmi
Student-id : 98799231
Acknowledgements:
- Some code inspired by an example from file-system access programming
given in Operating System course tutorial
- Thanks to Nicholas Riley ([email protected]),
for his assistance in troubleshooting the problem with chmod()
- Thanks to WhiteDragon in #unix channel in the IRC,
for his idea and assistance with umask()
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
/* Global variables definition */
struct stat fileinfo; /* Holds file information structure */
char temp; /* Used to clear buffers */
int main(void)
{
char selection;
extern errno, sys_nerr;
system("clear");
do
{
printf("\n\n File Manager menu \n");
printf(" ================= \n\n");
printf("[a] Display file information \n");
printf("[b] Change existing file permission \n");
printf("[c] Change file permission for the future \n");
printf("[d] Rename file \n");
printf("[e] Make new directory \n");
printf("[f] Delete a directory \n");
printf("[g] Print current directory \n");
printf("[h] Change the current directory \n");
printf("[i] List files \n");
printf("[j] Move all files to another directory \n");
printf("[x] eXit \n\n");
printf("Please select a menu from above: ");
scanf("%c", &selection ); while((temp=getchar()) != '\n');
switch (selection)
{
case 'a': display_fileinfo(); break;
case 'b': change_attribute(); break;
case 'c': change_umask(); break;
case 'd': rename_file(); break;
case 'e': make_dir(); break;
case 'f': remove_dir(); break;
case 'g': print_current_dir(); break;
case 'h': change_dir(); break;
case 'i': list_files(); break;
case 'j': move_file(); break;
case 'x': printf("\n\n\nProgram finished by user.\n\n"); break;
}
} while (selection != 'x');
return 0;
}
/* ----------------------------------------------------------------*/
int display_fileinfo(void)
{ char name[250];
printf("\n\n Please enter the filename : ");
scanf("%s", &name); while((temp=getchar()) != '\n');
printf(" ########## %s ###########\n", name);
if (stat(name, &fileinfo) == -1)
{
system("clear");
perror("Problem with file"); }
else {
printf("\n File type : %u", fileinfo.st_rdev);
printf("\n Access permissions : %o", fileinfo.st_mode & 0777);
printf("\n Size : %d", fileinfo.st_size);
printf("\n Owner's user-id : %d", fileinfo.st_uid);
printf("\n Owner's group-id : %d", fileinfo.st_gid);
printf("\n Last access time : %s", ctime(&fileinfo.st_atime));
}
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
int change_attribute(void)
{ char name[250];
uint attrib;
printf("\n\n Please enter the filename : ");
scanf("%s", name); while((temp=getchar()) != '\n');
printf(" ########## %s ###########\n", name);
if (stat(name, &fileinfo) == -1)
{
perror("Problem with file"); }
else {
printf("\n Current access permission : %o", fileinfo.st_mode & 0777);
printf("\n Enter new access permission: ");
scanf ("%o", &attrib); while((temp=getchar()) != '\n');
if (chmod(name, attrib) == -1)
{
perror("Problem while trying to change file's attribute");
}
else {
printf("\n\n File's attribute have been successfully changed");
}
}
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
int change_umask(void)
{ int new_umask, old_umask;
printf("\n Enter new umask (in octal): ");
scanf ("%o", &new_umask); while((temp=getchar()) != '\n');
old_umask = umask(new_umask);
printf("\n Old umask : %o", old_umask);
printf("\n\n File's umask have been successfully changed to %o", new_umask);
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
int rename_file(void)
{ char old_name[250], new_name[250];
printf("\n\n Please enter the filename : ");
scanf("%s", &old_name); while((temp=getchar()) != '\n');
printf(" ########## %s ###########\n", old_name);
printf("\n Enter new filename : ");
scanf ("%s", &new_name); while((temp=getchar()) != '\n');
if (rename(old_name, new_name) == -1)
{
perror("Problem while trying to change file's name");
}
else {
printf("File's name have been successfully changed");
}
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
int make_dir(void)
{ char name[250];
ushort attrib = 0777;
printf("\n\n Please enter the directory's name : ");
scanf("%s", &name); while((temp=getchar()) != '\n');
if (mkdir(name, attrib) == -1)
{
perror("Problem while trying to create directory");
}
else {
printf("Directory %s has been successfully created", name);
}
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
int remove_dir(void)
{ char name[250];
printf("\n\n Please enter the directory's name to be DELETED: ");
scanf("%s", &name); while((temp=getchar()) != '\n');
if (rmdir(name) == -1)
{
perror("Problem while trying to remove directory");
}
else {
printf("Directory %s has been successfully DELETED", name);
}
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
int print_current_dir(void)
{ char name[250];
if (getwd(name) == NULL)
{
perror("Problem while trying to view current directory");
}
else {
printf("Currently you're in directory %s", name);
}
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
int change_dir(void)
{ char name[250];
printf("\n\n Please enter the directory name to become the current dir: ");
scanf("%s", &name); while((temp=getchar()) != '\n');
if (chdir(name) == -1)
{
perror("Problem while trying to change directory");
}
else {
printf("Changing directory succesful");
}
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
int list_files(void)
{ char name[250];
DIR * dir_ptr;
struct dirent * dir_ent;
if (getwd(name) == NULL)
{
perror("Problem while trying to view current directory");
}
else {
printf("Currently you're in directory %s", name);
}
printf("\n\n Please enter the directory's name to be listed: ");
scanf("%s", &name); while((temp=getchar()) != '\n');
dir_ptr = opendir(name);
if (dir_ptr == NULL)
{
perror("Problem while trying to list directory");
}
do {
dir_ent = readdir(dir_ptr);
if (dir_ent)
{
printf("%s\n",dir_ent->d_name);
};
} while (dir_ent);
closedir(dir_ptr);
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
int move_file(void)
{ char source_name[250], target_name[250];
int critical_error; int dmode;
printf("\n\n Please enter the source directory : ");
gets(source_name);
// if user entered nothing, assume the current directory as the source
if (strlen(source_name) == 0) getwd(source_name);
printf(" %s chosen.", source_name);
printf("\n\n Please enter the target directory : ");
scanf("%s", &target_name); while((temp=getchar()) != '\n');
critical_error = 0;
dmode = S_IRWXU | S_IRWXG | S_IRWXO;
if (mkdir(target_name, dmode) == -1)
{
critical_error = 1;
switch (errno) {
case ENOENT: printf(" Target is not a valid directory. \n Its parent directory doesn't exist yet \n Please create its parent directory first.");
break;
case EEXIST: critical_error = 0; /* good, the directory is there already */
break;
}
}
else {
printf(" Target directory has been successfully created...\n");
}
if (critical_error == 0)
{
if (rename(source_name, target_name) == -1)
{
perror(" Problem while trying to move files");
}
else {
printf(" Files moved successfully.");
}
}
printf("\n Press Enter...");
getchar();
}
/* ----------------------------------------------------------------*/
GUIDE FOR THE PROGRAM
There are two version of the program available in the
floppy disk. The first named "menu-sun", it's the Sun
Solaris version. And the other is "menu-linux", the Linux
version of the program. To run either programs, just type
"./menu-sun" or "./menu-linux" in the shell prompt.
After the program is executed, the user will be
presented with the following menus:
File Manager menu
=================
[a] Display file information
[b] Change existing file permission
[c] Change file permission for the future
[d] Rename file
[e] Make new directory
[f] Delete a directory
[g] Print current directory
[h] Change the current directory
[i] List files
[j] Move all files to another directory
[x] eXit
Please select a menu from above:
Selection of one of the menu above is done by pressing
a key which is printed inside the bracket "[ ]".
A. Display file information
This menu will display a file's information. When this menu
is chosen, it will ask the name of the file to be
investigated. Enter a filename. If the file is in another
directory, also enter full path to the file. Then it will
print the type of the file, access permission, size, owner's
user-id, owner's group-id, and last access time.
In the following example, I enter "a.out" as the name of the
file. Here is the output of the menu:
Please enter the filename : a.out
########## a.out ###########
File type : 0
Access permissions : 755
Size : 13993
Owner's user-id : 0
Owner's group-id : 0
Last access time : Fri Jan 1 23:01:42 1999
Press Enter...
B. Change existing file permission
Enables you to change a file's permission. You'll just need
to enter the filename (with
complete path, if located in another directory). The program
will show the file's current
access permission. Enter a new access permission, and it
will be applied immediately.
Please enter the filename : menu.BAK
########## menu.BAK ###########
Current access permission : 744
Enter new access permission: 777
File's attribute have been successfully changed
Press Enter...
C. Change file permission for the future
This menu modifies umask, a parameter that contains access
permission that will be given
for new files. But the umask value set from this menu will
last only for as long as the
program runs. When the user quits the program, umask will be
set back to default again by
the shell.
Enter new umask (in octal): 777
Old umask : 22
File's umask have been successfully changed to 777
Press Enter...
D. Rename file
You may change a file's name using this menu. Just enter the
filename, and then enter the
new name.
One extra feature, it is also able to move the file too to
another directory. Enter the
filename, and then enter the target directory and the new
name.
The example provided below:
Please enter the filename : core
########## core ###########
Enter new filename : /root/coredump
File's name have been successfully changed
Press Enter...
In this example, the file /home/guest/core was moved to
/root/coredump
E. Make new directory
Very straightforward, just enter a new directory name, and
it will be created for you.
Please enter the directory's name : /root/mine
Directory /root/mine has been successfully created
Press Enter...
F. Delete a directory
Again, you'll just need to enter the name of the directory
that you'd like to delete, and it will
be done.
Please enter the directory's name to be DELETED: /root/mine
Directory /root/mine has been successfully DELETED
Press Enter...
G. Print current directory
This menu will print the name of the directory you're
currently on. Same result as the "pwd"
command in the Unix shell.
Currently you're in directory /home/guest
Press Enter...
H. Change the current directory
Will change the current directory
I. List files
This menu will print a list of a directory's contents.
It will also accept special directory entries, such as "."
and ".."
And, it will also prints out all of the hidden file names.
Currently you're in directory /home/guest
Please enter the directory's name to be listed: ..
.
..
ftp
httpd
samba
hari
guest
rc5
Press Enter...
The example above print the list for the /home directory.
J. Move all files to another directory
You could move the entire directory with this command. If no
name is supplied, it will
assume the current directory, which it will print first to
confirm you. Then you enter the (new)
target directory.
After it have these inputs, it will create the new directory
and move the files from the
previous directory to the new directory.
Please enter the source directory : /home/guest/temp/
/home/guest/temp/ chosen.
Please enter the target directory : /root/temp/
Target directory has been successfully created...
Files moved successfully.
Press Enter...
K. Exit
Quits from the program
MISCELLANEOUS
Program coded in WindowsNT environment using UltraEdit
software, needs the extra flexibility of a GUI-based editor
software while doing the coding.
The source-code itself is stored in a Linux server running
Samba daemon, to provide Windows95-like workgroup sharing
capability.
A guest account was setup to enable WindowsNT to connect and
access the menu.c file in the Linux server. Remote access to
the Linux box is achieved by setting up a telnet server, and
then telnet to it.
Compiled using gcc (GNU C compiler) included with Linux.
Also along the time when doing the programming of this
assignment, I come to even more appreciate Unix's standard.
A lot of ideas for the solutions come from source-codes from
different flavours of Unix (including FreeBSD and
Yggdrasil), yet actually no modification is required to make
it work in my Linux system.
REFERENCE LIST
1. Boyd, Dr. Michael (1997), SunOS File Management - tutorial for
Operating System course, University of Central England
2. Ritchie, Colin (1995), Operating Systems,
DP Publications
3. RedHat Linux 5.0 man pages, RedHat, Inc
4. Parker, Mike (1991), mv.c - source code for mv executable file,
Free Software Foundation, Inc
5. ls.c file - source code for ls executable program,
author unknown
___________________________________________________________
Last modified: 27 February 1999