判断path对于当前用户是不是可写的

1. 代码片段

def check_path_owner(path):
    # type: (str) -> bool
    # If we don't have a way to check the effective uid of this process, then
    # we'll just assume that we own the directory.
    if sys.platform == "win32" or not hasattr(os, "geteuid"):
        return True

    assert os.path.isabs(path)

    previous = None
    while path != previous:
        if os.path.lexists(path):
            # Check if path is writable by current user.
            if os.geteuid() == 0:
                # Special handling for root user in order to handle properly
                # cases where users use sudo without -H flag.
                try:
                    path_uid = get_path_uid(path)
                except OSError:
                    return False
                return path_uid == 0
            else:
                return os.access(path, os.W_OK)
        else:
            previous, path = path, os.path.dirname(path)
    return False  # assume we don't own the path

2. 代码赏析

2.1 代码的作用

check_path_owner函数的作用是判断path对于当前用户是不是可写的,windows系统应当是没有办法实现这一目标,因此注释里解释了,如果做不到就默认有权限。

2.2 默认的方法或函数

这段代码里使用了很多系统模块的函数,这些函数并不常用。

os.path.lexists 判断路径是否存在,我们常用的函数是os.path.exists, 功能描述是相同的,区别在于,exists 在判断软链时,如果软链被破坏了,会返回False, 而lexists会返回True

os.geteuid , 该函数返回当前进程的euid,我们熟悉的getuid返回的是当前进程的uid,这两个东西又有什么区别呢?linux系统中每个进程都有2个ID,分别为用户ID(uid)和有效用户ID(euid),uid表示用户的创建者,而euid表示用户的有效用户id,对应的是文件和资源的访问权限。

你的进程想做一个操作,操作系统自然要检查你是否有执行这个操作的权限,它检查权限时,看的是你的euid,根本不管uid,uid只是表示进程是谁创建的,但能干什么是由euid来决定的。通常,uid和euid是相同的,因此一直以来你都不需要关心euid是什么,反正和euid一样。

什么时候这两个id会不同呢? 在linux下,我们普通用户也可以修改自己的密码,使用的命令是passwd,passwd启动后就是一个进程,而密码存储在/etc/shadow中,这个文件可是只有root用户才能打开的哟。于是就矛盾了,普通用户也得有权利修改密码,修改密码就得打开/etc/shadow文件,而/etc/shadow文件是root账户的,普通用户打不开,否则普通用户就能把别人的密码恶意修改了。

怎么办呢,这就得用到euid了,你执行passwd命令启动的进程,它的uid是你这个普通用户,可操作系统检查权限时,看的是euid,不看uid,如果passwd进程的euid是0(root用户)就可以修改/etc/shadow了。passwd程序在其文件权限中设置了一个位(称为 setuid bit ),它向操作系统指示它应该使用程序所有者的 EUID 运行(root用户)。换一个解释:如果一个程序被设置了setuid位,那么它无论被哪个用户启用,都会具备程序所有者的权限,而passwd的所有者正是root用户。

os.geteuid() 返回值如果是0,就说明当前进程的有效用户id是root。

os.access, 使用真实的 uid/gid 来测试对路径的访问权限,os.W_OK表示写权限。

2.2 逻辑解析

如果传入的path路径不存在,函数并没有直接返回False,而是使用os.path.dirname函数获得path的上一级目录,继续判断。

当路径存在时,要分两种情况考虑,如果euid等于0,则调用get_path_uid函数返回path的uid。如果euid不等于0,则判断当前用户对于path是否有写权限。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案