常用的cp, mv, mkdir等Linux命令,在Android下是如何实现的呢
今天查验了一番,以cp为例,看下命令所在路径:
kona:/system/bin # which cp
显示如下
/sytem/bin/cp
我再次使用下面命令ls查看
kona:/system/bin # ls -al /system/bin/cp
显示如下:
lrwxr-xr-x 1 root shell 6 2009-01-01 00:00 /system/bin/cp -> toybox
同样的,查看mv和mkdir命令,显示如下:
kona:/system/bin # ls -al /system/bin/mv
lrwxr-xr-x 1 root shell 6 2009-01-01 00:00 /system/bin/mv -> toybox
kona:/system/bin # ls -al /system/bin/mkdir
lrwxr-xr-x 1 root shell 6 2009-01-01 00:00 /system/bin/mkdir -> toybox
发现命令都是个软连接文件,并且都指向了toybox,toybox是什么鬼?
kona:/ # ls -al /system/bin/toybox
-rwxr-xr-x 1 root shell 487072 2022-01-27 03:37 /system/bin/toybox
toybox不是软连接文件了,那么它是如何区分这些命令呢?
大胆猜测,这些命令软连接文件执行后,会读取当前可执行文件的名字,根据此名字的不同执行不同的操作
实际情况,也是这样,C/C++可执行文件的main方法的参数argv[0],就可以表示当前执行的文件名
int main(int argc, char *argv[])
写程序test_symlink.c验证:
#include<stdio.h>
int main(int argc, char* argv[]){
printf("%s\n", argv[0]);
return 0;
}
编译生成test_symlink可执行文件后,创建两个连接文件cp和mkdir都指向这个test_symlink
# ln -s test_symlink cp
# ln -s test_symlink mkdir
然后执行./cp和./mkdir
输出:
# ./cp
./cp
# ./mkdir
./mkdir
toybox的实现,使用argv来做不同命令的区分
int main(int argc, char *argv[])
{
// don't segfault if our environment is crazy
if (!*argv) return 127;
// Snapshot stack location so we can detect recursion depth later.
// Nommu has special reentry path, !stacktop = "vfork/exec self happened"
if (!CFG_TOYBOX_FORK && (0x80 & **argv)) **argv &= 0x7f;
else {
int stack_start; // here so probe var won't permanently eat stack
toys.stacktop = &stack_start;
}
// Android before O had non-default SIGPIPE, 7 years = remove in Sep 2024.
if (CFG_TOYBOX_ON_ANDROID) signal(SIGPIPE, SIG_DFL);
if (CFG_TOYBOX) {
// Call the multiplexer with argv[] as its arguments so it can toy_find()
toys.argv = argv-1;
toybox_main();
} else {
// single command built standalone with no multiplexer is first list entry
toy_singleinit(toy_list, argv);
toy_list->toy_main();
}
xexit();
}
toybox_main
void toybox_main(void)
{
char *toy_paths[] = {"usr/", "bin/", "sbin/", 0}, *s = toys.argv[1];
int i, len = 0;
unsigned width = 80;
// fast path: try to exec immediately.
// (Leave toys.which null to disable suid return logic.)
// Try dereferencing one layer of symlink
while (s) {
struct toy_list *tl = toy_find(basename(s));
if (tl==toy_list && s!=toys.argv[1]) unknown(basename(s));
toy_exec_which(toy_find(basename(s)), toys.argv+1);
s = (0<readlink(s, libbuf, sizeof(libbuf))) ? libbuf : 0;
}
....
}