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