У меня есть одно приложение (скажем, Приложение А), что я не могу контролировать, и я не могу изменять, записывает в файл и имеет возможность переименовать и удалить его.
У меня есть другое приложение (скажем, Приложение B), который читает из файла, написанного приложением А. У меня есть полный контроль над кодом в приложении Б.
Что мне делать (то есть какие права доступа к файлу или какую блокировку я должен использовать в приложении B, когда оно открывает файл), чтобы приложение A могло продолжать запись в файл, но не может переименовывать или удалять его.
Я пытался использовать fcntl()
в приложении B установить блокировки чтения и записи для файла, но приложение 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
Когда файл создается в первый раз, он создает запись каталога для файла а также что называется инода.
Иноды имеют счетчик ссылок. Когда первоначально создается индекс, он имеет счетчик ссылок 1. Когда программа открывает файл, счетчик ссылок индекса увеличивается, а когда программа закрывает дескриптор файла, счетчик ссылок индекса уменьшается.
Когда appA удаляет файл (через unlink(2)
), запись каталога удаляется, а счетчик ссылок в inode уменьшается. Если appA все еще имеет открытый файл, содержимое inode не удаляется до тех пор, пока appA не закроет дескриптор открытого файла (то есть счетчик ссылок должен перейти в 0).
Запись в каталоге [более или менее] просто: filename|inode-number
, Это индекс, который имеет такую информацию, как размер файла, список блоков, которые являются данными для файла, разрешения и т. Д.
Иноды находятся в отдельной таблице в файловой системе, проиндексированы inode-number
,
Если appB может открыть appAfile
до того, как appA переименует или удалит его (точнее, отсоединяет запись в каталоге, поэтому системный вызов unlink
вместо (например) delete
), данные по-прежнему доступны, потому что, когда appB открывал, счетчик ссылок inode увеличивался. То есть, теперь refcount равен 2. Если appA удаляет файл, refcount в inode уменьшается до 1. Он все еще не равен нулю, поэтому данные могут быть прочитаны appB.
Пока appB содержит открытый дескриптор, данные останутся. Но никакое другое приложение не может получить к нему доступ, потому что запись в каталоге потеряна. И когда appB закрывает файл, счетчик индекса inode становится равным 0 и блоки данных восстанавливаются.
Когда appB выполняет операцию жесткой ссылки, он создает вторую запись каталога с так же номер индекса и увеличивает его счет. То есть для данного индекса, если нет программы содержат дескрипторы открытых файлов, refcount inode — это количество записей каталога, которые имеют ссылки на него. Обычно это 1, но после того, как appB создаст жесткую ссылку, refcount будет равен 2. Можно создать много таких жестких ссылок (используя разные имена «псевдонимов»), и refcount inode будет увеличен соответственно.
Это позволяет файлу сохраняться, даже если appA удалила его, закрыла дескриптор файла, а appB закрыла его дескриптор файла. Индод будет иметь счет 1, который исходит от appBfile
, Если бы appB имел открытый дескриптор, refcount был бы равен 2 (который возвращается к 1, когда appB закрывает файл).
Обратите внимание, что если appA переименовывает запись каталога (например, rename(appAfile,appAfile2)
), переименование делает не уменьшить счет. У appB могут быть проблемы с поиском его под новым именем, но данные все равно будут существовать (т.е. не был удален).
Таким образом, индекс сохраняется до тех пор, пока приложение имеет открытый дескриптор или существует запись в каталоге с номером индекса inode. Другими словами, в любой момент времени рефконт в индексном узле представляет собой сумму количества записей каталога, связанных с ним, + количества файловых дескрипторов, открытых на нем. Чтобы удалить / удалить данные inode, счетчик должен перейти к 0.
Для записи в файл необходимо иметь разрешение на запись в файл но чтобы удалить или переименовать файл, вы должны иметь разрешение на запись в каталог.
Примечание: даже если вы не можете удалить файл, вы все равно можете его обрезать, если ваша файловая система не поддерживает концепцию добавления только файлов.