<Android开发> Android vold - 第七篇 vold 的runCommand()方法解析

本系列主要介绍 Android vold,分为以下篇章
<Android开发> Android vold - 第一篇 vold前言简介
<Android开发> Android vold - 第二篇 vold 的main()函数简介
<Android开发> Android vold - 第三篇 vold 的NetLinkManager类简介
<Android开发> Android vold - 第四篇 vold 的NetlinkHandler类简介
<Android开发> Android vold - 第五篇 vold 的VolumeManager类简介
<Android开发> Android vold - 第六篇 vold 的CommandListener类简介
<Android开发> Android vold - 第七篇 vold 的runCommand()方法解析

继第六篇vold 的CommandListener类涉及的FW数据处理的一些cmd类及类方法;接下具体分析一下上报函数sendMsg()和 接收处理函数runCommand()函数。

10 runCommand()方法解析
第10节的内容主要时对每一个cmd命令解析的过程进行分析。

10.1SocketClient ::sendMsg()方法
在讲解runCommand()之前先来了解一下SocketClient ::sendMsg()方法,这个方法是发送数据给SocketClient 的,vold与FW等SocketClient 通信是依靠这个接口的。

路径:LINUX/android/system/core/libsysutils/src/SocketClient.cpp
int SocketClient::sendMsg(int code, const char *msg, bool addErrno) {
    return sendMsg(code, msg, addErrno, mUseCmdNum);
}
//组合data所需要的信息,包括cmd 和 code.
int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum) {
    char *buf;
    int ret = 0;

    if (addErrno) {
        if (useCmdNum) {
            ret = asprintf(&buf, "%d %d %s (%s)", code, getCmdNum(), msg, strerror(errno));
        } else {
            ret = asprintf(&buf, "%d %s (%s)", code, msg, strerror(errno));
        }
    } else {
        if (useCmdNum) {
            ret = asprintf(&buf, "%d %d %s", code, getCmdNum(), msg);
        } else {
            ret = asprintf(&buf, "%d %s", code, msg);
        }
    }
    // Send the zero-terminated message
    if (ret != -1) {
        ret = sendMsg(buf);
        free(buf);
    }
    return ret;
}

int SocketClient::sendMsg(const char *msg) {
    // Send the message including null character
    if (sendData(msg, strlen(msg) + 1) != 0) {
        SLOGW("Unable to send msg '%s'", msg);
        return -1;
    }
    return 0;
}
//转换传输的数据格式
int SocketClient::sendData(const void *data, int len) {
    struct iovec vec[1];
    vec[0].iov_base = (void *) data;
    vec[0].iov_len = len;

    pthread_mutex_lock(&mWriteMutex);
    int rc = sendDataLockedv(vec, 1);
    pthread_mutex_unlock(&mWriteMutex);

    return rc;
}

int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {

    if (mSocket < 0) {
        errno = EHOSTUNREACH;
        return -1;
    }

    if (iovcnt <= 0) {
        return 0;
    }

    int ret = 0;
    int e = 0; // SLOGW和sigaction对于errno不是惰性的
    int current = 0;

    struct sigaction new_action, old_action;
    memset(&new_action, 0, sizeof(new_action));
    new_action.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &new_action, &old_action);

    for (;;) {
        ssize_t rc = TEMP_FAILURE_RETRY(
            writev(mSocket, iov + current, iovcnt - current));

        if (rc > 0) {
            size_t written = rc;
            while ((current < iovcnt) && (written >= iov[current].iov_len)) {
                written -= iov[current].iov_len;
                current++;
            }
            if (current == iovcnt) {
                break;
            }
            iov[current].iov_base = (char *)iov[current].iov_base + written;
            iov[current].iov_len -= written;
            continue;
        }

        if (rc == 0) {
            e = EIO;
            SLOGW("0 length write :(");
        } else {
            e = errno;
            SLOGW("write error (%s)", strerror(e));
        }
        ret = -1;
        break;
    }

    sigaction(SIGPIPE, &old_action, &new_action);

    if (e != 0) {
        errno = e;
    }
    return ret;
}

由上述函数调用可分析出SocketClient ::sendMsg()方法最后是调用 writev(mSocket, iov + current, iovcnt - current));进行写数据发送的。

10.2 DumpCmd::runCommand()方法

路径:LINUX/android/system/vold/CommandListener.cpp
int CommandListener::DumpCmd::runCommand(SocketClient *cli,
                 int /*argc*/, char ** /*argv*/) {
  cli->sendMsg(0, "Dumping loop status", false);
  if (Loop::dumpState(cli)) {
    cli->sendMsg(ResponseCode::CommandOkay, "Loop dump failed", true);
  }
  cli->sendMsg(0, "Dumping DM status", false);
  if (Devmapper::dumpState(cli)) {
    cli->sendMsg(ResponseCode::CommandOkay, "Devmapper dump failed", true);
  }
  cli->sendMsg(0, "Dumping mounted filesystems", false);
  FILE *fp = fopen("/proc/mounts", "re");
  if (fp) {
    char line[1024];
    while (fgets(line, sizeof(line), fp)) {
      line[strlen(line)-1] = '\0';
      cli->sendMsg(0, line, false);;
    }
    fclose(fp);
  }

  cli->sendMsg(ResponseCode::CommandOkay, "dump complete", false);
  return 0;
}

第4行:响应发送消息“Dumping loop status”给 SocketClient ;sendMsg()函数是SocketClient类的方法;
第5行:调用Loop::dumpState(cli),获取state信息,

路径:LINUX/android/system/vold/Loop.cpp
int Loop::dumpState(SocketClient *c) {
    int i;
    int fd;
    char filename[256];
    for (i = 0; i < LOOP_MAX; i++) {
        struct loop_info64 li;
        int rc;
        snprintf(filename, sizeof(filename), "/dev/block/loop%d", i);
        if ((fd = open(filename, O_RDWR | O_CLOEXEC)) < 0) {
            if (errno != ENOENT) {
                SLOGE("Unable to open %s (%s)", filename, strerror(errno));
            } else {
                continue;
            }
            return -1;
        }
        rc = ioctl(fd, LOOP_GET_STATUS64, &li);
        close(fd);
        if (rc < 0 && errno == ENXIO) {
            continue;
        }
        if (rc < 0) {
            SLOGE("Unable to get loop status for %s (%s)", filename,
                 strerror(errno));
            return -1;
        }
        char *tmp = NULL;
        asprintf(&tmp, "%s %d %lld:%lld %llu %lld:%lld %lld 0x%x {%s} {%s}", filename, li.lo_number,
                MAJOR(li.lo_device), MINOR(li.lo_device), li.lo_inode, MAJOR(li.lo_rdevice),
                        MINOR(li.lo_rdevice), li.lo_offset, li.lo_flags, li.lo_crypt_name,
                        li.lo_file_name);
        c->sendMsg(0, tmp, false);
        free(tmp);
    }
    return 0;
}

从Loop::dumpState()的内容可看出,主要是读取 “/dev/block/loop”的内容,然后发送给SocketClient。
具体信息是存储在结构体loop_info64中:

struct loop_info64 {
	__u64		   lo_device;			/* ioctl r/o */
	__u64		   lo_inode;			/* ioctl r/o */
	__u64		   lo_rdevice;			/* ioctl r/o */
	__u64		   lo_offset;
	__u64		   lo_sizelimit;/* bytes, 0 == max available */
	__u32		   lo_number;			/* ioctl r/o */
	__u32		   lo_encrypt_type;
	__u32		   lo_encrypt_key_size;		/* ioctl w/o */
	__u32		   lo_flags;			/* ioctl r/o */
	__u8		   lo_file_name[LO_NAME_SIZE];
	__u8		   lo_crypt_name[LO_NAME_SIZE];
	__u8		   lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
	__u64		   lo_init[2];
};

详细介绍可参考:https://www.onitroad.com/jc/linux/man-pages/linux/man4/loop.4.html
第6行:发送消息“Loop dump failed”给 SocketClient;
第8行:发送消息“Dumping DM status”给 SocketClient;
第9行:主要是调用 Devmapper::dumpState(SocketClient *c),函数输出DM的状态;主要还是读取“/dev/device-mapper”的内容放到 struct dm_ioctl 结构体中;该结构体如下:

/*所有ioctl参数都由一块内存组成,在开始时具有此结构。如果指定了uuid,则将对其进行任何查找(例如DM_INFO),而不是*名称*/
struct dm_ioctl{
/*版本号由三部分组成:主要-不向后或向前兼容,次要-仅向后兼容,补丁-向后和向前兼容。ioctl接口的所有客户端都应该填写编译时使用的接口的版本号。
所有识别的ioctl命令(即不返回-ENOTTY的命令)都填写此字段,即使命令失败。
*/
__u32version[3]/*输入/输出*/

__u32data_size;/*传入的数据(包括此结构)的总大小*/
__u32data_start;/*相对于此结构开头的数据开头偏移量*/
__u32target_count;/*输入/输出*/
__s32open_count;/*输出*/
__u32 flags;/*输入/输出*/
/*eventnr保存事件编号(输入和输出)或udev cookie值(仅输入)。DM_DEV_WAIT ioctl将事件编号作为输入。DM_SUSPEND、DM_DEV_REMOVE和DM_DEV_RENAME ioctl将该字段用作cookie,以在DM_cookie变量中返回它们发出的事件。对于输出,ioctl返回事件编号,而不是cookie*/
__u32 event_nr;/*输入/输出*/
__u32 padding;
__u64 dev;/*输入/输出*/
char name[DM_name_LEN]/*设备名称*/
char uuid[DM_UID_LEN]/*块设备的唯一标识符*/
char data[7]/*填充或数据*/
};

详细可参考其它网友介绍:https://www.cnblogs.com/pyjetson/p/14872499.html
第10行:发送消息“Devmapper dump failed”给 SocketClient;
第12行:发送消息“Dumping mounted filesystems”给 SocketClient;
第13行:打开文件“/proc/mounts”;
第14行:判断打开文件“/proc/mounts”成功;
第16行:循环读取“/proc/mounts”的内容;
第17行:在读取数据的结尾加上结束符;
第18行:将读取的数据发送给SocketClient;
第20行:关闭文件;
第23行:发送消息“dump complete”给 SocketClient;
总结,DumpCmd::runCommand()方法主要是获取一些设备的的信息。

10.3 VolumeCmd::runCommand()方法

路径:LINUX/android/system/vold/CommandListener.cpp
int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
                                           int argc, char **argv) {
    dumpArgs(argc, argv, -1);

    if (argc < 2) {
        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
        return 0;
    }

    VolumeManager *vm = VolumeManager::Instance();
    std::lock_guard<std::mutex> lock(vm->getLock());

    // TODO: tease out methods not directly related to volumes
    //TODO:梳理与卷不直接相关的方法

    std::string cmd(argv[1]);
    if (cmd == "reset") {
        return sendGenericOkFail(cli, vm->reset());

    } else if (cmd == "shutdown") {
        return sendGenericOkFail(cli, vm->shutdown());

    } else if (cmd == "debug") {
        return sendGenericOkFail(cli, vm->setDebug(true));

    } else if (cmd == "partition" && argc > 3) {
        // partition [diskId] [public|private|mixed] [ratio]
        std::string id(argv[2]);
        auto disk = vm->findDisk(id);
        if (disk == nullptr) {
            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown disk", false);
        }

        std::string type(argv[3]);
        if (type == "public") {
            return sendGenericOkFail(cli, disk->partitionPublic());
        } else if (type == "private") {
            return sendGenericOkFail(cli, disk->partitionPrivate());
        } else if (type == "mixed") {
            if (argc < 4) {
                return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
            }
            int frac = atoi(argv[4]);
            return sendGenericOkFail(cli, disk->partitionMixed(frac));
        } else {
            return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
        }

    } else if (cmd == "mkdirs" && argc > 2) {
        // mkdirs [path]
        return sendGenericOkFail(cli, vm->mkdirs(argv[2]));

    } else if (cmd == "user_added" && argc > 3) {
        // user_added [user] [serial]
        return sendGenericOkFail(cli, vm->onUserAdded(atoi(argv[2]), atoi(argv[3])));

    } else if (cmd == "user_removed" && argc > 2) {
        // user_removed [user]
        return sendGenericOkFail(cli, vm->onUserRemoved(atoi(argv[2])));

    } else if (cmd == "user_started" && argc > 2) {
        // user_started [user]
        return sendGenericOkFail(cli, vm->onUserStarted(atoi(argv[2])));

    } else if (cmd == "user_stopped" && argc > 2) {
        // user_stopped [user]
        return sendGenericOkFail(cli, vm->onUserStopped(atoi(argv[2])));

    } else if (cmd == "mount" && argc > 2) {
        // mount [volId] [flags] [user]
        std::string id(argv[2]);
        auto vol = vm->findVolume(id);
        if (vol == nullptr) {
            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
        }

        int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
        userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;

        vol->setMountFlags(mountFlags);
        vol->setMountUserId(mountUserId);

        int res = vol->mount();
        if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
            vm->setPrimary(vol);
        }
        return sendGenericOkFail(cli, res);

    } else if (cmd == "unmount" && argc > 2) {
        // unmount [volId]
        std::string id(argv[2]);
        auto vol = vm->findVolume(id);
        if (vol == nullptr) {
            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
        }

        return sendGenericOkFail(cli, vol->unmount());

    } else if (cmd == "format" && argc > 3) {
        // format [volId] [fsType|auto]
        std::string id(argv[2]);
        std::string fsType(argv[3]);
        auto vol = vm->findVolume(id);
        if (vol == nullptr) {
            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
        }

        return sendGenericOkFail(cli, vol->format(fsType));

    } else if (cmd == "move_storage" && argc > 3) {
        // move_storage [fromVolId] [toVolId]
        auto fromVol = vm->findVolume(std::string(argv[2]));
        auto toVol = vm->findVolume(std::string(argv[3]));
        if (fromVol == nullptr || toVol == nullptr) {
            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
        }

        (new android::vold::MoveTask(fromVol, toVol))->start();
        return sendGenericOkFail(cli, 0);

    } else if (cmd == "benchmark" && argc > 2) {
        // benchmark [volId]
        std::string id(argv[2]);
        nsecs_t res = vm->benchmarkPrivate(id);
        return cli->sendMsg(ResponseCode::CommandOkay,
                android::base::StringPrintf("%" PRId64, res).c_str(), false);

    } else if (cmd == "forget_partition" && argc > 2) {
        // forget_partition [partGuid]
        std::string partGuid(argv[2]);
        return sendGenericOkFail(cli, vm->forgetPartition(partGuid));

    } else if (cmd == "remount_uid" && argc > 3) {
        // remount_uid [uid] [none|default|read|write]
        uid_t uid = atoi(argv[2]);
        std::string mode(argv[3]);
        return sendGenericOkFail(cli, vm->remountUid(uid, mode));
    }

    return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
}

VolumeCmd::runCommand()方法主要是对Volume的一些操作,包括挂载、卸载、复位、关机、分区、创建目录、新增用户、删除用户等等cmd操作;具体操作以FW传输的cmd为准。由于操作cmd太对,就不一一解释。这里主要讲一下mount的内容;

else if (cmd == "mount" && argc > 2) {
        // mount [volId] [flags] [user]
        std::string id(argv[2]);
        auto vol = vm->findVolume(id);
        if (vol == nullptr) {
            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
        }

        int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
        userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;

        vol->setMountFlags(mountFlags);
        vol->setMountUserId(mountUserId);

        int res = vol->mount();
        if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
            vm->setPrimary(vol);
        }
        return sendGenericOkFail(cli, res);

    }

mount的内容入上截取部分;
首先通过findVolume()查找操作的Volume对象是否存在;
第二,setMountFlags()设置挂载的标志 和 setMountUserId()设置挂载的用户ID;
第三,调用mount()函数进行挂载操作;
mount()函数在“LINUX/android/system/vold/VolumeBase.cpp”中实现,如下:

status_t VolumeBase::mount() {
    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
        LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
        return -EBUSY;
    }
    setState(State::kChecking);
    status_t res = doMount();
    if (res == OK) {
        setState(State::kMounted);
    } else {
        setState(State::kUnmountable);
    }

    return res;
}

具体是调用doMount()函数实现;
doMount()函数的内容比较长,就不具体分析,想了解的可自行查阅函数源码“LINUX/android/system/vold/PublicVolume.cpp”;主要是根据不同的文件系统类型,调用 系统调用mount进行挂载,并赋予权限。

10.4StorageCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp
10.5AsecCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp
10.6ObbCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp
10.7FstrimCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp
10.8AppFuseCmd::runCommand()方法
LINUX/android/system/vold/CommandListener.cpp

上述的方法,都是根据具体解析 arg cmd调用对应的函数进行处理。读者可以根据具体cmd具体分析。

综上对vold的流程分析已完成;后续读者可根据后续遇到的问题再深入分析;

…欢迎联系作者沟通交流,qq759521350…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

waterAdmin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值