ruby/win32/dir.h
Jeremy Evans f2919bd11c
Add error checking to readdir, telldir, and closedir calls in dir.c
Raise SystemCallError exception when these functions return an error.

This changes behavior for the following case (found by the tests):

```ruby
dir1 = Dir.new('..')
dir2 = Dir.for_fd(dir1.fileno)
dir1.close
dir2.close
```

The above code is basically broken, as `dir1.close` closed the file
descriptor.  The subsequent `dir2.close` call is undefined behavior.
When run in isolation, it raises Errno::EBADF after the change, but
if another thread opens a file descriptor between the `dir1.close`
and `dir2.close` calls, the `dir2.close` call could close the file
descriptor opened by the other thread.  Raising an exception is much
better in this case as it makes it obvious there is a bug in the code.

For the readdir check, since the GVL has already been released,
reacquire it rb_thread_call_with_gvl if an exception needs to be
raised.

Due to the number of closedir calls, this adds static close_dir_data
and check_closedir functions.  The close_dir_data takes a
struct dir_data * and handles setting the dir entry to NULL regardless
of failure.

Fixes [Bug #20586]

Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>
2024-09-12 10:04:10 -07:00

50 lines
1.3 KiB
C

#ifndef RUBY_WIN32_DIR_H
#define RUBY_WIN32_DIR_H
#include <stdint.h> /* for uint8_t */
#include <basetsd.h> /* for WCHAR */
#include "ruby/encoding.h" /* for rb_encoding */
#define DT_UNKNOWN 0
#define DT_DIR (S_IFDIR>>12)
#define DT_REG (S_IFREG>>12)
#define DT_LNK 10
struct direct
{
long d_namlen;
ino_t d_ino;
char *d_name;
char *d_altname; /* short name */
short d_altlen;
uint8_t d_type;
};
typedef struct {
WCHAR *start;
WCHAR *curr;
long size;
long nfiles;
long loc; /* [0, nfiles) */
struct direct dirstr;
char *bits; /* used for d_isdir and d_isrep */
} DIR;
DIR* rb_w32_opendir(const char*);
DIR* rb_w32_uopendir(const char*);
struct direct* rb_w32_readdir(DIR *, rb_encoding *);
struct direct* rb_w32_ureaddir(DIR *);
long rb_w32_telldir(DIR *);
void rb_w32_seekdir(DIR *, long);
void rb_w32_rewinddir(DIR *);
int rb_w32_closedir(DIR *);
char *rb_w32_ugetcwd(char *, int);
#define opendir(s) rb_w32_uopendir((s))
#define readdir(d) rb_w32_ureaddir((d))
#define telldir(d) rb_w32_telldir((d))
#define seekdir(d, l) rb_w32_seekdir((d), (l))
#define rewinddir(d) rb_w32_rewinddir((d))
#define closedir(d) rb_w32_closedir((d))
#define getcwd(b, s) rb_w32_ugetcwd(b, s)
#endif /* RUBY_WIN32_DIR_H */