libcutils-system工具库

概述

android/system/core/libcutils系统的一些工具库

源码解析

1. uevent模块

1.1 uevent_kernel_multicast_recv

ssize_t uevent_kernel_multicast_recv(int socket, void* buffer, size_t length) {
    uid_t uid = -1;
    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
}

ssize_t uevent_kernel_multicast_uid_recv(int socket, void* buffer, size_t length, uid_t* uid) {
    return uevent_kernel_recv(socket, buffer, length, true, uid);
}

ssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) {
    struct iovec iov = {buffer, length};
    struct sockaddr_nl addr;
    char control[CMSG_SPACE(sizeof(struct ucred))];
    struct msghdr hdr = {
        &addr, sizeof(addr), &iov, 1, control, sizeof(control), 0,
    };
    struct ucred* cred;

    *uid = -1;
    // 接收socket的消息
    ssize_t n = TEMP_FAILURE_RETRY(recvmsg(socket, &hdr, 0));
    if (n <= 0) {
        return n;
    }

    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
        /* ignoring netlink message with no sender credentials */
        goto out;
    }

    cred = (struct ucred*)CMSG_DATA(cmsg);
    *uid = cred->uid;

    if (addr.nl_pid != 0) {
        /* ignore non-kernel */
        goto out;
    }
    if (require_group && addr.nl_groups == 0) {
        /* ignore unicast messages when requested */
        goto out;
    }

    return n;

out:
    /* clear residual potentially malicious data */
    bzero(buffer, length);
    errno = EIO;
    return -1;
}

1.2 uevent_open_socket

int uevent_open_socket(int buf_sz, bool passcred) {
    struct sockaddr_nl addr;
    int on = passcred;
    int buf_sz_readback = 0;
    socklen_t optlen = sizeof(buf_sz_readback);
    int s;

    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    addr.nl_groups = 0xffffffff;
// SOCK_CLOEXEC表示这个句柄我在fork子进程后执行exec时就关闭
    // SOCK_DGRAM 是无保障的面向消息的socket,主要用于在网络上发广播信息。
    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
    if (s < 0) return -1;
// 设置buffer的大小
    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||
          getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {
        close(s);
        return -1;
    }
    /* Only if SO_RCVBUF was not effective, try SO_RCVBUFFORCE. Generally, we
     * want to avoid SO_RCVBUFFORCE, because it generates SELinux denials in
     * case we don't have CAP_NET_ADMIN. This is the case, for example, for
     * healthd. */
    if (buf_sz_readback < 2 * buf_sz) {
        if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)) < 0) {
            close(s);
            return -1;
        }
    }

    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
// 连接socket
    if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        close(s);
        return -1;
    }

    return s;
}

2. android_get_control_file模块

int android_get_control_file(const char* path) {
    std::string given_path;
    // 看一下真实的路径是否对了
    if (!android::base::Realpath(path, &given_path)) return -1;

    // Try path, then realpath(path), as keys to get the fd from env.
    auto fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);
    if (fd < 0) {
        fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, given_path.c_str());
        if (fd < 0) return fd;
    }

    // Find file path from /proc and make sure it is correct
    auto proc = android::base::StringPrintf("/proc/self/fd/%d", fd);
    std::string fd_path;
    if (!android::base::Realpath(proc, &fd_path)) return -1;
// 看一下打开的真实路径是否一致
    if (given_path != fd_path) return -1;
    // It is what we think it is

    return fd;
}
// ANDROID_FILE_pathname
int __android_get_control_from_env(const char* prefix, const char* name) {
    if (!prefix || !name) return -1;

    char *key = NULL;
    if (asprintf(&key, "%s%s", prefix, name) < 0) return -1;
    if (!key) return -1;

    char *cp = key;
    while (*cp) {
        // 剔除识别不料的字符
        if (!isalnum(*cp)) *cp = '_';
        ++cp;
    }
// 通过getenv获取文件的fd
    const char* val = getenv(key);
    free(key);
    if (!val) return -1;

    errno = 0;
    // 将字符串fd转成long格式fd
    long fd = strtol(val, NULL, 10);
    if (errno) return -1;

    // Since we are inheriting an fd, it could legitimately exceed _SC_OPEN_MAX
    if ((fd < 0) || (fd > INT_MAX)) return -1;

    // Still open?
    // 是否还打开着
    if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1;

    return static_cast<int>(fd);
}

3. 分区工具

3.1 partition_wiped-判断分区是否被擦除了-读4096个字节,看是否全部是0或者全部是1

int partition_wiped(const char* source) {
    uint8_t buf[4096];
    int fd, ret;

    if ((fd = open(source, O_RDONLY)) < 0) {
        return 0;
    }

    ret = read(fd, buf, sizeof(buf));
    close(fd);

    if (ret != sizeof(buf)) {
        return 0;
    }

    /* Check for all zeros */
    if (only_one_char(buf, sizeof(buf), 0)) {
       return 1;
    }

    /* Check for all ones */
    if (only_one_char(buf, sizeof(buf), 0xff)) {
       return 1;
    }

    return 0;
}
static int only_one_char(uint8_t *buf, int len, uint8_t c)
{
    int i, ret;

    ret = 1;
    for (i=0; i<len; i++) {
        if (buf[i] != c) {
            ret = 0;
            break;
        }
    }
    return ret;
}

4. 文件系统

4.1 fs_mkdirs-创建文件夹

// /metadata/vold/metadata_encryption/key 0700
int fs_mkdirs(const char* path, mode_t mode) {
    // 必须要是绝对路径
    if (*path != '/') {
        ALOGE("Relative paths are not allowed: %s", path);
        return -EINVAL;
    }
// 先打开根目录
    int fd = open("/", 0);
    if (fd == -1) {
        ALOGE("Failed to open(/): %s", strerror(errno));
        return -errno;
    }

    struct stat sb;
    int res = 0;
    char* buf = strdup(path);
    // metadata/vold/metadata_encryption/key
    char* segment = buf + 1;
    // metadata/vold/metadata_encryption/key
    char* p = segment;
    // 并没有创建key文件夹
    while (*p != '\0') {
        // 一直到/
        if (*p == '/') {
            // p为'\0',那么segment就为metadata了
            *p = '\0';

            if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
                ALOGE("Invalid path: %s", buf);
                res = -EINVAL;
                goto done_close;
            }

            if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
                if (errno == ENOENT) {
                    /* Nothing there yet; let's create it! */
                    //  如果不存在,则创建文件夹
                    if (mkdirat(fd, segment, mode) != 0) {
                        if (errno == EEXIST) {
                            /* We raced with someone; ignore */
                        } else {
                            ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
                            res = -errno;
                            goto done_close;
                        }
                    }
                } else {
                    ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
                    res = -errno;
                    goto done_close;
                }
            } else {
                // 如果存在,则不能是符号链接
                if (S_ISLNK(sb.st_mode)) {
                    ALOGE("Symbolic links are not allowed: %s", buf);
                    res = -ELOOP;
                    goto done_close;
                }	// 如果存在,则必须是文件夹
                if (!S_ISDIR(sb.st_mode)) {
                    ALOGE("Existing segment not a directory: %s", buf);
                    res = -ENOTDIR;
                    goto done_close;
                }
            }

            /* Yay, segment is ready for us to step into */
            int next_fd;
            // 打开metadata文件夹
            if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) {
                ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
                res = -errno;
                goto done_close;
            }

            close(fd);
            fd = next_fd;

            *p = '/';	// 恢复回来
            segment = p + 1;	// 下一个
        }
        p++;
    }

done_close:
    close(fd);
    free(buf);
    return res;
}

补充

1. netlink和uevent事件

问题

参考

1. 关于linux进程间的close-on-exec机制
https://blog.csdn.net/ljxfblog/article/details/41680115
posted @ 2021-06-10 19:29  pyjetson  阅读(606)  评论(0编辑  收藏  举报