#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* mkdirat, openat */ #include /* mkdirat, openat */ #include /* unlinkat, linkat, close, write, … */ #include /* mkdirat, unlinkat, openat, linkat, … */ #include /* strlen */ #include /* bool */ #include #include /* Napište podprogram ‹has_loops›, kterému je předán popisovač * otevřené složky, a který zjistí, jestli takto zadaný podstrom * obsahuje měkký odkaz, který uzavírá cyklus mezi adresáři. Budeme * zkoumat pouze situace, kdy takový cyklus obsahuje jediný měkký * odkaz. Odkazy mimo zadaný strom neuvažujeme – můžete * předpokládat, že žádné takové neexistují.¹ * * Nápověda: aby měkký odkaz při daných omezeních uzavřel cyklus, * musí ukazovat přímo na některou nadřazenou složku. * * Návratová hodnota 0 znamená úspěšné ukončení kontroly – v takovém * případě je výsledek zapsán do výstupního parametru ‹found›. * V opačném případě je návratová hodnota -1. */ int has_loops( int dir_fd, bool *found ); /* ¹ S takovými odkazy se samozřejmě můžete vypořádat také. Pro * kontrolu, že Váš program pracuje správně i v této situaci je * níže zakomentovaný test. */ /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ static void mkdir_or_die( int base_dir, const char *name ) { if ( mkdirat( base_dir, name, 0777 ) == -1 ) err( 2, "creating directory %s", name ); } static int open_or_die( int base_dir, const char *name ) { int fd = openat( base_dir, name, O_RDONLY | O_DIRECTORY ); if ( fd == -1 ) err( 2, "opening directory %s", name ); return fd; } static void unlink_if_exists( int fd, const char *name, bool dir ) { if ( unlinkat( fd, name, 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 void unlink_test_dirs( const char *root ) { int dir = openat( AT_FDCWD, root, O_DIRECTORY ); if ( dir != -1 ) { unlink_if_exists( dir, "a/x/y", true ); unlink_if_exists( dir, "a/x/loop", false ); unlink_if_exists( dir, "a/x", true ); unlink_if_exists( dir, "a/z", true ); unlink_if_exists( dir, "a", true ); unlink_if_exists( dir, "b/x", true ); unlink_if_exists( dir, "b", true ); unlink_if_exists( dir, "c", true ); unlink_if_exists( dir, "d", false ); close_or_warn( dir, root ); } unlink_if_exists( AT_FDCWD, root, true ); } int main( void ) { const char *root_name = "zt.r3_root"; int root_fd, a_fd, x_fd; bool found; unlink_test_dirs( root_name ); mkdir_or_die( AT_FDCWD, root_name ); root_fd = open_or_die( AT_FDCWD, root_name ); mkdir_or_die( root_fd, "a" ); mkdir_or_die( root_fd, "a/x" ); mkdir_or_die( root_fd, "a/x/y" ); mkdir_or_die( root_fd, "a/z" ); mkdir_or_die( root_fd, "b" ); mkdir_or_die( root_fd, "b/x" ); mkdir_or_die( root_fd, "c" ); a_fd = open_or_die( root_fd, "a" ); x_fd = open_or_die( root_fd, "a/x" ); assert( has_loops( -1, &found ) == -1 ); assert( has_loops( root_fd, &found ) == 0 ); assert( !found ); assert( has_loops( a_fd, &found ) == 0 ); assert( !found ); if ( symlinkat( "b", root_fd, "d" ) == -1 ) err( 2, "creating symlink d" ); assert( has_loops( root_fd, &found ) == 0 ); assert( !found ); assert( has_loops( a_fd, &found ) == 0 ); assert( !found ); if ( symlinkat( "../..", a_fd, "x/loop" ) == -1 ) err( 2, "creating symlink a/x/loop" ); assert( has_loops( root_fd, &found ) == 0 ); assert( found ); assert( has_loops( a_fd, &found ) == 0 ); assert( found ); /* Následující 2 řádky projdou pouze v rozšířené verzi. */ // assert( has_loops( x_fd, &found ) == 0 ); // assert( !found ); close_or_warn( x_fd, "zt.r3_root/a/x" ); close_or_warn( a_fd, "zt.r3_root/a" ); close_or_warn( root_fd, root_name ); unlink_test_dirs( root_name ); return 0; }