Computer Science 010

Lecture Notes 7

More Unix File System and

Working with Large C Programs

Unix File System

File Protection

Unix is a multi-user operating system, and you must log in to use Unix. One major reason for logging in is so that Unix can support file protection. File protection allows the creator of a file to decide who should be able to view a file, modify a file, or execute a file. Every file that you create is created with the following protections by default:

Viewing the file protection

To see how a file is currently protected, you list the file in long format using ls -l:

-> ls -l
total 30
-rwxr-xr-x  1 jcool    15000     27488 Jan 15 11:02 face*
-rw-r--r--  1 jcool    15000      2173 Jan 15 11:02 face.c

Let's dissect this output. The total line indicates how many kilobytes of disk the files use. Each subsequent line provides information about each file. The first chunk of text is the file protection information (more on this below). Next is the number of hard links to the file. This is usually 1. We wil not discuss hard links further. Next is the user id of the file's owner, then the group the user belongs to (more on this below). In this case the group name is a number (15000). The rest is the size of the file in bytes, the date and time that the file was last modified, and the name of the file.

File protection is divided into 3 categories: permissions for the owner, permissions for the members of the group, and permissions for all other users. What is the group for your files? As mentioned earlier there are 3 types of permission:

The file protection string consists of 10 characters and defines the permission for each category of user (owner, group memebers, others) as follows. The first character is - for normal files. It is d for directories. The remaining characters are 3 character sequences. The first 3 characters define the permissions for the owner of the file, the next 3 define the permissions for the group and the last 3 define the permissions for all other users. The characters are always in the same order:

File protection of directories

The meaning of protection is a little bit different for directories than for ordinary files. For directories, they are interpreted as:

Changing the file protection

Only the owner of a file may change the file protection. To change file protection use the chmod command. You need to tell chmod how to change the protection and which files to change. To specify the protection changes, you identify the class(es) of users:

Next you indicate if you want to add or remove permission:

Finally, you indicate which type of permission you are adding or removing, using r, w, and x. So, if you want to change your files so that nobody else can read or execute them, you would say:

-> chmod go-rx *

Now, if you viewed the protections, you would see:

-> ls -l
total 30
-rwx------  1 jcool    15000     27488 Jan 15 11:02 face*
-rw-------  1 jcool    15000      2173 Jan 15 11:02 face.c

See the man page for chmod for more details.

Symbolic Links

Sometimes it is convenient for one file to have more than one name. This is particularly useful if multiple users want to have the same file in their local directories, not a copy of the file. When the file is modified through any of its names, the modified version is immediately visible through all of its names. You currently have several symbolic links in your home directory. This is how sysadmin ensures that you all have a standard setup to your accounts initially. All the symbolically linked files happen to be files whose names begin with . . These do not show up when you list your files unless you use the -a argument:

-> ls -a
.Xauthority
.Xdefaults@
.bash_history
.bash_logout@
.bash_profile@
.bashrc@
.dt/
.emacs@
.forward
.mailrc@
.mh_profile@
.netscape/
.saves-10041-boran.cs.williams.edu~  <a bunch of these>
   .twmrc@
.xinitrc@
   <and all your regular files>

The files whose names end in @ are symbolic links. These files were created by sysadmin to make the standard configuration. To see where the real file lives, use the "ls -l" command:

-> ls -l .bashrc
lrwxrwxrwx   1 root    other     33 Aug 26  1998 .bashrc -> /home/cs-students/standard/bsd_login_files/.bashrc*

Note that the first letter of the protection string is l. This indicates that it is a symbolic link. Symbolic links always have all protections listed. The owner of the file was root. That is the login id for the system administrator. After the .bashrc file name is the name of the file that this points to. We can look at the permission of the file being pointed to:

-> ls -l /home/cs-students/standard/bsd_login_files/.bashrc
-rw-r--r--  1 root  daemon  2470 Nov  6 14:19 /home/cs-students/standard/bsd_login_files/.bashrc

The owner is root. Since you are not the owner, you can read the file, but you cannot modify it. This means that you cannot change .bashrc in place. You could delete the .bashrc file in your home directory. That just removes the name from your directory but does not delete the file that it is linked to. So, you could create your own .bashrc file but if the adminstrator updates the default version, you would not get those changes. Like the .emacs file, the default .bashrc file loads your private .local_bashrc file. In general, if you want to customize your .bashrc, you should modify your .local_bashrc and leave the .bashrc file alone.

If you want to create a symbolic link, you do that with the "ln -s" command:

-> ln -s face happy_sad_face

The order of the arguments is the same as for cp or mv. This creates a new file named happy_sad_face that is symbolically linked to face.

Working with Large C Programs

Header files

So far we have been working with very small C programs. The entire program fits into a single file. As programs grow, it becomes necessary to split them into multiple files. When you do this, you will want to decide which part of the file you want to make visible to other files and which you want to keep private. C does not have keywords like public and private as Java and C++ do. Instead, you decide which parts you want to be public and you put their declarations into a header file.

Header files end in .h. The filenames that we have been giving in our #include statements are the names of header files. Header files typically include declarations only. They may declare types using typedef, constants using #define, and function prototypes. The function definitions stay in the .c file. Only the prototypes go in the .h file. Now, if somebody wants to use the things in that .h file, they must #include the .h file. Note that the .c file where the function declarations appear must also #include the .h file. When we include header files provided by Unix, we enclose the filename in <>. When we include our own files, we put the filename in "":

#include <string.h>
#include "myfile.h"

Compiling and Linking Large Programs

When you want to build an executable program that requires more than one C file, it requires several Unix commands. First, you compile each C file to create object code for that file. Then you link together the object code into an executable program. For example, suppose we have 2 C files: file1.c and file2.c. We wish to create an executable named myprog. We would issue the following Unix commands:

-> gcc -g -Wall -c file1.c
-> gcc -g -Wall -c file2.c
-> gcc -o myprog file1.o file2.o

There are several important differences in the use of the gcc command. When you are compiling, you include a -c argument. You omit the -o argument. The compiler will create an object code file using the same name as your source file but ending in .o instead of .c. .o is the traditional extension for object code that represents only part of an executable program. The last gcc command generates the executable program given a complete list of the .o files that make up the object code for the program. Note that this last line does not list any .c files.

Errors when Linking

As many of you have learned, the most common error by far when linking is "Undefined symbol". This indicates that your program has a declaration for something, but not a definition. Most likely, you have a function prototype but not the function body. This can happen if you declare a prototype but forget to define it. It can also happen if you include a header file, but do not link in the object code that goes with that header file. The object code gets linked in in one of several ways: