Linux基础 文件和目录


文件和目录

前言

本章讨论文件属性和文件系统内容。除了上一章讨论的普通文件,Linux的文件概念还包括:目录、设备等。在Linux系统中,文件的种类包括:普通文件、目录、符号链接、块设备、字符设备、管道、套接字。

本章讨论的主要内容为普通文件、目录和符号链接。它们的公共特点是,真实的保存在了硬盘中,而其它类型的文件是内核产生的文件,在硬盘中并不存在。

文件属性

通过stat函数或者stat命令可以获得文件属性。

  • 实际账户和有效账户

账户解释
实际账户 登陆系统时的账户
有效账户 决定进程的访问资源的账户

3.5 文件访问权限

文件使用了9个位来表示访问权限,和文件类型一起,保存在st_mode中。此9位分成3组,每组3个位,分别表示读/写/执行权限,而三个组分别表示拥有该文件的账户,拥有该文件的组,其它用户组的权限。如果对应位是1,表示有权限,如果是0表示没有权限。

111101101

文件访问权限经常用8进制来表示,比如上表的权限位可以表示为0755,意思是拥有它的账户对这个文件有读/写/执行权限,而拥有它的组有读/执行权限,其它账户对它有读/执行权限。

       Linux提供一些宏,来测试文件的权限位

            

  • 可以通过access函数来测试程序是否有访问某文件的权限。

  • 创建文件时,可以指定文件的访问权限位,但是会收到umask位影响。

  • 可以通过chmod来修改文件的权限位

3.6 其它权限位

3.6.1 SUID

只能对文件设置,如果文件设置了该位,那么该文件被运行时,对应的进程的权限,不是运行该程序账户的权限,而是拥有该用户的权限。

在对文件未设置SUID的情况下:

如果对文件设置了SUID,那么:

可以通过chmod u+s或者chmod u-s来设置获取去除SUID。
设置SUID可以让一个普通账户拥有它不该有的权限,容易产生安全漏洞。

3.6.2 SGID

可以对文件和目录设置,如果对文件设置,那么它的作用类似SUID,不过影响的是组。

如果对目录设置,那么拷贝到该目录下的文件都会被置位,除非拷贝是带-p参数。 在Ubuntu下测试并不如此。
在Ubuntu下设置了目录的SGID之后,在那个目录下创建的文件,拥有者是有效账户,而拥有组是该目录的拥有组。

命令:
chmod g+s
chmod g-s

3.6.3 StickyBit

可以对文件或者目录设置,如果对文件设置,那么当这个文件运行并退出后,系统依旧保留该文件对应的映象,这样这个程序再次运行时,就不需要加载再加载了。这个属性的作用并不大,因为它占用了内存。

如果对目录设置,那么表示在该目录下,账户只能删除和修改它自己创建的文件,对于其它账户创建的文件,它不能修改和删除。这个位作用比较大,在一些公共目录,往往有这个属性,比如/tmp

命令:
chmod o+t
chmod o-t

总结:

设置对象设置方法查看效果
SUID 文件 chmod u+s 如果用户执行权限位为s或者S,则表示SUID有设置 当该文件被执行时,进程所拥有的权限是拥有该文件的账户权限
SUID 目录 不可设置    
SGID 文件 chmod g+s 如果组执行权限位为s或者S,则表示GUID有设置 当执行该文件时,进程所属组是该拥有该文件的组
SGID 目录 chmod g+s 同上 在该目录中创建文件时,该文件的所属组是目录的所属组
StickyBit 文件 chmod o+t 如果其他执行权限位为t或者T,那么该文件有设置StickyBit 执行该文件并退出后,系统保留该文件占用的一些内存,以便加快下一次的加载运行
StickyBit 目录 chmod o+t 同上 账户只能修改和删除该目录下属于该账户的文件,不能修改该目录下其他账户创建的文件

3.7 文件长度

st_size保存文件的长度,write函数会修改该属性,也可以通过truncate修改文件大小,truncate可以扩大文件或者缩小文件,缩小文件时,文件内容会被删减。

文件大小可以通过lswc -cstat命令获取。
也可以通过fseekftell函数配合获取,或者直接通过stat函数获取文件长度。

3.8 文件系统

3.8.1 文件管理

文件系统描述文件在硬盘中的组织,保存在硬盘中的文件只有普通文件、目录、软链接。

为了更加方便的管理持久性文件存储,操作系统一般对硬盘进行有规划的管理,规划包括:

  • 分区

  • 格式化

文件系统指一个分区内,文件存储的组织方式。



Snip20161008_18

Snip20161019_1

3.9 文件删除

使用unlink命令和函数可以删除一个文件。
如果此时文件已经打开,那么该文件也可以被unlink,但是删除操作不会立即执行,而会被保留到文件关闭时执行。

unlink 删除文件,如果是链接,就删除链接,如果不是链接就删除文件。 rmdir 只能删除空目录 rm 会判断参数类型,如果是文件那么调用unlink,如果是目录调用rmdir 如果要删除非空目录,要使用rm -r,-r选项先删除目录中的文件,再调用rmdir。 int rm(const char* path); {     // 怎么遍历目录 }

3.10 文件时间

对文件的访问,会导致文件时间发生变化。系统会自动记录用户对文件的操作的时间戳,以便将来可以查询文件修改时间。

如果需要故意修改,那么可以通过utime函数,修改文件的访问时间和修改时间。

touch命令也可以将文件的时间修改为当前时间。touch命令的副作用是,如果参数所指文件不存在,那么创建一个空文件。

当用户进行大规模拷贝时,cp操作会修改文件的访问时间,如果想提高效率,可以使用-p选项,避免文件属性的修改,同时加快速度。

 
#include 
       #include 

int main()
{
    struct utimbuf buf;
    buf.actime = 0;
    buf.modtime = 0;
    utime("a.out", &buf);
}

利用utime来修改文件的访问时间和修改时间

#include "../h.h" int main() {     struct utimbuf buf;     buf.actime = 0;     buf.modtime = 0;     utime("testfile", &buf);       struct timeval tv[2];     tv[0].tv_sec = 100000;     tv[0].tv_usec = 10000;       tv[1].tv_sec = 100000;     tv[1].tv_usec = 10000;     utimes("testfile", tv); }

3.11 目录操作

3.11.1 创建和删除目录

mkdirrmdir

3.11.2 遍历目录

opendirclosedirreaddirrewinddirtelldirseekdir

遍历目录

#include "../h.h"   int rm(const char* path) {     // 判断path是个文件还是目录     // 如果是文件,直接unlink然后返回     struct stat stat_buf;     int ret = stat(path, &stat_buf);     if(ret < 0)     {         perror("stat");         return -1;     }     if(!S_ISDIR(stat_buf.st_mode))     {         unlink(path);         return 0;     }       // 如果path是目录,遍历目录中的所有目录项     char buf[1024];     DIR* dir = opendir(path);     if(dir == NULL)         return -1;       struct dirent* entry = readdir(dir);     while(entry)     {         // 通过entry得到文件信息         sprintf(buf, "%s/%s", path, entry->d_name);         if(entry->d_type == DT_REG || entry->d_type == DT_LNK)         {             unlink(buf);         }         if(entry->d_type == DT_DIR)         {             // 忽略.和..目录             if(strcmp(entry->d_name, ".") == 0                     ||strcmp( entry->d_name, "..") == 0)             {                 entry = readdir(dir);                 continue;             }             rm(buf);         }         entry = readdir(dir);     }       closedir(dir);     rmdir(path);     return 0; }   int main(int argc, char* argv[]) {     if(argc == 1)     {         printf("usage: %s [pathname]\n", argv[0]);         return 0;     }     rm(argv[1]);     return 0;    }

seekdir和telldir

  #include "../h.h"   int main() {     long loc;     DIR* dir = opendir("testdir");       // no     // seekdir(dir, 2);     //     struct dirent* entry;     while(1)     {         loc = telldir(dir);         entry = readdir(dir);         if(entry == NULL) break;           if(strcmp(entry->d_name, "a") == 0)         {             // 记录文件a的位置             break;         }     }       seekdir(dir, loc);       while(1)     {         entry = readdir(dir);         if(entry == NULL)             break;         printf("loc is %d, entry->d_name=%s\n", (int)telldir(dir), entry->d_name);     }       // 将文件指针回到a的位置 //    seekdir(dir, loc); }
#include 
#include 
#include 
#include 

int main(int argc, char* argv[])
{
    DIR* dir = opendir(argv[1]);
    struct dirent* entry;

    while(1)
    {
        entry = readdir(dir);
        if(entry == NULL)
            break;

        // linux下,.开头是隐藏文件
        if(entry->d_name[0] == '.')
            continue;

        printf("%s\n", entry->d_name);
    }

    closedir(dir);
}

3.12 练习

3.12.1 实现文件拷贝,保留文件属性

#include "../h.h"   int main(int argc, char* argv[]) {     if(argc != 3)     {         printf("usage: %s [srcfile] [dstfile]\n", argv[0]);         return -1;     }       const char* filesrc = argv[1];     const char* filedst = argv[2];       FILE* fp_src = fopen(filesrc, "r");     FILE* fp_dst = fopen(filedst, "w");       char buf[4096];     while(1)     {         int ret = fread(buf, 1, sizeof(buf), fp_src);         if(ret <= 0)             break;         fwrite(buf, ret, 1, fp_dst);     }       fclose(fp_src);     fclose(fp_dst);         // 获取源文件属性     struct stat src_stat;     stat(filesrc, &src_stat);       // 修改目标文件时间     struct utimbuf timbuf;     timbuf.actime = src_stat.st_atime;     timbuf.modtime = src_stat.st_mtime;     utime(filedst, &timbuf);       // 修改权限     chmod(filedst, src_stat.st_mode);       // 修改所有者     int ret = chown(filedst, src_stat.st_uid, src_stat.st_gid);     if(ret < 0)     {         perror("chown");     }       return 0; }

3.12.2 实现目录打包到文件,将文件解包成目录

  #include "../h.h"   #include  #include  using namespace std;   map savedfiles;   void tarfile(const char* filename, FILE* fpOut) {     struct stat stat_buf;     stat(filename, &stat_buf);       // 检查这个文件是否已经保存过,是否是其他文件的硬链接     std::string filesaved = savedfiles[stat_buf.st_ino];     if(filesaved.size() != 0)     {         // 此ino之前已经存过了         fprintf(fpOut, "h\n%s\n%s\n", filename, filesaved.c_str());         return;     }       fprintf(fpOut, "f\n%s\n%lld\n", filename, (long long int)stat_buf.st_size);       FILE* fpIn = fopen(filename, "r");     char buf[4096];     while(1)     {         int ret = fread(buf, 1, sizeof(buf), fpIn);         if(ret <= 0)             break;         fwrite(buf, ret, 1, fpOut);     }     fclose(fpIn);  //   savedfiles.insert(std::pair(stat_buf.st_ino, std::string(filename)));     // 如果该文件不是别人的硬链接,那么将文件拷贝之后,在全局map中记录ino     savedfiles[stat_buf.st_ino] = std::string(filename); }   int tardir(const char* dirname, FILE* fpOut) {     char filepath[1024];       // 写目录     fprintf(fpOut, "d\n");     fprintf(fpOut, "%s\n", dirname);       DIR* dir = opendir(dirname);     struct dirent* entry = readdir(dir);     while(entry)     {         sprintf(filepath, "%s/%s", dirname, entry->d_name);         // handle         if(entry->d_type == DT_REG)         {             // write file             tarfile(filepath, fpOut);         }         else if(entry->d_type == DT_DIR)         {             if(strcmp(entry->d_name, ".") == 0 ||                     strcmp(entry->d_name, "..") == 0)             {                 entry = readdir(dir);                 continue;             }               tardir(filepath, fpOut);         }         entry = readdir(dir);     }       closedir(dir); }   int tar(const char* dirname, const char* outfile) {     FILE* fpOut = fopen(outfile, "w");       fprintf(fpOut, "xgltar\n");     fprintf(fpOut, "1.0\n");       int ret = tardir(dirname, fpOut);       fclose(fpOut);     return ret; }     #define line_buf_size 1024 char line_buf[line_buf_size]; #define get_line() fgets(buf, line_buf_size, fin)   int untar1(FILE* fin) {     char* buf = line_buf;     if(get_line() == NULL)         return -1;       printf("now utar type=%s", buf);       if(strcmp(buf, "d\n") == 0)     {         get_line();         buf[strlen(buf)-1] = 0;         mkdir(buf, 0777);         printf("mkdir %s\n", buf);     }     else if(strcmp(buf, "f\n") == 0)     {         get_line();         buf[strlen(buf)-1] = 0; // filename         FILE* out = fopen(buf, "w");         printf("create file %s\n", buf);           get_line();         long long int len = atoll(buf); // 1987\n         printf("filelen %s\n", buf);           while(len > 0)         {             // 避免粘包问题             int readlen = len < line_buf_size ? len : line_buf_size;             int ret = fread(buf, 1, readlen, fin);             fwrite(buf, 1, ret, out);             len -= ret;         }           fclose(out);     }     else if(strcmp(buf, "h\n") == 0)     {         get_line();         buf[strlen(buf)-1] = 0; // filename new         std::string new_path(buf);           get_line();         buf[strlen(buf)-1] = 0; // hardline filename old           link(buf, new_path.c_str());     }       return 0; }   int untar(const char* tarfile) {     char* buf = line_buf;       FILE* fin = fopen(tarfile, "r");     //fgets(buf, line_buf_size, fin);     get_line();     if(strcmp(buf, "xgltar\n") != 0)     {         printf("unknown file format\n");         return -1;     }       get_line();     if(strcmp(buf, "1.0\n") == 0)     {         while(1)         {             int ret = untar1(fin);             if(ret != 0)                 break;         }       }     else     {         printf("unknown version\n");         return -1;     }     return 0; }     // ./mytar -c dir tarfile.xgl // ./mytar -u tarfile.xgl int main(int argc, char* argv[]) {     if(argc == 1)     {         printf("usage: \n\t%s -c [dir] [tarfile]\n\t%s -u [tarfile]\n", argv[0], argv[0]);         return -1;     }           const char* option = argv[1];     if(strcmp(option, "-c") == 0)     {         const char* dirname = argv[2];         const char* outfile = argv[3];         return tar(dirname, outfile);     }     else if(strcmp(option, "-u") == 0)     {         const char* tarfile = argv[2];         return untar(tarfile);     }       printf("option error\n");     return -1; }

3.13 函数和命令

3.13.1 函数

stat/lstat:查看文件属性
chmod:修改文件权限
chown:修改文件的所有者
utime:修改文件时间
unlink:删除文件
link:创建硬链接
symlink:创建软链接
rmdir:删除空目录
mkdir:创建空目录
opendir:打开目录
closedir:关闭目录
readdir:读取一个目录项,并且将目录项指针移到下一项
seekdir:修改目录项指针
rewainddir:重置目录项指针
telldir:获得当前目录向指针
判断权限位宏 S_IRUSR(stat.st_mode)
判断文件类型宏S_ISDIR(stat.st_mode)

3.13.2 命令

stat:查看文件属性
chmod:修改文件权限
chown
unlink:删除文件(不会跟随)
ln:创建链接
mkdir
rmdir
rm
cp
dd:拷贝数据(可以拷贝文件,也拷贝块设备)
wc:计算文件内容的行数、单词数、字节数
which:查找非内置的命令位置
fdisk:查看磁盘信息、分区
mkfs:在分区中创建文件系统(ext2,ext3,ext4, fat32, ntfs, xfs,nfs)
mount:挂载
umount:取消挂载