文件权限 ID 修改函数介绍

    在前面“ 设置用户 ID 和设置组 ID”一节中,我们介绍了与每个文件相关的 9 个访问权限位,在此基础上我们可以说明与每个进程相关联的文件模式创建屏蔽字。
    umask 函数为进程设置文件模式创建屏蔽字,并返回之前的值。
#include <sys/stat.h>

mode_t umask(mode_t cmask);       // cmask 是 9 个文件访问权限位常量的按位或
              /* 返回值:之前的文件模式创建屏蔽字 */

    在进程创建一个新文件或新目录时,就一定会使用文件模式创建屏蔽字。在文件模式创建屏蔽字中为 1 的位,在文件 mode 中的相应位就会被关闭。
    下例程序创建了两个文件,创建第一个时,umask 值为 0,创建第二个时,umask 值禁止了所有组和其他用户的访问权限。
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>

#define RWRWRW (S_IRUSR |S_IWUSR |S_IRGRP |S_IWGRP |S_IROTH |S_IWOTH)

int main(void){
	umask(0);
	if(creat("foo", RWRWRW) < 0){
		printf("creat error for foo\n");
		exit(2);
	}
	umask(S_IRGRP |S_IWGRP |S_IROTH |S_IWOTH);
	if(creat("bar", RWRWRW) < 0){
		printf("creat error for bar\n");
		exit(2);
	}
	exit(0);
}

    执行结果如下(从中可看出更改子进程的文件模式创建屏蔽字不会影响父进程(常常为 shell )的屏蔽字):
$ umask                 # 先查看当前文件模式创建屏蔽字
0022
$ ./umaskDemo.out 
$ ls -l foo bar
-rw-------. 1 lei root 0 7月   2 23:55 bar
-rw-rw-rw-. 1 lei root 0 7月   2 23:55 foo
$ umask                 # 观察文件模式创建屏蔽字是否更改
0022

    尽管在登录时,shell 的启动文件会自行设置一次 umask 值,但当编写创建新文件的程序时,如果想确保指定的访问权限位已经激活,那么最好在进程运行时修改 umask 值,以免进程运行时有效的 umask 值关闭该权限位。

    更改 umask 值会影响所有随后的文件,如果想更改特定文件的访问权限,可使用 chmod 函数族。
#include <sys/stat.h>

int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int fchmodat(int fd, const char *pathname, node_t mode, int flag);
                       /* 返回值:若成功,都返回 0;否则,都返回 -1 */

    chmod 函数在制定的文件上进行操作,而 fchmod 函数则对已打开的文件进行操作。fchmodat 函数与 chmod 函数在这两种情况下是相同的:一种是 pathname 参数为绝对路径,另一种是 fd 参数取值为 AT_FDCWD 而 pathname 为相对路径。否则,fchmodat 计算相对于打开目录(由 fd 指向)的 pathname。flag 参数可以改变 fchmodat 的行为,当设置了 AT_SYMLINK_NOFOLLOW 标志时,fchmodat 并不会跟随符号链接。
    要改变一个文件的权限位,进程的有效用户 ID 必须等于文件的所有者 ID,或者该进程必须具有超级用户权限。
    参数 mode 是下表中的常量(均位于头文件 <sys/stat.h> 中)的按位或:


    下面是使用 chmod 函数改变 umask 示例中生成的 foo 和 bar 文件的示例。
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

int main(void){
	struct stat statbuf;

	if(stat("foo", &statbuf) < 0){
		printf("stat error for foo\n");
		exit(2);
	}
	// turn on set-group-ID and turn off group-execute.
	if(chmod("foo",(statbuf.st_mode & ~S_IXGRP) |S_ISGID) < 0){
		printf("chmod error for foo\n");
		exit(2);
	}

	// set absolute mode to "rw-r--r--"
	if(chmod("bar", S_IRUSR |S_IWUSR |S_IRGRP |S_IROTH) < 0){
		printf("chmod error for bar\n");
		exit(2);
	}
	exit(0);
}

    执行结果:
$ ls -l foo bar        # 开始的文件权限位
-rw-------. 1 lei root 0 7月   3 23:39 bar
-rw-rw-rw-. 1 lei root 0 7月   3 23:39 foo
 
$ ./chmodDemo.out 
 
$ ls -l foo bar        # 改变后的文件权限位
-rw-r--r--. 1 lei root 0 7月   3 23:39 bar
-rw-rwSrw-. 1 lei root 0 7月   3 23:39 foo    # Solaris 中显示的是“l”而非“S”

    这里可看出,ls 命令列出的时间和日期并没有改变。这是因为 chmod 更新的只是 i 节点最近一次被更改的时间,而“ls -l”命令默认列出的是最后修改文件内容的时间。
    chmod 函数在下列条件下自动清除两个权限位。
    1、Solaris 等系统对用于普通文件的粘着位赋予了特殊含义(此时如果任何执行位都没有设置,那么操作系统就不会缓存文件内容),在这些系统上如果试图设置普通文件的粘着位(S_ISVTX),而又没有超级用户权限,那么 mode 中的粘着位会被自动关闭。
    2、新创建文件的组 ID 可能不是调用进程所属的组。特别地,当新文件的组 ID 不等于进程的有效组 ID 或者进程附属组 ID 中的一个,而且进程又没有超级用户权限,那么设置组 ID 位就被自动关闭。这就防止了用户创建一个设置组 ID 文件,而该文件是由并非该用户所属的组拥有的。
  
    下面再继续介绍用来更改文件的用户 ID 和组 ID 的 chown 函数族。
#include <unistd.h>

int chown(const char *pathname, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int fchownat(int fd, const char *pathname, uid_t owner, gid_t group, int flag);
int lchown(const char *pathname, uid_t, gid_t group);
                     /* 返回值:若成功,都返回 0;否则,都返回 -1 */

    其中,如果两个参数 owner 或 group 中的任意一个为 -1,则对应的 ID 不变。
    fchown 函数改变 fd 参数指向的打开文件的所有者,既然它在一个已打开的文件上操作,就不能用于改变符号链接的所有者。
    fchownat 函数与 chown 或者 lchown 函数在下面两种情况下是相同的:一种是 pathname 为绝对路径,另一种是 fd 参数取值为 AT_FDCWD 而 pathname 为相对路径。这两种情况下,如果 flag 设置了 AT_SYMLINK_NOFOLLOW 标志,fchownat 与 lchown 行为相同,如果 flag 清除了 AT_SYMLINK_NOFOLLOW 标志,则 fchownat 与 chown 行为相同。如果 fd 参数设置为打开目录的文件描述符,并且 pathname 是相对路径,fchownat 就计算相对于打开目录的 pathname。
    若 _POSIX_CHOWN_RESTRICTED 常量(它可选地定义在头文件 <unistd.h> 中,可以使用 pathconf 或 fpathconf 函数查询)对指定的文件生效,则 POSIX.1 规定:
    1、只有超级用户进程能更改该文件的用户 ID,以防用户改变其文件的所有者来摆脱磁盘空间限额的限制。
    2、如果进程拥有此文件(即其有效用户 ID 等于该文件的用户 ID),参数 owner 等于 -1 或文件的用户 ID,并且 group 参数等于进程的有效组 ID 或进程的附属组 ID 之一,则一个非超级用户进程可以更改该文件的组 ID。
    这意味着,当 _POSIX_CHOWN_RESTRICTED 有效时,不能更改其他用户文件的用户 ID,你可以更改你所拥有的文件的组 ID,但只能改到所属的组。如果这些函数由非超级用户进程调用,则在成功返回时,该文件的设置用户 ID 位和设置组 ID 位都将被清除。

猜你喜欢

转载自aisxyz.iteye.com/blog/2382492