Answers:
可以通过将grep
命令传递给lsattr
命令来部分实现。
lsattr -R | grep +i
但是,我相信当您提到整个ext3
文件系统时,搜索可能涉及到/proc
,/dev
还有一些其他目录,如果报告某些错误,则只想忽略它们。您可能可以将命令运行为
lsattr -R 2>/dev/null | grep -- "-i-"
您可能希望grep
通过使用grep
PCRE工具来更严格地匹配“ -i-”,以使其更加严格。
lsattr -R 2>/dev/null | grep -P "(?<=-)i(?=-)"
然后将适用于以下情况:
$ lsattr -R 2>/dev/null afile | grep -P "(?<=-)i(?=-)"
----i--------e-- afile
但是是不完美的。如果在不可变标志周围启用了其他属性,那么我们将不匹配它们,这将被名称也恰好与上述模式匹配的文件所欺骗,例如:
$ lsattr -R 2>/dev/null afile* | grep -P "(?<=-)i(?=-)"
----i--------e-- afile
-------------e-- afile-i-am
我们可以将模式收紧一些,如下所示:
$ lsattr -a -R 2>/dev/null afile* | grep -P "(?<=-)i(?=-).* "
----i--------e-- afile
但是它仍然有些脆弱,需要根据文件系统中的文件进行其他调整。更不用说 @StephaneChazeles在评论中提到,通过包含带有文件名的换行符来绕过上述模式,可以很容易地解决这个问题grep
。
https://groups.google.com/forum/#!topic/alt.os.linux/LkatROg2SlM
-i-
中的名字也很常见(我当前登录的系统上有34个文件名)。你可能想的-a
选项,以及
+i
在第一个示例中应该做什么?它对我不起作用。另外,grepping for -i-
假设未设置与相邻的属性i
(例如a
)。
^....i
?或至少类似的东西,^[^ ]*i
如果i
可以位于第五位以外的其他位置。
鉴于脚本的目的是审核,正确处理任意文件名(例如,包含换行符的名称)尤为重要。这使得不可能同时使用lsattr
多个文件,因为lsattr
在这种情况下的输出可能会模棱两可。
您可以递归find
并一次调用lsattr
一个文件。不过会很慢。
find / -xdev -exec sh -c '
for i do
attrs=$(lsattr -d "$i"); attrs=${attrs%% *}
case $attrs in
*i*) printf "%s\0" "$i";;
esac
done' sh {} +
我建议您使用诸如Perl,Python或Ruby之类的不那么胡扯的语言,并自己完成工作lsattr
。lsattr
通过发出FS_IOC_GETFLAGS
ioctl syscall并检索文件的inode标志来进行操作。这是Python的概念证明。
#!/usr/bin/env python2
import array, fcntl, os, sys
FS_IOC_GETFLAGS = 0x80086601
EXT3_IMMUTABLE_FL = 0x00000010
count = 0
def check(filename):
fd = os.open(filename, os.O_RDONLY)
a = array.array('L', [0])
fcntl.ioctl(fd, FS_IOC_GETFLAGS, a, True)
if a[0] & EXT3_IMMUTABLE_FL:
sys.stdout.write(filename + '\0')
global count
count += 1
os.close(fd)
for x in sys.argv[1:]:
for (dirpath, dirnames, filenames) in os.walk(x):
for name in dirnames + filenames:
check(os.path.join(dirpath, name))
if count != 0: exit(1)
FS_IOC_GETFLAGS
是0x80046601
。
FS_IOC_GETFLAGS
取决于sizeof(long)
。请参见下面的bash命令,以了解宏在C:中扩展的内容gcc -E - <<< $'#include <linux/fs.h>\nFS_IOC_GETFLAGS' | tail -n1
。我从中得到以下表达式:(((2U) << (((0 +8)+8)+14)) | ((('f')) << (0 +8)) | (((1)) << 0) | ((((sizeof(long)))) << ((0 +8)+8)))
,它简化为(2U << 30) | ('f' << 8) | 1 | (sizeof(long) << 16)
。
要处理任意文件名(包括包含换行符的文件名),通常的技巧是在.//.
而不是中查找文件.
。由于//
遍历目录树通常不会发生,因此请确保//
信号在find
(或here lsattr -R
)输出中指示新文件名的开始。
lsattr -R .//. | awk '
function process() {
i = index(record, " ")
if (i && index(substr(record,1,i), "i"))
print substr(record, i+4)
}
{
if (/\/\//) {
process()
record=$0
} else {
record = record "\n" $0
}
}
END{process()}'
请注意,输出仍将以换行符分隔。如果您需要对其进行后期处理,则必须对其进行调整。例如,您可以添加-v ORS='\0'
以便将其提供给GNU的xargs -r0
。
另请注意,lsattr -R
(至少为1.42.13)无法报告路径大于PATH_MAX(通常为4096)的文件的标志,因此有人可以隐藏通过移动它的父目录这样的不可变的文件(或任何路径组件的原因,致使它,除了它本身是不变的以外)进入一个非常深的目录。
一个解决办法是使用find
具有-execdir
:
find . -execdir sh -c '
a=$(lsattr -d "$1") &&
case ${a%% *} in
(*i*) ;;
(*) false
esac' sh {} \; -print0
现在,有了-print0
,它可以进行后期处理,但是如果您打算对这些路径进行任何操作,请注意,对大于PATH_MAX的文件路径的任何系统调用仍然会失败,并且目录组件可以在该间隔中重命名。
如果我们要获得关于目录树的可靠报告,并且其他人可能会写这些报告,那么lsattr
我们还需要提及命令本身固有的一些其他问题:
lsattr -R .
遍历目录树的方式受竞争条件的影响。.
通过在适当的时候用符号链接替换某些目录,可以使目录下降到目录树之外的目录。lsattr -d file
有比赛条件。这些属性仅适用于常规文件或目录。所以,lsattr
做了lstat()
第一次检查该文件是正确的类型,然后就open()
接着ioctl()
检索属性。但是它调用时open()
不带O_NOFOLLOW
(也不带O_NOCTTY)。有人可能会替换file
为符号链接/dev/watchdog
,例如在lstat()
和之间,open()
从而导致系统重新启动。应该open(O_PATH|O_NOFOLLOW)
紧随其后fstat()
,openat()
并ioctl()
在此处避免出现竞争情况。感谢Ramesh,slm和Stéphane向我指出了正确的方向(我错过了的-R
开关lsattr
)。不幸的是,到目前为止,没有一个答案对我有用。
我想出了以下几点:
lsattr -aR .//. | sed -rn '/i.+\.\/\/\./s/\.\/\///p'
这样可以防止换行符使文件在不换行时显得不可变。它不能防止设置为不可变且文件名中包含换行符的文件。但是由于必须通过root用户以这种方式创建这样的文件,因此我可以确信在我的用例中此类文件不存在于我的文件系统中。(此方法不适用于可能损害root用户但又没有使用同样lsattr
由root用户拥有的相同系统实用程序的入侵检测的情况。)
使用find -exec
过慢,解析的输出lsattr
是不可靠的类似是的ls
,使用Python作为由吉尔斯答案需要选择常数ioctl
依赖于Python解释器是32位还是64位?
当前的问题或多或少是低级别的,所以我们走到低级别:C ++作为脚本语言还不错:)另外,它可以使用C预处理器的全部功能访问系统C头文件。
以下程序搜索不可变文件,它们位于一个文件系统中,即永不越过装入点。要搜索表观树,并根据需要越过安装点,请删除调用中的FTW_MOUNT
标志nftw
。而且它不遵循符号链接。要遵循它们,请删除FTW_PHYS
标志。
#define _FILE_OFFSET_BITS 64
#include <iostream>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <sys/stat.h>
#include <ftw.h>
bool isImmutable(const char* path)
{
static const int EXT3_IMMUTABLE_FLAG=0x10;
const int fd=open(path,O_RDONLY|O_NONBLOCK|O_LARGEFILE);
if(fd<=0)
{
perror(("Failed to open file \""+std::string(path)+"\"").c_str());
return false;
}
unsigned long attrs;
if(ioctl(fd,FS_IOC_GETFLAGS,&attrs)==-1)
{
perror(("Failed to get flags for file \""+std::string(path)+"\"").c_str());
close(fd);
return false;
}
close(fd);
return attrs & EXT3_IMMUTABLE_FLAG;
}
int processPath(const char* path, const struct stat* info, int type, FTW* ftwbuf)
{
switch(type)
{
case FTW_DNR:
std::cerr << "Failed to read directory: " << path << "\n";
return 0;
case FTW_F:
if(isImmutable(path))
std::cout << path << '\n';
return 0;
}
return 0;
}
int main(int argc, char** argv)
{
if(argc!=2)
{
std::cerr << "Usage: " << argv[0] << " dir\n";
return 1;
}
static const int maxOpenFDs=15;
if(nftw(argv[1],processPath,maxOpenFDs,FTW_PHYS|FTW_MOUNT))
{
perror("nftw failed");
return 1;
}
}
为什么不使用awk只匹配输出的第一个字段中的“ i”,而不是将输出通过管道传递到grep?
lsattr -Ra 2>/dev/null /|awk '$1 ~ /i/ && $1 !~ /^\// {print}'
实际上,我每天都通过cron运行此程序,以扫描数百台服务器上的/ etc目录,并将输出发送到syslog。然后,我可以通过Splunk生成每日报告:
lsattr -Ra 2>/dev/null /etc|awk '$1 ~ /i/ && $1 !~ /^\// {print "Immutable_file="$2}'|logger -p local0.notice -t find_immutable
/etc
。但是两个命令都错误地找到了使用touch `"echo -e "bogus\n---------i---e-- changeable"`"
touch "`echo -e 'bogus\n---------i---e-- changeable'`"
非常简单,请转到可疑文件夹并运行以下命令:
lsattr -laR | grep *immutable