11/*
22 * Copyright (c) 2017 Jan Kokemüller
33 * All rights reserved.
4+ * Copyright (c) 2025 Klara, Inc.
45 *
56 * Redistribution and use in source and binary forms, with or without
67 * modification, are permitted provided that the following conditions
2526 */
2627
2728#include <sys/param.h>
29+ #include <sys/stat.h>
30+
2831#include <errno.h>
2932#include <fcntl.h>
3033#include <stdio.h>
3437
3538#include <atf-c.h>
3639
40+ ATF_TC (realpath_null );
41+ ATF_TC_HEAD (realpath_null , tc )
42+ {
43+ atf_tc_set_md_var (tc , "descr" , "Test null input" );
44+ }
45+ ATF_TC_BODY (realpath_null , tc )
46+ {
47+ ATF_REQUIRE_ERRNO (EINVAL , realpath (NULL , NULL ) == NULL );
48+ }
49+
50+ ATF_TC (realpath_empty );
51+ ATF_TC_HEAD (realpath_empty , tc )
52+ {
53+ atf_tc_set_md_var (tc , "descr" , "Test empty input" );
54+ }
55+ ATF_TC_BODY (realpath_empty , tc )
56+ {
57+ char resb [PATH_MAX ] = "" ;
58+
59+ ATF_REQUIRE_EQ (0 , mkdir ("foo" , 0755 ));
60+ ATF_REQUIRE_EQ (0 , chdir ("foo" ));
61+ ATF_REQUIRE_ERRNO (ENOENT , realpath ("" , resb ) == NULL );
62+ ATF_REQUIRE_STREQ ("" , resb );
63+ }
64+
3765ATF_TC (realpath_buffer_overflow );
3866ATF_TC_HEAD (realpath_buffer_overflow , tc )
3967{
@@ -44,16 +72,11 @@ ATF_TC_HEAD(realpath_buffer_overflow, tc)
4472
4573ATF_TC_BODY (realpath_buffer_overflow , tc )
4674{
47- char path [MAXPATHLEN ] = { 0 };
48- char resb [MAXPATHLEN ] = { 0 };
49- size_t i ;
75+ char path [PATH_MAX ] = "" ;
76+ char resb [PATH_MAX ] = "" ;
5077
51- path [ 0 ] = 'a' ;
78+ memset ( path , 'a' , sizeof ( path ) - 1 ) ;
5279 path [1 ] = '/' ;
53- for (i = 2 ; i < sizeof (path ) - 1 ; ++ i ) {
54- path [i ] = 'a' ;
55- }
56-
5780 ATF_REQUIRE (realpath (path , resb ) == NULL );
5881}
5982
@@ -66,9 +89,9 @@ ATF_TC_HEAD(realpath_empty_symlink, tc)
6689
6790ATF_TC_BODY (realpath_empty_symlink , tc )
6891{
69- char path [MAXPATHLEN ] = { 0 } ;
70- char slnk [MAXPATHLEN ] = { 0 } ;
71- char resb [MAXPATHLEN ] = { 0 } ;
92+ char path [PATH_MAX ] = "" ;
93+ char slnk [PATH_MAX ] = "" ;
94+ char resb [PATH_MAX ] = "" ;
7295 int fd ;
7396
7497 (void )strlcat (slnk , "empty_symlink" , sizeof (slnk ));
@@ -89,11 +112,77 @@ ATF_TC_BODY(realpath_empty_symlink, tc)
89112 ATF_REQUIRE (unlink (slnk ) == 0 );
90113}
91114
92- ATF_TP_ADD_TCS (tp )
115+ ATF_TC (realpath_partial );
116+ ATF_TC_HEAD (realpath_partial , tc )
117+ {
118+ atf_tc_set_md_var (tc , "descr" ,
119+ "Test that failure leaves a partial result" );
120+ atf_tc_set_md_var (tc , "require.user" , "unprivileged" );
121+ }
122+
123+ ATF_TC_BODY (realpath_partial , tc )
93124{
125+ char resb [PATH_MAX ] = "" ;
126+ size_t len ;
127+
128+ /* scenario 1: missing directory */
129+ ATF_REQUIRE_EQ (0 , mkdir ("foo" , 0755 ));
130+ ATF_REQUIRE_ERRNO (ENOENT , realpath ("foo/bar/baz" , resb ) == NULL );
131+ len = strnlen (resb , sizeof (resb ));
132+ ATF_REQUIRE (len > 8 && len < sizeof (resb ));
133+ ATF_REQUIRE_STREQ ("/foo/bar" , resb + len - 8 );
134+
135+ /* scenario 2: dead link 1 */
136+ ATF_REQUIRE_EQ (0 , symlink ("nix" , "foo/bar" ));
137+ ATF_REQUIRE_ERRNO (ENOENT , realpath ("foo/bar/baz" , resb ) == NULL );
138+ len = strnlen (resb , sizeof (resb ));
139+ ATF_REQUIRE (len > 8 && len < sizeof (resb ));
140+ ATF_REQUIRE_STREQ ("/foo/nix" , resb + len - 8 );
141+
142+ /* scenario 3: missing file */
143+ ATF_REQUIRE_EQ (0 , unlink ("foo/bar" ));
144+ ATF_REQUIRE_EQ (0 , mkdir ("foo/bar" , 0755 ));
145+ ATF_REQUIRE_ERRNO (ENOENT , realpath ("foo/bar/baz" , resb ) == NULL );
146+ len = strnlen (resb , sizeof (resb ));
147+ ATF_REQUIRE (len > 12 && len < sizeof (resb ));
148+ ATF_REQUIRE_STREQ ("/foo/bar/baz" , resb + len - 12 );
149+
150+ /* scenario 4: dead link 2 */
151+ ATF_REQUIRE_EQ (0 , symlink ("nix" , "foo/bar/baz" ));
152+ ATF_REQUIRE_ERRNO (ENOENT , realpath ("foo/bar/baz" , resb ) == NULL );
153+ len = strnlen (resb , sizeof (resb ));
154+ ATF_REQUIRE (len > 12 && len < sizeof (resb ));
155+ ATF_REQUIRE_STREQ ("/foo/bar/nix" , resb + len - 12 );
156+
157+ /* scenario 5: unreadable directory */
158+ ATF_REQUIRE_EQ (0 , chmod ("foo" , 000 ));
159+ ATF_REQUIRE_ERRNO (EACCES , realpath ("foo/bar/baz" , resb ) == NULL );
160+ len = strnlen (resb , sizeof (resb ));
161+ ATF_REQUIRE (len > 8 && len < sizeof (resb ));
162+ /*
163+ * This is arguably wrong. The problem is not with bar, but with
164+ * foo. However, since foo exists and is a directory and the only
165+ * reliable way to detect whether a directory is readable is to
166+ * attempt to read it, we do not detect the problem until we try
167+ * to access bar.
168+ */
169+ ATF_REQUIRE_STREQ ("/foo/bar" , resb + len - 8 );
170+
171+ /* scenario 6: not a directory */
172+ ATF_REQUIRE_EQ (0 , close (creat ("bar" , 0644 )));
173+ ATF_REQUIRE_ERRNO (ENOTDIR , realpath ("bar/baz" , resb ) == NULL );
174+ len = strnlen (resb , sizeof (resb ));
175+ ATF_REQUIRE (len > 4 && len < sizeof (resb ));
176+ ATF_REQUIRE_STREQ ("/bar" , resb + len - 4 );
177+ }
94178
179+ ATF_TP_ADD_TCS (tp )
180+ {
181+ ATF_TP_ADD_TC (tp , realpath_null );
182+ ATF_TP_ADD_TC (tp , realpath_empty );
95183 ATF_TP_ADD_TC (tp , realpath_buffer_overflow );
96184 ATF_TP_ADD_TC (tp , realpath_empty_symlink );
185+ ATF_TP_ADD_TC (tp , realpath_partial );
97186
98187 return atf_no_error ();
99188}
0 commit comments