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