CreateRightsDir - создать каталог (проверить существование) и задать его права

Функция CreateRightsDir создает каталог в файловой системе сервера (сайта) и определяет права каталога для владельца, для его группы и всех остальных пользователей.

Если каталог уже существует, то функция только выполняет попытку переопределить права. В завершение работы функция сравнивает права, которые установились, с заявленными правами, а именно с правами, которые должны были установиться. Если желание не совпадает с фактом, то выдается сообщение об ошибке или формируется исключение.

Имя каталога задается по спецификации, то есть имя задается вместе с относительным или абсолютным путем, например:

$imgDir="Gallery"; здесь явно путь не указан, поэтому новый каталог "Gallery" создается в каталоге из которого запущен текущий PHP-сценарий.
$ImgDir=$_SERVER['DOCUMENT_ROOT'].'/Gallery'; здесь новый каталог "Gallery" создается в корневом каталоге сайта.

Права определяются в традиционной нотации через трех-четырехразрядное восьмеричное число, задающее режим доступа к каталогу для владельца каталога, для группы, в которую входит владелец, и для прочих пользователей.

Права передаются через биты соответствующих восьмеричных разрядов числа:

OCT BIN Маска Права на каталог
0 000 - - - доступ к каталогу и его подкаталогам запрещен, ничего нельзя делать
1 001 - - x можно выполнить двоичный файл о существовании которого известно пользователю, зайти или прочитать каталог запрещено
2 010 - w - отсутствие прав
3 011 - w x можно добавить, удалить, изменить файл каталога. Прочитать содержимое каталога невозможно
4 100 r - - можно прочитать содержимое каталога, узнать имена файлов, там лежащих
5 101 r - x можно зайти в каталог, прочитать его содержимое, посмотреть атрибуты файлов, удалять или добавлять файлы нельзя
6 110 r w - можно изменить файл каталога, удалять или добавлять файлы нельзя
7 111 r w x можно читать файлы, удалять их, добавлять новые, изменять файлы, сделать каталог текущим

Для полноценного просмотра каталога, необходимы права на чтение каталога и доступ к файлам, а главное к их атрибутам, то есть минимальные разумные права на каталог 5 (r-x). Прав 4 (r--) хватит только на просмотр имен файлов, без атрибутов и не будут известны ни размер файла, ни права доступа. На практике для каталогов используется только три режима: 7 (rwx), 5 (r-x) и 0 (---).

Например, при вызове функции CreateRightsDir следующим образом:

$ImgDir=$_SERVER['DOCUMENT_ROOT'].'/CreateRightsDir';
$Result=prown\CreateRightsDir($ImgDir,0755,rvsReturn);

установятся права: "d rwx r-x r-x".

Очень любопытный режим доступа к каталогу - 3 (-wx): он позволяет делать в директории все, что угодно, но не позволяет прочитать имена объектов. То есть если вам не известны названия объектов в этом каталоге, то вы сделать с ними ничего не сможете (даже удалить по маске *, так как маску не к чему применять - имена недоступны).

При работе с каталогами сервера, основанного на Unix, можно использовать старший четвертый восьмеричный разряд, управляя битами SUID, SGID, Sticky.

SUID (Set User ID - бит смены идентификатора пользователя) и SGID (Set Group ID - бит смены идентификатора группы). Если установить SGID для каталога, то все файлы созданные в нем при запуске будут принимать идентификатор группы каталога, а не группы владельца, который создал файл в этом каталоге. Аналогично SUID. Одним словом, если пользователь поместил исполняемый файл в такой каталог, запустив его, процесс запустится от имени владельца (группы) каталога, в котором лежит этот файл.

Установка Sticky-бита на каталог означает, что удалить файл из этого каталога сможет только владелец файла или суперпользователь. Другие пользователи лишаются права удалять файлы.

Это правило будет действовать и при вызове функции CreateRightsDir с установкой каждого бита "d rws rws rwt" следующим образом:

$ImgDir=$_SERVER['DOCUMENT_ROOT'].'/CreateRightsDir';
$Result=prown\CreateRightsDir($ImgDir,07777,rvsReturn);

Правильный CSS!

<?php namespace prown;
// PHP7/HTML5, EDGE/CHROME                          *** CreateRightsDir.php ***
// ****************************************************************************
// * TPhpPrown                      Создать каталог (проверить существование) *
// *                                                       и задать его права *
// *                                                                          *
// * v1.2, 24.12.2021                               Автор:      Труфанов В.Е. *
// * Copyright © 2018 tve                           Дата создания: 08.12.2021 *
// ****************************************************************************

// Синтаксис:
//
//   $Result=CreateRightsDir($Dir,$modeDir=0777,$ModeError=rvsTriggerError);
//
// Параметры:
//
//   $Dir - спецификация создаваемого каталога, то есть абсолютный или 
//      относительный путь к каталогу и имя каталога;
//   $modeDir - параметр назначения прав каталога. Это восьмеричное число,
//      состоящее из четырех цифр: первая цифра всегда равна нулю (так как 
//      указывает восьмеричное число); вторая цифра указывает разрешения для 
//      владельца каталога; третья цифра указывает разрешения для группы 
//      пользователей владельца; четвертая цифра указывает разрешения для всех 
//      остальных. Возможные значения: 1 = выполнение, 2 = право на запись,
//      4 = разрешения на чтение (суммы значений дают возможность установить
//      несколько разрешений)
//
//      rwx --- ---     => $modeDir=700, только владелец может входить в этот 
//                      каталог, читать и записывать в него файлы
//
//      rwx r-x r-x     => $modeDir=755, любой пользователь может входить в этот 
//                      каталог и читать содержимое каталога, но изменять 
//                      содержимое может только владелец
//
//   $ModeError - режим вывода сообщений об ошибке (по умолчанию сообщение 
//      выводится через исключение с пользовательской ошибкой на сайте 
//      doortry.ru)
//
// Возвращаемое значение: 
//
//   $Result - текст сообщения об ошибке (string) при $ModeError=rvsReturn; 
//      false в случае неуспешного выполнения функции;
//      true - в случае успешного выполнения функции 
//
// Зарегистрированные ошибки/исключения:
//   
//   DirСreateError      - "Ошибка создания каталога". Функция дополнительно 
//      сбрасывает детальные данные об ошибках в лог-файл (фиксируются следующие
//      события: 
//      DirNameIncorrect - "Неверно указано название каталога";
//      NoErrReporting   - "Указан оператор @, ошибки отключены";
//   NonWellNumeric      - "Некорректное числовое значение в правах каталога";
//   DirRightsNoAssign   - "Ошибка назначения прав каталога"; 
//   NoDeterminRights    - "Ошибка определения прав каталога";
//   RightsDonotMatch    - "Установленные и желаемые права не совпадают".

require_once 'iniConstMem.php';
require_once 
'iniErrMessage.php';
require_once 
'MakeUserError.php';
require_once 
'ViewPerms.php';
 
// ****************************************************************************
// *       Создать каталог (проверить существование) и задать его права       *
// ****************************************************************************
function CreateRightsDir($Dir,$modeDir=0777,$ModeError=rvsTriggerError)
// https://habr.com/ru/sandbox/124577/  - статья про удаление каталога 
// https://advancedhosters.com/ru/chmod - статья по правам каталога или файла
{
   
$Result=true;
   
// Если каталога нет, то будем создавать его
   
clearstatcache(true,$Dir); // сбросили кэш состояния файла
   
if (!is_dir($Dir))
   {
      
// Обыгрываем возможные ошибки создания каталога:
      // задаём пользовательский обработчик ошибок, запускаем функцию создания
      // каталога, восстанавливаем прежний обработчик ошибок
      
set_error_handler("prown\CreateRightsMkdirHandler");
      
$is=mkdir($Dir,$modeDir);
      
restore_error_handler();
      
// Так как возникла ошибка создания каталога, то выводим сообщение
      
if (!$is)
      {
         
// Отмечаем ошибку создания каталога - Directory creation error по 
         // одной из причин: а) неправильно указана спецификация каталога 
         // (например, в пути или в названии каталога присутствуют запрещенные 
         // символы); б) ...
         
$Result=MakeUserError(DirСreateError.': '.$Dir,'TPhpPrown',$ModeError);
         if (
$ModeError<>rvsReturn$Result=false;
      }
   }
   
// Каталог есть, сравниваем заявленные и установленные права
   
clearstatcache(true,$Dir); // сбросили кэш состояния файла
   
if (is_dir($Dir))
   {
      
// Инициируем строковые представления заявленных и установленных прав
      
$fPermissions='-1'$xPermissions='-2';
      
// Определяем и сравниваем строки с заявленными и установленными правами
      
$is=getFilePerms($Dir,$modeDir,$fPermissions,$xPermissions);
      
// Если права совпали, то с успехом завершаем работу
      
if ($fPermissions===$xPermissions
      {
         
// ConsoleLog('Установленные и желаемые права совпали с первого раза');
      
}
      
// Права не совпали, предполагаем, что находимся в Windows,
      // будем устанавливать права отдельно
      
else 
      {
         
// Обыгрываем возможные ошибки задания прав каталога:
         
set_error_handler("prown\CreateRightsChmodHandler");
         
clearstatcache(true,$Dir); // сбросили кэш состояния файла
         
$is=chmod($Dir,$modeDir);
         
restore_error_handler();
         
// Так как возникла ошибка задания прав, то выводим сообщение
         
if (!$is)
         {
            
$Result=MakeUserError(DirRightsNoAssign.'[onerror]: '.$Dir,'TPhpPrown',$ModeError);
            if (
$ModeError<>rvsReturn$Result=false;
         }
         
// Ошибки задания прав нет, поэтому повторно определяем и сравниваем 
         // строки с заявленными и установленными правами
         
else
         {
            
// Инициируем строковые представления заявленных и установленных прав
            
$fPermissions='-3'$xPermissions='-4';
            
// Определяем и сравниваем строки с заявленными и установленными правами
            
$is=getFilePerms($Dir,$modeDir,$fPermissions,$xPermissions); 
            
// Завершаем работу, если установленные и желаемые права совпадают
            
if ($fPermissions===$xPermissions) {}
            
// Отмечаем ошибку, если установленные и желаемые права НЕ совпадают
            
else 
            {
               
$Result=MakeUserError(RightsDonotMatch.': '.
               
$fPermissions.'<>'.$xPermissions,'TPhpPrown',$ModeError);
               if (
$ModeError<>rvsReturn$Result=false;
            }
         }
      }
   } 
   return 
$Result;
}
// ****************************************************************************
// *          Получить строки с установленными и заявленными правами          *
// ****************************************************************************
function getFilePerms($Dir,$modeDir,&$fPermissions,&$xPermissions)
{
   
$Result=true;
   
// Формируем строку с заявленными правами 
   
if ($modeDir==0000$xPermissions='0000';
   else 
$xPermissions='0'.sprintf('%o',$modeDir);
   
// Если права нулевые, то раздвигаем   
   
clearstatcache(true,$Dir);
   
// Определяем установленные права и обыгрываем возможные ошибки определения:
   
set_error_handler("prown\CreateRightsPermsHandler");
   
$permissions=fileperms($Dir);
   
restore_error_handler();
   
// Так как возникла ошибка определения прав, то выводим сообщение
   
if (!$permissions)
   {
      
$Result=MakeUserError(NoDeterminRights.'[onerror]: '.$Dir,'TPhpPrown',$ModeError);
      if (
$ModeError<>rvsReturn$Result=false;
   }
   
// Формируем строку с установленными правами
   
else
   {
      
$fPermissions=substr(sprintf('%o',$permissions),-4);
      
// Если задействованы дополнительные биты прав (Unix), добавляем 0 восьмеричности
      
if (substr($fPermissions,0,1)<>'0'$fPermissions='0'.$fPermissions;
   }
   return 
$Result;  
}
// ****************************************************************************
// *                 Обыграть возможные ошибки создания каталога              *
// ****************************************************************************
function CreateRightsMkdirHandler($errno,$errstr,$errfile,$errline)
{
   
$modul='CreateRightsMkdirHandler';
   
// Если error_reporting нулевой, значит, использован оператор @,
   // все ошибки должны игнорироваться
   
if (!error_reporting()) 
   {
      
putErrorInfo($modul,$errno,
         
'['.NoErrReporting.'] '.$errstr,$errfile,$errline);
   }
   else
   {
      
// Отлавливаем ошибку "Неверно указано название каталога"
      // "Directory name is incorrect"
      
$Find='No such file or directory';
      
$Resu=Findes('/'.$Find.'/u',$errstr); 
      if (
$Resu==$Find
      {
         
putErrorInfo($modul,$errno,
            
'['.DirNameIncorrect.'] '.$errstr,$errfile,$errline);
      }
      
// Обобщаем остальные ошибки
      
else 
      {
         
putErrorInfo($modul,$errno,
            
'['.DirСreateError.'] '.$errstr,$errfile,$errline);
      }
   }
}  
// ****************************************************************************
// *               Обыграть возможные ошибки задания прав каталога            *
// ****************************************************************************
function CreateRightsChmodHandler($errno,$errstr,$errfile,$errline)
{
   
$modul='CreateRightsChmodHandler';
   
// Отлавливаем ошибку "Некорректное числовое значение в правах каталога"
   // "A non well formed numeric value encountered"
   
$Find='A non well formed numeric value encountered';
   
$Resu=Findes('/'.$Find.'/u',$errstr); 
   if (
$Resu==$Find
   {
      
putErrorInfo($modul,$errno,
         
'['.NonWellNumeric.'] '.$errstr,$errfile,$errline);
   }
   
// Обобщаем остальные ошибки
   
else 
   {
      
putErrorInfo($modul,$errno,
         
'['.DirRightsNoAssign.'] '.$errstr,$errfile,$errline);
   }
}  
// ****************************************************************************
// *            Обыграть возможные ошибки определения прав каталога           *
// ****************************************************************************
function CreateRightsPermsHandler($errno,$errstr,$errfile,$errline)
{
   
putErrorInfo('CreateRightsPermsHandler',$errno,
      
'['.NoDeterminRights.'] '.$errstr,$errfile,$errline);

// **************************************************** CreateRightsDir.php ***


Сообщения выполненного теста функции


CreateRightsDir

$ImgDir='-$_SERVER["DOCUMENT_ROOT"]/CreateRightsDir';
$Result=prown\CreateRightsDir($ImgDir);......................................... Проверена ошибка создания каталога с неправильно указанным путем
$modeDir=0555; $Result=prown\CreateRightsDir($ImgDir,$modeDir,rvsCurrentPos); .. Сравнительные (IIS-Unix) тесты создания каталога с правами: 0600,0755,0700,0555
$Result=prown\CreateRightsDir($ImgDir,0777,rvsCurrentPos); ..................... Обыграно исключение IIS при удалении каталога с правами 0555
$modeDir=0700; $Result=prown\CreateRightsDir($ImgDir,$modeDir,rvsCurrentPos); .. Сравнительные (IIS-Unix) тесты изменения прав: 0777->0600->0755->0700
$modeDir=07777; $Result=prown\CreateRightsDir($ImgDir,$modeDir,rvsCurrentPos); . Проверено использования в правах бит - SUID,SGID,Sticky
Фактически установленные права: d.rws.rws.rwt 7777
1/1 test cases complete: 16 passes, 0 fails and 0 exceptions.