我有一个应用程序(假设应用程序 A(,我无法控制它,我无法修改,写入文件并能够重命名和删除它。
我有另一个应用程序(假设应用程序 B(正在读取应用程序 A 编写的文件。我可以完全控制应用程序 B 中的代码。
我应该怎么做(即为文件设置什么权限,或者在应用程序 B 打开文件时我应该使用什么锁(,以便应用程序 A 可以继续写入文件,但无法重命名或删除它。
我尝试在应用程序 B 中使用fcntl()
对文件设置读写锁定,但应用程序 A 仍然可以重命名文件。
下面是从应用 B 获取锁定的示例代码(fd
文件的文件描述符(:
int fd = open(filePath, O_RDONLY);
struct flock lock_it;
lock_it.l_type = F_WRLCK; // I've tried F_RDLCK as well.
lock_it.l_whence = SEEK_SET;
lock_it.l_start = 0;
lock_it.l_len = 0; // I want to lock the entire file.
int lockingResult = fcntl(fd,F_SETLK,&lock_it);
cout << "Got a fcntl lock: " << lockingResult << " on FD: " << fd << endl;
但上面的代码并不能阻止应用 A 重命名文件。
任何想法我应该怎么做?我的代码是C++的,我的应用程序在RedHat Linux上运行。
要写入文件,您必须具有文件的写入权限,但要删除或重命名文件,您必须对目录具有写入权限。
注意:即使您无法删除文件,您仍然可以截断它,除非您的文件系统支持仅追加文件的概念。
警告:下面假设 appB 足够快来执行此操作(即存在一些竞争条件(。
appB 可以创建指向不同名称的文件条目的硬链接。这将阻止 appA 删除文件(请参阅man 2 link
(:
link("appAfile","appBfile");
现在,如果 appA 删除 [或重命名] 该文件,它仍然可以通过appBfile
首次创建文件时,它会为该文件创建一个目录条目,即所谓的inode。
索引节点具有引用计数。最初创建索引节点时,它的引用计数为 1。当程序打开文件时,索引节点的引用计数递增,当程序关闭文件描述符时,索引节点的引用计数递减。
当 appA 删除文件(通过unlink(2)
(时,目录条目将被删除,并且索引节点的引用计数将递减。如果 appA 仍然打开文件,则在 appA 关闭打开的文件描述符之前不会删除 inode 的内容(即引用计数必须变为 0(。
目录条目 [或多或少] 只是:filename|inode-number
。它是包含文件大小、作为文件数据的块列表、权限等信息的索引节点。
索引节点位于文件系统中的单独表中,按inode-number
索引。
如果 appB 能够在 appA 重命名或删除它之前打开appAfile
(更准确地说,它取消了目录条目的链接,这就是为什么系统调用unlink
而不是(例如(delete
(,数据仍然可以访问,因为当 appB 打开时,索引节点的引用计数会增加。也就是说,引用计数现在是 2。如果 appA 删除了该文件,则索引节点的引用计数将递减为 1。它仍然不为零,因此数据可由 appB 读取。
只要 appB 持有开放描述符,数据就会保留。但是,没有其他应用程序可以访问它,因为目录条目丢失。而且,当 appB 关闭文件时,索引节点的引用计数变为 0,数据块将被回收。
当 appB 执行硬链接操作时,它会创建具有相同索引节点编号的第二个目录条目,并递增该索引节点的引用计数。也就是说,对于给定的 inode,如果没有程序保存打开的文件描述符,则 inode 的 refcount 是具有指向它的目录条目的数量。这通常是 1,但在 appB 创建硬链接后,引用计数将为 2。可以创建许多这样的硬链接(使用不同的"别名"(,并且索引节点的引用计数会相应地增加。
这允许文件保留,即使 appA 已将其删除、关闭文件描述符且 appB 已关闭其文件描述符。索引节点的引用计数为 1,来自appBfile
。如果 appB 有一个开放的描述符,则引用计数将为 2(当 appB 关闭文件时,该描述符将返回到 1(。
请注意,如果 appA 重命名目录条目(例如rename(appAfile,appAfile2)
(,重命名不会减少引用计数。appB 可能无法在新名称下找到它,但数据仍然存在(即 inode 尚未被删除(。
因此,只要应用程序具有打开的描述符,或者存在带有 inode 的 inode 编号的目录条目,inode 就会保留。换句话说,在任何给定时间,inode 的 refcount 是链接到它的目录条目数 + 在其上打开的文件描述符数的总和。要删除/移除索引节点数据,引用计数必须变为 0。