#define _POSIX_C_SOURCE 200809L #include /* errno */ #include /* fcntl, mkdirat, open, openat */ #include /* uint64_t */ #include /* strlen, memset */ #include /* opena, openat, mkdirat */ #include /* close, unlinkat, write */ /* Napište podprogram ‹largest_files›, kterému je předán: * * • popisovač otevřené složky ‹dir_fd›, * • počet hledaných souborů ‹n›, * • odkaz na pole ‹n› čísel, do kterých vyplní velikosti * nalezených největších souborů (v bajtech, v pořadí od * největšího). * * Uvažujeme pouze obyčejné soubory odkazované přímo předanou * složkou. Nezáporná návratová hodnota určuje počet uvažovaných * souborů (tzn. počet platných kandidátů), -1 indikuje systémovou * chybu. */ int largest_files( int dir_fd, int n, uint64_t *sizes ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* assert */ #include /* err, warn */ #include /* bool, true, false */ static int create_and_open_dir( int base_dir, const char *name ) { if ( mkdirat( base_dir, name, 0777 ) == -1 ) err( 2, "creating directory %s", name ); int fd = openat( base_dir, name, O_RDONLY | O_DIRECTORY, 0777 ); if ( fd == -1 ) err( 2, "opening directory %s", name ); return fd; } static void unlink_if_exists( int fd, const char *name, bool is_dir ) { if ( unlinkat( fd, name, is_dir ? AT_REMOVEDIR : 0 ) == -1 && errno != ENOENT ) err( 2, "unlinking %s", name ); } static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static int create_file( int dir_fd, const char *name ) { int fd = openat( dir_fd, name, O_CREAT | O_TRUNC | O_RDWR, 0666 ); if ( fd == -1 ) err( 2, "creating %s", name ); return fd; } static void write_file( int dir, const char *name, const char *str ) { int fd = create_file( dir, name ); if ( write( fd, str, strlen( str ) ) == -1 ) err( 2, "writing file %s", name ); close_or_warn( fd, name ); } static void write_files( int dir_fd, char **names, char **contents, int n ) { for ( int i = 0; i < n; ++ i ) write_file( dir_fd, names[ i ], contents[ i ] ); } static int check_sizes( int n, const uint64_t *s_sizes, const uint64_t *our_sizes ) { int correct = 0; for ( int i = 0; i < n; ++ i ) if ( s_sizes[ i ] == our_sizes [ i ] ) ++ correct; return correct; } static void unlink_test_files( void ) { int dir1, dir2; dir1 = openat( AT_FDCWD, "zt.r6_test_dir1", O_DIRECTORY ); if ( dir1 == -1 ) return; unlink_if_exists( dir1, "zt.r6_a", false ); unlink_if_exists( dir1, "zt.r6_b", false ); unlink_if_exists( dir1, "zt.r6_c", false ); unlink_if_exists( dir1, "zt.r6_d", false ); unlink_if_exists( dir1, "zt.r6_e", false ); unlink_if_exists( dir1, "zt.r6_f", false ); unlink_if_exists( dir1, "zt.r6_g", false ); dir2 = openat( dir1, "zt.r6_test_dir2", O_DIRECTORY ); if ( dir2 != -1 ) { unlink_if_exists( dir2, "zt.r6_h", false ); close_or_warn( dir2, "zt.r6_test_dir2" ); unlink_if_exists( dir1, "zt.r6_test_dir2", true ); } close_or_warn( dir1, "zt.r6_test_dir1" ); unlink_if_exists( AT_FDCWD, "zt.r6_test_dir1", true ); } int main( void ) { unlink_test_files(); int dir1, dir2, dir; uint64_t sizes[ 10 ] = { 0 }; char *our_files[] = { "zt.r6_a", "zt.r6_b", "zt.r6_c", "zt.r6_d", "zt.r6_e" }; char *our_contents[] = { "All I ever wanted", "All I ever needed", "Is here in my arms", "Words are very unnecessary", "They can only do harm", }; uint64_t our_sizes[] = { strlen( our_contents[ 3 ] ), strlen( our_contents[ 4 ] ), strlen( our_contents[ 2 ] ), strlen( our_contents[ 0 ] ), strlen( our_contents[ 1 ] ), strlen( our_contents[ 1 ] ), 0 }; dir = open( ".", O_RDONLY ); if ( dir == -1 ) err( 2, "opening working directory" ); close_or_warn( dir, "working directory" ); assert( largest_files( dir, 1, sizes ) == -1 ); dir1 = create_and_open_dir( AT_FDCWD, "zt.r6_test_dir1" ); assert( largest_files( dir1, 5, sizes ) == 0 ); write_files( dir1, our_files, our_contents, 5 ); assert( largest_files( dir1, 5, sizes ) == 5 ); assert( check_sizes( 5, sizes, our_sizes ) == 5 ); assert( fcntl( dir1, F_GETFD ) != -1 ); memset( sizes, 0, sizeof( sizes ) ); assert( largest_files( dir1, 2, sizes ) == 5 ); assert( check_sizes( 2, sizes, our_sizes ) == 2 ); assert( fcntl( dir1, F_GETFD ) != -1 ); memset( sizes, 0, sizeof( sizes ) ); dir2 = create_and_open_dir( dir1, "zt.r6_test_dir2" ); if ( linkat( dir1, "zt.r6_a", dir1, "zt.r6_f", 0 ) == -1 ) err( 2, "creating a link to %s", "zt.r6_a" ); assert( largest_files( dir1, 10, sizes ) == 6 ); assert( check_sizes( 6, sizes, our_sizes ) == 6 ); assert( fcntl( dir1, F_GETFD ) != -1 ); assert( fcntl( dir2, F_GETFD ) != -1 ); if ( symlinkat( "zt.r6_b", dir1, "zt.r6_g" ) == -1 ) err( 2, "creating a symlink to %s", "zt.r6_b" ); assert( largest_files( dir1, 10, sizes ) == 6 ); assert( check_sizes( 7, sizes, our_sizes ) == 7 ); assert( fcntl( dir1, F_GETFD ) != -1 ); assert( fcntl( dir2, F_GETFD ) != -1 ); memset( sizes, 0, sizeof( sizes ) ); assert( largest_files( dir2, 10, sizes ) == 0 ); assert( sizes[ 0 ] == 0 ); assert( fcntl( dir1, F_GETFD ) != -1 ); assert( fcntl( dir2, F_GETFD ) != -1 ); write_file( dir2, "zt.r6_h", "Life is a fight." ); memset( sizes, 0, sizeof( sizes ) ); assert( largest_files( dir2, 10, sizes ) == 1 ); assert( sizes[ 0 ] == strlen( "Life is a fight." ) ); assert( sizes[ 1 ] == 0 ); close_or_warn( dir2, "zt.r6_test_dir2" ); close_or_warn( dir1, "zt.r6_test_dir1" ); unlink_test_files(); return 0; }