From patchwork Fri Sep 23 11:44:27 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 15947 Received: (qmail 32872 invoked by alias); 23 Sep 2016 11:44:35 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 32822 invoked by uid 89); 23 Sep 2016 11:44:31 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-5.0 required=5.0 tests=BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=Obtain, 2988 X-HELO: mx1.redhat.com Date: Fri, 23 Sep 2016 13:44:27 +0200 To: libc-alpha@sourceware.org Subject: [PATCH COMMITTED] tst-open-tmpfile: Add checks for open64, openat64, linkat User-Agent: Heirloom mailx 12.5 7/5/10 MIME-Version: 1.0 Message-Id: <20160923114427.1DD2D439942E1@oldenburg.str.redhat.com> From: fweimer@redhat.com (Florian Weimer) The /proc/self/fd approach is expected to work for all users (with and without CAP_DAC_READ_SEARCH). 2016-09-23 Florian Weimer * io/tst-open-tmpfile.c (wrap_open64, wrap_openat64) (get_random_name): New functions. (check_wrapper_flags_mode): Use linkat to give the new file a name determined by get_random_name. (do_test): Add calls to test open64 and openat64. diff --git a/io/tst-open-tmpfile.c b/io/tst-open-tmpfile.c index 9af1875..9242d62 100644 --- a/io/tst-open-tmpfile.c +++ b/io/tst-open-tmpfile.c @@ -64,6 +64,34 @@ wrap_openat (const char *path, int flags, mode_t mode) return ret; } +/* Error-checking wrapper for the open64 function, compatible with the + wrapper_func type. */ +static int +wrap_open64 (const char *path, int flags, mode_t mode) +{ + int ret = open64 (path, flags, mode); + if (ret < 0) + { + printf ("error: open64 (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode); + exit (1); + } + return ret; +} + +/* Error-checking wrapper for the openat64 function, compatible with the + wrapper_func type. */ +static int +wrap_openat64 (const char *path, int flags, mode_t mode) +{ + int ret = openat64 (AT_FDCWD, path, flags, mode); + if (ret < 0) + { + printf ("error: openat64 (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode); + exit (1); + } + return ret; +} + /* Return true if FD is flagged as deleted in /proc/self/fd, false if not. */ static bool @@ -97,6 +125,32 @@ is_file_deteted (int fd) deleted, strlen (deleted)) == 0; } +/* Obtain a file name which is difficult to guess. */ +static char * +get_random_name (void) +{ + unsigned long long bytes[2]; + int random_device = open ("/dev/urandom", O_RDONLY); + if (random_device < 0) + { + printf ("error: open (\"/dev/urandom\"): %m\n"); + exit (1); + } + ssize_t ret = read (random_device, bytes, sizeof (bytes)); + if (ret < 0) + { + printf ("error: read (\"/dev/urandom\"): %m\n"); + exit (1); + } + if (ret != sizeof (bytes)) + { + printf ("error: short read from /dev/urandom: %zd\n", ret); + exit (1); + } + close (random_device); + return xasprintf ("tst-open-tmpfile-%08llx%08llx.tmp", bytes[0], bytes[1]); +} + /* Check open/openat (as specified by OP and WRAPPER) with a specific PATH/FLAGS/MODE combination. */ static void @@ -127,6 +181,53 @@ check_wrapper_flags_mode (const char *op, wrapper_func wrapper, exit (1); } + /* Check that the file can be turned into a regular file with + linkat. Open a file descriptor for the directory at PATH. Use + AT_FDCWD if PATH is ".", to exercise that functionality as + well. */ + int path_fd; + if (strcmp (path, ".") == 0) + path_fd = AT_FDCWD; + else + { + path_fd = open (path, O_RDONLY | O_DIRECTORY); + if (path_fd < 0) + { + printf ("error: open (\"%s\"): %m\n", path); + exit (1); + } + } + + /* Use a hard-to-guess name for the new directory entry. */ + char *new_name = get_random_name (); + + /* linkat does not require privileges if the path in /proc/self/fd + is used. */ + char *proc_fd_path = xasprintf ("/proc/self/fd/%d", fd); + if (linkat (AT_FDCWD, proc_fd_path, path_fd, new_name, + AT_SYMLINK_FOLLOW) == 0) + { + if (unlinkat (path_fd, new_name, 0) != 0 && errno != ENOENT) + { + printf ("error: unlinkat (\"%s/%s\"): %m\n", path, new_name); + exit (1); + } + } + else + { + /* linkat failed. This is expected if O_EXCL was specified. */ + if ((flags & O_EXCL) == 0) + { + printf ("error: linkat failed after %s (\"%s\", 0x%x, 0%03o): %m\n", + op, path, flags, mode); + exit (1); + } + } + + free (proc_fd_path); + free (new_name); + if (path_fd != AT_FDCWD) + close (path_fd); close (fd); } @@ -197,6 +298,8 @@ do_test (void) supported = true; check_wrapper ("open", wrap_open, paths[i]); check_wrapper ("openat", wrap_openat, paths[i]); + check_wrapper ("open64", wrap_open64, paths[i]); + check_wrapper ("openat64", wrap_openat64, paths[i]); } if (!supported)