umask 函数为进程设置文件模式创建屏蔽字,并返回之前的值。
#includemode_t umask(mode_t cmask); // cmask 是 9 个文件访问权限位常量的按位或 /* 返回值:之前的文件模式创建屏蔽字 */
在进程创建一个新文件或新目录时,就一定会使用文件模式创建屏蔽字。在文件模式创建屏蔽字中为 1 的位,在文件 mode 中的相应位就会被关闭。
下例程序创建了两个文件,创建第一个时,umask 值为 0,创建第二个时,umask 值禁止了所有组和其他用户的访问权限。
#include#include #include #include #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 函数族。
#includeint 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 是下表中的常量(均位于头文件
下面是使用 chmod 函数改变 umask 示例中生成的 foo 和 bar 文件的示例。
#include#include #include 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 函数族。
#includeint 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 常量(它可选地定义在头文件
1、只有超级用户进程能更改该文件的用户 ID,以防用户改变其文件的所有者来摆脱磁盘空间限额的限制。
2、如果进程拥有此文件(即其有效用户 ID 等于该文件的用户 ID),参数 owner 等于 -1 或文件的用户 ID,并且 group 参数等于进程的有效组 ID 或进程的附属组 ID 之一,则一个非超级用户进程可以更改该文件的组 ID。
这意味着,当 _POSIX_CHOWN_RESTRICTED 有效时,不能更改其他用户文件的用户 ID,你可以更改你所拥有的文件的组 ID,但只能改到所属的组。如果这些函数由非超级用户进程调用,则在成功返回时,该文件的设置用户 ID 位和设置组 ID 位都将被清除。