LCOV - code coverage report
Current view: top level - tests/src - test0026.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 163 163
Test Date: 2026-07-12 01:01:34 Functions: 100.0 % 7 7
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : #include "sute.h"
       2                 :             : #include <sys/stat.h>
       3                 :             : #include <unistd.h>
       4                 :             : #include <limits.h>
       5                 :             : 
       6                 :             : /**
       7                 :             :  * @brief Create the temporary directory used by one file access test
       8                 :             :  *
       9                 :             :  * The directory path is stored in @p tmpdir. A read-only view of the same path
      10                 :             :  * is returned through @p tmpdir_path_out for use with standard filesystem calls
      11                 :             :  *
      12                 :             :  * @param[out] tmpdir Memory descriptor that receives the directory path
      13                 :             :  * @param[out] tmpdir_path_out Receives a read-only view of the stored path
      14                 :             :  * @return SUCCESS when the directory was created and both outputs are ready,
      15                 :             :  *         otherwise FAILURE
      16                 :             :  */
      17                 :           5 : static Return test0026_prepare_tmpdir(
      18                 :             :         memory     *tmpdir,
      19                 :             :         const char **tmpdir_path_out)
      20                 :             : {
      21                 :             :         /* Status returned by this function through provide()
      22                 :             :            Default value assumes successful completion */
      23                 :           5 :         Return status = SUCCESS;
      24                 :             : 
      25                 :           5 :         status = create_tmpdir(tmpdir);
      26                 :           5 :         *tmpdir_path_out = m_text(tmpdir);
      27                 :             : 
      28                 :           5 :         provide(status);
      29                 :             : }
      30                 :             : 
      31                 :             : /**
      32                 :             :  * @brief Verify that an existing relative path is reported as accessible
      33                 :             :  *
      34                 :             :  * @details
      35                 :             :  * Creates a readable temporary file, opens its parent directory, and passes
      36                 :             :  * only the file name to file_check_access(). Because the file exists and can
      37                 :             :  * be read, the function must return FILE_ACCESS_ALLOWED
      38                 :             :  */
      39                 :           1 : static Return test0026_1(void)
      40                 :             : {
      41                 :           1 :         INITTEST;
      42                 :             : 
      43                 :           1 :         m_create(char,tmpdir,MEMORY_STRING);
      44                 :           1 :         m_create(char,relative_path,MEMORY_STRING);
      45                 :           1 :         const char *tmpdir_path = "";
      46                 :             : 
      47                 :           1 :         ASSERT(SUCCESS == test0026_prepare_tmpdir(tmpdir,&tmpdir_path));
      48                 :           1 :         ASSERT(SUCCESS == m_copy_literal(relative_path,"test0026_accessible.txt"));
      49                 :             : 
      50                 :           1 :         char absolute_file_path[PATH_MAX] = "";
      51                 :           1 :         FILE *file = NULL;
      52                 :           1 :         m_create(char,absolute_path,MEMORY_STRING);
      53                 :           1 :         const int written = snprintf(absolute_file_path,
      54                 :             :                 sizeof(absolute_file_path),
      55                 :             :                 "%s/%s",
      56                 :             :                 tmpdir_path,
      57                 :           1 :                 m_text(relative_path));
      58                 :             : 
      59                 :           1 :         ASSERT(written > 0 && (size_t)written < sizeof(absolute_file_path));
      60                 :             : 
      61                 :           1 :         ASSERT(SUCCESS == m_copy_string(absolute_path,(size_t)written + 1U,absolute_file_path));
      62                 :           1 :         ASSERT(SUCCESS == open_file_stream(absolute_path,"wb",&file));
      63                 :           1 :         ASSERT(file != NULL);
      64                 :           1 :         ASSERT(fclose(file) == 0);
      65                 :             : 
      66                 :           1 :         int directory_fd = -1;
      67                 :             : 
      68                 :           1 :         ASSERT(directory_open(tmpdir,&directory_fd) == FILE_ACCESS_ALLOWED);
      69                 :           1 :         ASSERT(file_check_access(directory_fd,relative_path,R_OK) == FILE_ACCESS_ALLOWED);
      70                 :             : 
      71                 :           1 :         const int close_status = close(directory_fd);
      72                 :           1 :         const int remove_file_status = remove(absolute_file_path);
      73                 :           1 :         call(m_del(absolute_path));
      74                 :           1 :         const int remove_tmpdir_status = rmdir(tmpdir_path);
      75                 :             : 
      76                 :           1 :         ASSERT(close_status == 0);
      77                 :           1 :         ASSERT(remove_file_status == 0);
      78                 :           1 :         ASSERT(remove_tmpdir_status == 0);
      79                 :             : 
      80                 :           1 :         call(m_del(relative_path));
      81                 :           1 :         call(m_del(tmpdir));
      82                 :             : 
      83                 :           1 :         RETURN_STATUS;
      84                 :             : }
      85                 :             : 
      86                 :             : /**
      87                 :             :  * @brief Verify that a missing file is reported as FILE_NOT_FOUND
      88                 :             :  *
      89                 :             :  * @details
      90                 :             :  * Builds a path inside a temporary directory without creating a file at that
      91                 :             :  * location. The access check must distinguish this normal missing-file case
      92                 :             :  * from permission problems and unexpected filesystem errors
      93                 :             :  */
      94                 :           1 : static Return test0026_2(void)
      95                 :             : {
      96                 :           1 :         INITTEST;
      97                 :             : 
      98                 :           1 :         m_create(char,tmpdir,MEMORY_STRING);
      99                 :           1 :         m_create(char,relative_path,MEMORY_STRING);
     100                 :           1 :         const char *tmpdir_path = "";
     101                 :             : 
     102                 :           1 :         ASSERT(SUCCESS == test0026_prepare_tmpdir(tmpdir,&tmpdir_path));
     103                 :           1 :         ASSERT(SUCCESS == m_copy_literal(relative_path,"test0026_missing.txt"));
     104                 :             : 
     105                 :           1 :         int directory_fd = -1;
     106                 :             : 
     107                 :           1 :         ASSERT(directory_open(tmpdir,&directory_fd) == FILE_ACCESS_ALLOWED);
     108                 :           1 :         ASSERT(file_check_access(directory_fd,relative_path,R_OK) == FILE_NOT_FOUND);
     109                 :             : 
     110                 :           1 :         const int close_status = close(directory_fd);
     111                 :           1 :         const int remove_tmpdir_status = rmdir(tmpdir_path);
     112                 :             : 
     113                 :           1 :         ASSERT(close_status == 0);
     114                 :           1 :         ASSERT(remove_tmpdir_status == 0);
     115                 :             : 
     116                 :           1 :         call(m_del(relative_path));
     117                 :           1 :         call(m_del(tmpdir));
     118                 :             : 
     119                 :           1 :         RETURN_STATUS;
     120                 :             : }
     121                 :             : 
     122                 :             : /**
     123                 :             :  * @brief Verify that an unreadable path is reported as FILE_ACCESS_DENIED
     124                 :             :  *
     125                 :             :  * @details
     126                 :             :  * Creates a file and then removes every permission from its parent directory.
     127                 :             :  * The file still exists, but the process cannot pass through the directory to
     128                 :             :  * reach it. file_check_access() must therefore return FILE_ACCESS_DENIED
     129                 :             :  * rather than FILE_NOT_FOUND
     130                 :             :  */
     131                 :           1 : static Return test0026_3(void)
     132                 :             : {
     133                 :           1 :         INITTEST;
     134                 :             : 
     135                 :           1 :         m_create(char,tmpdir,MEMORY_STRING);
     136                 :           1 :         m_create(char,relative_path,MEMORY_STRING);
     137                 :           1 :         const char *tmpdir_path = "";
     138                 :             : 
     139                 :           1 :         ASSERT(SUCCESS == test0026_prepare_tmpdir(tmpdir,&tmpdir_path));
     140                 :           1 :         ASSERT(SUCCESS == m_copy_literal(relative_path,"test0026_locked_dir/file.txt"));
     141                 :             : 
     142                 :           1 :         char locked_dir[PATH_MAX] = "";
     143                 :           1 :         const int dir_written = snprintf(locked_dir,sizeof(locked_dir),"%s/%s",tmpdir_path,"test0026_locked_dir");
     144                 :             : 
     145                 :           1 :         ASSERT(dir_written > 0 && (size_t)dir_written < sizeof(locked_dir));
     146                 :             : 
     147                 :           1 :         ASSERT(mkdir(locked_dir,0700) == 0);
     148                 :             : 
     149                 :           1 :         char locked_file_path[PATH_MAX] = "";
     150                 :           1 :         FILE *file = NULL;
     151                 :           1 :         m_create(char,locked_path,MEMORY_STRING);
     152                 :           1 :         const int file_written = snprintf(locked_file_path,
     153                 :             :                 sizeof(locked_file_path),
     154                 :             :                 "%s/%s",
     155                 :             :                 tmpdir_path,
     156                 :           1 :                 m_text(relative_path));
     157                 :             : 
     158                 :           1 :         ASSERT(file_written > 0 && (size_t)file_written < sizeof(locked_file_path));
     159                 :             : 
     160                 :           1 :         ASSERT(SUCCESS == m_copy_string(locked_path,(size_t)file_written + 1U,locked_file_path));
     161                 :           1 :         ASSERT(SUCCESS == open_file_stream(locked_path,"wb",&file));
     162                 :           1 :         ASSERT(file != NULL);
     163                 :           1 :         ASSERT(fclose(file) == 0);
     164                 :             : 
     165                 :           1 :         ASSERT(chmod(locked_dir,0000) == 0);
     166                 :             : 
     167                 :           1 :         int directory_fd = -1;
     168                 :             : 
     169                 :           1 :         ASSERT(directory_open(tmpdir,&directory_fd) == FILE_ACCESS_ALLOWED);
     170                 :           1 :         ASSERT(file_check_access(directory_fd,relative_path,R_OK) == FILE_ACCESS_DENIED);
     171                 :             : 
     172                 :           1 :         const int close_status = close(directory_fd);
     173                 :           1 :         const int restore_status = chmod(locked_dir,0700);
     174                 :           1 :         const int remove_file_status = remove(locked_file_path);
     175                 :           1 :         const int remove_dir_status = rmdir(locked_dir);
     176                 :             : 
     177                 :           1 :         call(m_del(locked_path));
     178                 :           1 :         const int remove_tmpdir_status = rmdir(tmpdir_path);
     179                 :             : 
     180                 :           1 :         ASSERT(close_status == 0);
     181                 :           1 :         ASSERT(restore_status == 0);
     182                 :           1 :         ASSERT(remove_file_status == 0);
     183                 :           1 :         ASSERT(remove_dir_status == 0);
     184                 :           1 :         ASSERT(remove_tmpdir_status == 0);
     185                 :             : 
     186                 :           1 :         call(m_del(relative_path));
     187                 :           1 :         call(m_del(tmpdir));
     188                 :             : 
     189                 :           1 :         RETURN_STATUS;
     190                 :             : }
     191                 :             : 
     192                 :             : /**
     193                 :             :  * @brief Verify that an unexpected access failure is reported as FILE_ACCESS_ERROR
     194                 :             :  *
     195                 :             :  * @details
     196                 :             :  * Creates a real file and uses the test access-status hook to force
     197                 :             :  * FILE_ACCESS_ERROR for that relative path. Because this error means neither
     198                 :             :  * "missing" nor "permission denied", file_check_access() must return
     199                 :             :  * FILE_ACCESS_ERROR
     200                 :             :  */
     201                 :           1 : static Return test0026_4(void)
     202                 :             : {
     203                 :           1 :         INITTEST;
     204                 :             : 
     205                 :           1 :         m_create(char,tmpdir,MEMORY_STRING);
     206                 :           1 :         m_create(char,relative_path,MEMORY_STRING);
     207                 :           1 :         const char *tmpdir_path = "";
     208                 :             : 
     209                 :           1 :         ASSERT(SUCCESS == test0026_prepare_tmpdir(tmpdir,&tmpdir_path));
     210                 :           1 :         ASSERT(SUCCESS == m_copy_literal(relative_path,"test0026_error.txt"));
     211                 :             : 
     212                 :           1 :         char error_path[PATH_MAX] = "";
     213                 :           1 :         FILE *file = NULL;
     214                 :           1 :         m_create(char,error_target,MEMORY_STRING);
     215                 :           1 :         const int written = snprintf(error_path,
     216                 :             :                 sizeof(error_path),
     217                 :             :                 "%s/%s",
     218                 :             :                 tmpdir_path,
     219                 :           1 :                 m_text(relative_path));
     220                 :             : 
     221                 :           1 :         ASSERT(written > 0 && (size_t)written < sizeof(error_path));
     222                 :             : 
     223                 :           1 :         ASSERT(SUCCESS == m_copy_string(error_target,(size_t)written + 1U,error_path));
     224                 :           1 :         ASSERT(SUCCESS == open_file_stream(error_target,"wb",&file));
     225                 :           1 :         ASSERT(file != NULL);
     226                 :           1 :         ASSERT(fclose(file) == 0);
     227                 :             : 
     228                 :           1 :         int directory_fd = -1;
     229                 :             : 
     230                 :           1 :         ASSERT(directory_open(tmpdir,&directory_fd) == FILE_ACCESS_ALLOWED);
     231                 :           1 :         ASSERT(file_check_access(directory_fd,relative_path,R_OK) == FILE_ACCESS_ALLOWED);
     232                 :             : 
     233                 :           1 :         ASSERT(SUCCESS == set_environment_variable("TESTITALL_TEST_ENV_FILE_ACCESS_SUFFIX",m_text(relative_path)));
     234                 :           1 :         ASSERT(SUCCESS == set_environment_variable("TESTITALL_TEST_ENV_FILE_ACCESS_STATUS","FILE_ACCESS_ERROR"));
     235                 :           1 :         ASSERT(file_check_access(directory_fd,relative_path,R_OK) == FILE_ACCESS_ERROR);
     236                 :           1 :         ASSERT(SUCCESS == set_environment_variable("TESTITALL_TEST_ENV_FILE_ACCESS_SUFFIX",""));
     237                 :           1 :         ASSERT(SUCCESS == set_environment_variable("TESTITALL_TEST_ENV_FILE_ACCESS_STATUS",""));
     238                 :             : 
     239                 :           1 :         const int close_status = close(directory_fd);
     240                 :           1 :         const int remove_file_status = remove(error_path);
     241                 :           1 :         call(m_del(error_target));
     242                 :           1 :         const int remove_tmpdir_status = rmdir(tmpdir_path);
     243                 :             : 
     244                 :           1 :         ASSERT(close_status == 0);
     245                 :           1 :         ASSERT(remove_file_status == 0);
     246                 :           1 :         ASSERT(remove_tmpdir_status == 0);
     247                 :             : 
     248                 :           1 :         call(m_del(relative_path));
     249                 :           1 :         call(m_del(tmpdir));
     250                 :             : 
     251                 :           1 :         RETURN_STATUS;
     252                 :             : }
     253                 :             : 
     254                 :             : /**
     255                 :             :  * @brief Verify access through a directory that can be searched but not listed
     256                 :             :  *
     257                 :             :  * Creates a readable file and changes its parent directory mode to 0111. This
     258                 :             :  * keeps permission to pass through the directory when a child name is already
     259                 :             :  * known, but removes permission to list the directory contents. Opening the
     260                 :             :  * directory and checking the known relative file name must still succeed,
     261                 :             :  * proving that this workflow does not require directory read permission
     262                 :             :  */
     263                 :           1 : static Return test0026_5(void)
     264                 :             : {
     265                 :           1 :         INITTEST;
     266                 :             : 
     267                 :           1 :         m_create(char,tmpdir,MEMORY_STRING);
     268                 :           1 :         m_create(char,relative_path,MEMORY_STRING);
     269                 :           1 :         const char *tmpdir_path = "";
     270                 :             : 
     271                 :           1 :         ASSERT(SUCCESS == test0026_prepare_tmpdir(tmpdir,&tmpdir_path));
     272                 :           1 :         ASSERT(SUCCESS == m_copy_literal(relative_path,"test0026_5_search_only.txt"));
     273                 :             : 
     274                 :           1 :         char absolute_file_path[PATH_MAX] = "";
     275                 :           1 :         const int written = snprintf(absolute_file_path,
     276                 :             :                 sizeof(absolute_file_path),
     277                 :             :                 "%s/%s",
     278                 :             :                 tmpdir_path,
     279                 :           1 :                 m_text(relative_path));
     280                 :             : 
     281                 :           1 :         ASSERT(written > 0 && (size_t)written < sizeof(absolute_file_path));
     282                 :             : 
     283                 :           1 :         FILE *file = fopen(absolute_file_path,"wb");
     284                 :             : 
     285                 :           1 :         ASSERT(file != NULL);
     286                 :           1 :         ASSERT(fclose(file) == 0);
     287                 :           1 :         ASSERT(chmod(tmpdir_path,0111) == 0);
     288                 :             : 
     289                 :           1 :         int directory_fd = -1;
     290                 :             : 
     291                 :           1 :         ASSERT(directory_open(tmpdir,&directory_fd) == FILE_ACCESS_ALLOWED);
     292                 :           1 :         ASSERT(file_check_access(directory_fd,relative_path,R_OK) == FILE_ACCESS_ALLOWED);
     293                 :             : 
     294                 :           1 :         const int close_status = close(directory_fd);
     295                 :           1 :         const int restore_status = chmod(tmpdir_path,0700);
     296                 :           1 :         const int remove_file_status = remove(absolute_file_path);
     297                 :           1 :         const int remove_tmpdir_status = rmdir(tmpdir_path);
     298                 :             : 
     299                 :           1 :         ASSERT(close_status == 0);
     300                 :           1 :         ASSERT(restore_status == 0);
     301                 :           1 :         ASSERT(remove_file_status == 0);
     302                 :           1 :         ASSERT(remove_tmpdir_status == 0);
     303                 :             : 
     304                 :           1 :         call(m_del(tmpdir));
     305                 :           1 :         call(m_del(relative_path));
     306                 :             : 
     307                 :           1 :         RETURN_STATUS;
     308                 :             : }
     309                 :             : 
     310                 :             : /**
     311                 :             :  * @brief Test directory-relative file access checks
     312                 :             :  *
     313                 :             :  * @details
     314                 :             :  * Covers readable, missing, permission-denied, and unexpected-error results for
     315                 :             :  * paths checked relative to an opened directory. It also verifies that a
     316                 :             :  * directory can be opened once and reused to check relative file names,
     317                 :             :  * including a directory that can be searched but cannot be listed
     318                 :             :  */
     319                 :           1 : Return test0026(void)
     320                 :             : {
     321                 :           1 :         INITTEST;
     322                 :             : 
     323                 :           1 :         TEST(test0026_1,"file_check_access(): readable path relative to a directory descriptor");
     324                 :           1 :         TEST(test0026_2,"file_check_access(): missing path relative to a directory descriptor");
     325                 :           1 :         TEST(test0026_3,"file_check_access(): inaccessible path relative to a directory descriptor");
     326                 :           1 :         TEST(test0026_4,"file_check_access(): forced access-check failure");
     327                 :           1 :         TEST(test0026_5,"file_check_access(): readable path below a search-only directory");
     328                 :             : 
     329                 :           1 :         RETURN_STATUS;
     330                 :             : }
        

Generated by: LCOV version 2.0-1