2018-06-07 17:10:20 -07:00
/*
* Copyright © 2018 Alexey Dobriyan < adobriyan @ gmail . com >
*
* Permission to use , copy , modify , and distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
// Test /proc/*/fd lookup.
2018-10-12 03:53:50 +08:00
2018-06-07 17:10:20 -07:00
# undef NDEBUG
# include <assert.h>
# include <dirent.h>
# include <errno.h>
# include <limits.h>
# include <sched.h>
# include <stdio.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include "proc.h"
/* lstat(2) has more "coverage" in case non-symlink pops up somehow. */
static void test_lookup_pass ( const char * pathname )
{
struct stat st ;
ssize_t rv ;
memset ( & st , 0 , sizeof ( struct stat ) ) ;
rv = lstat ( pathname , & st ) ;
assert ( rv = = 0 ) ;
assert ( S_ISLNK ( st . st_mode ) ) ;
}
static void test_lookup_fail ( const char * pathname )
{
struct stat st ;
ssize_t rv ;
rv = lstat ( pathname , & st ) ;
assert ( rv = = - 1 & & errno = = ENOENT ) ;
}
static void test_lookup ( unsigned int fd )
{
char buf [ 64 ] ;
unsigned int c ;
unsigned int u ;
int i ;
snprintf ( buf , sizeof ( buf ) , " /proc/self/fd/%u " , fd ) ;
test_lookup_pass ( buf ) ;
/* leading junk */
for ( c = 1 ; c < = 255 ; c + + ) {
if ( c = = ' / ' )
continue ;
snprintf ( buf , sizeof ( buf ) , " /proc/self/fd/%c%u " , c , fd ) ;
test_lookup_fail ( buf ) ;
}
/* trailing junk */
for ( c = 1 ; c < = 255 ; c + + ) {
if ( c = = ' / ' )
continue ;
snprintf ( buf , sizeof ( buf ) , " /proc/self/fd/%u%c " , fd , c ) ;
test_lookup_fail ( buf ) ;
}
for ( i = INT_MIN ; i < INT_MIN + 1024 ; i + + ) {
snprintf ( buf , sizeof ( buf ) , " /proc/self/fd/%d " , i ) ;
test_lookup_fail ( buf ) ;
}
for ( i = - 1024 ; i < 0 ; i + + ) {
snprintf ( buf , sizeof ( buf ) , " /proc/self/fd/%d " , i ) ;
test_lookup_fail ( buf ) ;
}
for ( u = INT_MAX - 1024 ; u < = ( unsigned int ) INT_MAX + 1024 ; u + + ) {
snprintf ( buf , sizeof ( buf ) , " /proc/self/fd/%u " , u ) ;
test_lookup_fail ( buf ) ;
}
for ( u = UINT_MAX - 1024 ; u ! = 0 ; u + + ) {
snprintf ( buf , sizeof ( buf ) , " /proc/self/fd/%u " , u ) ;
test_lookup_fail ( buf ) ;
}
}
int main ( void )
{
struct dirent * de ;
unsigned int fd , target_fd ;
if ( unshare ( CLONE_FILES ) = = - 1 )
return 1 ;
/* Wipe fdtable. */
do {
DIR * d ;
d = opendir ( " /proc/self/fd " ) ;
if ( ! d )
return 1 ;
de = xreaddir ( d ) ;
assert ( de - > d_type = = DT_DIR ) ;
assert ( streq ( de - > d_name , " . " ) ) ;
de = xreaddir ( d ) ;
assert ( de - > d_type = = DT_DIR ) ;
assert ( streq ( de - > d_name , " .. " ) ) ;
next :
de = xreaddir ( d ) ;
if ( de ) {
unsigned long long fd_ull ;
unsigned int fd ;
char * end ;
assert ( de - > d_type = = DT_LNK ) ;
fd_ull = xstrtoull ( de - > d_name , & end ) ;
assert ( * end = = ' \0 ' ) ;
assert ( fd_ull = = ( unsigned int ) fd_ull ) ;
fd = fd_ull ;
if ( fd = = dirfd ( d ) )
goto next ;
close ( fd ) ;
}
closedir ( d ) ;
} while ( de ) ;
/* Now fdtable is clean. */
fd = open ( " / " , O_PATH | O_DIRECTORY ) ;
assert ( fd = = 0 ) ;
test_lookup ( fd ) ;
close ( fd ) ;
/* Clean again! */
fd = open ( " / " , O_PATH | O_DIRECTORY ) ;
assert ( fd = = 0 ) ;
/* Default RLIMIT_NOFILE-1 */
target_fd = 1023 ;
while ( target_fd > 0 ) {
if ( dup2 ( fd , target_fd ) = = target_fd )
break ;
target_fd / = 2 ;
}
assert ( target_fd > 0 ) ;
close ( fd ) ;
test_lookup ( target_fd ) ;
close ( target_fd ) ;
return 0 ;
}