标签:
四个stat函数:返回文件或者目录的信息结构:
#include<sys/stat.h>
int stat(const char * restrict pathname, struct stat*restrict buf);
int fstat(int fd, struct stat* buf);
int lstat(const char* restrict pathname,struct stat *restrict buf);
int fstatat(int fd,const char*restrict pathname,struct stat*restrict buf,int flag);
参数:
pathname:文件或者目录的名字buf:存放信息结构的缓冲区fd:打开的文件描述符 fstat,该文件就是待查看信息的文件fstatat,该文件是并不是待查看信息的文件。待查看信息的文件时已该fd对于的目录相对路径定位的flag:控制着fstatat函数是否跟随一个符号链接。对于fstatat函数:
fd和pathname共同决定的。 pathname是个绝对路径,则忽略fd参数pathname是个相对路径路径,且 fd=AT_FDCWD,则在当前工作目录的路径下查找pathnamepathname是个相对路径路径,且 fd!=AT_FDCWD,则在fd对应的打开目录下查找pathnameflag:控制着fstatat函数是否跟随一个符号链接。当!AT_SYMLINK_FOLLOW标志被设置时,查看的是pathname(如果它是个符号链接)本身的信息;否则默认查看的是pathname(如果它是个符号链接)链接引用的文件的信息。 返回值:
注意:
lstat类似于stat,但是当pathname是个符号链接时,lstat查看的是该符号链接的有关信息;而stat是查看该符号链接引用的文件的信息。ubuntu 16.04上,虽然有 AT_SYMLINK_NOFOLLOW这个常量,但是不支持。必须用 !AT_SYMLINK_FOLLOW。其常量定义为: AT_SYMLINK_FOLLOW: 1024 (有效)!AT_SYMLINK_FOLLOW: 0(有效)AT_SYMLINK_NOFOLLOW: 256(无效)AT_SYMLINK_FOLLOW: -1025(无效)stat数据结构:其定义可能与具体操作系统相关,但是基本形式为:
struct stat{
mode_t st_mode; //文件权限和类型信息
ino_t st_ino; //i-node 号
dev_t st_dev; // 设备号
dev_t st_rdev; // 特殊文件的设备号
nlink_t st_nlink; // 硬链接数量
uid_t st_uid; // owner 的用户ID
gid_t st_gid; // owner 的组ID
off_t st_size; //对普通文件,它是文件字节大小
struct timespec st_atime; // 上次访问时间
struct timespec st_mtile; // 上次修改时间
struct timespec st_ctime; // 上次文件状态改变的时间
blksize_t st_blksize; // 最佳的 I/O block 大小
blkcnt_t st_blocks; //分配的磁盘块数量
}
其中timespec结构与具体操作系统相关,但是至少包括下面两个字段:
struct timespec{
time_t tv_sec; // 秒
long tv_nsec; //纳秒
}UNIX 文件类型:
系统的所有设备,要么是字符特殊文件,要么是块特殊文件
FIFO:这种类型的文件用于进程间通信,有时也称为命名管道stat.st_mode成员中,可以用下列的宏测试文件类型:
S_ISREG():测试是否普通文件S_ISDIR():测试是否目录文件S_ISCHR():测试是否字符特殊文件S_ISBLK():测试是否块特殊文件S_ISFIFO():测试是否FIFOS_ISLNK():测试是否符号链接文件S_ISSOCK():测试是否套接字另外 POSIX.1 允许将进程间通信对象说明为文件。但是下面的宏测试的不是stat.st_mode,而是stat*(stat指针):
S_TYPEISMQ():测试是否消息队列S_TYPEISSEM():测试是否信号量S_TYPEISSHM():测试是否共享存储对象与一个进程有关的ID有很多:
exec函数保存每个文件都有一个所有者和组所有者,分别有 stat.st_uid和stat.st_gid指定。当一个文件时可执行文件时,如果执行这个文件,那么进程的有效用户ID就是实际用户ID,有效组ID就是实际组ID,除了下面的情况:
stat.st_mode中设置了一个特殊标志:设置用户ID位时,则将进程的有效用户ID设置为文件所有者的用户IDstat.st_mode中设置了一个特殊标志:设置组ID位时,则将进程的有效组ID设置为文件所有者的组ID 任何进程都是由可执行文件被执行而得到。因此位于磁盘上的可执行文件的所属的用户ID和组ID会影响到进程的用户ID和组ID
root,且该文件的设置用户ID位已经被设置,那么无论谁执行这个可执行文件时,该可执行文件产生的进程就具有超级用户权限。
设置用户ID位、设置组ID位 都包含在`stat.st_mode`中,可以通过下列两个宏测试:
S_ISUID():测试是否设置了设置用户ID位文件访问权限:所有文件类型(包括目录,字符特别文件等)都有访问权限。每个文件都有9个访问权限位:
S_IRUSR:用户读S_IWUSR:用户写S_IXUSR:用户执行S_IRGRP:组读S_IWGRP:组写S_IXGRP:组执行S_IROTH:其他读S_IWOTH:其他写S_IXOTH:其他执行访问权限规则:
pathname打开任何一个类型的文件时,对pathname中包含的每一个目录,包括pathname可能隐含的当前工作目录都应该具有执行权限 因此目录的执行权限位也称之为搜索位
open函数中对一个文件指定了O_TRUNC标志,则必须对该文件具有写权限exec函数中的任何一个执行某个文件,则必须对该文件具有执行权限,且该文件必须是个普通文件只要有一个权限通过,则不再进行测试。若所有权限都不通过,则不允许访问。
对一个目录的读权限和可执行权限是不同的:
当一个进程通过open或者creat创建一个新文件时:
具体选择哪个,由具体操作系统决定
示例
#include <stdio.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
typedef struct stat Stat;
void test_file_type(Stat* file_stat,const char* file_name)
{
printf("\t%s type is:",file_name);
if(S_ISREG(file_stat->st_mode))
printf(" regular file, ") ;
if(S_ISDIR(file_stat->st_mode))
printf("directory file ") ;
if(S_ISCHR(file_stat->st_mode))
printf("char file, ") ;
if(S_ISBLK(file_stat->st_mode))
printf(" block file, ") ;
if(S_ISFIFO(file_stat->st_mode))
printf("fifo file, ") ;
if(S_ISLNK(file_stat->st_mode))
printf("ink file, ") ;
if(S_ISSOCK(file_stat->st_mode))
printf("socket, ") ;
if(S_TYPEISMQ(file_stat))
printf("message queue file, ") ;
if(S_TYPEISSEM(file_stat))
printf("semaphore, ") ;
if(S_TYPEISSHM(file_stat))
printf("share memory, ") ;
printf("\n");
}
void test_file_id(Stat* file_stat,const char* file_name)
{
printf("\t%s file id:");
printf("owner id < %d >, ",file_stat->st_uid);
printf("group id < %d >, ",file_stat->st_gid);
if(S_ISUID&file_stat->st_mode)
printf("set_user_id, ");
if(S_ISGID&file_stat->st_mode)
printf("set_group_id , ");
printf("\n");
}
void test_file_permission(Stat* file_stat,const char* file_name)
{
printf("\t%s file permission:");
if(S_IRUSR&file_stat->st_mode)
printf("user read, ");
if(S_IWUSR&file_stat->st_mode)
printf("user write, ");
if(S_IXUSR&file_stat->st_mode)
printf("user exec, ");
if(S_IRGRP&file_stat->st_mode)
printf("group read, ");
if(S_IWGRP&file_stat->st_mode)
printf("group write, ");
if(S_IXGRP&file_stat->st_mode)
printf("group exec, ");
if(S_IROTH&file_stat->st_mode)
printf("other read, ");
if(S_IWOTH&file_stat->st_mode)
printf("other write, ");
if(S_IXOTH&file_stat->st_mode)
printf("other exec, ");
printf("\n");
}
int main(int argc, char *argv[])
{
Stat stat_buf;
int fd;
printf("Test regular file:\n");
stat("/home/huaxz1986/main.c",&stat_buf);
test_file_type(&stat_buf,"/home/huaxz1986/main.c");
test_file_id(&stat_buf,"/home/huaxz1986/main.c");
test_file_permission(&stat_buf,"/home/huaxz1986/main.c");
printf("Test directory file:\n");
stat("/home/huaxz1986/APUE",&stat_buf);
test_file_type(&stat_buf,"/home/huaxz1986/APUE");
test_file_id(&stat_buf,"/home/huaxz1986/APUE");
test_file_permission(&stat_buf,"/home/huaxz1986/APUE");
printf("Test block file:\n");
stat("/dev/loop0",&stat_buf);
test_file_type(&stat_buf,"/dev/loop0E");
test_file_id(&stat_buf,"/dev/loop0");
test_file_permission(&stat_buf,"/dev/loop0");
printf("Test char file:\n");
stat("/dev/mem",&stat_buf);
test_file_type(&stat_buf,"/dev/mem");
test_file_id(&stat_buf,"/dev/mem");
test_file_permission(&stat_buf,"/dev/mem");
printf("Test link file:\n");
stat("/dev/cdrom",&stat_buf);
test_file_type(&stat_buf,"/dev/cdrom");
test_file_id(&stat_buf,"/dev/cdrom");
test_file_permission(&stat_buf,"/dev/cdrom");
printf("Test fifo file:\n");
stat("/run/systemd/initctl/fifo",&stat_buf);
test_file_type(&stat_buf,"/run/systemd/initctl/fifo");
test_file_id(&stat_buf,"/run/systemd/initctl/fifo");
test_file_permission(&stat_buf,"/run/systemd/initctl/fifo");
printf("Create new file:\n");
fd=openat(AT_FDCWD,"test",O_WRONLY|O_CREAT,S_IRUSR);
fstat(fd,&stat_buf);
test_file_type(&stat_buf,"./test");
test_file_id(&stat_buf,"./test");
test_file_permission(&stat_buf,"./test");
return 0;
}
当用open()函数打开一个文件时,内核根据进程的有效用户ID和有效组ID为依据来执行访问权限测试。但是如果你想测试进程的实际用户ID和实际组ID是否能够通过权限测试时,可以用下列两个函数:
#include<unistd.h>
int access(const char *pathname,int mode);
int faccess(int fd,const char*pathname,int mode,int flag);
参数:
pathname:文件路径名mode:指定要测试的模式。 mode设为F_OKR_OK:测试读权限W_OK:测试写权限X_OK:测试执行权限对于 faccess函数:
fd:一个打开目录文件的描述符,或者AT_FDCWDpathname: fd参数fd指定。 fd=AT_FDCWD,则表示相对于当前工作目录fd对于的打开的目录flag:如果是AT_EACCESS,则访问检查使用进程的有效用户ID和有效组ID,而不是实际用户ID和实际组ID返回值:
文件模式创建屏蔽字:当进程创建一个新的目录或者文件时,会使用文件模式创建屏蔽字。在文件模式创建屏蔽字中为1的位,在文件mode中的相应位一定被关闭。设置进程的文件模式创建屏蔽字的函数为:
#include<sys/stat.h>
mode_t umask(mode_t cmask);
cmask:要设置的新的文件模式创建屏蔽字如果你在通过creat或者open函数指定了mode,那么该mode必须通过文件模式创建屏蔽字的屏蔽之后才是最终新创建的文件的权限模式。
shell 有一个
umask命令。我们可以通过该命令来设置或者打印当前的文件模式创建屏蔽字
示例
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>
typedef struct stat Stat;
void test_access(const char*pathname,int mode)
{
int ok;
printf("\tAccess %s:",pathname);
ok=access(pathname,mode);
if(ok==-1)
{
printf("failed! Beause %s \n",strerror(errno));
}
else
{
printf("ok!\n");
}
}
void test_umask(mode_t cmask)
{
mode_t old_mode;
old_mode=umask(cmask);
printf("\t old mask is:");
print_mode(old_mode);
printf("\t current mask is:");
print_mode(cmask);
}
void print_mode(mode_t expected_mode)
{
if(S_IRUSR&expected_mode)
printf("U_RD, ");
if(S_IWUSR&expected_mode)
printf("U_WR, ");
if(S_IXUSR&expected_mode)
printf("U_EXC, ");
if(S_IRGRP&expected_mode)
printf("G_RD, ");
if(S_IWGRP&expected_mode)
printf("G_WR, ");
if(S_IXGRP&expected_mode)
printf("G_EXC, ");
if(S_IROTH&expected_mode)
printf("O_RD, ");
if(S_IWOTH&expected_mode)
printf("O_WR, ");
if(S_IXOTH&expected_mode)
printf("O_EXC, ");
printf("\n");
}
void test_file_mode(int expected_mode,const char* file_name)
{
printf("\t Expected file mode:");
print_mode(expected_mode);
Stat stat_buf;
int fd;
fd=openat(AT_FDCWD,file_name,O_CREAT|O_EXCL|O_RDWR,expected_mode);
if(fd==-1)
{
printf("open %s in test_file_mode,because %s",file_name,strerror(errno));
return ;
}
fstat(fd,&stat_buf);
printf("\n \t Real file mode:");
print_mode(stat_buf.st_mode);
unlinkat(AT_FDCWD,"test_file",!AT_SYMLINK_FOLLOW);
}
int main(int argc, char *argv[])
{
printf("Test access: no exist file:\n");
test_access("/no/exist",F_OK);
printf("Test access: no write:\n");
test_access("/etc/shadow",W_OK);
printf("Test access: write ok:\n");
test_access("/home/huaxz1986/APUE",W_OK);
printf("\n\n\n");
printf("Current creat file:\n");
test_file_mode(S_IRWXU|S_IRWXG|S_IRWXO,"test1");
printf("umask:\n");
test_umask(S_IRUSR|S_IRGRP|S_IROTH);
printf("After umask :\n");
test_file_mode(S_IRWXU|S_IRWXG|S_IRWXO,"test2");
return 0;
}
可以看到:
access函数:对于不存在的文件名访问失败;对没有写权限的名字写访问失败修改文件的现有的访问权限:
#include<sys/stat.h>
int chmod(const char*pathname,mode_t mode);
int fchmod(int fd,mode_t mode);
int fchmodat(int fd,const char*pathname,mode_t mode,int flag);
参数:
pathname:文件路径名mode:文件修改后的权限。对于 fchmod函数:
fd:打开的文件描述符对于 fchmod函数:
fd:一个打开目录文件的描述符,或者AT_FDCWDpathname: fd参数fd指定。 fd=AT_FDCWD,则表示相对于当前工作目录fd对于的打开的目录flag:如果是!AT_SYMLINK_FOLLOW,则fchmodtat并不跟随符号链接返回值:
参数mode可以是下面常量的按位或:(来自头文件<sys/stat.h>
S_ISUID:执行时设置用户IDS_ISGID:执行时设置组IDS_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:用户执行粘着位:如果对一个目录设置了粘着位,则任何对该目录具有写权限的进程都能够在该目录中创建文件。但是:只有满足下列条件之一的用户才能删除或者重命名该目录下的文件:
对于未设置粘着位的目录,则只要用户对该目录有写权限,那么就有修改和重命名该目录下其他文件的能力
修改用户的ID和组ID:
#include<unistd.h>
int chown(const char *pathname,uid_t owner,gid_t group);
int fchown(int fd,uid_t owner,gid_t group);
int fchownat(int fd,const char *pathname,uid_t owner,gid_t group,int flag);
int lchown(const char *pathname,uid_t owner,gid_t group);
参数:
pathname:文件路径名owner:文件修改后的用户IDgroup:文件修改后的组ID对于fchown函数:
fd:打开的文件描述符,要修改的就是这个文件对于 fchmod函数:
fd:一个打开目录文件的描述符,或者AT_FDCWDpathname: fd参数fd指定。 fd=AT_FDCWD,则表示相对于当前工作目录fd对于的打开的目录flag:如果是!AT_SYMLINK_FOLLOW,则fchmodtat并不跟随符号链接,修改的是符号链接本身而不是符号链接指向的文件返回值:
有两点注意:
lchown函数更改的是符号链接本身,而chown遇到符号链接时更改的是符号链接指向的文件示例
#include <stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
typedef struct stat Stat;
void print_mode(mode_t expected_mode)
{
printf("\t");
if(S_IRUSR&expected_mode)
printf("U_RD, ");
if(S_IWUSR&expected_mode)
printf("U_WR, ");
if(S_IXUSR&expected_mode)
printf("U_EXC, ");
if(S_IRGRP&expected_mode)
printf("G_RD, ");
if(S_IWGRP&expected_mode)
printf("G_WR, ");
if(S_IXGRP&expected_mode)
printf("G_EXC, ");
if(S_IROTH&expected_mode)
printf("O_RD, ");
if(S_IWOTH&expected_mode)
printf("O_WR, ");
if(S_IXOTH&expected_mode)
printf("O_EXC, ");
printf("\n");
}
int main(int argc, char *argv[])
{
Stat stat_buf;
int fd;
fd=openat(AT_FDCWD,"test",O_CREAT|O_RDWR,S_IRWXU|S_IRWXG|S_IRWXO);
fstat(fd,&stat_buf);
printf("Current mode:\n");
print_mode(stat_buf.st_mode);
printf("Current user: %d, current group: %d\n",stat_buf.st_uid,stat_buf.st_gid);
if(fchmod(fd,S_IRWXU)==-1)
{
printf("chmod failed,beause of %s",strerror(errno));
}else
{
printf("After Change mode:\n");
fstat(fd,&stat_buf);
print_mode(stat_buf.st_mode);
}
if(fchown(fd,1001,1001)==-1)
{
printf("chown failed,beause of %s\n",strerror(errno));
}else
{
fstat(fd,&stat_buf);
printf("After Change owner--> user : %d, current group: %
d\n",stat_buf.st_uid,stat_buf.st_gid);
}
return 0;
}
可以看到:
文件长度:stat.st_size字段存放的是以字节为单位的文件的长度。此字段只对普通文件、目录文件、符号链接才有意义:
null字节,因为这些字符是存放在文件中,而不是存放在内存中的字符串)另外stat.st_blksize存放的是对于文件 I/O 较合适的块长度;stat.st_blocks存放的是所分配的块的数量(一个块512字节)。注意:
read返回的是字节0截断文件:通常可以用带O_TRUNC选项的open()函数来清空一个文件(截断到0)。但是如果希望截断文件使得文件大小为指定字节数,则可以用下列的函数:
#include<unistd.h>
int truncate(const char*pathname,off_t length);
int ftruncate(int fd,off_t length);
pathname:文件路径名length:文件修改后大小(字节数)fd:打开的文件描述符,要修改的就是这个文件有两点注意:
length小于文件的原大小,则修改文件大小之后,文件新的尾端之后的位置不再可以访问length大于文件的原大小,则修改文件大小之后,会形成空洞。即从文件原大小新的尾端形成了空洞示例
#include <stdio.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>
typedef struct stat Stat;
void print_file_size(int fd)
{
int ok;
Stat stat;
ok=fstat(fd,&stat);
if(ok==01)
{
printf("\tfstat error,because of %s\n",strerror(errno));
}else
{
printf("\tfile size is :%d; blksize:%d; blocks:%d
\n",stat.st_size,stat.st_blksize,stat.st_blocks);
}
}
void test_truncate(int fd,off_t len)
{
int ok;
ok=ftruncate(fd,len);
if(ok==-1)
{
printf("\tftruncate error,because of %s\n",strerror(errno));
}else
{
print_file_size(fd);
}
}
void test_open_trunc(const char* filename)
{
int fd;
fd=openat(AT_FDCWD,filename,O_TRUNC|O_RDWR);
if(fd==-1)
{
printf("\topenat %s failed,because of %s\n",strerror(errno));
}else
{
print_file_size(fd);
}
}
void read_fd(int fd,int len)
{
char bytes[2048];
int ok;
ok=read(fd,bytes,len);
int i;
if(ok==-1)
{
printf("\tread error,because of %s\n",strerror(errno));
}else
{
lseek(fd,0,SEEK_SET);
printf("\tthe bytes is:");
for(i=0;i<ok;i++)
{
printf("\t%d",bytes[i]);
}
printf("\n");
}
}
int main(int argc, char *argv[])
{
int fd;
fd=openat(AT_FDCWD,"test",O_CREAT|O_RDWR,S_IRWXU);
if(fd==-1)
return;
write(fd,"abcdefg",8);
lseek(fd,0,SEEK_SET);
printf("Original size:\n");
print_file_size(fd);
printf("Truncate to 100\n");
test_truncate(fd,100);
read_fd(fd,100);
printf("Truncate to 2\n");
test_truncate(fd,2);
read_fd(fd,2);
close(fd);
printf("Test open with trunc:\n");
test_open_trunc("test");
read_fd(fd,0);
return 0;
}
可以看到:
UNIX文件系统简介(传统的基于BSD的UNIX文件系统,称作UFS):
<i 节点编号>|<文件名> 这种格式的记录的列表stat结构中,链接计数包含在st_nlink成员中(POSIX常量:LINK_MAX指定了一个文件链接数的最大值) stat结构中的大多数信息来自于 i 结点。只有两项重要数据存放在目录项中:文件名、i节点编号因此硬链接不能跨文件系统
mv命令的操作方式与硬链接对应的概念是软链接。软链接也称作符号链接,它是一种特殊的文件。该文件的实际内容(在数据块中)包含了该符号链接所指向的文件的名字。同时该文件的 i 节点指示了该文件类型是 S_IFLNK,于是系统知道了这个文件是个符号链接。
i节点引入符号链接的原因是为了避开硬链接的一些限制:
对于符号链接以及它指向何种类型的文件并没有什么限制。任何用户都可以创建指向目录的符号链接。但是使用符号链接有可能在文件系统中引入循环
对于处理文件和目录的函数,如果传递的是一个符号链接的文件名,则应该注意:函数是否跟随符号链接,即函数是处理符号链接指向的文件,还是处理符号链接本身。
access、chdir、chmod、chown、creat、exec、link、open、opendir、pathconf、stat、truncatelchown、lstat、readlink、remove、rename、unlink O_CREAT和O_EXCL选项调用open,此时若参数是个符号链接的文件名,则open出错返回(并不考虑符号链接指向的文件是否存在),同时将errno设为EEXIST任何一个目录 dirxxx 的硬链接至少为2:
.记录,该记录的 <i节点编号> 指向dirxxx目录的节点dirxxx,记录的 <i节点编号> 指向dirxxx目录的节点dirxxx 的任何子目录的内容有一条名为..的记录,该记录的 <i节点编号> 指向dirxxx目录的节点 因此父目录中的每个子目录都使得父目录的链接计数加 1
link/linkat函数:创建一个指向现有文件的硬链接
#include<unistd.h>
int link(const char *existingpath,const char *newpath);
int linkat(int efd,const char*existingpath,int nfd,const char *newpath,int flag);
参数:
existingpath:现有的文件的文件名(新创建的硬链接指向它)newpath:新创建的目录项 newpath已存在,则返回出错newpath中的最后一个分量,路径中的其他部分应当已经存在。 假设
newpath为:/home/aaa/b/c.txt,则要求/home/aaa/b已经存在,只创建c.txt
linkat函数:
efd和existingpath指定。 existingpath是绝对路径,则忽略efdexistingpath是相对路径,则: efd=AT_FDCWD,则existingpath是相对于当前工作目录来计算efd是一个打开的目录文件的文件描述符,则existingpath是相对于efd对应的目录文件nfd和newpath指定。 newpath是绝对路径,则忽略nfdnewpath是相对路径,则: nfd=AT_FDCWD,则newpath是相对于当前工作目录来计算nfd是一个打开的目录文件的文件描述符,则newpath是相对于nfd对应的目录文件flag:当现有文件是符号链接时的行为: flag=AT_SYMLINK_FOLLOW:创建符号链接指向的文件的硬链接(跟随行为)flag=!AT_SYMLINK_FOLLOW:创建符号链接本身的硬链接(默认行为)返回值:
这两个函数创建新目录项并对链接计数加1。创建新目录项和增加链接计数是一个原子操作。
另外,大多数操作系统中,只有超级用户才能创建指向一个目录的硬链接,因为这样做很有可能在文件系统中形成循环。
unlink函数:删除一个现有的目录项
#include<unistd.h>
int unlink(const char*pathname);
int unlinkat(int fd,const char*pathname,int flag);
参数:
pathname:现有的、待删除的目录项的完整路径名。对于unlinkat函数:
fd和pathname指定。 pathname是绝对路径,则忽略fdpathname是相对路径,则: fd=AT_FDCWD,则pathname是相对于当前工作目录来计算fd是一个打开的目录文件的文件描述符,则pathname是相对于fd对应的目录文件flag: flag=AT_REMOVEDIR:可以类似于rmdir一样的删除目录flag=!AT_REMOVEDIR:与unlink执行同样的操作为了解除对文件的链接,必须对包含该目录项的目录具有写和执行权限。如果还对该目录设置了粘着位,则对该目录必须具有写权限以及下列三个条件之一:
这两个函数删除目录项并对链接计数减1。创建新目录和增加链接计数是一个原子操作。
这个特性常用于创建临时文件,先
open,create一个文件,然后立即调用unlink。这样即使程序崩溃,它所创建的临时文件也不会遗留下来
pathname是个符号链接,则unlink删除该符号链接,而不会删除由该符号链接所引用的文件。 如果文件系统支持,超级用户可以调用unlink,其参数pathname指定一个目录
通常推荐用
rmdir函数,其语义更加清晰
link/unlink实例:
#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<errno.h>
typedef struct stat Stat;
void print_link_num(const char*path)
{
Stat stat;
if(-1==fstatat(AT_FDCWD,path,&stat,0))
{
printf("\tfstatat %s error,because %s\n",path,strerror(errno));
}else
{
printf("\t%s link is %d\n",path,stat.st_nlink);
}
}
void add_link(const char *path,const char *new_path)
{
if(-1==linkat(AT_FDCWD,path,AT_FDCWD,new_path,0))
{
printf("\tadd_link %s error,because %s\n",path,strerror(errno));
}else
{
print_link_num(path);
}
}
void del_link(const char *path)
{
if(-1==unlinkat(AT_FDCWD,path,0))
{
printf("\tun_link %s error,because %s\n",path,strerror(errno));
}else
{
print_link_num(path);
}
}
int main(int argc, char *argv[])
{
int fd;
fd=openat(AT_FDCWD,"test",O_CREAT|O_RDWR,S_IRWXU);
if (-1==fd) return;
close(fd);
printf("The original link num\n");
print_link_num("test"); // link_num=1
printf("Add link\n");
add_link("test","new_test");// link_num=2
printf("\t The new file link:\n\t");
print_link_num("new_test");
printf("Del new file link\n");
del_link("new_test"); // link_num=1
printf("\tThe original file link\n\t");
print_link_num("test");
printf("Del original file link\n");
del_link("test"); // link_num=0
// test link is 0
printf("Del after he original file link is 0:\n");
del_link("test");
return 0;
}
可以看到:
test和new_test这两个文件共享一个 i 结点。因此该节点的 硬链接数为2new_test,则对new_test执行 fstatat失败(因为已经被unlink)。同时test的硬链接数为1test也被删除,则 i节点被释放。执行unlink失败。remove函数:解除对一个目录或者文件的链接。
#include<stdio.h>
int remove(const char *pathname);
pathname:文件名或者目录名对于文件,remove功能与unlink相同;对于目录,remove功能与rmdir相同
rename/renameat函数:重命名文件或目录
#inluce<stdio.h>
int rename(const char*oldname,const char *newname);
int renameat(int oldfd,const char*oldname,int newfd,const char* newname);
参数:
oldname:现有的文件名或者目录名newname:重命名的名字 oldname是个文件名,则为该文件或者符号链接重命名。 newname已存在:若newname是个目录则报错;若newname不是个目录:则先将newname目录项删除,然后将oldname重命名为newnamenewname不存在:则直接将oldname重命名为newname oldname是个目录名,则为该目录重命名。 newname已存在:若newname是个目录且该目录是个空目录,则先将它删除,然后oldname重命名为newname;若newname是个目录且该目录不是个空目录,则报错;若newname不是个目录,则报错newname不存在:则直接将oldname重命名为newname
oldname不能是newname的前缀。因为重命名时,需要删除oldname
oldname或者newname引用的是符号链接,则处理的是符号链接本身,而不是它引用的文件.和..重命名。即.和..不能出现在oldname和newname的最后部分newname和oldname引用同一个文件,则函数不作任何更改而成功返回renameat函数:
oldfd和oldname指定。 oldname是绝对路径,则忽略oldfdoldname是相对路径,则: oldfd=AT_FDCWD,则oldname是相对于当前工作目录来计算oldfd是一个打开的目录文件的文件描述符,则oldname是相对于oldfd对应的目录文件newfd和newname指定。 newname是绝对路径,则忽略newfdnewname是相对路径,则: newfd=AT_FDCWD,则newname是相对于当前工作目录来计算newfd是一个打开的目录文件的文件描述符,则newname是相对于newfd对应的目录文件flag:当现有文件是符号链接时的行为: flag=AT_SYMLINK_FOLLOW:创建符号链接指向的文件的硬链接(跟随行为)flag=!AT_SYMLINK_FOLLOW:创建符号链接本身的硬链接(默认行为)返回值:
对于包含oldname以及newname的目录,调用进程必须具有写和执行的权限,因为将同时更改这两个目录。
symlink/symlinkat函数:创建一个符号链接
#include<unistd.h>
int symlink(const char*actualpath,const char *sympath);
int symlinkat(const char*actualpath,int fd,const char*sympath);
参数:
actualpath:符号链接要指向的文件或者目录(可能尚不存在)sympath:符号链接的名字 二者不要求位于同一个文件系统中
symlinkat函数:
fd和sympath指定。 sympath是绝对路径,则忽略fdsympath是相对路径,则: fd=AT_FDCWD,则sympath是相对于当前工作目录来计算fd是一个打开的目录文件的文件描述符,则sympath是相对于fd对应的目录文件返回值:
readlink/readlinkat函数:打开符号链接本身
open函数是跟随链接的,即打开符号链接指向的文件
#include<unistd.h>
ssize_t readlink(const char *restrict pathname,char *restrict buf,size_t bufsize);
ssize_t readlinkat(int fd, const char* restrict pathname,char *restrict buf,
size_t bufsize);
参数:
pathname:符号链接的名字buf:存放符号链接内容的缓冲区bufsize:期望读入缓冲区的字节数对于readlinkat函数:
fd和pathname指定。 pathname是绝对路径,则忽略fdpathname是相对路径,则: fd=AT_FDCWD,则pathname是相对于当前工作目录来计算fd是一个打开的目录文件的文件描述符,则pathname是相对于fd对应的目录文件返回值:
readlink和readlinkat函数组合了open、read、close函数的所有操作。
注意:读入buf中的符号链接的内容,并不是以null字节终止。
以
null字节终止的是内存中的字符串这种数据结构。而符号链接文件的内容是简单的字符序列,并不是字符串。
符号链接示例:
#include <stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
typedef struct stat Stat;
void test_file_type(const char* file_name)
{
Stat _stat;
if(-1==stat(file_name,&_stat))
{
printf("test_file_type file %s fail,because of %s
\n",file_name,strerror(errno));
return;
}
printf("%s file type is:",file_name);
if(S_ISLNK(_stat.st_mode))
printf("link file, ") ;
if(S_ISREG(_stat.st_mode))
printf(" regular file, ") ;
printf("\n");
}
void creat_file(const char* file_name)
{
int fd;
fd=open(file_name,O_CREAT|O_RDWR,S_IRWXU);
if(-1==fd)
{
printf("creat file %s fail,because of %s\n",file_name,strerror(errno));
}
else{
close(fd);
}
}
int creat_symlink(const char* actual_name,const char* sym_name)
{
int ok;
ok=symlink(actual_name,sym_name);
if(-1==ok)
{
printf("creat symlink %s fail,because of %s\n",sym_name,strerror(errno));
}
return ok;
}
void read_symlink(const char* sym_name)
{
char buffer[2048];
int len,i;
len=readlink(sym_name,&buffer,2048);
if(-1==len)
{
printf("read symlink %s fail,because of %s\n",sym_name,strerror(errno));
}else
{
printf("length <%d>, content:",len);
for(i=0;i<len;i++)
{
printf("%c",buffer[i]);
}
printf("\n");
}
}
int main(int argc, char *argv[])
{
creat_file("/home/huaxz1986/test");
test_file_type("/home/huaxz1986/test");
creat_symlink("/home/huaxz1986/test","/home/huaxz1986/sym_test");
test_file_type("/home/huaxz1986/sym_test");
read_symlink("/home/huaxz1986/sym_test");
}
可以看到:
null字节ubuntu 16.04中,经多次测试,符号链接文件和普通文件的 st_mode完全相同。文件的时间:在stat结构中存放着文件的三个时间:
st_atim:文件数据的最后访问时间st_mtim:文件数据的最后修改时间st_ctim: i 节点状态的最后更改时间关于这三个时间:
st_ctim),但是并不修改文件数据,也并不访问文件数据i 节点的最后访问时间。因此对于 access函数和 stat函数,他们并不修改这三个时间中的任何一个futimens/utimensat/utimes函数:修改文件的访问和修改时间
#include<sys/stat.h>
int futimens(int fd,const struct timespec times[2]);
int utimensat(int fd,const char*path,const struct timespec times[2],int flag);
#include<sys/time.h>
int utimes(const char*pathname,const struct timeval times[2]);
参数:
对于 futimens和 utimensat函数:
times:指向待修改文件的指定的文件数据访问和文件数据修改时间的指针。
对于C语言,参数中的数组自动转换为指向数组的指针
st_atim;数组的第二个元素指定 st_ctimtimes可以按照下列四种方式之一指定: times为空指针: 则将文件的数据访问时间和文件数据修改时间设置为当前时间 此时要求进程的有效用户ID等于该文件所有者的ID;或者进程对该文件有写权限;或者进程是个超级用户进程
times参数是指向timespec数组的指针: tv_nsec字段为 UTIME_NOW,则相应的时间戳就设置为当前时间,忽略相应的tv_sec字段 tv_nsec字段为 UTIME_OMIT,则相应的时间戳保持不变,忽略相应的tv_sec字段 tv_nsec字段为不是上面的两种之一,则相应的时间戳就设置为相应的tv_sec和tv_nsec字段 对于 utimes函数:
pathname:文件的路径名times:指向timeval数组的指针。 timeval结构用秒和微秒表示。
struct timeval{
time_t tv_sec;//秒
long tv_usec; //微秒对于 futimens函数:
fd:待修改文件的打开的文件描述符对于 utimensat函数:
fd和path指定。 path是绝对路径,则忽略fdpath是相对路径,则: fd=AT_FDCWD,则path是相对于当前工作目录来计算fd是一个打开的目录文件的文件描述符,则path是相对于fd对应的目录文件flag:若待修改的文件是符号链接 !AT_SYMLINK_FOLLOW,则符号链接本身的时间就会被修改返回值:
我们不能对st_ctim(i节点最后被修改时间)指定一个值。这个时间是被自动更新的。
示例:
#include <stdio.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>
typedef struct stat Stat;
typedef struct timespec Timespec;
void print_time(const char*path)
{
Stat buffer;
if(-1==stat(path,&buffer))
{
printf("stat file <%s> error,because %s",path,strerror(errno));
}else
{
printf("\t%s time:\n",path);
printf("\t\tdata last access time:<%d s,%d ns
>\n",buffer.st_atim.tv_sec,buffer.st_atim.tv_nsec);
printf("\t\tdata last modify time:<%d s,%d ns>
\n",buffer.st_mtim.tv_sec,buffer.st_mtim.tv_nsec);
printf("\t\tinfo last access time:<%d s,%d ns
>\n",buffer.st_ctim.tv_sec,buffer.st_ctim.tv_nsec);
}
}
void creat_file(const char* file_name)
{
int fd;
fd=open(file_name,O_CREAT|O_RDWR,S_IRWXU);
if(-1==fd)
{
printf("creat file %s fail,because of %s\n",file_name,strerror(errno));
}
else{
close(fd);
}
}
void set_time(const char* file_name,Timespec time[2])
{
if(utimensat(AT_FDCWD,file_name,time,0)==-1)
{
printf("utimensat file <%s> error,because %s",file_name,strerror(errno));
}else
{
print_time(file_name);
}
}
int main(int argc, char *argv[])
{
Timespec times[2];
times[0].tv_nsec=10;
times[1].tv_sec=10;
times[1].tv_nsec=10;
printf("Create file:\n");
creat_file("/home/huaxz1986/test_time");
print_time("/home/huaxz1986/test_time");
sleep(2);
printf("Access not modify time:\n");
access("/home/huaxz1986/test_time",F_OK);
print_time("/home/huaxz1986/test_time");
sleep(2);
printf("Chmod only modify st_ctime:\n");
chmod("/home/huaxz1986/test_time",S_IRUSR|S_IWUSR);
print_time("/home/huaxz1986/test_time");
sleep(2);
printf("Set data access time , data modify time to now:");
set_time("/home/huaxz1986/test_time",NULL);
sleep(2);
printf("Set data access time , data modify time ");
set_time("/home/huaxz1986/test_time",times);
return 0;
}
可以看到:
st_ctim是由系统自动维护的,程序员无法手动指定mkdir/mkdirat函数创建一个空目录:
#include<sys/stat.h>
int mkdir(const char*pathname,mode_t mode);
int mkdirat(int fd,const char *pathname,mode_t mode);
参数:
pathname:被创建目录的名字mode:被创建目录的权限对于 mkdirat,被创建目录的名字是由fd和pathname共同决定的。
pathname是绝对路径,则忽略fdpathname是相对路径,则: fd=AT_FDCWD,则pathname是相对于当前工作目录来计算fd是一个打开的目录文件的文件描述符,则pathname是相对于fd对应的目录文件返回值:
注意:
rmdir函数:删除一个空目录
#include<unistd.h>
int rmdir(const char *pathname);
参数:
pathname:待删除的空目录的名字返回值:
如果调用此函数使得目录的链接计数为0时:
.和..项,直到最后一个打开该目录的进程关闭该目录时此目录才真正被释放。 读、写目录:对于某个目录具有访问权限的任何用户都可以读该目录。但是为了防止文件系统产生混乱,只有内核才能写目录。
一个目录的写权限和执行权限位决定了在该目录中能否创建新文件以及删除文件,它们并不能写目录本身
#include<dirent.h>
DIR *opendir(const char *pathname);
DIR *fdopendir(int fd);
struct dirent *readdir(DIR *dp);
void rewinddir(DIR *dp);
int closedir(DIR *dp);
long telldir(DIR *dp);
void seekdir(DIR *dp,long loc);
各个函数:
opendir:打开目录。 pathname:目录的名字NULLfdopendir:打开目录。 fd:目录文件的文件描述符NULLreaddir:读取目录 dp:目录指针NULLrewinddir:将目录的文件偏移量清零(这样下次读取就是从头开始) dp:目录指针closedir:关闭目录。 dp:目录指针telldir:返回目录的文件偏移量 dp:目录指针seekdir:设置目录的当前位置 dp:目录指针;loc:要设定的文件偏移量对于 DIR结构,它是一个内部结构。起作用类似于 FILE结构。
对于dirent结构,它是定义在<dirent.h>头文件中。其与具体操作系统相关。但是它至少定义了两个成员:
struct dirent{
ino_t d_ino; // i 节点编号
char d_name[];// 以 null 结尾的文件名字符串
}
d_name项的大小并没有指定,但必须保证它能包含至少NAME_MAX个字节(不包含终止null字节)
目录中各目录项的顺序与操作系统有关。它们通常不按照字母顺序排列
当前工作目录:每个进程都有一个当前工作目录。此目录是搜索所有相对路径名的起点。
当前工作目录是本进程的一个属性
与当前工作目录相关的有三个函数:
#include<unistd.h>
int chdir(const char *pathname);
int fchdir(int fd);
char *getcwd(char *buf,size_t size);
各个函数:
chdir:更改当前工作目录。 pathname:将该目录作为当前工作目录fchdir:更改当前工作目录。 fd:将该 fd 文件描述符对应的目录作为当前工作目录getcwd:返回当前工作目录的名字 buf:缓冲区地址;size:缓冲区长度。这两个参数决定了当前工作目录名字字符串存放的位置。 缓冲区必须足够长以容纳绝对路径名加上一个终止
null字节。否则返回出错。
buf;失败返回 NULL示例
#include <stdio.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<dirent.h>S
typedef struct dirent Dirent;
void print_current_work_dir()
{
char buffer[1024];
if(getcwd(buffer,1024)==NULL)
{
printf("\t getcwd failed,because:%s\n",strerror(errno));
}else
{
printf("\t current work dir is :%s\n",buffer);
}
}
void change_current_work_dir(const char*path)
{
if(chdir(path)==-1)
{
printf("change current work dir to %s failed,because:%s\n"
,path,strerror(errno));
}else
{
print_current_work_dir();
}
}
void list_dir(const char *path)
{
DIR * dir;
Dirent * dir_ent;
dir=opendir(path);
if(dir==NULL)
{
printf("\t open dir %s failed,because:%s\n",path,strerror(errno));
return;
}
printf("%s contents is :\n",path);
while((dir_ent=readdir(dir)) !=NULL)
{
printf("\tid:<%d>, file_name :<%s>\n",dir_ent->d_ino,dir_ent->d_name);
}
closedir(dir);
}
int main(int argc, char *argv[])
{
printf("Current work dir:\n");
print_current_work_dir();
printf("Expected change current work dir to /home/huaxz1986\n");
change_current_work_dir("/home/huaxz1986");
list_dir("/home/huaxz1986");
return 0;
}
标签:
原文地址:http://blog.csdn.net/taoyanqi8932/article/details/51884612