linux c检查修改文件属性

2018-06-06 23:33:23

相关系统调用:utime(),utimes(),chown(),lchown(),fchown(),access(),umask(),chmod()

修改文件时间

修改文件的访问时间和修改时间可用 utime()或与之相关的系统调用,使用 tar 和 zip 之类的程序会使用这些系统调用去重置时间,只要调用成功,状态变更时间会变成当前时间。

#include <utime.h>

struct utimbuf{
    time_t actime;
    time_t modtime;
}

int utime(const char *pathname, const struct utimbuf *buf);

//成功返回0,失败-1

如果buf为 NULL,则会用当前时间来修改。

#include <sys/time.h>

struct timeval{
    time_t tv_sec;        /*自1970-01-01 00:00:00到现在的秒数*/
    suseconds_t tv_usec;	/*额外的微妙数*/
};

int utimes(const char *pathname, const struct timeval tv[2]);

//成功返回0,失败-1

功能几乎一样,只是utimes 可以以微妙级别改变。

例子1

#include <sys/stat.h>
#include <utime.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
    char *pathname;
    struct stat sb;
    struct utimbuf utb;

    if (argc != 2 || strcmp(argv[1], "--help") == 0){
        printf("%s file\n", argv[0]);
        return 1;
    }

    pathname = argv[1];

    //获取当前文件时间
    if (stat(pathname, &sb) == -1)
        return 1;

    //把最近修改时间改成访问时间
    utb.actime = sb.st_atime;
    utb.modtime = sb.st_atime;        /* Make modify time same as access time */
    if (utime(pathname, &utb) == -1)  /* Update file times */
        return 1;

    return 0;
}
[root@izj6cfw9yi1iqoik31tqbgz c]# stat a.c
...
Access: 2018-05-14 11:55:50.567914966 +0800
Modify: 2018-05-14 11:55:17.615457958 +0800
Change: 2018-05-14 11:55:17.615457958 +0800

[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out a.c
[root@izj6cfw9yi1iqoik31tqbgz c]# stat a.c
...
#从这里可以看出,只能支持到秒级别
Access: 2018-05-14 11:55:50.000000000 +0800
Modify: 2018-05-14 11:55:50.000000000 +0800
Change: 2018-06-06 22:54:33.399565288 +0800

例子2

#include <sys/stat.h>
#include <sys/time.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
    struct stat sb;
    struct timeval tv[2];

    if (argc != 2 || strcmp(argv[1], "--help") == 0){
        printf("%s file\n", argv[0]);
        return 1;
    }
        

    if (stat(argv[1], &sb) == -1){
        printf("stat");
        return 1;
    } 

    tv[0].tv_sec = sb.st_atime;
    tv[0].tv_usec = 223344;
    tv[1].tv_sec = sb.st_atime;
    tv[1].tv_usec = 667788;

    if (utimes(argv[1], tv) == -1){
        printf("utimes");
        return 1;
    }
        
    return 0;
}
[root@izj6cfw9yi1iqoik31tqbgz c]# stat a.c
...
Access: 2018-06-06 23:02:50.730426714 +0800
Modify: 2018-06-06 23:02:25.400841913 +0800
Change: 2018-06-06 23:02:25.402841880 +0800

[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out a.c

#下面已经精确到了微妙级别
[root@izj6cfw9yi1iqoik31tqbgz c]# stat a.c
...
Access: 2018-06-06 23:02:50.223344000 +0800
Modify: 2018-06-06 23:02:50.667788000 +0800
Change: 2018-06-06 23:03:09.330121823 +0800

还有下面几个系统调用跟前面大同小异,留给读者自己试验,可以参考 man7手册

#include <sys/time.h>
int futimes(int fd, const struct timeval tv[2]);
int lutimes(const char *pathname, const struct timeval tv[2]);

#define _XOPEN_SOURCE 700 /* Or define _POSIX_C_SOURCE >= 200809 */
#include <sys/stat.h>
int utimensat(int dirfd, const char *pathname,
const struct timespec times[2], int flags);

#include _GNU_SOURCE
#include <sys/stat.h>
int futimens(int fd, const struct timespec times[2]);

修改文件拥有者

#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);

#define _XOPEN_SOURCE 500 /* Or: #define _BSD_SOURCE */
#include <unistd.h>

#如果是符号链接,只改变符号链接文件本身。
int lchown(const char *pathname, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);

例子

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{

    if (argc != 4 || strcmp(argv[1], "--help") == 0){
        printf("%s file uid gid\n", argv[0]);
        return 1;
    }
    
    if (chown(argv[1], atoi(argv[2]), atoi(argv[3]) ) == -1) {
        return 1;
    }

    return 0;
}
[root@izj6cfw9yi1iqoik31tqbgz c]# cat /etc/passwd | grep nobody
nobody:x:99:99:Nobody:/tmp:/bin/bash

[root@izj6cfw9yi1iqoik31tqbgz c]# ll a.c
-rw-r--r-- 1 root root 191 Jun  6 23:02 a.c

[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out a.c 99 99
[root@izj6cfw9yi1iqoik31tqbgz c]# ll a.c
-rw-r--r-- 1 nobody nobody 191 Jun  6 23:02 a.c

文件权限

stat 结构中 st_mod 字段低12位定义了文件权限。其中前3位为专用位,分别是 set-user-ID 位、set-group-ID 位和sticky 位(途中的U、G、T)。

符号链接自创建起,其所有权限为所有用户共享,且这些权限不得更改。对符号链接解引用将忽略这些权限,任何人只要父目录有写权限,都可以删除符号链接(除非父目录设置了黏着位,这时,删除符号链接,就会先检查符号链接的权限,再来决定是否可以删除)。


头文件<sys/stat.h> 定义了可与stat结构中st_mode 想与的常量(8进制)。

常量  其他值权限位 
 S_ISUID 04000 set-user-ID
 S_ISGID 02000 set-group-ID
 S_ISVTX 01000 sticky
 S_IRUSR 0400 用户读
 S_IWUSR 0200 用户写
 S_IXUSR 0100 用户执行
 S_IRGRP 040 组读
 S_IWGRP 020 组写
 S_IXGRP 010 组执行
 S_IROTH 04 其他读
 S_IWOTH 02 其他写
 S_IXOTH 01 其他执行

目录权限

目录与文件拥有相同的权限方案,只是对3中权限的含义另有所指。

读:可列出目录之下的内容(即目录下的文件名)。
写:可在目录内创建、删除文件。注意,要删除文件,对文件本身无需有任何权限。
执行:可访问目录中的文件。有时也称为搜索权限

读权限可以让我们知道该目录下有哪些文件(列出文件名),但是如果你已经知道了文件名,则只要有执行权限(x)即可访问文件。在控制对公共目录内容访问时,这是一种常见的技术。

检查对文件的访问权限:access()

access() 会根据进程的真实用户id和组id(以及附属组id),去检查pathname参数所指定的文件访问权限。如果pathname为符号链接,access会对其解引用。

#include <unistd.h>
int access(const char *pathname, int mode);

//成功返回0, 失败-1

参数mode是下表中常量相或(|),只要有一项权限未得到满足(或发生错误)返回-1。

 常量描述 
 F_OK 有这个文件吗
 R_OK 有读权限吗
 W_OK 有写权限吗
 X_OK 有执行权限吗

黏着位:t位

set-user-id 和 set-group-id请参照 linux进程凭证(权限)。对目录设置了黏着位,那么仅当非特权进程对目录有写权限,且为文件或目录的属主时,餐能对目录下的文件进行删除( unlink()、rmdir() )和重命名( rename() )操作。对文件设置黏着位,ls -l 就会在 其他用户执行位 显示t 或者 T。linux系统上的/tmp目录就设置了黏着位。

文件掩码:umask()

umask的作用是在新建文件时,来指定屏蔽掉哪些权限位,掩码会从父进程继承。创建普通文件时,默认的权限为 0666, 创建目录时,默认的权限位为 0777,而一般默认的掩码为 022。

所以当我们创建新普通文件权限为 rw-r--r--(0644),创建新目录权限为 rwxr-xr-x(0755)。

#include <sys/stat.h>

mode_t umask(mode_t mask);

//总是成功,返回进程之前的umask

更改文件权限

#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);

#define _XOPEN_SOURCE 500 /* Or: #define _BSD_SOURCE */
#include <sys/stat.h>
int fchmod(int fd, mode_t mode);

//成功返回0,失败-1

如果文件为符号链接,都会先解引用。

//手动指定新权限
chmod("myfile", S_IRUSR | S_IRGRP | S_IROTH);
chmod("myfile", 0444);

//更改特定权限等同于
//chmod u+w,o-r myfile
struct stat sb;
mode_t mode;

stat("myfile", &sb);
mode = (sb.st_mode | S_IWUSR) & ~S_IROTH;
chmod("myfile", mode);

例子

#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

#define MYFILE "myfile"
#define MYDIR  "mydir"
#define FILE_PERMS    (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
#define DIR_PERMS     (S_IRWXU | S_IRWXG | S_IRWXO)
#define UMASK_SETTING (S_IWGRP | S_IXGRP | S_IWOTH | S_IXOTH)
#define FP_SPECIAL 1
#define STR_SIZE sizeof("rwxrwxrwx")

char *filePermStr(mode_t perm, int flags);

char * filePermStr(mode_t perm, int flags){
    static char str[STR_SIZE];

    snprintf(str, STR_SIZE, "%c%c%c%c%c%c%c%c%c",
        (perm & S_IRUSR) ? 'r' : '-', (perm & S_IWUSR) ? 'w' : '-',
        (perm & S_IXUSR) ?
            (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
            (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
                
        (perm & S_IRGRP) ? 'r' : '-', (perm & S_IWGRP) ? 'w' : '-',
        (perm & S_IXGRP) ?
            (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
            (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
                
        (perm & S_IROTH) ? 'r' : '-', (perm & S_IWOTH) ? 'w' : '-',
        (perm & S_IXOTH) ?
            (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 't' : 'x') :
            (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 'T' : '-'));

    return str;
}

int
main(int argc, char *argv[])
{
    int fd;
    struct stat sb;
    mode_t u;

    umask(UMASK_SETTING);

    fd = open(MYFILE, O_RDWR | O_CREAT | O_EXCL, FILE_PERMS);
    if (fd == -1)
        return 1;
    if (mkdir(MYDIR, DIR_PERMS) == -1)
        return 1;

    u = umask(0);               /* Retrieves (and clears) umask value */

    if (stat(MYFILE, &sb) == -1)
        return 2;
    printf("Requested file perms: %s\n", filePermStr(FILE_PERMS, 0));
    printf("Process umask:        %s\n", filePermStr(u, 0));
    printf("Actual file perms:    %s\n\n", filePermStr(sb.st_mode, 0));

    if (stat(MYDIR, &sb) == -1)
        return 3;
    printf("Requested dir. perms: %s\n", filePermStr(DIR_PERMS, 0));
    printf("Process umask:        %s\n", filePermStr(u, 0));
    printf("Actual dir. perms:    %s\n", filePermStr(sb.st_mode, 0));

    if (unlink(MYFILE) == -1)
        return 4;
    if (rmdir(MYDIR) == -1)
        return 5;
    
    return 0;
}
[root@izj6cfw9yi1iqoik31tqbgz c]# gcc main.c
[root@izj6cfw9yi1iqoik31tqbgz c]# ./a.out 
Requested file perms: rw-rw----
Process umask:        ----wx-wx
Actual file perms:    rw-r-----

Requested dir. perms: rwxrwxrwx
Process umask:        ----wx-wx
Actual dir. perms:    rwxr--r--


总结

本文对linux c检查修改文件属性做了简单的介绍,如果有疑问可以给我留言。


备注
1.编译器版本gcc4.8,运行环境centos7 64位
2.原文地址http://www.freecls.com/a/2712/49

©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。