-
stat、fstat、lstat
-
函数定义
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf); -
功能描述
- 这三个函数都通过buf返回一个包含文件详细信息的结构体。
- 这些文件本身不需要权限,但是这些函数必须拥有在path指向的文件所在的文件夹下的执行权限。
- stat:通过文件路径获取属性。面对符号链接时,获取的是符号链接指向的文件的属性。
- fstat:同stat,只是通过文件描述符获取属性。
- lstat:面对符号链接时,获取的是符号链接的属性。
-
参数解析
-
struct stat 结构体:
struct stat {
dev_t st_dev; /* 包含改文件的设备id号 /
ino_t st_ino; / i节点号 /
mode_t st_mode; / protection /
nlink_t st_nlink; / number of hard links /
uid_t st_uid; / user ID of owner /
gid_t st_gid; / group ID of owner /
dev_t st_rdev; / device ID (if special file) /设备id号
off_t st_size; / total size, in bytes /
blksize_t st_blksize; / blocksize for file system I/O /文件系统IO的块大小
blkcnt_t st_blocks; / number of 512B blocks allocated /块数
time_t st_atime; / time of last access /最后一次访问时间
time_t st_mtime; / time of last modification /最后一次修改文件内容时间
time_t st_ctime; / time of last status change */最后一次修改inode 信息的时间。(i.e., owner, group, link count, mode, etc.).};
-
st_mode
st_mode是一个十六位的整形数,以位图的方式存储了文件类型、文件权限、特殊权限位。- 文件类型:
- d: 目录文件
- c:字符设备文件
- b: 二进制文件
- -: 常规文件
- l: 符号链接文件
- s: 网络socket文件
- p: 匿名管道文件
-
文件权限(也就是三类用户的rwx权限)
-
判断一个文件的类型有两种办法:
-
利用宏定义的函数判断。为真返回1;为假返回0。
S_ISREG(m) //is it a regular file?S_ISDIR(m) //directory?
S_ISCHR(m) //character device?
S_ISBLK(m) //block device?
S_ISFIFO(m)//FIFO (named pipe)?
S_ISLNK(m) //symbolic link? (Not in POSIX.1-1996.)S_ISSOCK(m)//socket? (Not in POSIX.1-1996.)
-
利用位运算,得到结果。
S_IFMT 0170000 //bit mask for the file type bit fields
S_IFSOCK 0140000 // socket
S_IFLNK 0120000 //symbolic link
S_IFREG 0100000 // regular file
S_IFBLK 0060000 //block device
S_IFDIR 0040000 //directory
S_IFCHR 0020000 //character device
S_IFIFO 0010000 //FIFO
S_ISUID 0004000 //set UID bit
S_ISGID 0002000 //set-group-ID bit (see below)
S_ISVTX 0001000 //sticky bit (see below)
S_IRWXU 00700 //mask for file owner permissions
S_IRUSR 00400 //owner has read permission
S_IWUSR 00200 //owner has write permission
S_IXUSR 00100 //owner has execute permission
S_IRWXG 00070 //mask for group permissions
S_IRGRP 00040 // group has read permission
S_IWGRP 00020 //group has write permission
S_IXGRP 00010 //group has execute permission
S_IRWXO 00007 // mask for permissions for others (not in group)
S_IROTH 00004 //others have read permission
S_IWOTH 00002 // others have write permission
S_IXOTH 00001 //others have execute permission
如上所示,可以利用bit mask等得到想要的文件类型信息、文件权限信息、特殊位信息。(见下文中的例子。)
-
- 文件类型:
-
返回值
- 正常返回0,失败返回-1,并设置对应的errno。
- 通过buf返回该文件的详细信息。
-
例子
-
-
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
struct stat sb;
if (argc != 2) {
fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (stat(argv[1], &sb) == -1) {
perror("stat");
exit(EXIT_SUCCESS);
}
printf("File type: ");
switch (sb.st_mode & S_IFMT) {
case S_IFBLK: printf("block device\n"); break;
case S_IFCHR: printf("character device\n"); break;
case S_IFDIR: printf("directory\n"); break;
case S_IFIFO: printf("FIFO/pipe\n"); break;
case S_IFLNK: printf("symlink\n"); break;
case S_IFREG: printf("regular file\n"); break;
case S_IFSOCK: printf("socket\n"); break;
default: printf("unknown?\n"); break;
}
printf("I-node number: %ld\n", (long) sb.st_ino);
printf("Mode: %lo (octal)\n",
(unsigned long) sb.st_mode);
printf("Link count: %ld\n", (long) sb.st_nlink);
printf("Ownership: UID=%ld GID=%ld\n",
(long) sb.st_uid, (long) sb.st_gid);
printf("Preferred I/O block size: %ld bytes\n",
(long) sb.st_blksize);
printf("File size: %lld bytes\n",
(long long) sb.st_size);
printf("Blocks allocated: %lld\n",
(long long) sb.st_blocks);
printf("Last status change: %s", ctime(&sb.st_ctime));
printf("Last file access: %s", ctime(&sb.st_atime));
printf("Last file modification: %s", ctime(&sb.st_mtime));
exit(EXIT_SUCCESS);
}
-
空洞文件
空洞文件介绍.
我们可以利用lseek构造一个包含空洞的文件。原本想构造5G的,结果我用的系统,long类型也是32位,只好改为5MB。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char ** argv)
{
int fd;
printf("long SIZE :%d\n",sizeof(long));
if(argc<2)
{
fprintf(stderr,"Usage....\n");
exit(EXIT_FAILURE);
}
fd = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0600);
if(fd==-1)
{
perror("open failed!\n");
exit(EXIT_FAILURE);
}
if(lseek(fd,5LL*1024LL*1024LL-1LL,SEEK_SET)==-1)
{
perror("lseek()");
exit(EXIT_FAILURE);
}
if(write(fd,"",1)==-1)
{
perror("write()");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
执行后得到一个空洞文件。
从上图可以注意到如下几点:
1. 文件size为5242800B,而块数只有8,也就是文件真实分配的磁盘大小只有4KB。
2. 使用cp命令后,得到的备份文件,磁盘块数为0。
-
分析目录/读取目录内容
-
方法一:glob
-
函数定义
#include <glob.h>
int glob(const char *pattern, int flags,
int (*errfunc) (const char *epath, int eerrno),
glob_t *pglob);void globfree(glob_t *pglob);//释放glob申请的内存
-
函数功能
- 与pattern模式匹配符合的所有文件都存放在pglob中。
- flags指定了多种执行模式。不指定模式,就设置为0。
- errfunc用于获得函数执行出错的具体信息。不需要出错信息时,设置为NULL。
- pglob用于存储模式匹配得到的信息。
- typedef struct {
size_t gl_pathc; /* Count of paths matched so far */
char *gl_pathv; / List of matched pathnames. /
size_t gl_offs; / Slots to reserve in gl_pathv. */
} glob_t;
- typedef struct {
-
注意事项
- gl_pathv与main函数中的argv有相似之处。如argc为1时,则argv[1]==NULL。所以可用pglob.gl_pathv[i]==NULL判别读取完毕。
-
返回值
-
执行成功返回0。否则有如下情况:
GLOB_NOSPACE
// for running out of memory,GLOB_ABORTED
//for a read error, andGLOB_NOMATCH
// for no found matches.
-
-
例子
-
-
#include <stdlib.h>
#include <stdio.h>
#include <glob.h>
#define PAT "*.c"
int main(int argc,char ** argv)
{
glob_t pglob;
int i;
if(glob(PAT,0,NULL,&pglob))
{
fprintf(stderr,"glob error\n");
exit(EXIT_FAILURE);
}
for(i=0;pglob.gl_pathv[i];i++)
{
puts(pglob.gl_pathv[i]);
}
exit(EXIT_SUCCESS);
}
-
方法二:通过opendir、readdir、closedir(在man手册的第三章)
-
opendir:
- DIR *opendir(const char *name);
DIR *fdopendir(int fd);- opendir通过目录名字打开一个目录流,返回一个目录流指针。指针指向目录流的起始位置。
- fopendir与opendir一样,只是是从一个已经打开的文件描述符读取信息。
- 执行成功返回目录流,否则返回NULL,并设置errno。
- DIR *opendir(const char *name);
-
readdir
-
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
-
readdir通过目录流drip返回一个dirent结构体指针,指向下一个目录项位置。
-
当读到目录流结束位置或者出现错误,返回NULL。出现错误时,设置errno。
-
dirent的定义如下:
struct dirent {
ino_t d_ino; /* inode number /
off_t d_off; / offset to the next dirent /
unsigned short d_reclen; / length of this record /
unsigned char d_type; / type of file; not supported
by all file system types /
char d_name[256]; / filename */
};
-
-
closedir
-
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
-
关闭drip对应的目录流。执行成功的话,也会关闭目录流drip所使用的的文件描述符fd。
-
执行成功返回0;否则返回-1,并设置errno。
-
-
例子
-
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#define PAT "."
int main()
{
DIR * tmp;
struct dirent * cur;
if((tmp=opendir(PAT))==NULL)
{
perror("opendir()");
exit(EXIT_FAILURE);
}
while((cur=readdir(tmp)) != NULL)
{
puts(cur->d_name);
}
closedir(tmp);
exit(EXIT_SUCCESS);
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <glob.h>
#define PATHSIZE 1024
//排除每个目录中存在的 . 和 .. 目录文件
static int path_noloop(const char * path)
{
char * pos;
pos = strrchr(path,'/');//寻找最后一个'/'
if(pos == NULL)
exit(1);
if(strcmp(pos+1,".") == 0 || strcmp(pos+1,"..")==0 )
return 0;
return 1;
}
static int64_t mydu(const char * path)
{
char pathname[PATHSIZE];
struct stat buf;
glob_t pglob;
int64_t sum;
int i;
int flag;
if(lstat(path,&buf) == -1)
{
perror("lstat()");
exit(EXIT_FAILURE);
}
if(!S_ISDIR(buf.st_mode))
return buf.st_blocks;
else
{
strncpy(pathname,path,PATHSIZE);
strncat(pathname,"/*",PATHSIZE);
flag = glob(pathname,0,NULL,&pglob);
if(flag == GLOB_NOSPACE || flag == GLOB_ABORTED)
{
fprintf(stderr,"/* glob err!");
exit(EXIT_FAILURE);
}
strncpy(pathname,path,PATHSIZE);
strncat(pathname,"/.*",PATHSIZE);
flag = glob(pathname,GLOB_APPEND,NULL,&pglob);//这里使用追加模式。把隐藏文件名加入pglob中。
if(flag == GLOB_NOSPACE || flag == GLOB_ABORTED)
{
fprintf(stderr,"/.* glob err!\n");
exit(EXIT_FAILURE);
}
sum= buf.st_blocks;//目录文件本身的大小也得加上
for(i = 0;i<pglob.gl_pathc;i++)
{
if(path_noloop(pglob.gl_pathv[i]))
sum += mydu(pglob.gl_pathv[i]);
}
printf("%-8lld %s\n",sum/2,path);
}
globfree(pglob);//一定要记得释放glob申请的堆空间
return sum;
}
int main(int argc,char ** argv)
{
if(argc!=2)
{
fprintf(stderr,"Usage------\n");
exit(EXIT_FAILURE);
}
printf("%lld\n",mydu(argv[1])/2);
exit(EXIT_SUCCESS);
}