UNIX环境高级编程-4.11-4.16

4.11 函数

chown,fchown,fchownat,lchown
下面几个chown函数可以改变文件的用户ID和组ID。如果两个参数owner或group中的任意一个是-1,则对应的ID不变。
#include<unistd.h>
int chown(const char*pathname,uid_t owner,git_t group);
int fchown(int fd,uid_t owner,gid_t group);
int fchownat(int fd,const char*pathname,uid_t owner,git_t group,int flag);
int lchown(const char*pathname,uid_t owner,gid_t group);
注意:显然,fchown不可能更改符号链接的属主和属组,但是fchownat可以通过设置参数flag为AT_SYMLINK_NOFOLLOW参数来改变符号链接的属主和属组。
注意:如果常亮_POSIX_CHOWN_RESTRICTED已经定义,那么,
    (1)只有超级用户进程能够更改该文件的属主ID。
    (2)如果进程拥有此文件(其有效用户ID=该文件的属主ID),参数owner=-1或文件的属主ID,并且参数group等于进程的有效组ID或进程的附属组ID之一,那么一个非超级用户进程可以更改该文件的属组ID。
    这意味着,不能更改其他用户的文件的属主ID,你可以更改你所拥有的文件的组ID,但是只能更改到你所属的组。
//写个程序测试以上规定:
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char**argv){
  //创建一个文件,打印该文件的属主和属组
  int fd=creat("aa.txt",0666);
  if(fd<0){
    printf("文件aa.txt创建失败\n");
    exit(0);
  }
  struct stat st;
  if(fstat(fd,&st)<0){
    printf("获取aa.txt文件的属性失败\n");
    exit(0);
  }
  printf("aa.txt的文件属主是:%d,属组是:%d\n",st.st_uid,st.st_gid);
  //现在更改此文件的属主ID,不同用户不拥有更改属主的权限,预计更改失败
  if(fchown(fd,1000,-1)<0){
    printf("不能将文件更改成属主为xcl,只有超级管理员才具有属主的更改权限\n");
  }else{
    printf("您的有效用户ID必定是root,文件aa.txt的属主成功更改为xcl\n");
  }
  //更改文件的属组,若设置了sgid,suid位,则更改属主的必定成功,并且下面的也必定成功
  if(fchown(fd,-1,1000)<0){
    printf("不能将属组改成xcl所在组,超级管理员,或者如果你拥有aa.txt并且您只能更改到您的有效组\n");
  }else{
    printf("您拥有此文件,并且将此文件的属组更改为您的有效组了,更改成功\n");
  }
  if(fstat(fd,&st)<0){
    printf("获取aa.txt文件的属性失败\n");
    exit(0);
  }
  printf("aa.txt的文件属主是:%d,属组是:%d\n",st.st_uid,st.st_gid);
  return 0;
}
//测试结果如下:
oracle@xcl:~/桌面$ ll main
-rwsr-xr-x 1 root root 10864  616 12:38 main*
oracle@xcl:~/桌面$ ./main
aa.txt的文件属主是:0,属组是:1001
您的有效用户ID必定是root,文件aa.txt的属主成功更改为xcl
您拥有此文件,并且将此文件的属组更改为您的有效组了,更改成功
aa.txt的文件属主是:1000,属组是:1000
oracle@xcl:~/桌面$

4.12 文件长度

stat结构成员st_size表一以字节为单位的文件的长度。此字段只对普通文件,目录文件和符号连接有意义。
注意:对于符号连接,文件长度是文件名中的实际字节数。
//编写程序,测试符号链接的文件长度
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char**argv){

  struct stat st;
  if(lstat("w",&st)<0){
    printf("获取w的属性失败\n");
    exit(0);
  }
  printf("w符号链接的文件大小是:%ld\n",st.st_size);

  return 0;
}
//测试结果如下:
xcl@xcl:~/桌面$ ./main
w符号链接的文件大小是:13
xcl@xcl:~/桌面$ ll w
lrwxrwxrwx 1 xcl xcl 13  616 12:53 w -> /etc/password
xcl@xcl:~/桌面$

4.13 文件截断

#include<unistd.h>
int truncate(const char*pathname,off_t length);
int ftruncate(int fd,off_t length);
这两个函数将现有文件阶段为指定的长度length,如果length小于文件长度,则length长度以后的字节都将截断,如果length大于文件长度,那么文件长度将增加,并且增加的部分将会是一个空洞。
//证明truncate会导致文件空洞
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char**argv){

  if(truncate("nohole",4096)<0){
    printf("文件阶段失败\n");
    exit(0);
  }
  return 0;
}
//结果如下:
xcl@xcl:~/桌面$ ll nohole
-rw-rw-r-- 1 xcl xcl 14  6月 16 13:08 nohole
xcl@xcl:~/桌面$ gcc -g -o main ./xcl.c
xcl@xcl:~/桌面$ ./main
xcl@xcl:~/桌面$ ll nohole
-rw-rw-r-- 1 xcl xcl 4096  6月 16 13:16 nohole
xcl@xcl:~/桌面$

4.15 函数link,linkat,unlink,unlinkat,remove

----------------------------------文件的硬链接的创建和删除----------------------------------------
#include<unistd.h>
int link(const char*pathname,const char*newpath);
int linkat(int efd,const char*existingpath,int nfd,const char*newpath,int flag);
注意:有很多文件系统实现不允许对目录的硬链接
注意:linkat的flag可以取三个值:0,AT_EMPTY_PATH,AT_SYMLINK_FOLLOW
#include<unistd.h>
int unlink(const char*pathname);
int unlinkat(int fd,const char*pathname,int flag);
默认unlinkat是删除文件,当flag设置了AT_REMOVEDIR的时候,unlinkat删除目录。
注意:flag参数给出了一种方法,使调用进程可以改变unlinkat函数的默认行为,当AT_REMOVEDIR标志被设置的时候,unlinkat函数可以类似于rmdir一样删除目录,如果这个标志被清除了,unlinkat与unlink执行同样的操作。
注意:这两个函数删除目录项,并允许pathname所引用的文件的链接计数减1。如果对该文件还有其他链接,则仍可通过其他连接访问该文件的数据。
注意:为了接触对文件的链接,必须对包含该目录项的目录具有写和执行权限,正如4.10所述,如果该目录还设置了粘连位S_ISVTX,那么必须对该目录具有写权限,并且满足下面三个条件之一:
    拥有该目录
    拥有该文件
    拥有超级管理员权限
只有当链接计数为0的时候该文件的内容才被删除。另一个条件也会阻止删除文件的内容,只要有进程打开了文件,其内容页不能被删除。关闭一个文件时,内核首先检查打开文件的进程个数,如果这个计数达到0,内核再去检查其链接计数,如果计数为0,那么就删除该文件的内容:要删除文件时,先检查打开该文件的进程计数,再检查链接计数,只有都为0才会删除该文件。
//编写一个程序,首先获取文件nohole的i节点编号,然后建立一个硬链接并获取该i节点编号,之后删除nohole文件
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char**argv){

  struct stat st;
  if(stat("nohole",&st)<0){
    printf("获取nohole的属性出错\n");
    exit(0);
  }
  printf("nohole的i节点编号是:%ld\n",st.st_ino);
  if(link("nohole","mynohole")<0){
    printf("创建链接错误\n");
    exit(0);
  }else{
    printf("成功创建链接\n");
    if(stat("mynohole",&st)<0){
      printf("获取mynohole信息失败\n");
      exit(0);
    }else{
      printf("mynohole的i节点是:%ld\n",st.st_ino);
    }
  }
  if(unlink("nohole")<0){
    printf("删除nohole失败\n");
    exit(0);
  }else{
    printf("删除链接成功\n");
  }

  return 0;
}
//结果如下:
xcl@xcl:~/桌面$ ./main
nohole的i节点编号是:26490467
成功创建链接
mynohole的i节点是:26490467
删除链接成功
------------------------
注意:进程调用open或creat创建一个文件,然后立即调用unlink,因为该文件仍旧是打开的,所以不会将其内容删除。只有进程关闭文件或终止时,该文件的内容才会被删除。
注意:如果pathname是符号链接,那么unlink删除该符号链接,而不是由该符号链接指向的文件,给出符号链接的情况下,没有一个函数能够删除由该符号链接引用的文件。
注意:如果文件系统支持的话,超级用户可以调用unlink,其参数指定一个目录,但是通常应该使用rmdir函数,而不是使用unlink方式。

#include<stdio.h>
int remove(const char*pathname);
-------------------------------------
对于文件remove和unlink相同,对于目录remove和rmdir相同。

4.16 函数rename和renameat

文件或目录可以用rename或renameat函数进行重命名。
#include<stdio.h>
int rename(const char*oldname,const char*newname);
int renameat(int oldfd,const char*oldname,int newfd,const char*newname);

//将目录bb命名为空目录aa
#include<strings.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char**argv){

  if(rename("bb","aa")<0){
    printf("重命名非空目录bb为aa失败,其中aa是已经存在的空目录\n");
    exit(0);
  }else{
    printf("重命名成功\n");
  }
  return 0;
}
//将非空目录重命名为非空目录将会失败
#include<strings.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char**argv){

  if(rename("bb","aa")<0){
    printf("重命名非空目录bb为aa失败,其中aa是已经存在的非空目录\n");
    exit(0);
  }else{
    printf("重命名成功\n");
  }
  return 0;
}
注意:rename是不跟随符号连接的
(1) 如果oldname指的是一个文件而不是目录,那么为该文件或符号连接重命名。
(2) 如果oldname值得是一个目录,那么为该目录重命名。
(3) 如果oldname或newname引用一个符号链接,则处理的是符号链接本身,而不是它引用的文件。
(4) 不能对.和..重命名。
(5) 如果oldname和newname相同,则直接返回。
(6) 如果newname已经存在,则调用进程对他需要有写权限,另外,调用进程将删除oldname目录项,并可能要创建newname项,所以进程需要对包含oldname和newname的目录具有写和执行权限。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值