UNIX环境高级编程-4.11-4.16
2016-06-16 20:44
459 查看
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 6月 16 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 6月 16 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:0 e563 8 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的目录具有写和执行权限。
相关文章推荐
- Unix 即将迎来 50 岁
- Linux VS Unix:Linux欲一统天下 Unix不死
- C语言实现字符转unix时间戳的简单实例
- 看UNIX高手的10个习惯第1/2页
- 通过PHP修改Linux或Unix口令的方法分享
- mysql unix准换时间格式查找指定日期数据代码
- php strtotime 函数UNIX时间戳
- 安全检测Unix和Linux服务器安全设置入门精讲
- 分享20个Unix/Linux 命令技巧
- PHP使用gmdate实现将一个UNIX 时间格式化成GMT文本的方法
- Linux/UNIX和Window平台上安装Mysql
- Mac OS下配置远程Linux 服务器SSH密钥认证自动登录
- 读The How and Why of User Private Groups in Unix
- C的温故知新
- 使用golang和docker守护进程交互
- 在Linux和Unix中使用chmod命令改变文件权限的用法
- python UNIX_TIMESTAMP时间处理方法分析
- Linux/UNIX和Window平台上安装Mysql
- Alice梦游UNIX仙境
- LINUX之前UNIX的历史