Files and Directories 2009 E. Im 1
How to write the ls program?
Is this a directory?
Permissions for the owner
Permissions for the members of the owner group group
Symbolic links ($ln -s RigidBodyWin.h RR) group
Contents File attributes handling stat Unix file system structure and symbolic links Directory operations
stat, fstat, and lstat #include <sys/stat.h> int stat(const char *pathname, struct stat *buf); int fstat(int filedes, struct stat *buf); int lstat(const char *pathname, struct stat *buf); stat/fstat returns a structure of information about the named file. lstat returns information about the symbolic link, not th e file referenced by the symbolic link.
stat, fstat, and lstat struct stat { mode_t st_mode; /* file type & mode (permission) */ ino_t st_ino; /* i-node number (serial number) */ dev_t st_dev; /* device number (file system) */ dev_t st_rdev; /* device number for special files */ nlink_t st_nlink; /* number of links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ off_t st_size; /* size in bytes, for regular files */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last file status change */ long st_blksize;/* best I/O block size */ long st_blocks; /* no. of disk blocks allocated */ };
File Size st_size of the stat structure: file size in bytes Regular file Directory a multiple of a number such as 16 or 512 Symbolic link the actual number of bytes in the filename For example, lrwxrwxrwx 1 root 7 Sep 25 07:14 lib -> usr/lib st_blksize: the preferred block size for I/O st_blocks: the actual number of 512-byte blocks that are allocated
File Truncation #include <unistd.h> int truncate(const char *pathname, off_t length); int ftruncate(int filedes, off_t length); They truncate an existing file to length bytes.
File Types Encoded in the st_mode member of the stat structure Regular file Directory file pairs of (file name, pointer to information on the file) Character special file, e.g. tty Block special file, e.g. disk devices FIFO named pipe Socket used for network communication between processes Symbolic link A type of file pointing to another file Figure 4.3
Sample output from Figure 4.3 $./a.out /etc/passwd /etc /dev/initctl /dev/log /dev/tty /dev/scsi/host0/bus0/target0/lun0/cd /dev/cdrom /etc/passwd: regular /etc: directory /dev/initctl: fifo /dev/log: socket /dev/tty: character special /dev/scsi/host0/bus0/target0/lun0/cd: block special /dev/cdrom: symbolic link
#include "apue.h int main(int argc, char *argv[]) { int i; struct stat buf; char *ptr; for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (lstat(argv[i], &buf) < 0) { err_ret("lstat error"); continue; } if (S_ISREG(buf.st_mode)) ptr = "regular"; else if (S_ISDIR(buf.st_mode)) ptr = "directory"; else if (S_ISCHR(buf.st_mode)) ptr = "character special"; else if (S_ISBLK(buf.st_mode)) ptr = "block special"; else if (S_ISFIFO(buf.st_mode)) ptr = "fifo"; else if (S_ISLNK(buf.st_mode)) ptr = "symbolic link"; else if (S_ISSOCK(buf.st_mode)) ptr = "socket"; else ptr = "** unknown mode **"; printf("%s\n", ptr); } Figure 4.3 exit(0); }
#include "apue.h int main(int argc, char *argv[]) { int i; struct stat buf; char *ptr; for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (lstat(argv[i], &buf) < 0) { err_ret("lstat error"); continue; } if (S_ISREG(buf.st_mode)) ptr = "regular"; else if (S_ISDIR(buf.st_mode)) ptr = "directory"; else if (S_ISCHR(buf.st_mode)) ptr = "character special"; else if (S_ISBLK(buf.st_mode)) ptr = "block special"; else if (S_ISFIFO(buf.st_mode)) ptr = "fifo"; else if (S_ISLNK(buf.st_mode)) ptr = "symbolic link"; else if (S_ISSOCK(buf.st_mode)) ptr = "socket"; else ptr = "** unknown mode **"; printf("%s\n", ptr); } Figure 4.3 exit(0); }
File Access Permissions The nine file access permission bits from <sys/stat.h> st_mode mask S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH S_IWOTH S_IXOTH Meaning $ ls l foo bar -rwxr-xr-x 1 stevens 0 Nov 16 16:23 bar -rw-r--r-- 1 stevens 0 Nov 16 16:23 foo chmod(1), e.g. chmod g+w bar User-read User-write User-execute Group-read Group-write Group-execute Other-read Other-write Other-execute
File Access Permissions Directory File R to obtain a list of all the file names in the dir. W to create/delete a file in the directory, both X and W are necessary. X - to pass through the directory comprising a pathname (e.g., execute permission in /, /usr, and /usr/include to op en /usr/include/stdio.h), also called search bit. R O_RDONLY and O_RDWR for the open function W O_WRONLY, O_RDWR, O_TRUNC X exec functions Q: To create a new file in a directory, which permission(s) do we need to have? Q: To delete an existing file in a dir, which permission(s)?
User IDs of a process User IDs and group IDs associated with each process real user ID, real group ID (st_uid & st_gid) Owner of the cat program is root Real user ID of the cat process is taesoo who cannot access /etc/shadow
Set-User-ID and Set-Group-ID User IDs and group IDs associated with each process real user ID, real group ID (st_uid & st_gid) effective user ID, effective group ID, supplementary group IDs saved set-user-id, saved set-group-id set-user-id bit and set-group-id bit in st_mode When this file is executed, set the effective user/group ID of the process to be the owner/group of the file. As an example, passwd(1) is a set-user-id program. passwd command is for changing my password
File Access Permissions user ID and group ID of the file effective user ID, effective group ID, and supplementary group IDs of the process 1. if the effective user ID is 0, 2. if the effective user ID equals the user ID of the file, 3. if the effective group ID equals the group ID of the file, 4. if the appropriate other access permission bit is set,. Q: what if permissions of a.out are as follows?: -rw-rwx-r-x a.out Hint: OR condition
Ownership of New Files and Directories The user ID of a new file is set to the effective user ID of the process. The group ID of a new file is set to one of the followings The effective group ID of the process The group ID of the containing directory FreeBSD, Mac OS X 10.3 set-group-id bit (Linux, Solaris)
access Function #include <unistd.h> int access(const char *pathname, int mode); mode: R_OK, W_OK, X_OK, and F_OK Accessibility test based on the real user ID/group ID to verify that the real user can access the given file. not the effective user ID of a process Figure 4.8
Figure 4.8 #include "apue.h #include <fcntl.h> int main(int argc, char *argv[]) { if (argc!= 2) err_quit("usage: a.out <pathname>"); if (access(argv[1], R_OK) < 0) err_ret("access error for %s", argv[1]); else printf("read access OK\n"); if (open(argv[1], O_RDONLY) < 0) err_ret("open error for %s", argv[1]); else printf("open for reading OK\n"); } exit(0);
access Function $ ls l a.out -rwxrwxr-w 1 sar 15945 Nov 30 12:10 a.out $./a.out a.out read access OK open for reading OK $ ls l /etc/shadow -r-------- 1 root 1315 Jul 17 2002 /etc/shadow $./a.out /etc/shadow access error for /etc/shadow: Permission denied open error for /etc/shadow: Permission denied $ su become superuser Passwd: enter superuser password $ chown root a.out change file s user ID to root $ chmod u+s a.out and turn on set-user-id bit $ ls l a.out check owner and SUID bit -rwsrwxr-x 1 root 15945 Nov 30 12:10 a.out $ exit go back to normal user $./a.out /etc/shadow access error for /etc/shadow: Permission denied open for reading OK
Changing file permissions Using umask at creation time Using chmod after the file is created
umask command 000 = 00 ( ) 001 = 01 ( X) 010 = 02 ( W ) 011 = 03 ( WX) 100 = 04 (R ) 101 = 05 (R X) 110 = 06 (RW ) 111 = 07 (RWX) Touch command ignores x flag
umask Function #include <sys/stat.h> mode_t umask(mode_t cmask); It sets the file mode creation mask for the process an d returns the previous value. Any bits that are on in the file mode creation mask ar e turned off in the file s mode. Figure 4.9 $ umask first print the current file mode creation mask 002 $ a.out $ ls l foo bar -rw------- 1 sar 0 Nov 16 16:23 bar -rw-rw-rw- 1 sar 0 Nov 16 16:23 foo $ umask see if the file mode creation mask changed 002
Figure 4.9 #include "apue.h #include <fcntl.h> #define RWRWRW (S_IRUSR S_IWUSR S_IRGRP S_IWGRP S_IROTH S_IWOT H) int main(void) { umask(0); if (creat("foo", RWRWRW) < 0) err_sys("creat error for foo"); umask(s_irgrp S_IWGRP S_IROTH S_IWOTH); if (creat("bar", RWRWRW) < 0) err_sys("creat error for bar"); } exit(0);
chmod and fchmod Functions #include <sys/stat.h> int chmod(const char *pathname, mode_t mode); int fchmod(int filedes, mode_t mode); Figure 4.12 $ ls l foo bar -rw------- 1 sar bar -rw-rw-rw- 1 sar foo $ a.out $ ls l foo bar??? mode S_ISUID S_ISGID S_ISVTX S_IRWXU S_IRUSR S_IWUSR S_IXUSR S_IRWXG S_IRGRP S_IWGRP S_IXGRP S_IRWXO S_IROTH S_IWOTH S_IXOTH Description set-user-id on execution set-group-id on execution saved-text (sticky bit) read, write, and execute by user (owner) read by user (owner) write by user (owner) execute by user (owner) read, write, and execute by group read by group write by group execute by group read, write, and execute by other (world) read by other (world) write by other (world) execute by other (world)
chmod command 000 = 00 ( ) 001 = 01 ( X) 010 = 02 ( W ) 011 = 03 ( WX) 100 = 04 (R ) 101 = 05 (R X) 110 = 06 (RW ) 111 = 07 (RWX)
Figure 4.12 #include "apue.h int main(void) { struct stat statbuf; /* turn on set-group-id and turn off group-execut e */ if (stat("foo", &statbuf) < 0) err_sys("stat error for foo"); if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) S_ISGID) < 0) err_sys("chmod error for foo"); if (chmod("bar", S_IRUSR S_IWUSR S_IRGRP S_IROTH) < 0) err_sys("chmod error for bar"); } exit(0);
Figure 4.12 #include "apue.h int main(void) { struct stat statbuf; /* turn on set-group-id and turn off group-execut e */ if (stat("foo", &statbuf) < 0) err_sys("stat error for foo"); if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) S_ISGID) < 0) err_sys("chmod error for foo"); if (chmod("bar", 0644) < 0) err_sys("chmod error for bar"); } exit(0);
Sticky Bit S_ISVTX: sticky bit = saved text bit File is used to save the program s text in the swap area to spe ed up memory loading the next time set only by the superuser (in FreeBSD, Mac OS X, Solaris. but not in Linux) Directory e.g. /tmp and /var/spool/uucppublic A file in the directory can be removed or renamed only if t he user has write permission for the directory, AND either owns the file, owns the directory, or is the superuser
Changing file ownership using chown, fchown, and lchown #include <unistd.h> int chown(const char *pathname, uid_t owner, gid_t group); int fchown(int filedes, uid_t owner, gid_t group); int lchown(const char *pathname, uid_t owner, gid_t group); To change the user/group ID of a file. lchwon changes the owners of the symbolic link its elf.
chown, fchown, and lchown If _POSIX_CHOWN_RESTRICTED is in effect, Only a superuser process can change the user ID of the fil e; A nonsuperuser process can change the group ID of the fi le if the process owns the file (the owner equals to the user ID of the file), and the group equals either the effective group ID of the proc ess or one of the process s supplementary group IDs. You can t change the user ID of other users files You can change the group ID of files that you own, but o nly to groups that you belong to.
File Systems Various implementations of the UNIX file system UFS disk drive partition partition partition file system cylinder group 0 cylinder group 1 cylinder group n boot block(s) super block super block copy cg info i-node map block bitmap i-nodes data blocks i-nodei-node i-node
File Systems i-node contains info about the file, including file type, access permission, ref-count, size, ptrs to data blocks, and so on. Only two items (filename and i-node no.) are stored in the dir entry. i-node array data block second data block data block first data block third data block directory blocks and data blocks directory block data block directory block i-nodei-node i-node i-node i-node number filename i-node number filename
File Systems A link count in an i-node = the number of directory entries that point to the i- node st_nlink in the stat structure Hard links vs. soft links Symbolic links (soft links) The actual content of the file (the data blocks) contains the filename that the symbolic link points to. lrwxrwxrwx 1 root 7 Sep 25 07:14 lib->usr/lib No directory entry pointing to an i-node in a different file system. After $ mkdir testdir i-list array directory blocks and data blocks data block directory block data block directory block i-node 0 i-node 1267 i-node 2549 2549. 1267.. 1267. i-node number.. 2549 testdir
Link count of directory ~/test
Link count of file ~/test/a (hard links)
link, unlink, remove, and rename F unctions #include <unistd.h> int link(const char *existingpath, const char *newpath); Creates a new dir entry that references the existing path (, w hich increments the link count.) Both pathnames must be on the same file system (although P OSIX.1 supports linking across file systems.) Only a superuser can create a link to a directory. #include <unistd.h> int unlink(const char *pathname); Removes the dir entry and decrements the link count (the file is deleted, when it reaches 0). If a symbolic link, unlink references the symbolic link itself.
link, unlink, remove, and rename F unctions #include <stdio.h> int remove(const char *pathname); For a file, identical to unlink and, for a directory, to rmdir #include <stdio.h> int rename(const char *oldname, const char *newname);
Program 4.16 #include "apue.h #include <fcntl.h> int main(void) { if (open("tempfile", O_RDWR) < 0) err_sys("open error"); if (unlink("tempfile") < 0) err_sys("unlink error"); printf("file unlinked\n"); sleep(15); printf("done\n"); } exit(0);
link, unlink, remove, and rename F unctions Program 4.16 $ ls l tempfile -rw-r----- 1 sar 413265408 Jan 21 07:14 tempfile $ df /home Filesystem 1K-blocks Used Available Use% Mounted on /dev/hda4 11021440 1956332 9056108 18% /home $./a.out & 1364 $ file unlinked ls l tempfile ls: tempfile: No such file or directory $ df /home Filesystem 1K-blocks Used Available Use% Mounted on /dev/hda4 11021440 1956332 9056108 18% /home $ done df /home Filesystem 1K-blocks Used Available Use% Mounted on /dev/hda4 11021440 1552352 9469088 15% /home
Symbolic links ($ln -s RigidBodyWin.h RR) group
Symbolic Links To get around the limitation of hard links Linking across file systems A hard link to a directory (only by superuser) foo $ mkdir foo $ touch foo/a $ ln s../foo foo/testdir $ ls l foo total 0 a testdir -rw-rw-r-- 1 sar 0 Dec 6 06:06 a lrwxrwxrwx 1 sar 6 Dec 6 06:06 testdir->../foo
symlink and readlink Functions #include <unistd.h> int symlink(const char *actualpath, const char *sympath); A new dir entry, sympath, is created that points to a ctual path. #include <unistd.h> ssize_t readlink(const char *pathname, char *buf, size_ t bufsize); open follows a symbolic link, while readlink ope ns the link itself and reads the name in the link. Equivalent to the actions of open, read, and cl ose.
File times group
File Times Field Description Example ls(1) option st_atime st_mtime st_ctime Last access time of file data Last modification time of file data Last change time of i-node status read write chmod, chown -u default -c The modification time is when the file contents were last modified. The changed-status time indicates when the i-node was last modified, e.g., changing the file access permission, the user ID, the number of links, etc. The three times for a file/directory and its parent directory For example, creating a new file affects the containing dir, and it af fects the i-node for the new file. (Figure 4.20)
utime Function #include <sys/types.h> #include <utime.h> int utime(const char *pathname, const struct utimbuf *times); struct utimbuf { time_t actime; /* access time */ time_t modtime; /* modification time */ } The utime changes the access/modification time of a file. If times is NULL, set to current time. Effective UID must equal the real ID of the file, or write permissio n for the file. Otherwise, set to values pointed by times. Effective UID must equal the real ID of the file, or superuser privil ege Program 4.21
#include "apue.h #include <fcntl.h> #include <utime.h> int main(int argc, char *argv[]) { int i, fd; struct stat statbuf; struct utimbuf timebuf; Program 4.21 } for (i = 1; i < argc; i++) { if (stat(argv[i], &statbuf) < 0) { /* fetch current times */ err_ret("%s: stat error", argv[i]); continue; } if ((fd = open(argv[i], O_RDWR O_TRUNC)) < 0) { /* truncate */ err_ret("%s: open error", argv[i]); continue; } close(fd); timebuf.actime = statbuf.st_atime; timebuf.modtime = statbuf.st_mtime; if (utime(argv[i], &timebuf) < 0) {/* reset times */ err_ret("%s: utime error", argv[i]); continue; } } exit(0);
mkdir and rmdir Functions #include <sys/stat.h> int mkdir(const char *pathname, mode_t mode); The mode is modified by the umask of the proce ss. The user ID and group ID of the new directory. #include <unistd.h> int rmdir(const char *pathname); If the link count of the dir becomes 0, and no other process has the dir open, then the space occupied by the dir is freed.
Reading directories group
Reading Directories #include <dirent.h> DIR *opendir(const char *pathname); struct dirent *readdir(dir *dp); void rewinddir(dir *dp); int closedir(dir *dp); long telldir(dir *dp); void seekdir(dir *dp, long loc); dp = opendir(fullpath); while ((dirp = readdir(dp))!= NULL) printf( %s\n,dirp->d_name); closedir(dp); struct dirent { ino_t d_ino; /* i-node number */ char d_name[name_max + 1]; /* null-terminated fname */ } Only the kernel can write to a directory. Write and execute permission to create/delete f iles Program 4.22
#include "apue.h #include <dirent.h> #include <limits.h>/* function type that is called for each filename */ typedefint Myfunc(const char *, const struct stat *, int); static Myfunc myfunc; static int myftw(char *, Myfunc *); static int dopath(myfunc *); static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot; int main(int argc, char *argv[]) { int ret; if (argc!= 2) err_quit("usage: ftw <starting-pathname>"); ret = myftw(argv[1], myfunc); /* does it all */ ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock; if (ntot == 0) ntot = 1; /* avoid divide by 0; print 0 for all counts */ printf("regular files = %7ld, %5.2f %%\n", nreg, nreg*100.0/ntot); printf("directories = %7ld, %5.2f %%\n", ndir, ndir*100.0/ntot); printf("block special = %7ld, %5.2f %%\n", nblk, nblk*100.0/ntot); printf("char special = %7ld, %5.2f %%\n", nchr, nchr*100.0/ntot); printf("fifos = %7ld, %5.2f %%\n", nfifo, nfifo*100.0/ntot); printf("symbolic links = %7ld, %5.2f %%\n", nslink, nslink*100.0/ntot); printf("sockets = %7ld, %5.2f %%\n", nsock, nsock*100.0/ntot); exit(ret); } Program 4.22
Program 4.22 /* * Descend through the hierarchy, starting at "pathname". * The caller's func() is called for every file. */ #define FTW_F 1 /* file other than directory */ #define FTW_D 2 /* directory */ #define FTW_DNR 3 /* directory that can't be read */ #define FTW_NS4 /* file that we can't stat */ static char *fullpath; /* contains full pathname for every file */ static int /* we return whatever func() returns */ myftw(char *pathname, Myfunc *func) { int len; fullpath = path_alloc(&len); /* malloc's for PATH_MAX+1 bytes */ /* ({Prog pathalloc}) */ strncpy(fullpath, pathname, len);/* protect against */ fullpath[len-1] = 0; /* buffer overrun */ } return(dopath(func));
Program 4.22 /* * Descend through the hierarchy, starting at "fullpath". If "fullpath" is anything other than a directory, we lstat() i t, * call func(), and return. For a directory, we call ourself recursively for each name in the directory. */ static int /* we return whatever func() returns */ dopath(myfunc* func) { struct stat statbuf; struct dirent *dirp; DIR *dp; int ret; char *ptr; if (lstat(fullpath, &statbuf) < 0) /* stat error */ return(func(fullpath, &statbuf, FTW_NS)); if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */ return(func(fullpath, &statbuf, FTW_F)); /* It's a directory. First call func() for the directory, * then process each filename in the directory. */ if ((ret = func(fullpath, &statbuf, FTW_D))!= 0) return(ret); ptr = fullpath + strlen(fullpath); /* point to end of fullpath */ *ptr++ = '/ ; *ptr = 0; if ((dp = opendir(fullpath)) == NULL) /* can't read directory */ return(func(fullpath, &statbuf, FTW_DNR)); while ((dirp = readdir(dp))!= NULL) { if (strcmp(dirp->d_name, ".") == 0 strcmp(dirp->d_name, "..") == 0) continue; /* ignore dot and dot-dot */ strcpy(ptr, dirp->d_name); /* append name after slash */ if ((ret = dopath(func))!= 0) /* recursive */ break; /* time to leave */ } ptr[-1] = 0; /* erase everything from slash onwards */ if (closedir(dp) < 0) err_ret("can't close directory %s", fullpath); return(ret); }
Program 4.22 static int myfunc(const char *pathname, const struct stat *statptr, int type) { switch (type) { case FTW_F: switch (statptr->st_mode & S_IFMT) { case S_IFREG: nreg++; break; case S_IFBLK: nblk++; break; case S_IFCHR: nchr++; break; case S_IFIFO: nfifo++; break; case S_IFLNK: nslink++; break; case S_IFSOCK: nsock++; break; case S_IFDIR: err_dump("for S_IFDIR for %s", pathname); /* directories should have type = FTW_D */ } break; case FTW_D: ndir++; break; case FTW_DNR: err_ret("can't read directory %s", pathname); break; case FTW_NS: err_ret("stat error for %s", pathname); break; default: err_dump("unknown type %d for pathname %s", type, pathname); } return(0); }
chdir, fchdir, and getcwd #include <unistd.h> int chdir(const char *pathname); int fchdir(int filedes); char *getcwd(char *buf, size_t size); Correspond to cd and pwd
Special Device Files Every file system is known by its major/minor devic e numbers stored in a dev_t object. major and minor macros to access 8/8, 8/24, 14/ 18, or 32/32 major/minor bits. The st_dev is the dev no. of the file system conta ining the file. The st_rdev contains the dev no. of the characte r/block special files. Program 4.25
Program 4.25 #include "apue.h #ifdef SOLARIS #include <sys/mkdev.h> #endif int main(int argc, char *argv[]) { int i; struct stat buf; for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (stat(argv[i], &buf) < 0) { err_ret("stat error"); continue; } printf("dev = %d/%d", major(buf.st_dev), minor(buf.st_dev)); if (S_ISCHR(buf.st_mode) S_ISBLK(buf.st_mode)) { printf(" (%s) rdev = %d/%d, (S_ISCHR(buf.st_mode))? "character" : "block", major(buf.st_rdev), minor(buf.st_rdev)); } printf("\n"); } exit(0); }