Renaming an in-use directory results in access-denied error in 9p-mounted windows directory
Windows Version
Microsoft Windows [Version10.0.22631.5189]
WSL Version
2.3.26.0
Are you using WSL 1 or WSL 2?
- [x] WSL 2
- [ ] WSL 1
Kernel Version
No response
Distro Version
No response
Other Software
No response
Repro Steps
Compile and run the code in linux-distro, expecting the error Permission denied
// repro_2.c
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int OPEN_DIR_FLAG = __O_DIRECTORY | O_RDONLY;
int CREATE_DIR_MDOE =
S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH;
int CREATE_FILE_FLAG = O_RDWR | O_CREAT | O_EXCL | O_SYNC | __O_DIRECT;
int CREATE_FILE_MDOE = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
char test_str[100] = "1e2d22";
int main() {
// device "D" should be under-Windows device
char log_pool_tmp_path[100] = "/d/tmp/log_pool.tmp";
char log_pool_path[100] = "/d/tmp/log_pool";
mkdir(log_pool_tmp_path, CREATE_DIR_MDOE);
int tmp_dir_fd = -1;
tmp_dir_fd = open(log_pool_tmp_path, OPEN_DIR_FLAG);
int meta_fd_ = -1;
meta_fd_ =
openat(tmp_dir_fd, "meta", CREATE_FILE_FLAG, CREATE_FILE_MDOE);
pwrite(meta_fd_, test_str, 2, 0);
sleep(1);
int ret = rename(log_pool_tmp_path, log_pool_path);
if(ret == -1) {
printf("error: %d, %s\n", errno, strerror(errno));
return errno;
}
close(meta_fd_);
return 0;
}
Expected Behavior
No error happens, as in linux-based distro like Ubuntu and CentOS which i have tested
Actual Behavior
Compile and run the code in linux-distro, expecting the error Permission denied
Diagnostic Logs
No response
Logs are required for review from WSL team
If this a feature request, please reply with '/feature'. If this is a question, reply with '/question'. Otherwise please attach logs by following the instructions below, your issue will not be reviewed unless they are added. These logs will help us understand what is going on in your machine.
How to collect WSL logs
Download and execute collect-wsl-logs.ps1 in an administrative powershell prompt:
Invoke-WebRequest -UseBasicParsing "https://raw.githubusercontent.com/microsoft/WSL/master/diagnostics/collect-wsl-logs.ps1" -OutFile collect-wsl-logs.ps1
Set-ExecutionPolicy Bypass -Scope Process -Force
.\collect-wsl-logs.ps1
The script will output the path of the log file once done.
If this is a networking issue, please use collect-networking-logs.ps1, following the instructions here
Once completed please upload the output files to this Github issue.
Click here for more info on logging If you choose to email these logs instead of attaching to the bug, please send them to [email protected] with the number of the github issue in the subject, and in the message a link to your comment in the github issue and reply with '/emailed-logs'.
#6664 reported similar problem long time ago.
Reasoning
i explore further and conclude that this issuse results from internel distinction in Windows file system, because when running similar code in original Windows environment same problems will be reproduced as well:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <direct.h> // for _mkdir
#include <io.h> // for _open, _write, _lseek, _close
#include <fcntl.h> // for _O_RDWR, etc.
#include <windows.h> // for Sleep
int CREATE_FILE_FLAG = _O_RDWR | _O_CREAT | _O_EXCL;
int CREATE_FILE_MODE = 0;
// int CREATE_FILE_MODE = _S_IREAD | _S_IWRITE;
char test_str[100] = "1e2d22";
int main() {
char log_pool_tmp_path[100] = "D:\\tmp\\log_pool.tmp";
char log_pool_path[100] = "D:\\tmp\\log_pool";
if (_mkdir(log_pool_tmp_path) != 0) {
if (errno != EEXIST) {
printf("mkdir failed: %s\n", strerror(errno));
return errno;
}
}
char meta_path[200];
snprintf(meta_path, sizeof(meta_path), "%s\\meta", log_pool_tmp_path);
int meta_fd = _open(meta_path, CREATE_FILE_FLAG, CREATE_FILE_MODE);
if (meta_fd == -1) {
printf("open meta failed: %s\n", strerror(errno));
return errno;
}
if (_lseek(meta_fd, 0, SEEK_SET) == -1) {
printf("lseek failed: %s\n", strerror(errno));
_close(meta_fd);
return errno;
}
if (_write(meta_fd, test_str, 2) == -1) {
printf("write failed: %s\n", strerror(errno));
_close(meta_fd);
return errno;
}
Sleep(1000); // sleep 1s
if (rename(log_pool_tmp_path, log_pool_path) != 0) {
printf("rename failed: %s\n", strerror(errno));
_close(meta_fd);
return errno;
}
_close(meta_fd);
return 0;
}
So in my opinion the full process of this issue is that WSL2-9p-client request renameat to Windows-9p-Server, and because the server can't rename an directory if any file under this directory in use( while this is legal in linux-based system), it issued an error.
Possible way to fix
Though this issues appears to be distinctions between two file systems, i still think that WSL2 should implement the linux-semantics as complete as possible. One way to solve this is requiring software to close resources before renaming their parent directory. Another way to get around it is making WSL-9p-client do the manually-close-resources task( which may involve maintaining the directory tree in client-side and take care of any concurrent fault) and reopen these resources after ranaming.
running into this exact issue using the zig compiler - the deviation from linux behavior from this issue has made the compiler completely unusable on ReFS/9p drives on WSL