Linux下用C语言实现lsattr/chattr命令的功能


=Start=

缘由:

学习、提高需要

正文:

参考解答:

之前对ioctl()函数不熟,导致在用它实现lsattr/chattr命令的过程中踩了几个坑,差点要放弃了,还好后来有一个成功的例子可以参考,以便对比两者之间的不同之处,从而找打问题所在。先给出修改后可以用的版本:

/* setflags.c - set the ext2 attributes for a set of files */
/* The first parameter to this program is a string consisting of
   0 (an empty string is okay) or more of the letters I, A, S, and
   N. This string specifies which ext2 attributes should be turned
   on for the files which are specified on the rest of the command
   line -- the rest of the attributes are turned off. The letters
   stand for immutable, append-only, sync, and nodump, respectively.
   For example, the command "setflags IN file1 file2" turns on the
   immutable and nodump flags for files file1 and file2, but turns
   off the sync and append-only flags for those files. */
#include <errno.h>
#include <fcntl.h>
#include <linux/ext2_fs.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main(int argc, char ** argv) {
    char ** filename = argv + 1;
    int fd;
    int old_flags = 0;
    
    /* make sure the flags to set were specified, along with
       some file names */
    if (argc < 3) {
        fprintf(stderr, "setflags usage: [I][A][S][N] <filenames>\n");
        return 1;
    }
    printf("FS_IMMUTABLE_FL = 0x%08x, %d\n", FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
    printf("FS_APPEND_FL = 0x%08x, %d\n", FS_APPEND_FL, FS_APPEND_FL);
    printf("FS_SYNC_FL = 0x%08x, %d\n", FS_SYNC_FL, FS_SYNC_FL);
    printf("FS_NODUMP_FL = 0x%08x, %d\n", FS_NODUMP_FL, FS_NODUMP_FL);
    /* iterate over all of the file names in argv[] */
    while (*(++filename)) {
        /* Unlike normal attributes, ext2 attributes can only
         be set if we have a file descriptor (a file name isn't sufficient).
         We don't need write access to set the ext2 attributes, so O_RDONLY is fine. */
        fd = open(*filename, O_RDONLY);
        if (fd < 0) {
            fprintf(stderr, "cannot open %s: %s\n", *filename, strerror(errno));
            continue;
        }
        /* Sets the attributes as specified by the contents of flags. */
        if (ioctl(fd, FS_IOC_GETFLAGS, &old_flags) == -1) {
            fprintf(stderr, "ioctl failed on %s: %s\n", *filename, strerror(errno));
            close(fd);
            continue;
        else {
            printf("current attrs: 0x%08x\n", old_flags);
            // old_flags |= FS_IMMUTABLE_FL;
            // printf("\nold_flags |= FS_IMMUTABLE_FL >>> old_flags = 0x%08x\n", old_flags);
        }
        if (strchr(argv[1], 'I')) old_flags |= FS_IMMUTABLE_FL;
        if (strchr(argv[1], 'A')) old_flags |= FS_APPEND_FL;
        if (strchr(argv[1], 'S')) old_flags |= FS_SYNC_FL;
        if (strchr(argv[1], 'N')) old_flags |= FS_NODUMP_FL;
        printf("\nold_flags = 0x%08x\n", old_flags);
        /* Sets the attributes as specified by the contents of flags. */
        if (ioctl(fd, FS_IOC_SETFLAGS, &old_flags) == -1) {
            fprintf(stderr, "ioctl failed on %s: %s\n", *filename, strerror(errno));
            close(fd);
            continue;
        }
        close(fd);
    }
    return 0;
}

然后再说一下在修改过程中碰到的问题:

问题一、无法编译成功,报错如下:

/usr/include/linux/ext2_fs.h: In function ‘ext2_mask_flags’:
/usr/include/linux/ext2_fs.h:182: error: ‘FS_DIRSYNC_FL’ undeclared (first use in this function)
/usr/include/linux/ext2_fs.h:182: error: (Each undeclared identifier is reported only once
/usr/include/linux/ext2_fs.h:182: error: for each function it appears in.)
/usr/include/linux/ext2_fs.h:182: error: ‘FS_TOPDIR_FL’ undeclared (first use in this function)
/usr/include/linux/ext2_fs.h:184: error: ‘FS_NODUMP_FL’ undeclared (first use in this function)
/usr/include/linux/ext2_fs.h:184: error: ‘FS_NOATIME_FL’ undeclared (first use in this function)

原因:

需要在「#include <linux/ext2_fs.h>」之前先引入「#include <linux/fs.h>」,有一个顺序关系!

问题二:执行「ioctl(fd, FS_IOC_SETFLAGS, &flags)」时报错「Operation not supported」
current flags: 0x00080000
desire flags = 0x00080010
ioctl(flags) = 0x00000010
ioctl failed on filename: Operation not supported

原因:

在语句「ioctl(fd, FS_IOC_SETFLAGS, &flags)」里面使用的flags变量的值不能是0和FS_IMMUTABLE_FL等变量的异或值,flags的初始值最好为文件原先的属性值(一般为524288,十六进制表示为0x00080000),再和需要设置的FS_IMMUTABLE_FL等变量进行异或,然后作为ioctl的参数才不会报错。

所以,推荐的解决办法就是先「ioctl(fd, FS_IOC_GETFLAGS, &old_flags)」,然后对old_flags进行异或,最后执行「ioctl(fd, FS_IOC_SETFLAGS, &old_flags)」。

参考链接:

#函数手册
http://man7.org/linux/man-pages/man2/ioctl.2.html
http://man7.org/linux/man-pages/man2/ioctl_iflags.2.html

#错误版本(只差一点点)
https://stackoverflow.com/questions/16961592/performing-the-equivalent-of-chattr-i-filename-txt-from-linux-application
http://www.danlj.org/lad/src/setflags.c.html
https://stackoverflow.com/questions/32488017/how-do-i-chattr-i-an-open-file-descriptor-in-c

#成功版本(写得太复杂了)
https://github.com/posborne/linux-programming-interface-exercises/blob/master/15-file-attributes/chattr.c

#FS_DIRSYNC_FL等变量未定义错误
https://stackoverflow.com/questions/6969303/whats-wrong-with-linux-ext2-fs-h
https://ubuntuforums.org/showthread.php?t=1408802
http://forum.ubuntu.org.cn/viewtopic.php?f=77&t=319496

=END=

, ,

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注