[PATCH 2/3] io: Add closefrom [BZ #10353]
Adhemerval Zanella
adhemerval.zanella@linaro.org
Tue Dec 22 11:50:58 GMT 2020
On 22/12/2020 07:49, Florian Weimer wrote:
> * 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”
>
Ack.
>
>> 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.
Not sure in fact, it seems so.
>
> __close should probably be __close_nocancel_nostatus. The error check
> is incorrect.
Yeah, I figure out after I sent it. I will chance to __close_nocancel_nostatus.
>
>> 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?
It assumes a contiguous, if you prefer I can remove the array and just use
a maximum value to hold the value.
>
>> +#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.
>
Yeah, from close_range discussion it does seems a better alternative.
>> +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.
Do you mean EMFILE and issue a close on [from, ...] and then try again?
I am not really fan of this heuristics, it might mitigate the failure if
limit of file-descriptors are reached, but it would really depend whether
if 'from' is close of the first open descriptor and the how many close
we issue.
>
>> + enum { buffer_size = 1024 };
>> + char buffer[buffer_size];
>> + bool ret = true;
>
> buffer_size is only used once, so I think it's not needed.
Ack.
>
>> + 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.
Alright, this should only add some overhead; although based on various
current usages it really seems not required.
>
> 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?
Alright, I will remove the check.
>
> Please also add a brief documentation to the manual (likewise for
> close_range).
Ack.
More information about the Libc-alpha
mailing list