文件和目录管理

1、文件与其元数据

Linux获取文件状态的时候会把其相关的元数据放在stat结构中,如下:

struct stat {
dev_t st_dev; //文件设备节点
ino_t st_ino; //ino
mode_t st_mode; //文件模式
nlink_t st_nlink; //硬链接数目
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //如果是设备,设备编号
off_t st_size; //大小,字节
blksize_t st_blksize; //有效率文件I/O的首选的块大小
blkcnt_t st_blocks; //文件系统块的数目
time_t st_atime; //最近访问时间
time_t st_mtime; //最近修改时间
time_t st_ctime; //最近变更时间
};

下面是通过stat调用获得文件信息,eg:

int main(){
int ret, i;
struct stat sb;
ret = stat("test", &sb);
if(ret == -1)
printf("stat error.\n");
printf("test is %ld bytes.\n", sb.st_size);
return 0;
}

fstat和stat的不同之处是调用的时候参数是文件描述符,下面程序判断文件是否位于一个物理设备,eg:

int main(){
int ret, fd;
struct stat sb;
fd = open("test", O_RDWR);
ret = fstat(fd, &sb);
if(ret == -1)
printf("stat error.\n");
if(gnu_dev_major(sb.st_dev))
printf("test is in physical device.\n");
return 0;
}

可以通过chmod(fchmod)设置文件所属的用户,eg:

int main(){
int ret, x;
struct stat sp;
ret = stat("test", &sp);
printf("%d\n", x=sp.st_mode);
while(x > 0){
printf("%d", x%2);
x/=2;
}
printf("\n");

x = sp.st_mode|3;
ret = chmod("test", x);
if(ret == -1)
printf("chmod error.\n");

ret = stat("test", &sp);
printf("%d\n", x=sp.st_mode);
while(x > 0){
printf("%d", x%2);
x/=2;
}
printf("\n");
return 0;
}

  以前没有写过mode中的各个位代表的是什么意思,其实也不需要知道。定义了一些常量来表示,如果要设定该属性只需要将mode与该值进行或运算就可以了,取消的话与该值的非进行与运算,下面列出这些值:

  1. S_IRWXU:拥有者可以读、写、运行。
  2. S_IRUSR:拥有者可以读。
  3. S_IWUSR:拥有者可以写。
  4. S_IXUSR:拥有者可以运行。
  5. S_IRWXG:组可以读、写、运行。
  6. S_IRGRP:组可以读。
  7. S_IWGRP:组可以写。
  8. S_IXGRP:组可以运行。
  9. S_IRWXO:其他人可以读、写、运行。
  10. S_IROTH:其他人可以读。
  11. S_IWOTH:其他人可以写。
  12. S_IXOTH:其他人可以运行。

  在调用chown可以修改文件的所有者,需要注意的是,如果哦path指向的是一个链接,那么chown会跟随链接改变目标的所有权,而lchown则只是变更符号链接本身的所有权,可能发生错误的原因也是非常地多,可以更具errno来判断,eg:

int main(){
int ret, x;
struct stat sp;
ret = chown("test", 1000, 1000);
if(ret == -1){
printf("chown error.\n");
}
return 0;
}

2、扩展属性

  扩展属性(xattrs)提供了一个机制用来将《键/值》对永久地关联到文件,让现有的文件系统得以支持在原始设计中未提供的功能。扩展属性是文件系统不可知论者,应用程序可以通过一个标准的接口来操纵他们,此接口不因文件系统而异。每个扩展属性可以通过唯一的键来区分,键的内容必须是有效的UTF-8,格式为namespace.attribute,每个键采用完全限定的形式。

  需要注意的是这里的值可以是任意字节的数组,未必是字符存,而且最后可能不是null,这样在访问的时候必须知道值的大小。在设置的时候当然也就需要设置值大小。

  一个扩展属性的用处:GUI的文件管理程序的行为根据文件类型而异。要判断文件的格式,Winodws之类的操作系统仅需要查看文件的扩展名就可以了,而Unix系统往往需要查看文件的内容来判断类型。有些文件管理程序会直接产生此信息,有些则会将产生的信息缓存起来以备下次使用。一个更好的做法就是将此类元数据存入扩展属性。
Linux下定义的4种扩展属性命名空间:

  1. system:用于实现利用扩展属性的内核功能,例如访问控制表。eg:system.posix_acl_access便是位于此用户空间的扩展属性,用户是否可以读取或写入这些属性取决于所使用的安全模块。
  2. security:用于实现安全模块。
  3. trusted:把受限制的信息存入用户空间。
  4. user:一般进程所使用的标准命名空间,经过一般文件权限位来控制此命名空间的访问。

下面给出set、get、remove、list四个操作的一个简单的例子:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/xattr.h>
#include <sys/types.h>

void testset(){
char key[7] = {'u','s','e','r','.','#','\0'};
char value[2] = {'#','\0'};
int i, ret;
for(int i = 0; i < 10; i++){
key[5] = value[0] = '0'+i;
ret = setxattr("test", key, value, 2, 0);
}
}

void testlist(){
char buf[1000];
int ret, i=0, j = 0;
printf("The key on test are:\n");
ret = listxattr("test", buf, 1000);
while(i < ret){
printf("%s\n", buf+i);
i += strlen(buf+i);
i++;
}
}

void testremove(){
char key[7] = "user.2";
int ret;
ret = removexattr("test", key);
printf("%d\n", ret);
}

void testget(){
char key[7] = "user.#";
char value[3];
int ret, i;
printf("The <key,value> on test are:\n");
for(i = 0; i < 10; i++){
key[5] = '0'+i;
ret = getxattr("test", key, value, 3);
if(ret != -1)
printf("<%s,%s>\n", key, value);
}
}

int main(){
testset();
testlist();
testremove();
testget();
return 0;
}

3、目录
  每个进程都有一个当前目录,该目录最初继承自父进程,当前工作目录是内核用于解析相对路径名称的起点。下面的代码可用与获得当前的工作目录,eg:

int main(){
char buf[20];
if(getcwd(buf, 20)){
printf("%s\n", buf);
}

char *cwd = get_current_dir_name();
printf("%s\n", cwd);

return 0;
}

  当一个用户首次登录她的系统,login进程会根据/etc/passwd文件中设定将她的当前工作目录设定为她的home目录,然而有些时候进程会想变更它的当前工作目录,为此系统提供两个系统调用:

int main(){
int fd, ret;
char *cwd;
ret = chdir("/");
if(ret != -1){
printf("%s\n", get_current_dir_name());
}

fd = open("/home/ggzwtj/", O_DIRECTORY);
ret = fchdir(fd);
if(ret != -1){
printf("%s\n", get_current_dir_name());
}
return 0;
}

创建删除目录的系统调用,eg:

int main(){
int fd, ret;
ret = mkdir("testdir", O_RDWR);
if(ret != -1)
printf("创建目录成功!\n");

ret = rmdir("testdir");
if(ret != -1)
printf("删除目录成功!\n");
return 0;
}

读目录:

int main(){
int ret;
DIR *dir;
struct dirent *entry;

dir = opendir("/home/ggzwtj/xx");

while((entry = readdir(dir)) != NULL){
printf("%s\n", entry->d_name);
}

closedir(dir);

return 0;
}

4、链接
硬链接和软链接的区别就不说了,下面进入正题,创建硬链接。eg:

int main(){
int ret;
ret = link("test", "testlink");
if(ret == -1)
printf("link error.\n");
return 0;
}

创建软链接。eg:

int main(){
int ret;
ret = symlink("test", "testlink");
if(ret == -1)
printf("link error.\n");
return 0;
}

解除链接。eg:

int main(){
int ret;
ret = link("test", "testlink");
if(ret == -1)
printf("link error.\n");

unlink("testlink");
return 0;
}

5、文件的复制、移动
  在文件系统看来,拷贝文件就是复制一份文件内容,然后在新的目录下创建该拷贝的硬链接。而移动文件的代价就小的多,只是对文件的目录项更名。虽然没有提供系统调用来专门地实现复制、移动,但是知道文件系统的实质操作后也就简单了。eg:

int main(){
int ret;
ret = rename("test", "/home/ggzwtj/test");
if(ret == -1)
printf("rename error.\n");
return 0;
}

需要注意是两个位置必须是在相同的文件系统中,还必须有访问权限。

6、设备节点
  设备节点是一种特殊的文件,应用程序可以通过它链接到设备驱动程序。当一个应用程序在设备节点上进行普通的Unix I/O,内核会把此类请求传递给一个设备驱动程序,然后设备把处理结构返回给用户。下面是几个有意思的特殊文件:

  1. /dev/null,丢掉对该设备的所有写入,对于读请求总是返回eof。
  2. /dev/zero,丢掉所有写入请求,对读请求返回null字节的无穷流。
  3. /dev/full,对写请求触发ENOSPC,表示设备已满,对读请求返回无穷null。
  4. /dev/random,内核的随机数产生器。它收集杂乱的数据,把他们连接在一起并进行单向散列运算,所得到的结果放入一个熵池,内核不断估算熵的位数目。

转载于:https://www.cnblogs.com/ggzwtj/archive/2011/10/19/2215379.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值