[ Android实战 ] 判断文件是否为软链接或硬链接

  • 背景
  • 硬链接和软链接的区别
  • file.getCanonicalPath
  • Files.isSymbolicLink
  • path.toRealPath
  • lstat
  • 总结

尊重原创,转载请注明出处!
创作不易,如有帮助请点赞支持~

背景

最近项目遇到了一个需求,出于安全方面的考虑,需要在操作文件前判断文件是否为软链接,做一些处理。于是研究了一下,对比尝试了多种解决方案,记录下来。

硬链接和软链接的区别

搜索相关资料的过程中,了解了一下软链接和硬链接的区别,这里先介绍下(只是简单科普一下,虽然不准确,但是方便理解,具体的解释可以自行百度):

硬链接:假设 A 是 B 的硬链接,可以理解为 A 和 B 指向同一个文件 C。修改 A 或 B 时,都会进行同步,影响另一个文件的内容;删除 A 或 B,都不会影响另一个文件,只是节点链接数会减 1。

软链接:假设 A 是 B 的软链接,可以理解为 A 是 B 的快捷方式。修改 A 时,实际修改的是 B 的内容;删除 A 对 B 没有任何的影响;而如果删除 B,虽然 A 仍然存在,但是会导致 A 指向一个无效路径。

对硬链接和软链接的区别有一个简单的认识后,我们来实践一下,分别创建软链接和硬链接:
PS:这里是有 root 权限的,但是显示问题,为了格式好看一点,把 # 改成了 $

XXX:/data/test $ echo "file1" > file1 # 创建file1
XXX:/data/test $ echo "file2" > file2 # 创建file2
XXX:/data/test $ ln -s file1 soft_link # 创建一个soft_link指向file1的软链接
XXX:/data/test $ ln file2 hard_link # 创建一个hard_link指向file2的硬链接
XXX:/data/test $ ln -s file3 invalid_link # 创建一个指向无效链接的软链接
XXX:/data/test $ ls -al
total 48
drwxrwxrwx  2 root   root   4096 2022-03-03 19:35 .
drwxrwx--x 53 system system 4096 2022-03-03 19:19 ..
-rw-rw-rw-  1 root   root      6 2022-03-03 19:34 file1
-rw-rw-rw-  2 root   root      6 2022-03-03 19:34 file2
-rw-rw-rw-  2 root   root      6 2022-03-03 19:34 hard_link
lrwxrwxrwx  1 root   root      5 2022-03-04 16:19 invalid_link -> file3
lrwxrwxrwx  1 root   root      5 2022-03-03 19:34 soft_link -> file1

从以上指令可以更加直观地看出两种链接方式的差异:

软链接通过以下指令创建(即使 file 不存在,一样可以创建 link):ln -s file link

硬链接通过以下指令创建(file 必须存在,才能创建 link):ln file link

软链接能够很直观地看到 soft_link -> file1 的提示,并且在属性那列,可以看出它的文件类型是 l(link file)。

而相比起来,硬链接则无法看到它的指向,并且文件类型也只是 -(file),只能看到硬链接和原文件的 inode 节点数都变成了 2。

file.getCanonicalPath

如何判断文件是否为软链接,网上找到最多的一种方法如下:

public static boolean isSymlink(String path) throws IOException {File file = new File(path);File canon;if (file.getParent() == null) {canon = file;} else {File canonDir = file.getParentFile().getCanonicalFile();canon = new File(canonDir, file.getName());}return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
}

其实,就是根据 file.getAbsoluteFile 获取到文件本身的路径,file.getCanonicalFile 获取文件真正指向的路径,来实现是否为软链接的判断。

这个方案,在 Android 7.1、8.1、10.0 上均测试通过,但是只能用于判断有效链接的软链接,无法区分是否为硬链接或无效链接的软链接。

日志如下:

03-04 16:23:39.616  6247  6264 D DEBUG   : path =========== /data/test/soft_link
03-04 16:23:39.617  6247  6264 D DEBUG   : getAbsolutePath: /data/test/soft_link, getCanonicalPath: /data/test/file1
03-04 16:23:39.618  6247  6264 D DEBUG   : isSymlink: true
03-04 16:23:39.637  6247  6264 D DEBUG   : path =========== /data/test/hard_link
03-04 16:23:39.637  6247  6264 D DEBUG   : getAbsolutePath: /data/test/hard_link, getCanonicalPath: /data/test/hard_link
03-04 16:23:39.638  6247  6264 D DEBUG   : isSymlink: false
03-04 16:23:39.639  6247  6264 D DEBUG   : path =========== /data/test/invalid_link
03-04 16:23:39.639  6247  6264 D DEBUG   : getAbsolutePath: /data/test/invalid_link, getCanonicalPath: /data/test/invalid_link
03-04 16:23:39.639  6247  6264 D DEBUG   : isSymlink: false

注意:如果某些路径下发现该方法失效,很可能是 selinux 策略限制的问题,可以执行 adb shell setenforce 0 暂时关闭 selinux 进行验证。

Files.isSymbolicLink

除了第一种方案,Files.isSymbolicLink 也是网上找到的另一个实现方案,Files 是 java.nio.file 包下的一个类。

实现代码很简单,如下:

public static boolean isSymbolicLink(String path) throws IOException {Path path1 = Paths.get(path);return Files.isSymbolicLink(path1);
}

但是这种方案,在 Android 7.1 上直接崩溃,在 Android 8.1、10.0 则有效,测试通过。

相比上一种方案,Files.isSymbolicLink 的优势是即使指向无效链接,依然可以知道它是软链接。

Android 7.1 上报错如下:

E AndroidRuntime: java.lang.NoSuchMethodError: No virtual method toPath()Ljava/nio/file/Path; in class Ljava/io/File;
or its super classes (declaration of 'java.io.File' appears in /system/framework/core-oj.jar)

搜索了一下 Android 7.1 的代码,在 libcore\ojluni\src\main\java\java\nio 下确实没有找到 file 这个包,自然也就不支持 Files 的调用了。

Android 8.1、10.0 上日志如下:

03-04 16:36:45.409  6631  6648 D DEBUG   : path =========== /data/test/soft_link
03-04 16:36:45.438  6631  6648 D DEBUG   : isSymbolicLink: true
03-04 16:36:45.438  6631  6648 D DEBUG   : path =========== /data/test/hard_link
03-04 16:36:45.439  6631  6648 D DEBUG   : isSymbolicLink: false
03-04 16:36:45.439  6631  6648 D DEBUG   : path =========== /data/test/invalid_link
03-04 16:36:45.440  6631  6648 D DEBUG   : isSymbolicLink: true

path.toRealPath

path.toRealPath 返回的是文件的真实路径,它同样是 java.nio.file 包里的一个方法,因此跟上一种方案一样,不适用于 Android 7.1,但是在 Android 8.1、10.0 上能测试通过。

实现代码也很简单:

public static boolean isSymbolicLink2(String path) throws IOException {Path path1 = Paths.get(path);return !path1.equals(path1.toRealPath());
}

Android 8.1、10.0 上日志如下:

03-04 17:07:14.973 10782 10818 D DEBUG   : path =========== /data/test/soft_link
03-04 17:07:14.983 10782 10818 D DEBUG   : toRealPath1: /data/test/soft_link
03-04 17:07:14.989 10782 10818 D DEBUG   : toRealPath2: /data/test/file1
03-04 17:07:14.990 10782 10818 D DEBUG   : isSymbolicLink2: true
03-04 17:07:14.991 10782 10818 D DEBUG   : path =========== /data/test/hard_link
03-04 17:07:14.991 10782 10818 D DEBUG   : toRealPath1: /data/test/hard_link
03-04 17:07:14.992 10782 10818 D DEBUG   : toRealPath2: /data/test/hard_link
03-04 17:07:14.992 10782 10818 D DEBUG   : isSymbolicLink2: false
03-04 17:07:14.992 10782 10818 D DEBUG   : path =========== /data/test/invalid_link
03-04 17:07:14.993 10782 10818 D DEBUG   : toRealPath1: /data/test/invalid_link
03-04 17:07:14.993 10782 10818 E DEBUG   : IOException: /data/test/invalid_link
03-04 17:07:14.994 10782 10818 W System.err: java.nio.file.NoSuchFileException: /data/test/invalid_link
03-04 17:07:14.994 10782 10818 W System.err:    at sun.nio.fs.UnixPath.toRealPath(UnixPath.java:837)
03-04 17:07:14.994 10782 10818 W System.err:    at com.example.demo.SimpleActivity$1.run(SimpleActivity.java:191)
03-04 17:07:14.994 10782 10818 W System.err:    at java.lang.Thread.run(Thread.java:919)

注意,path.toRealPath 函数定义有一个 LinkOption 的不定参数,LinkOption 是一个枚举,只有一个常量 NOFOLLOW_LINKS,表示不指向符号链接。

Path toRealPath(LinkOption... options) throws IOException;

上面的日志中,toRealPath1 对应 path.toRealPath(LinkOption.NOFOLLOW_LINKS) 的返回值,而 toRealPath2 对应 path.toRealPath() 的返回值。

可以发现,当 path.toRealPath 传入 LinkOption.NOFOLLOW_LINKS,获取到的路径是文件本身的真实路径。

而如果不传参数,如果操作的是软链接,则能获取到软链接指向的文件的真实路径。

如果该软链接是一个无效连接,则会抛出异常。

相比上一种方案,这种方案能区分出有效软链接和无效软链接,这也算是它的一个优势吧。

lstat

除了 java 的实现方案,通过 JNI 调用 C 去实现也是一种思路。C 提供了 fstatlstat 函数来获取文件的信息。

fstat 直接操作的是软链接指向的文件,因此无法判断是否为软链接,在这种场景下不使用它,而使用 lstat 来实现。

JNI 接口实现方式如下:

JNIEXPORT jint JNICALL Java_com_example_jni_Test_isSymbolicLinkNative(JNIEnv *env, jobject obj, jstring jstr)
{char *filename = (char*) env->GetStringUTFChars(jstr, JNI_FALSE);struct stat _stat;int ret = -1;if(lstat(filename, &_stat) < 0) {LOGE("%s lstat error", filename);return -1;}// 也可以通过 _stat.st_mode & S_IFLNK 进行判断ret = S_ISLNK(_stat.st_mode);env->ReleaseStringUTFChars(jstr, filename);return ret;
}JNIEXPORT jint JNICALL Java_com_example_jni_Test_getNLinkNative(JNIEnv *env, jobject obj, jstring jstr)
{char *filename = (char*) env->GetStringUTFChars(jstr, JNI_FALSE);struct stat _stat;int ret = -1;if(lstat(filename, &_stat) < 0) {LOGE("%s lstat error", filename);return -1;}ret = _stat.st_nlink;env->ReleaseStringUTFChars(jstr, filename);return ret;
}

在 Android 7.1、8.1、10.0 上均测试通过,日志如下:

03-04 18:15:55.536 12317 12352 D DEBUG   : path =========== /data/test/soft_link
03-04 18:15:55.538 12317 12352 D DEBUG   : isSymbolicLinkNative: 1
03-04 18:15:55.539 12317 12352 D DEBUG   : getNLinkNative: 1
03-04 18:15:55.539 12317 12352 D DEBUG   : path =========== /data/test/hard_link
03-04 18:15:55.539 12317 12352 D DEBUG   : isSymbolicLinkNative: 0
03-04 18:15:55.539 12317 12352 D DEBUG   : getNLinkNative: 2
03-04 18:15:55.539 12317 12352 D DEBUG   : path =========== /data/test/invalid_link
03-04 18:15:55.539 12317 12352 D DEBUG   : isSymbolicLinkNative: 1
03-04 18:15:55.539 12317 12352 D DEBUG   : getNLinkNative: 1

可以发现,只要是软链接,不管链接文件是否有效,都可以通过 stat.st_mode 进行判断。

另外,我上面还用到了 stat.st_nlink,它可以获取到文件的硬链接数目,用于判断是否为硬链接。

总结

对上面几种方案的总结如下:

1、file.getCanonicalPath 适用于不同 Android 平台,能获取到有效软链接的文件路径,但是无法判断无效软连接和硬链接

2、Files.isSymbolicLink 只适用于 Android 8 以后的平台,能用于判断是否为软链接(不管软链接是否有效),无法判断是否为硬链接

3、Path.toRealPath 同样只适用于 Android 8 以后的平台,能用于获取到有效软链接的文件路径,虽然无法直接判断无效软连接,但可以通过捕获异常的方式进行处理,无法判断是否为硬链接

4、lstat 是通过 JNI 调用 C 代码实现,适用于不同 Android 平台,因为是直接读取文件属性,能用于判断是否为软链接(不管软链接是否有效),也可以通过硬链接节点数判断是否为硬链接

PS:在高版本上进行测试的时候,随着文件路径的改变,可能会发现方法执行失效的问题,这很有可能是因为 selinux 策略的限制导致的,这时就需要考虑是否修改对应的 selinux 策略了。

[ Android实战 ] 判断文件是否为软链接或硬链接相关推荐

  1. linux创建文件软链接命令,Linux创建文件或目录软链接、硬链接的技巧

    有时Linux用户们为了使用方便,会想给电脑中的文件或目录创建软链接或硬链接.不过有些用户不清楚该怎么给这些文件或目录创建软链接或硬链接该怎么办呢?别急,现在小编就来教你解决的方法. 解决方法: 当我 ...

  2. Linux下如何查看一个文件是否拥有软链接或硬链接文件?

    问题1:我创建了一个硬链接文件,但是我不知道放在那里了,请问怎么办? 思路: 查找文件可以利用find命令. 硬链接的特性,不会跨文件系统,所以源文件在哪个文件系统中,就在那里找 硬链接文件和源文件具 ...

  3. linux文件软链接与硬链接

    1.命令格式: ln [参数][源文件或目录][目标文件或目录] 软链接只会在你选定的位置上生成一个文件的镜像,不会占用磁盘空间. 2.命令功能: Linux文件系统中,有所谓的链接(link),我们 ...

  4. linux 软硬文件类型,linux文件属性和类型、系统链接文件、软链接和硬链接

    文件属性和类型 [[email protected] ~]# ll -d wzh d rwxr-xr-x. 2 root root 6 Mar 26 06:27 wzh 目录 ,权限 硬链接数 属主 ...

  5. 【C语言】【unix c】获取文件的元数据(软链接,硬链接)(meta data)

    一.获取文件的元数据(软链接,硬链接)(meta data) 1.元数据也就是常说的文件的属性 举例:用ls -l察看详细信息: - rw-rw-r-- 1 tarena tarena 0 8月 9 ...

  6. Linux 基础I/O :文件描述符,重定向,文件系统,软链接和硬链接,动态库和静态库

    文件描述符 重定向 文件系统 软链接和硬链接 动态库和静态库 文件描述符 上面两个接口分别是c语言的fread接口和linux的read接口,当我们在使用的时,可能会有疑问,为什么linux的io接口 ...

  7. linux文件存储、inode、硬链接、软链接

    目录 介绍 inode的内容 inode的大小 inode号码 目录文件 硬链接 软链接 介绍 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存512 ...

  8. linux给文件添加链接,Linux给文件或目录创建软链接或硬链接的方法

    有时linux用户们为了使用方便,会想给电脑中的文件或目录创建软链接或硬链接.不过有些用户不清楚该怎么给这些文件或目录创建软链接或硬链接该怎么办呢?别急,现在小编就来教你解决的方法. 解决方法: 当我 ...

  9. Linux---文件、软链接于硬链接文件

    在Linux中,一切皆文件,因此我们需要对Linux下的文件系统有个清楚的认识. 文件属性 在Linux中,我们可以通过ls -l或者ll查看具体文件信息. 注意:ls命令是用于查看当前路目录下的文件 ...

最新文章

  1. 服务管理--systemctl命令
  2. java中过滤器Filter的使用总结【转载】
  3. zxing android最新下载,Zxing简单集成
  4. Word Count Example of Hadoop V1.0 – Mapper的实现
  5. 算法设计和数据结构学习_2(常见排序算法思想)
  6. absolute绝对定位的参考坐标和参考对象问题详解
  7. Spring+Mybatis+SpringMVC后台与前台分页展示实例(附工程)(转)
  8. workman php教程_Workerman
  9. 解决:应用程序无法启动,因为应用程序的并行配置不正确
  10. Java开发报表——Grid++Report 报表设计器
  11. 备品管理方案怎么写_备品备件管理制度62802
  12. Burpsuite工具与浏览器之间设置代理、安装证书
  13. 【企业为什么要进行数字化转型】之数字时代新模式
  14. 【项目记录/vue移动端】仿京东到家登录页
  15. 深入浅出PA和LNA
  16. win10 台式机麦克风杂音 去除
  17. 一些常用的网站或工具(二)
  18. win10系统下vs2015编写的C++程序在XP系统里运行
  19. Java矩阵计算库UJMP
  20. 电脑护眼软件Mac版本和Windows

热门文章

  1. 记录pyinstaller打包过程中遇到的坑
  2. 这6个好用的图片素材网站,你一定要知道
  3. 如何在xp下利用4G内存
  4. 初始化java_Java 初始化
  5. 《养个皮皮虾:开心赶海水族箱》用户隐私政策
  6. 如何发现品牌潜客?目标人群优选算法模型及实践解析
  7. 2021年8月券商App行情刷新及交易体验评测报告
  8. Linux命令之一句获取网卡名字
  9. 毕业设计-基于机器安全帽检测方法- YOLO
  10. 关于IBM的专用集成电路设计流程以及RLM设计流程的优点详解