目录与(硬)链接
- 在 i-node 条目中,会将目录标记为一种不同的文件类型
- 目录是经过特殊组织而成的文件【本质上是一个表格,包含文件名和 i-node 编号】
i-node 表的编号始于 1,而非 0 ,若目录条目的 i-node 字段值为 0,则表明该条目尚未使用。i-node 1 用来记录文件系统的坏块。文件系统根目录 /
总是存储在 i-node 条目 2 中
1
2
3
4
5
6
7
8
9
|
echo -n 'abc, ' > abc
ls -li abc
ln abc xyz
echo 'xyz' >> xyz
cat abc
ls -li abc xyz
rm abc
ls -li xyz
|
The i-node entry and data blocks for the file are removed (deallocated) only when the i-node’s link count falls to 0.
the rm
command removes a filename from a directory list, decrements the link count of the corresponding i-node by 1, and, if the link count thereby falls to 0, deallocates the i-node and the data blocks to which it refers.
符号(软)链接
The pathname to which a symbolic link refers may be either absolute or relative. A relative symbolic link is interpreted relative to the location of the link itself.
Starting with kernel 2.6.18, Linux implements the SUSv3-specified minimum of 8 deferences.
Symbolic links in the directory part of a pathname (i.e., all of the components preceding the final slash) are always dereferenced.
The ownership and permissions of a symbolic link are ignored for most operations (symbolic links are always created with all permissions enabled).
创建和移除(硬)链接 link, unlink
The link()
and unlink()
system call create and remove hard links.
1
2
3
4
|
#include <unistd.h>
int link(const char* oldpath, const char *newpath);
int unlink(const char* pathname);
|
unlink
removes a link (deletes a filename) and, if that is the last link to the file, also removes the file itself. If the link specified in pathname doesn’t exist, then unlink
fails with the error ENOENT
Remove a link with unlink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"
#define CMD_SIZE 200
#define BUF_SIZE 1024
int main(int argc, char *argv[])
{
int fd, j, numBlocks;
char shellCmd[CMD_SIZE]; /* Command to be passed to system() */
char buf[BUF_SIZE]; /* Random bytes to write to file */
if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s temp-file [num-1kB-blocks] \n", argv[0]);
numBlocks = (argc > 2) ? getInt(argv[2], GN_GT_0, "num-1kB-blocks")
: 100000;
/* O_EXCL so that we ensure we create a new file */
fd = open(argv[1], O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd == -1)
errExit("open");
if (unlink(argv[1]) == -1) /* Remove filename */
errExit("unlink");
for (j = 0; j < numBlocks; j++) /* Write lots of junk to file */
if (write(fd, buf, BUF_SIZE) != BUF_SIZE)
fatal("partial/failed write");
snprintf(shellCmd, CMD_SIZE, "df -k `dirname %s`", argv[1]);
system(shellCmd); /* View space used in file system */
if (close(fd) == -1) /* File is now destroyed */
errExit("close");
printf("********** Closed file descriptor\n");
/* See the erratum for page 348 at http://man7.org/tlpi/errata/.
Depending on factors such as random scheduler decisions and the
size of the file created, the 'df' command executed by the second
system() call below does may not show a change in the amount
of disk space consumed, because the blocks of the closed file
have not yet been freed by the kernel. If this is the case,
then inserting a sleep(1) call here should be sufficient to
ensure that the the file blocks have been freed by the time
of the second 'df' command.
*/
system(shellCmd); /* Review space used in file system */
exit(EXIT_SUCCESS);
}
|
更改文件名 rename
The rename
system call can be used both to rename a file and to move it into another directory on the same file system.
1
2
3
|
#include <stdio.h>
int rename(const char *oldpath, const char* newpath);
|
The rename
call just manipulates directory entries; it doesn’t move file data.
- If newpath already exists, it is overwritten
- If newpath and oldpath refer to the same file, then no changes are made (and the call succeed).
- The
rename
system call doesn’t dereference symbolic links in either of its arguments
- If oldpath refers to a file other than a directory, then newpath can’t specify the pathname of a directory (the error is EISDIR)
- Specifying the name of a directory in oldpath allows us to rename that directory
- If oldpath is a directory, then newpath can’t contain a directory prefix that is the same as oldpath
- The files referred to by oldpath and newpath must be on the same file system.
使用符号链接 symlink, readlink
The symlink
system call creates a new symbolic link, to the pathname specified in filepath. (Use unlink
to remove a symbolic link)
1
2
3
|
#include <unistd.h>
int symlink(const char* filepath, const char *linkpath);
|
The readlink
system call places a copy of the symbolic link string in the character array pointed to by buffer.
1
2
3
|
#include <unistd.h>
ssize_t readlink(const char* pathname, char* buffer, size_t bufsiz);
|
创建和删除文件夹 mkdir & rmdir
The mkdir
system call creates a new directory. The rmdir
removes the directory specified in pathname, which may be an absolute or a relative pathname.
1
2
3
4
5
|
#include <unistd.h>
int mkdir(const char* pathname, mode_t mode);
int rmdir(const char* pathname); // the directory must be empty
|
移除一个文件或目录 remove
The remove
removes a file or an empty directory
1
2
3
|
#include <stdio.h>
int remove(const char* pathname);
|
It pathname is a file, remove
calls unlink
; if pathname is a directory, remove
calls rmdir
读目录 opendir, readdir
The opendir
opens a directory and returns a handle that can be used to refer to the directory in later calls.
1
2
3
4
|
#include <dirent.h>
DIR* opendir(const char* dirpath);
DIR* fopendir(int fd);
|
The readdir
read successive entries from a directory stream.
1
2
3
4
5
6
7
8
|
#include <dirent.h>
struct dirent {
ino_t d_ino; // file i-node number
char d_name[]; // Null-terminated name of file
}
struct dirent *readdir(DIR* dirp);
|
On end-of-directory or error, readdir
returns NULL, in the later case setting errno to indicate the error.
1
2
3
4
5
6
7
8
9
|
errno = 0;
direntp = readdir(dirp);
if (direntp == NULL) {
if (errno != 0) {
// handle error
} else {
// reach EOD
}
}
|
The rewinddir
moves the directory stream back to the beginning so that the next call to readdir
will begin again with the first file in the directory.
1
2
3
|
#include <dirent.h>
void rewinddir(DIR *dirp);
|
The closedir
closes the open directory stream referred to by dirp
, freeing the resources used by the stream.
1
2
3
|
#include <dirent.h>
int closedir(DIR* dirp);
|
The dirfd
returns the file descriptor associated with the directory stream referred to by dirp.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
#if defined(__APPLE__)
/* Darwin requires this header before including <dirent.h> */
#include <sys/types.h>
#endif
#include <dirent.h>
#include "tlpi_hdr.h"
static void /* List all files in directory 'dirpath' */ listFiles(const char *dirpath)
{
DIR *dirp;
struct dirent *dp;
Boolean isCurrent; /* True if 'dirpath' is "." */
isCurrent = strcmp(dirpath, ".") == 0;
dirp = opendir(dirpath);
if (dirp == NULL)
{
errMsg("opendir failed on '%s'", dirpath);
return;
}
/* For each entry in this directory, print directory + filename */
for (;;)
{
errno = 0; /* To distinguish error from end-of-directory */
dp = readdir(dirp);
if (dp == NULL)
break;
if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
continue; /* Skip . and .. */
if (!isCurrent)
printf("%s/", dirpath);
printf("%s\n", dp->d_name);
}
if (errno != 0)
errExit("readdir");
if (closedir(dirp) == -1)
errMsg("closedir");
}
int main(int argc, char *argv[])
{
if (argc > 1 && strcmp(argv[1], "--help") == 0)
usageErr("%s [dir-path...]\n", argv[0]);
if (argc == 1) /* No arguments - use current directory */
listFiles(".");
else
for (argv++; *argv; argv++)
listFiles(*argv);
exit(EXIT_SUCCESS);
}
|
文件树遍历 nftw
The nftw
allows a program to recursively walk through an entire directory subtree performing some operation (calling some user-defined function) for each file in the subtree.
1
2
3
4
5
6
|
#define _XOPEN_SOURCE 500
#include <ftw.h>
int nftw(const char* dirpath,
int (*func) (const char* pathname, const struct stat *statbuf, int typeflag, struct FTW *ftwbuf),
int nopenfd, int flags); // return 0 after successful walk of entir tree, or -1 on error, or the first non-zero value return by a call to _func_
|
进程的当前工作目录
A process’s current working directory defines the starting point for the resolution of relative pathnames referred to by the process. A new process inherits its current working directory from its parent.
A process can retrieve its current working directory by getcwd
1
2
3
|
#include <unistd.h>
char* getcwd(char *cwdbuf, size_t size);
|
The chdir
changes the calling process’s current working directory to the relative or absolute pathname specified in pathname(which is dereferenced if it is a symbolic link)
1
2
3
4
5
|
#include <unistd.h>
int chdir(const char* pathname);
#define _XOPEN_SOURCE 500
int fchdir(int fd);
|
改变进程的根目录 chroot
Every process has a root directory, which is the point from which absolute pathnames are interpreted.
The chroot
changes the process’s root directory to the directory specified by pathname.
1
2
3
4
|
#define _BSD_SOURCE
#include <unistd.h>
int chroot(const char *pathname);
|
解析路径名 realpath
The realpath
dereferences all symbolic links in pathname (a null-terminated string) and resolves all references to /. and /.. to produce a null-terminated string containing the corresponding absolute pathname.
1
2
3
|
#include <stdlib.h>
char* realpath(const char* pathname, char* resolved_path);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
#include <sys/stat.h>
#include <limits.h> /* For definition of PATH_MAX */
#include "tlpi_hdr.h"
#define BUF_SIZE PATH_MAX
int main(int argc, char *argv[])
{
struct stat statbuf;
char buf[BUF_SIZE];
ssize_t numBytes;
if (argc != 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s pathname\n", argv[0]);
/* User lstat() to check whether the supplied pathname is
a symbolic link. Alternatively, we could have checked to
whether readlink() failed with EINVAL. */
if (lstat(argv[1], &statbuf) == -1)
errExit("lstat");
if (!S_ISLNK(statbuf.st_mode))
fatal("%s is not a symbolic link", argv[1]);
numBytes = readlink(argv[1], buf, BUF_SIZE - 1);
if (numBytes == -1)
errExit("readlink");
buf[numBytes] = '\0'; /* Add terminating null byte */
printf("readlink: %s --> %s\n", argv[1], buf);
if (realpath(argv[1], buf) == NULL)
errExit("realpath");
printf("realpath: %s --> %s\n", argv[1], buf);
exit(EXIT_SUCCESS);
}
|
解析路径名字符串 dirname, basename
The dirname
and basename
break a pathname string into directory and filename parts.
1
2
3
4
|
#include <libgen.h>
char* dirname(char* pathname);
char* basename(char* pathname);
|
- Trailing slash characters in pathname are ignored
- If pathname doesn’t contain a slash, then
dirname
returns the string .(dot) and basename
returns pathname
- If pathname consists of just a slash, then both
dirname
and basename
return the string /.
- If pathname is a NULL pointer or an empty string, then both return the string .(dot).
Pathname string |
dirname |
basename |
/ |
/ |
/ |
/usr/bin/zip |
/usr/bin |
zip |
/etc/passwd//// |
/etc |
passwd |
/etc////passwd |
/etc/passwd |
|
etc/passwd |
etc |
passwd |
passwd |
. |
passwd |
passwd/ |
. |
passwd |
.. |
. |
.. |
NULL |
. |
. |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#include <libgen.h>
#include "tlpi_hdr.h"
int main(int argc, char *argv[])
{
char *t1, *t2;
int j;
for (j = 1; j < argc; j++)
{
t1 = strdup(argv[j]);
if (t1 == NULL)
errExit("strdup");
t2 = strdup(argv[j]);
if (t2 == NULL)
errExit("strdup");
printf("%s ==> %s + %s\n", argv[j], dirname(t1), basename(t2));
free(t1);
free(t2);
}
exit(EXIT_SUCCESS);
}
|