[PATCH 2/3] io: Add closefrom [BZ #10353]

Florian Weimer fweimer@redhat.com
Tue Dec 22 10:49:53 GMT 2020


* Adhemerval Zanella via Libc-alpha:

> diff --git a/NEWS b/NEWS
> index 9e829841f6..221eb4d98e 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -30,6 +30,10 @@ Major new features:
>  
>  * On Linux, the close_range function has been added.
>  
> +* The function closefrom has been adeed.  It closes all file descriptors
> +  greater than given integer.  This function is a GNU extension, although it
> +  also present in other systems.

Typo: “adeed”


> diff --git a/io/closefrom.c b/io/closefrom.c
> new file mode 100644
> index 0000000000..7e8f5a1ce5
> --- /dev/null
> +++ b/io/closefrom.c
> @@ -0,0 +1,34 @@

> +void
> +__closefrom (int lowfd)
> +{
> +  int maxfd = __getdtablesize ();
> +  if (maxfd == -1)
> +    __fortify_fail ("closefrom failed to get the file descriptor table size");
> +
> +  for (int i = 0; i < maxfd; i++)
> +    if (i >= lowfd)
> +      if (__close (i) == -1)
> +        __fortify_fail ("closefrom failed to close a file descriptor");
> +}
> +weak_alias (__closefrom, closefrom)

Is __getdtablesize accurate on Hurd?  Then I guess this part is okay.

__close should probably be __close_nocancel_nostatus.  The error check
is incorrect.

> diff --git a/io/tst-closefrom.c b/io/tst-closefrom.c
> new file mode 100644
> index 0000000000..6eb97cec10
> --- /dev/null
> +++ b/io/tst-closefrom.c

> +  int fds[maximum_fd + 1];

Same issue as the other test?  Contiguous set of descriptors or not?

> +#include <support/test-driver.c>
> diff --git a/posix/unistd.h b/posix/unistd.h
> index 32b8161619..43ddd516aa 100644
> --- a/posix/unistd.h
> +++ b/posix/unistd.h
> @@ -352,6 +352,14 @@ extern __off64_t lseek64 (int __fd, __off64_t __offset, int __whence)
>     __THROW.  */
>  extern int close (int __fd);
>  
> +#ifdef __USE_GNU
> +/* Close all open file descriptors greater than or equal to LOWFD.
> +   Negative LOWFD is clamped to 0.
> +
> +   Similar to close, this functions is a cancellation point.  */
> +extern void closefrom (int __lowfd);
> +#endif

I think this function should not be a cancellation point, so you can add
__THROW.

> +weak_alias (__closefrom, closefrom)
> diff --git a/sysdeps/unix/sysv/linux/closefrom_fallback.c b/sysdeps/unix/sysv/linux/closefrom_fallback.c
> new file mode 100644
> index 0000000000..48815941dd
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/closefrom_fallback.c

> +/* Fallback code: iterates over /proc/self/fd, closing each file descriptor
> +   that fall on the criteria.  */
> +_Bool
> +__closefrom_fallback (int from)
> +{
> +  int dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
> +			       0);
> +  if (dirfd == -1)
> +    return false;

You could try to close a few descriptors if the error is ENOENT and try
again.

> +  enum { buffer_size = 1024 };
> +  char buffer[buffer_size];
> +  bool ret = true;

buffer_size is only used once, so I think it's not needed.

> +  while (true)
> +    {
> +      ssize_t ret = __getdents64 (dirfd, buffer, sizeof (buffer));
> +      if (ret == -1)
> +        goto err;
> +      else if (ret == 0)
> +        break;
> +
> +      char *begin = buffer, *end = buffer + ret;
> +      while (begin != end)
> +	{
> +          unsigned short int d_reclen;
> +	  memcpy (&d_reclen, begin + offsetof (struct dirent64, d_reclen),
> +		  sizeof (d_reclen));
> +	  const char *dname = begin + offsetof (struct dirent64, d_name);
> +	  begin += d_reclen;
> +
> +	  if (dname[0] == '.')
> +	    continue;
> +
> +	  int fd = 0;
> +	  for (const char *s = dname; (unsigned int) (*s) - '0' < 10; s++)
> +	    fd = 10 * fd + (*s - '0');
> +
> +	  if (fd == dirfd || fd < from)
> +	    continue;
> +
> +	  if (__close_nocancel (fd) < 0)
> +	    goto err;
> +	}
> +    }
> +
> +  ret = true;
> +err:
> +  __close_nocancel (dirfd);
> +  return ret;
> +}

I still think it's necessary to seek to position zero after exhausting
the current buffer and some files have been closed.

The error check for __close_nocancel seems superfluous.  It's possible
that you get EBADF due to a race with another thread, and that does not
seem sufficient reason to terminate the process?

Please also add a brief documentation to the manual (likewise for
close_range).

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill



More information about the Libc-alpha mailing list