C доступен ли файл для записи
БлогNot. Лекции по C/C++: работа с файлами (fstream)
Лекции по C/C++: работа с файлами (fstream)
Механизм ввода-вывода, разработанный для обычного языка С, не соответствует общепринятому сегодня стилю объектно-ориентированного программирования, кроме того, он активно использует операции с указателями, считающиеся потенциально небезопасными в современных защищённых средах выполнения кода. Альтернативой при разработке прикладных приложений является механизм стандартных классов ввода-вывода, предоставляемый стандартом языка C++.
Открытие файлов
Наиболее часто применяются классы ifstream для чтения, ofstream для записи и fstream для модификации файлов.
Все поточные классы ввода-вывода являются косвенными производными от общего предка ios , полностью наследуя его функциональность. Так, режим открытия файлов задает член данных перечисляемого типа open_mode, который определяется следующим образом:
Ниже приведены возможные значения флагов и их назначение.
Режим | Назначение |
in | Открыть для ввода (выбирается по умолчанию для ifstream) |
out | Открыть для вывода (выбирается по умолчанию для ofstream) |
binary | Открыть файл в бинарном виде |
aрр | Присоединять данные; запись в конец файла |
ate | Установить файловый указатель на конец файла |
trunc | Уничтожить содержимое, если файл существует (выбирается по умолчанию, если флаг out указан, а флаги ate и арр — нет) |
Например, чтобы открыть файл с именем test.txt для чтения данных в бинарном виде, следует написать:
Оператор логического ИЛИ ( | ) позволяет составить режим с любым сочетанием флагов. Так, чтобы, открывая файл по записи, случайно не затереть существующий файл с тем же именем, надо использовать следующую форму:
Предполагается, что к проекту подключён соответствующий заголовочный файл:
Для проверки того удалось ли открыть файл, можно применять конструкцию
Операторы включения и извлечения
Переопределённый в классах работы с файлами оператор включения ( << ) записывает данные в файловый поток. Как только вы открыли файл для записи, можно записывать в него текстовую строку целиком:
Можно также записывать текстовую строку по частям:
Оператор endl завершает ввод строки символом "возврат каретки":
С помощью оператора включения несложно записывать в файл значения переменных или элементов массива:
В результате выполнения кода образуется три строки текстового файла Temp.txt :
Обратите внимание, что числовые значения записываются в файл в виде текстовых строк, а не двоичных значений.
Оператор извлечения ( >> )производит обратные действия. Казалось бы, чтобы извлечь символы из файла Temp.txt , записанного ранее, нужно написать код наподобие следующего:
Однако оператор извлечения остановится на первом попавшемся разделителе (символе пробела, табуляции или новой строки). Таким образом, при разборе предложения "Текстовый массив содержит переменные" только слово "Текстовый" запишется в массив buff , пробел игнорируется, а слово "массив" станет значением целой переменной vx и исполнение кода "пойдет вразнос" с неминуемым нарушением структуры данных. Далее, при обсуждении класса ifstream , будет показано, как правильно организовать чтение файла из предыдущего примера.
Класс ifstream: чтение файлов
Как следует из расшифровки названия, класс ifstream предназначен для ввода файлового потока. Далее перечислены основные методы класса. Большая часть из них унаследована от класса istream и перегружена с расширением родительской функциональности. К примеру, функция get , в зависимости от параметра вызова, способна считывать не только одиночный символ, но и символьный блок.
Метод | Описание |
open | Открывает файл для чтения |
get | Читает один или более символов из файла |
getline | Читает символьную строку из текстового файла или данные из бинарного файла до определенного ограничителя |
read | Считывает заданное число байт из файла в память |
eof | Возвращает ненулевое значение (true), когда указатель потока достигает конца файла |
peek | Выдает очередной символ потока, но не выбирает его |
seekg | Перемещает указатель позиционирования файла в заданное положение |
tellg | Возвращает текущее значение указателя позиционирования файла |
close | Закрывает файл |
Теперь понятно, как нужно модифицировать предыдущий пример, чтобы использование оператора извлечения данных давало ожидаемый результат:
Метод getline прочитает первую строку файла до конца, а оператор >> присвоит значения переменным.
Следующий пример показывает добавление данных в текстовый файл с последующим чтением всего файла. Цикл while (1) используется вместо while(!file2.eof()) по причинам, которые обсуждались в предыдущей лекции.
Этот код под ОС Windows также зависит от наличия в последней строке файла символа перевода строки, надежнее было бы сделать так:
Явные вызовы методов open и close не обязательны. Действительно, вызов конструктора с аргументом позволяет сразу же, в момент создания поточного объекта file , открыть файл:
Вместо метода close можно использовать оператор delete , который автоматически вызовет деструктор объекта file и закроет файл. Код цикла while обеспечивает надлежащую проверку признака конца файла.
Класс ofstream: запись файлов
Класс ofstream предназначен для вывода данных из файлового потока. Далее перечислены основные методы данного класса.
Метод | Описание |
open | Открывает файл для записи |
put | Записывает одиночный символ в файл |
write | Записывает заданное число байт из памяти в файл |
seekp | Перемещает указатель позиционирования в указанное положение |
tellp | Возвращает текущее значение указателя позиционирования файла |
close | Закрывает файл |
Описанный ранее оператор включения удобен для организации записи в текстовый файл:
Бинарные файлы
В принципе, бинарные данные обслуживаются наподобие текстовых. Отличие состоит в том, что если бинарные данные записываются в определенной логической структуре, то они должны считываться из файла в переменную того же структурного типа.
Первый параметр методов write и read (адрес блока записи/чтения) должен иметь тип символьного указателя char * , поэтому необходимо произвести явное преобразование типа адреса структуры void * . Второй параметр указывает, что бинарные блоки файла имеют постоянный размер байтов независимо от фактической длины записи. Следующее приложение дает пример создания и отображения данных простейшей записной книжки. Затем записи файла последовательно считываются и отображаются на консоли.
P.S. При выполнении этого и других листингов в Visual Studio последних версий может дополнительно понадобиться подключение директивы _CRT_SECURE_NO_WARNINGS.
В результате выполнения этого кода образуется бинарный файл Notebook.dat из трех блоков размером по 80 байт каждый (при условии, что символы - однобайтовые). Естественно, вы можете использовать другие поточные методы и проделывать любые операции над полями определенной структуры данных.
Класс fstream: произвольный доступ к файлу
Предположим что в нашей записной книжке накопилось 100 записей, а мы хотим считать 50-ю. Конечно, можно организовать цикл и прочитать все записи с первой по заданную. Очевидно, что более целенаправленное решение - установить указатель позиционирования файла pos прямо на запись 50 и считать ее:
Подобные операции поиска эффективны, если файл состоит из записей известного и постоянного размера. Чтобы заменить содержимое произвольной записи, надо открыть поток вывода в режиме модификации:
Если не указать флаг ios::ate (или ios::app ), то при открытии бинарного файла Notebook.dat его предыдущее содержимое будет стерто!
Дополнительно может понадобиться указать, откуда отсчитывается смещение.
Наконец, можно открыть файл одновременно для чтения/записи, используя методы, унаследованные поточным классом fstream от своих предшественников. Поскольку класс fstream произведен от istream и ostream (родителей ifstream и ofstream соответственно), все упомянутые ранее методы становятся доступными в приложении.
Мне нужно проверить, может ли пользователь писать в папку, прежде чем пытаться это сделать.
Когда я гуглял, как проверить доступ на запись, ничего подобного не возникало, и на самом деле оказалось очень сложно на самом деле проверить разрешения в Windows. Я обеспокоен тем, что я чрезмерно упрощаю вещи и что этот метод не является надежным, хотя, похоже, он работает.
Будет ли мой метод проверки правильности работы текущего пользователя с правами на запись?
Разве доступ к просмотру разрешений не такой же, как и запрет на запись в него?Были и другие подобные вопросы, задаваемые ранее.
Как ни странно, один из этих других вопросов был открыт на другой вкладке, но я не видел ответа о DirectorySecurity, научите меня читать все ответы, а не только принятые ;-) Не упадет ли он и при использовании длинных путей в Windows? Это не скажет вам, если у вас есть разрешение на запись, оно только скажет вам, можете ли вы искать разрешения для этой папки или нет. Также вы можете писать, но не можете искать разрешения.Я ценю, что для этого поста немного поздно, но этот фрагмент кода может оказаться полезным.
Перетащите это в консольное приложение и посмотрите, делает ли оно то, что вам нужно.
Я получаю исключение при вызове, GetAccessControl но мое программное обеспечение действительно способно записывать в каталог, на который я смотрю . @JonCage - какое исключение ты получаешь? По иронии судьбы первое, что приходит на ум, - это проблема безопасности. Имеет ли учетная запись, в которой работает ваше приложение, разрешение на получение информации ACL? Вам необходимо добавить проверку для типа FileSystemAccessRule. Если это правило запрета, вы ошибочно сообщаете о нем как о доступном для записи. Я пытаюсь использовать это. Нашел еще одну проблему. Если права назначаются только группам, а не конкретным пользователям, это будет неправильно сообщать о том, что у них нет прав на запись. Например, доступ на запись предоставлен «Аутентифицированным пользователям» Этот ответ будет перехватывать все исключения, которые могут возникнуть при попытке записи файла, а не только нарушения прав доступа. @GY, string tempFileName = Path.GetRandomFileName(); видимо @ Matt, это точно отвечает на заданный вопрос «каталог доступен для записи», независимо от причины сбоя. Вы скорее отвечаете на « почему я не могу написать в каталог». :) Я получаю ложное срабатывание с этим кодом. File.Create () работает нормально (и оставляет временный файл, если вы изменили последний параметр), даже если исполняющий пользователь не имеет разрешения на запись в эту папку. Действительно очень странно - потратил час, пытаясь понять, почему, но я в тупике. Из всех альтернатив, которые я попробовал ниже (и ссылки) - это единственный, который работает надежно.Я пробовал большинство из них, но они дают ложные срабатывания, все по той же причине. Недостаточно проверить каталог на наличие доступных разрешений, вы должны проверить, что вошедший в систему пользователь является членом группы, которая имеет это разрешение. Для этого вы получаете удостоверение пользователя и проверяете, является ли он членом группы, содержащей FileSystemAccessRule IdentityReference. Я проверил это, работает без нареканий ..
Спасибо Джон, у меня также есть ложные срабатывания, пока я не использую твой код, чтобы снова проверить группу пользователей по правилу IdentifyReference! Мне нужно было добавить дополнительную проверку для идентичности. Владелец == rule.IdentityReference, поскольку у меня был пользователь, который предоставил доступ, но не в каких-либо группах, как выделенная локальная учетная запись для служб AccessControlType deny имеет преимущество перед разрешением, поэтому следует также проверять, чтобы были полностью проработаны правила, запрещающие право доступа, и при проверке запрещенных типов это должно быть (AccessRight & rule.FileSystemRights) > 0 потому, что любой запрещенный тип подчиненного доступа AccessRight означает, что у вас нет полного доступ к AccessRight Как упоминалось выше grinder22, мне нужно было поменять; if (identity.Groups.Contains (rule.IdentityReference)) в if (identity.Groups.Contains (rule.IdentityReference) || identity.Owner.Equals (rule.IdentityReference)), поскольку у меня был пользователь, который имел доступ, но не был т в любой из групп.ИМХО, единственный 100% надежный способ проверить, можете ли вы записать в каталог, - это фактически писать в него и в конечном итоге перехватывать исключения.
Например, для всех пользователей (Builtin \ Users) этот метод работает отлично - наслаждайтесь.
Если я не ошибаюсь, это близко, но не совсем там - оно упускает из виду тот факт, что fsAccessRule.AccessControlType может быть AccessControlType.Deny .Ваш код получает DirectorySecurity для данного каталога и обрабатывает исключение (из-за того, что у вас нет доступа к информации о безопасности) правильно. Однако в вашем примере вы на самом деле не запрашиваете возвращенный объект, чтобы увидеть, какой доступ разрешен - и я думаю, что вам нужно добавить это.
+1 - Я только что столкнулся с этой проблемой, когда исключение не было выдано при вызове GetAccessControl, но я получаю несанкционированное исключение при попытке записи в тот же каталог.Вот модифицированная версия ответа CsabaS , которая учитывает явные правила запрета доступа. Функция просматривает все FileSystemAccessRules для каталога и проверяет, находится ли текущий пользователь в роли, которая имеет доступ к каталогу. Если такие роли не найдены или пользователь находится в роли с запрещенным доступом, функция возвращает false. Чтобы проверить права на чтение, передайте FileSystemRights.Read функции; для прав записи передайте FileSystemRights.Write. Если вы хотите проверить произвольные права пользователя, а не права текущего, замените currentUser WindowsIdentity на желаемый WindowsIdentity. Я также не рекомендую полагаться на такие функции, чтобы определить, может ли пользователь безопасно использовать каталог. Этот ответ прекрасно объясняет почему.
Код Csaba для меня не удался в Windows 10 (но хорошо на моей машине с Win7). Выше, кажется, чтобы решить проблему.Вышеуказанные решения хороши, но для меня этот код прост и выполним. Просто создайте временный файл. Если файл создан, его средний пользователь имеет право на запись.
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Предоставляет свойства и методы экземпляра для создания, копирования, удаления, перемещения и открытия файлов, а также позволяет создавать объекты FileStream. Этот класс не наследуется.
Примеры
В следующем примере показаны некоторые основные члены FileInfo класса.
При первом извлечении свойств FileInfo вызывает Refresh метод и кэширует сведения о файле. При последующих вызовах необходимо вызвать, Refresh чтобы получить последнюю копию информации.
В этом примере создаются выходные данные, аналогичные приведенным ниже.
Комментарии
Используйте FileInfo класс для выполнения типичных операций, таких как копирование, перемещение, переименование, создание, открытие, удаление и добавление в файлы.
При выполнении нескольких операций над одним и тем же файлом может оказаться более эффективным использование FileInfo методов экземпляра вместо соответствующих статических методов File класса, поскольку проверка безопасности не всегда требуется.
Многие из FileInfo методов возвращают другие типы ввода-вывода при создании или открытии файлов. Эти другие типы можно использовать для дальнейшего управления файлом. Дополнительные сведения см. в разделе определенные FileInfo элементы, такие как Open ,, OpenRead OpenText , CreateText или Create .
По умолчанию всем пользователям предоставляется полный доступ на чтение и запись к новым файлам.
В следующей таблице описаны перечисления, используемые для настройки поведения различных FileInfo методов.
Перечисление | Описание |
---|---|
FileAccess | Указывает доступ к файлу для чтения и записи. |
FileShare | Указывает уровень доступа, разрешенный для уже используемого файла. |
FileMode | Указывает, будет ли содержимое существующего файла сохранено или перезаписано, и должны ли запросы на создание существующего файла вызывать исключение. |
В членах, принимающих путь в качестве входной строки, этот путь должен иметь правильный формат или возникнет исключение. Например, если путь является полным, но начинается с пробела, путь не усекается в методах класса. Поэтому путь имеет неправильный формат и возникает исключение. Аналогично, путь или сочетание путей не могут быть полными и дважды. Например, "c:\temp c:\Windows" также вызывает исключение в большинстве случаев. При использовании методов, принимающих строку пути, убедитесь, что пути имеют правильный формат.
В членах, принимающих путь, путь может ссылаться на файл или только на каталог. Указанный путь может также ссылаться на относительный путь или путь в формате UNC для имени сервера и общего ресурса. Например, все следующие допустимые пути:
FileInfoКласс предоставляет следующие свойства, позволяющие получить сведения о файле. Пример использования каждого свойства см. в разделе страницы свойств.
DirectoryСвойство получает объект, представляющий родительский каталог файла.
DirectoryNameСвойство получает полный путь к родительскому каталогу файла.
ExistsСвойство проверяет наличие файла перед его обработам.
IsReadOnlyСвойство извлекает или задает значение, указывающее, можно ли изменить файл.
LengthВозвращает размер файла.
NameВозвращает имя файла.
Конструкторы
Выполняет инициализацию нового экземпляра класса FileInfo, который служит оболочкой для пути файла.
Представляет полный путь к каталогу или файлу.
Первоначально заданный пользователем относительный или абсолютный путь.
Свойства
Получает или задает атрибуты для текущего файла или каталога.
Получает или задает время создания текущего файла или каталога.
Получает или задает время создания текущего файла или каталога в формате UTC.
Получает экземпляр родительского каталога.
Получает строку, представляющую полный путь к каталогу.
Получает значение, показывающее, существует ли файл.
Возвращает часть имени файла, включая начальную точку . даже если это полное имя файла, или пустая строка, если расширение отсутствует.
Получает полный путь к каталогу или файлу.
Возвращает или задает значение, позволяющее определить, является ли текущий файл доступным только для чтения.
Получает или задает время последнего доступа к текущему файлу или каталогу.
Получает или задает дату и время последнего доступа к заданному файлу или каталогу в формате всеобщего скоординированного времени (UTC).
Получает или задает время последней операции записи в текущий файл или каталог.
Получает или задает время последней операции записи в текущий файл или каталог в формате всеобщего скоординированного времени (UTC).
Получает размер текущего файла в байтах.
Если этот FileSystemInfo экземпляр представляет ссылку, возвращает путь к цели ссылки. Если ссылка не существует в FullName или этот экземпляр не представляет ссылку, возвращает null .
Получает имя файла.
Методы
Создает StreamWriter, который добавляет текст в файл, представленный этим экземпляром FileInfo.
Копирует существующий файл в новый файл и запрещает перезапись существующего файла.
Копирует существующий файл в новый файл и разрешает перезапись существующего файла.
Создает символическую ссылку, расположенную в FullName , указывающую на указанный объект pathToTarget .
Создает объект, который содержит всю необходимую информацию для создания прокси-сервера, используемого для взаимодействия с удаленным объектом.
Создает StreamWriter, который записывает новый текстовый файл.
Расшифровывает файл, зашифрованный текущей учетной записью с помощью метода Encrypt().
Удаляет файл без возможности восстановления.
Шифрует файл таким образом, чтобы его можно было расшифровать только с помощью учетной записи, которая использовалась для шифрования.
Определяет, равен ли указанный объект текущему объекту.
Возвращает объект FileSecurity, который инкапсулирует записи списка управления доступом (ACL) для файла, описываемого текущим объектом FileInfo.
Получает объект FileSecurity, который инкапсулирует заданный тип записей списка управления доступом для файла, описываемого текущим объектом FileInfo.
Служит хэш-функцией по умолчанию.
Извлекает объект обслуживания во время существования, который управляет политикой времени существования данного экземпляра.
Устанавливает объект SerializationInfo с именем файла и дополнительными сведениями об исключении.
Возвращает объект Type для текущего экземпляра.
Получает объект службы времени существования для управления политикой времени существования для этого экземпляра.
Создает неполную копию текущего объекта Object.
Создает неполную копию текущего объекта MarshalByRefObject.
Перемещает заданный файл в новое местоположение и разрешает переименование файла.
Перемещает указанный файл в новое расположение, предоставляя параметры для указания нового имени файла и перезаписи конечного файла, если он уже существует.
Открывает файл в заданном режиме.
Открывает файл в заданном режиме с доступом для чтения или записи, или и для чтения, и для записи.
Открывает файл в заданном режиме с доступом для чтения, записи или и для чтения, и для записи и с заданным параметром совместного доступа.
Инициализирует новый экземпляр FileStream класса с заданным режимом создания, разрешением на чтение, запись и совместное использование, доступ к другим потокам FILESTREAM может иметь один и тот же файл, размер буфера, дополнительные параметры файла и размер выделения.
Создает доступный только для чтения поток FileStream.
Создает поток StreamReader с кодировкой UTF-8, который считывает данные из существующего текстового файла.
Создает доступный только для чтения поток FileStream.
Обновляет состояние объекта.
Заменяет содержимое заданного файла на содержимое файла, которое описано в текущем объекте FileInfo, удаляет исходный файл и создает резервную копию замененного файла.
Заменяет содержимое заданного файла на содержимое файла, которое описано в текущем объекте FileInfo, удаляет исходный файл и создает резервную копию замененного файла. Также позволяет определить, нужно ли игнорировать ошибки слияния.
Возвращает целевой объект указанной ссылки.
Применяет записи списка управления доступом (ACL), описанные объектом FileSecurity, к файлу, который описывается текущим объектом FileInfo.
Возвращает путь в виде строки. Используйте свойство Name для полного пути.
Методы расширения
Создает файловый поток с указанными свойствами и параметрами безопасности.
Есть путь к файлу и uid пользователя. Нужно проверить, имеет ли пользователь права на запись в файл, учитывая acl. Что-то типа access(), но чтобы проверяло для заданного uid. Можно ли использовать сочетание setfsuid()+access() в многопоточном приложении?
Похоже, только setuid из-под root + access. setfsuid во-первых, linux-specific, во-вторых, даже в его мане написано что использовать его не стоит.
Я мог где-то, что-то не проверить, но идея такая. При fork'е остается рабочим только один поток, так что должно все отработать как надо. Еще лучше перед запуском потоков отфоркнуть маленького детеныша от рута и передавать ему запросы.
Может кто-то проверить? По-идее результат ACCESS_UID и ACCESS_SUID должен быть одинаков, в любых ситуациях .что хотелось бы.Не стоит переизобретать код, который уже есть в ядре. Никто никогда не сможет тебе гарантировать, что проверка прав в ядре будет давать тот же результат, что и твой код.
Даже сейчас он неправильно работает. chattr +i и получаем:
Делаем файл с a+rwx внутри директории с a-rwx и получаем то же самое.Если в книге этот алгоритм предлагается для реализации в реальных проектах, не связанных с реализацией FS/VFS, и не в качестве примера того, как это может быть реализовано в операционной системе, то эту книгу следует немедленно выкинуть.
И ещё gcc ругается:
uid - свойство всего процесса, так что понятно как. По-хорошему, нужно делать fork.
Еще лучше перед запуском потоков отфоркнуть маленького детеныша от рута и передавать ему запросы.
Т.е. держать всю дорогу рядом форкнутый процесс, этакий сервачок, и просить его сделать setuid()+access()? Хм.. интересно. Надо попробовать.
Попробовал с форкнутым детенышем, работающим в фоне.
Во-первых, нужно использовать seteuid(), не setuid(), иначе единожды обработав запрос, детеныш так и остается под юзером, на рута обратно не переключается.
Есть еще какие-нибудь идеи?
Поэтому он у меня и отфоркивался, чтобы не надо было обратно получать рута. seteuid не в POSIX, но я не представляю современную систему его не поддерживающую. Странно, что потоки очень активно посылают запросы, тем более, что в man access написано, что его использование не рекомендовано и предлагается делать open, во избежание уязвимости. Может вам не так уж и нужен access?
Может всё-таки своим велосипедом сделать?
Я для самодельного однопоточного FTP-сервера, работающего под рутом, для перехода
в другой каталог написал специальную СИ-функцию-перминатор, с вызовом stat().
Не использовал acl (я и не знаю, что это таое). А выдача и прием самих
файлов и листингов каталога одноразовым fork-нутым процессом с дерутизацией.
Основной рабочий процесс чтоб мог создавать сокеты на привилегированном порту.
Пришлось в цикле проходить все элементы пути на права доступа.
Конечно, у меня задача попроще, и допускается отказ в обслуживании, но
не допускается доступ туда, куда не следует. Вот фрагмент проверки прав:
или под конкретную систему, может и ничего?
Что мешает создать столько дочерних процессов, сколько нужно для решения твоей задачи?
Может всё-таки своим велосипедом сделать?
Вот зачем советовать хрень, а?
К каждому потоку в многопоточной программе подвесить по процессу
тоже как-то не очень весело. Есть же правила доступа, их не может
не быть, иначе получится игра без правил. Сложны ли эти правила,
можно ли их познать, и не слишком ли сложной окажется проверка, это
вопрос.
А если Вы настаиваете, если запрсы не слишком частые, тогда так:
struct passwd *pw = getpwnam(user);
Структура статическая, не будет ли накладки. К каждому потоку будет
своя, или одна общая структура? Если сомнительно, то это нужно вынести
в fork-нутый процесс.
И еще, только сейчас вспомнил, дерутизацию надо делать по-другому, так:
так и надо его перезарядить с root-а на user-а.
К каждому потоку в многопоточной программе подвесить по процессу тоже как-то не очень весело.
Во-первых, кто вам сказал, что дочерних процессов должно быть столько же, сколько и потоков? Их может быть больше или меньше, в зависимости от предполагаемой нагрузки (которая скорее всего будет мзерной). Во-вторых, какая вам разница: процессы у вас или потоки?
Есть же правила доступа, их не может не быть, иначе получится игра без правил. Сложны ли эти правила, можно ли их познать, и не слишком ли сложной окажется проверка, это вопрос.
Если нужно проверить доступность файла для конкретного пользователя, то нужно использовать access() или даже open() для большей надёжности.
Читайте также: