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