Linux 文件权限

zh

概念

Linux 中的每一个文件都有其 所属用户 及 所属用户组,根据这两个属性可将文件访问者分为三类:所属用户自己、所属用户组中的用户 和 其他用户,我们可以针对不同的访问者设置不同的用户权限。

“访问”可分为三类:读、写 与 执行。我们可以用 ls -l 命令查看一个文件的权限:

$ touch test
$ ls -l test
-rw-rw-r-- 1 hsfzxjy hsfzxjy 0 Jul 3 23:44 test

首部的 -rw-rw-r-- 即为文件的权限位。权限应该分为四部分来看:-/rw-/rw-/r--。第一部分标志文件的类型,如 普通文件(-)、目录(d)、UNIX 套接字(s)、符号链接(l)、块设备(b)等等。接下来的三个部分依次代表 所属用户、所属用户组、其他用户 的权限,每部分由三个标志位组成:读标志位、写标志位、执行标志位。

目录的权限

目录是一种特殊的文件,因此也拥有文件权限的概念,但权限的语义与普通文件稍有差异:

  • 读:读取目录下文件列表,相关命令如 ls
  • 写:创建、删除目录下的文件,相关命令如 touch(当文件不存在时)、rm 等
  • 执行:进入目录,相关命令如 cd

特殊权限

出于某些特殊目的,Linux 中存在两个特殊的权限位:粘滞位(t)、Set Id(s)。这两个权限可以 叠加 在执行权限位上,其中 Set Id 可以置于 所属用户 和 所属用户组 的权限组上,而 粘滞位 只能置于 其他用户 权限组上。当特殊权限被设置时,执行权限位上即会显示 s/t (已有 x 权限)或 S/T (尚未有 x 权限)。

粘滞位

粘滞位的作用是 防止他人误删自己的文件。当某个目录的其他用户权限组有 w 权限时,系统中的其他用户即可随意删除目录中的文件。而一旦叠加上 t 权限,只有文件的所有者方能删除文件。一个经典的例子是 /tmp:

$ ls -l /
drwxrwxrwt 13 root root 12288 Jul 4 00:15 tmp/

Set Id

Linux 中的进程也有自己所属用户与用户组。一般而言,进程的所属用户即为其发起者,但这会引起一些麻烦。一个例子是 passwd 命令,该命令需要修改属于 root 用户的系统文件以保存密码,倘若进程所属用户即为所属者,此功能则无法实现。

Set Id 权限的作用是:在文件被执行时,将其有效用户/用户组设置为文件的用户/用户组,而不是当前执行者。下面是一个演示:

设当前用户为 hsfzxjy,我们在 /tmp 下创建一个 test 文件,并删去其他用户的 r 权限:

$ cd /tmp
$ echo test text > test
$ chmod o-r test
$ ll test
-rw-rw---- 1 hsfzxjy hsfzxjy 0 Jul 4 00:28 test

由于 test 文件的所属用户是 hsfzxjy,其他用户没有权限读取其中的内容:

$ sudo -u mysql cat test
cat: test: Permission denied

现在我们修改一下 cat 命令的权限,为了不影响系统文件,我们拷贝一份 cat 副本至当前目录:

$ cp /bin/cat .
$ chmod u+s cat
$ ll cat
-rwsr-xr-x 1 hsfzxjy hsfzxjy 52080 Jul 4 00:34 cat*

再以 mysql 的身份执行命令:

$ sudo -u mysql ./cat test
test text

可见 ./cat 在执行时所属用户是 hsfzxjy。我们可以使用 ps 命令更清楚地看到这点:

$ sudo -u mysql cat
# 在另一个终端中
$ ps -eo euser,ruser,comm | grep cat
mysql mysql cat

# -----------

$ sudo -u mysql ./cat
# 在另一个终端中
$ ps -eo euser,ruser,comm | grep cat
hsfzxjy mysql cat
READ MORE

HSFZMUN 4.0 部署小记

zh

技术流水账一篇,记录踩过的坑

Channels 异构

Django Channels 官方文档宣称 channels 的最佳配置是使用其自带的服务器组件 Daphne,但在开发中我发现 daphne 处理普通请求比在 WSGI 架构下慢了好几倍,更何况使用 daphne 派发静态文件是十分不切实际的。于是我将 http.request 和 websocket.* 两个 channel 解耦,前者使用 nginx 配合 uwsgi 处理,后者使用 nginx 反向代理至 daphne 处理。这样一来便可充分利用两种架构的优势。

旧架构:

新架构:

READ MORE

午后雨·科大

zh

从昏睡中惊醒——现在是下午,屋内却很昏暗。屋外,隐约有一阵持续的背景噪音,我拉开窗帘,只见白茫茫的一片。

下雨了,而且是大暴雨。

打开窗,一股辛辣扑面而至——雨和着尘土的气息,再熟悉不过了。伴随着辛辣,一种沁人心脾的清凉涌入屋内,稀释着绿军衣挥发的氨味,一同驱走的,还有午后应有的烦躁。

八月的这里,原来可以不那么热。

一直以为,旱和热是这里夏天的常态。走出机场时,第一感受,也是几天来唯一的感受,便是热——无风的热,黏人;无云的热,灼人。想到将在这样的地方生活,心中顿生怯意——

但现在看来,似乎并不那么糟。

合上窗,雨声再次成为背景音。白色的水汽慢慢爬上玻璃,模糊了摇曳的树,漫水的街,雨中的整个世界。

这一幕似乎很熟悉。

没错,在更早些的时候,当我还在另一个校园时。那是个多雨的城市,雨大而急。记忆中,不知有多少次,也是透过模糊的窗,看着屋外湿透的一切。

面对暴雨,心中有过埋怨,有过恐惧,有过敬畏——但此时,我却体会到了偶遇老友般的亲切。

在陌生的校园,我又遇到了熟悉的雨。

我开始喜欢这里了。

READ MORE

最后的雨夜·广州

zh

明天就要走了。

我贪婪地呼吸着这座城市,想要记住一切。

这是一个雨夜。雨不大,淅淅沥沥的,一反八月羊城的狂暴。雨丝划过夜空,拂去了暑气,夹带着一丝清冽;雨点打在地上,泛起一阵辛辣,这是雨的独特气息。昔日湿热而令人厌烦的南方雨,此刻却如此亲近。

步入校园,城市的灯光隐去了,笼下来的是夜的静谧。走在林荫道,可以闻到空中漫着的甘甜——这是芒果花的香气。

记忆中,最为深刻的花香便是芒果花。这种热带的植物,在羊城开得到处都是——在童年的大院里,在中学的校道里,在这邻近的大学里,在记忆中的每一个角落。游子最难以忘怀的,当属这南国的果香。

出了大学的门,转过街角,城市的灯又亮了。这是学校后门的一条食街,寄存了我六年的回忆。

那股诱人的酸菜味,来自一家无名的东北菜馆。“15 块吃到饱”。豪爽的东北大叔,曾在那个三月给予我信心。

空中那悠扬的,是全家的迎客铃。数不清有多少个早晨,因匆忙而在此买饭团。一口咬下,让柴鱼的美味充盈鼻腔,让翻腾的蒸汽吹走清晨的倦意。

远处是那家熟悉的 M 记。24 小时永不停歇。在这里,我度过了高三的每个周末,一份板烧一杯咖啡,一小时的免费 wifi,抹去一上午的沮丧,换来下午持续的好心情。

再往前便是学校。此时正值军训,校园里却一片静寂——也许是太晚了。六年前,梦从这里开始;六年后,我又将梦重新埋在这里。

雨下大了,我却不急——毕竟,我将去往一个少雨的城市,这里的雨,我要细细记忆。

这是一座现代化的都市。雨中的光光影影,雨中 BRT 的笛鸣,雨中泛着的柏油气息,都是这座城市的印记。

这是一座古老的城市。人们仍执念于古老的语言,过着悠然的生活。饱经沧桑,却历久弥新。

天一亮,我就将去往一个陌生的城市。那里少雨,泛着中原常见的普通。我将在那里度过四年,甚至更久。

而在此之前,一个恋家的动物,要努力带走所有的回忆。

READ MORE

揭秘·变态的平方根倒数算法

zh

神的时代已离去 神的故事却化为传说 流落凡间 供凡人传颂、膜拜

这是什么

在上世纪 90 年代,出现过一款不可思议的游戏——雷神之锤(Quake series)。除了优秀的情节设定和精美的画面,最让人称道的莫过于它的运行效率——要知道在那个计算机配置低下的时代,一段小动画都是一个奇迹,但 Quake 却能流畅地运行于各种配置的电脑上。

直至 2005 年,当 Quake Engine 开源时,Quake 系列的秘密才被揭开。在代码库中,人们发现了许多堪称神来之笔的算法。它们以极其变态的高效率,压榨着计算机的性能,进而才支撑起了 90 年代 3D 游戏的传奇。其中的某些算法,甚至比系统原生的实现还要快!

我们今天的主角——快速平方根倒数算法(Fast Inverse Square Root)就是其中一个。

READ MORE

神坑·Python 装饰类无限递归

zh

《神坑》系列将会不定期更新一些可遇而不可求的坑 防止他人入坑,也防止自己再次入坑

简化版问题

现有两个 View 类:

class View(object):

def method(self):
# Do something...
pass

class ChildView(View):

def method(self):
# Do something else ...
super(ChildView, self).method()

以及一个用于修饰该类的装饰器函数 register——用于装饰类的装饰器很常见(如 django.contrib.admin 的 register),通常可极大地减少定义相似类时的工作量:

class Mixin(object):
pass

def register(cls):

return type(
'DecoratedView',
(Mixin, cls),
{}
)

这个装饰器为被装饰类附加上一个额外的父类 Mixin,以增添自定义的功能。

完整的代码如下:

class Mixin(object):
pass

def register(cls):

return type(
cls.__name__,
(Mixin, cls),
{}
)

class View(object):

def method(self):
# Do something...
pass

@register
class ChildView(View):

def method(self):
# Do something else ...
super(ChildView, self).method()

看上去似乎没什么问题。然而一旦调用 View().method(),却会报出诡异的 无限递归 错误:

# ...
File "test.py", line 23, in method
super(ChildView, self).method()
File "test.py", line 23, in method
super(ChildView, self).method()
File "test.py", line 23, in method
super(ChildView, self).method()
RuntimeError: maximum recursion depth exceeded while calling a Python object

【一脸懵逼】

猜想 & 验证

从 Traceback 中可以发现:是 super(ChildView, self).method() 在不停地调用自己——这着实让我吃了一惊,因为 按理说 super 应该沿着继承链查找父类,可为什么在这里 super 神秘地失效了呢?

为了验证 super(...).method 的指向,可以尝试将该语句改为 print(super(ChildView, self).method),并观察结果:

<bound method ChildView.method of <__main__.ChildView object at 0xb70fec6c>>

输出表明: method 的指向确实有误,此处本应为 View.method。

super 是 python 内置方法,肯定不会出错。那,会不会是 super 的参数有误呢?

super 的签名为 super(cls, instance),宏观效果为 遍历 cls 的继承链查找父类方法,并以 instance 作为 self 进行调用。如今查找结果有误,说明 继承链是错误的,因而极有可能是 cls 出错。

因此,有必要探测一下 ChildView 的指向。在 method 中加上一句: print(ChildView):

<class '__main__.DecoratedView'>

原来,作用域中的 ChildView 已经被改变了。

真相

一切都源于装饰器语法糖。我们回忆一下装饰器的等价语法:

@decorator
class Class:
pass

等价于

class Class:
pass

Class = decorator(Class)

这说明:装饰器会更改该作用域内被装饰名称的指向。

这本来没什么,但和 super 一起使用时却会出问题。通常情况下我们会将本类的名称传给 super(在这里为 ChildView),而本类名称和装饰器语法存在于同一作用域中,从而在装饰时被一同修改了(在本例中指向了子类 DecoratedView),进而使 super(...).method 指向了 DecoratedView 的最近祖先也就是 ChildView 自身的 method 方法,导致递归调用。

解决方案

找到了病因,就不难想到解决方法了。核心思路就是:不要更改被装饰名称的引用。

如果你只是想在内部使用装饰后的新类,可以在装饰器方法中使用 DecoratedView,而在装饰器返回时 return cls,以保持引用不变:

def register(cls):

decorated = type(
'DecoratedView',
(Mixin, cls),
{}
)

# Do something with decorated

return cls

这种方法的缺点是:从外部无法使用 ChildView.another_method 调用 Mixin 上的方法。可如果真的有这样的需求,可以采用另一个解决方案:

def register(cls):

cls.another_method = Mixin.another_method
return cls

即通过赋值的方式为 cls 添加 Mixin 上的新方法,缺点是较为繁琐。

两种方法各有利弊,要根据实际场景权衡使用。

READ MORE

Python“黑魔法”之 Encoding & Decoding

zh

写在前面

  • 本文为科普文
  • 本文中的例子在 Ubuntu 14.04 / Python 2.7.11 下运行成功,Python 3+ 的接口有些许不同,需要读者自行转换

引子

先看一段代码:

example.py:

# -*- conding=yi -*-

从 math 导入 sin, pi

打印 'sin(pi) =', sin(pi)

这是什么?!是 Python 吗?可以运行吗?——想必你会问。

我可以明确告诉你:这不是 Python,但它可以用 Python 解释器运行。当然,如果你愿意,可以叫它“Yython” (易语言 + Python)。

怎么做到的?也许你已经注意到第一行的奇怪注释——没错,秘密全在这里。

这种黑魔法,还要从 PEP 263 说起。

READ MORE

Ubuntu 重新映射键盘布局

zh

键盘持续失灵,已经到了让我忍无可忍的地步了。

刚开始只是方向键失灵,好在可以用小键盘替代;后来右 Ctrl 和 Alt 也失灵了,好在可以用左边的替代;直到最近 Fn 键也失灵了,终于逼疯了我——因为这意味着 F1 ~ F12 都将不能使用。

我曾试图寻找方法将 CapsLock 键映射为 Fn 键映射,但失败了——Fn 键消息是由 BIOS 拦截的,无法被操作系统捕获。

但今天我找到了一个更好的替代方案:

  • 交换 Fn 和 Ctrl。这是唯一一种能让 Fn 键移位的方式,在所有的 BIOS 中都可以设置。
  • 将 CapsLock 映射为 Ctrl。反正 CapsLock 闲着也是闲着,不如用它代替坏了的键。

ubuntu 下需要执行:

setxkbmap -layout us -option ctrl:nocaps

参考: How do I turn Caps Lock into an extra Control key? - Ask Ubuntu

READ MORE

为什么我要翻墙

zh

很多人不解为什么我要翻墙。他们认为,国家明令禁止的东西就不应该去做——而更何况,墙内的世界已经足够精彩,异世界的天空也不一定令人神往。

在中国大陆,能上网的人被分为两类:翻墙的 与 不翻墙的。这里说的“翻墙”不是“有能力翻墙”,而是“有意愿翻墙”。这两类人奇怪地构成了一个鄙视环。翻墙的 看不起 不翻墙的,认为后者甘于“虚假”的现状。其中不乏有狂热的传教者,不遗余力地宣传墙外的世界,并对不接受者嗤之以鼻;同时,不翻墙的 又鄙视 翻墙的,认为后者崇洋媚外,被国外反华势力蛊惑,以致对 翻墙 产生如信仰宗教般的虔诚。

对于不理解我的人,我表示理解。如果我也以你们的身份生活在你们的环境,墙内的世界的确很大、很精彩,的确足够了。再者,翻墙 很麻烦、很不稳定,牺牲流畅性与易用性去浏览一些无趣的内容,确实不值得。

但我想说的是:墙内的世界对于某些人而言,贫乏得可伶。

我喜欢编程。与写作不同的是,编程不仅要靠思考,更需要一些外部工具的辅助。思维是编程工作的灵魂,但思维仅仅提供了一个脉络,在思维的引领下,我需要组合使用各种工具来解决问题。工具的数量很多,细节很复杂,我不可能记住,也不应该都记住。因此,我需要搜索它们的使用说明。一个好的搜索引擎是编程时必备的利器,而我选择了 Google。不是因为我崇洋媚外,而是 它 的确 能高效地让我找到答案,解决我的问题。

我要翻墙,因为我想要的工具在墙内不存在。同时,有一些我想要的答案也在墙外(如 Wikipedia)或部分在墙外(如 引用了 Google CDN 的 StackOverflow)或时不时在墙外(如 Github)。当你的合理需求无法满足时,理智的人都会想方设法地去满足——这很自然,就像吃完了一片草地的羊群会自动迁徙至另一片草地一样。

是的,我翻墙,只是因为我有翻墙的需求。对于某些翻墙发烧友所推荐的某些“真相”,我不感兴趣。隐匿的东西之所以被隐匿,总有它的理由;而被揭露的东西之所以被揭露,也总有他们的目的。

百度作的恶,不能原谅。但抛开这一点,我并不排斥百度。百度不如 Google 科学,但它更适合中国。西方人更注重逻辑思维,同时他们也有更高的受教育程度,科学的 Google 是他们所需要的。而在中国,真正需要科学的只有很少一部分人,更多的人需要的只是科普,一个浅显的答案,只要结论正确,甚至不需要完全严密的推理,足够了。更何况,中国的网络是一个娱乐至上的环境,一个八卦的搜索引擎也许会比一个严谨刻板的搜索引擎更受国人青睐。

对于 GFW,即 墙 本身,我也不怨恨。起初接触到这样一个存在时,我很惊讶,感叹国家还有这样的一面。这个无形的庞然大物产生于旧世纪末,有其特定的时代背景,被人诟病的同时却也有着它的合理性。有人认为,思想自由是人权的一部分,GFW 的存在是反人类的。有段时间我信以为然,并传播这样的思想。但当我真实地接触到那些“被禁锢的人”时,我却犹豫了。前段时间的“诺贝尔哥”事件就是一个很好的样例。游荡在网络中的许多中国人是不理智的——尽管他们也受过教育,但接受的只是知识,而不是思维。他们甚至不知逻辑为何物。这些人看起来是无害的,可一旦他们接触到所谓的“真相”,或说是一些人想让我们接触到的“真相”时,感性会使他们意气用事,造成社会的不稳定。诚然,人权至上,但社会稳定更重要,人权在乱世只是一纸空话。历史的对错,国家自己知道。民众应该知道历史,但如果真相会对现实产生不可预测的坏影响,倒不如让它烂在土里。

我不嘲笑 不翻墙 的人,他们扮演着自己的角色,有着自己的需求与选择;我也会继续 翻墙,因为我有这样的需要。

驱使一切的,只是需求而已。

READ MORE