Cygwin: fcntl.h: Define O_TMPFILE and implement it

Difference to Linux: We can't create files which don't show up
in the filesystem due to OS restrictions.  As a kludge, make a
(half-hearted) attempt to hide the file in the filesystem.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2017-11-14 21:28:45 +01:00
parent f94fe74aad
commit 0aa99373c1
4 changed files with 103 additions and 3 deletions

View File

@ -52,6 +52,7 @@ extern "C" {
#define _FNOFOLLOW 0x100000
#define _FDIRECTORY 0x200000
#define _FEXECSRCH 0x400000
#define _FTMPFILE 0x800000
#define O_BINARY _FBINARY
#define O_TEXT _FTEXT
@ -63,6 +64,7 @@ extern "C" {
#define O_DIRECTORY _FDIRECTORY
#define O_EXEC _FEXECSRCH
#define O_SEARCH _FEXECSRCH
#define O_TMPFILE _FTMPFILE
#endif
#if __MISC_VISIBLE

View File

@ -137,6 +137,11 @@ fhandler_base::set_name (path_conv &in_pc)
char *fhandler_base::get_proc_fd_name (char *buf)
{
/* If the file had been opened with O_TMPFILE | O_EXCL, don't
expose the filename. linkat is supposed to return ENOENT in this
case. See man 2 open on Linux. */
if ((get_flags () & (O_TMPFILE | O_EXCL)) == (O_TMPFILE | O_EXCL))
return strcpy (buf, "");
if (get_name ())
return strcpy (buf, get_name ());
if (dev ().name ())
@ -582,7 +587,7 @@ fhandler_base::open (int flags, mode_t mode)
/* Don't use the FILE_OVERWRITE{_IF} flags here. See below for an
explanation, why that's not such a good idea. */
if ((flags & O_EXCL) && (flags & O_CREAT))
if (((flags & O_EXCL) && (flags & O_CREAT)) || (flags & O_TMPFILE))
create_disposition = FILE_CREATE;
else
create_disposition = (flags & O_CREAT) ? FILE_OPEN_IF : FILE_OPEN;
@ -594,6 +599,18 @@ fhandler_base::open (int flags, mode_t mode)
if (pc.is_rep_symlink ())
options |= FILE_OPEN_REPARSE_POINT;
/* O_TMPFILE files are created with delete-on-close semantics, as well
as with FILE_ATTRIBUTE_TEMPORARY. The latter speeds up file access,
because the OS tries to keep the file in memory as much as possible.
In conjunction with FILE_DELETE_ON_CLOSE, ideally the OS never has
to write to the disk at all. */
if (flags & O_TMPFILE)
{
access |= DELETE;
file_attributes |= FILE_ATTRIBUTE_TEMPORARY;
options |= FILE_DELETE_ON_CLOSE;
}
if (pc.fs_is_nfs ())
{
/* Make sure we can read EAs of files on an NFS share. Also make
@ -617,7 +634,7 @@ fhandler_base::open (int flags, mode_t mode)
&& has_attribute (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM))
file_attributes |= pc.file_attributes ();
if (flags & O_CREAT)
if (flags & (O_CREAT | O_TMPFILE))
{
file_attributes |= FILE_ATTRIBUTE_NORMAL;

View File

@ -1248,6 +1248,37 @@ fhandler_disk_file::link (const char *newpath)
return -1;
}
}
else if (pc.file_attributes () & FILE_ATTRIBUTE_TEMPORARY)
{
/* If the original file has been opened with O_TMPFILE the file has
FILE_ATTRIBUTE_TEMPORARY set. After a successful hardlink the
file is not temporary anymore in the usual sense. So we remove
FILE_ATTRIBUTE_TEMPORARY here, even if this makes the original file
visible in directory enumeration. */
OBJECT_ATTRIBUTES attr;
status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
pc.init_reopen_attr (attr, fh), &io,
FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
if (!NT_SUCCESS (status))
debug_printf ("Opening for removing TEMPORARY attrib failed, "
"status = %y", status);
else
{
FILE_BASIC_INFORMATION fbi;
fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart
= fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
fbi.FileAttributes = (pc.file_attributes ()
& ~FILE_ATTRIBUTE_TEMPORARY)
?: FILE_ATTRIBUTE_NORMAL;
status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
FileBasicInformation);
if (!NT_SUCCESS (status))
debug_printf ("Removing the TEMPORARY attrib failed, status = %y",
status);
NtClose (fh);
}
}
return 0;
}
@ -2064,12 +2095,14 @@ fhandler_disk_file::readdir (DIR *dir, dirent *de)
PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
PWCHAR FileName;
ULONG FileNameLength;
ULONG FileAttributes = 0;
ULONG FileAttributes;
IO_STATUS_BLOCK io;
UNICODE_STRING fname;
/* d_cachepos always refers to the next cache entry to use. If it's 0
we must reload the cache. */
restart:
FileAttributes = 0;
if (d_cachepos (dir) == 0)
{
if ((dir->__flags & dirent_get_d_ino))
@ -2183,6 +2216,10 @@ go_ahead:
FileAttributes =
((PFILE_BOTH_DIR_INFORMATION) buf)->FileAttributes;
}
/* We don't show O_TMPFILE files in the filesystem. This is a kludge,
so we may end up removing this snippet again. */
if (FileAttributes & FILE_ATTRIBUTE_TEMPORARY)
goto restart;
RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
d_mounts (dir)->check_mount (&fname);
if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))

View File

@ -36,6 +36,7 @@ details. */
#include <unistd.h>
#include <sys/wait.h>
#include <dirent.h>
#include <ntsecapi.h>
#include "ntdll.h"
#undef fstat
@ -1415,6 +1416,49 @@ open (const char *unix_path, int flags, ...)
set_errno (EEXIST);
__leave;
}
if (flags & O_TMPFILE)
{
if ((flags & O_ACCMODE) != O_WRONLY && (flags & O_ACCMODE) != O_RDWR)
{
set_errno (EINVAL);
__leave;
}
if (!fh->pc.isdir ())
{
set_errno (fh->exists () ? ENOTDIR : ENOENT);
__leave;
}
/* Unfortunately Windows does not allow to create a nameless file.
So create unique filename instead. It starts with ".cyg_tmp_",
followed by an 8 byte unique hex number, followed by an 8 byte
random hex number. */
int64_t rnd;
fhandler_base *fh_file;
char *new_path;
new_path = (char *) malloc (strlen (fh->get_name ())
+ 1 /* slash */
+ 10 /* prefix */
+ 16 /* 64 bit unique id as hex*/
+ 16 /* 64 bit random number as hex */
+ 1 /* trailing NUL */);
if (!new_path)
__leave;
fh->set_unique_id ();
RtlGenRandom (&rnd, sizeof rnd);
__small_sprintf (new_path, "%s/%s%016X%016X",
fh->get_name (), ".cyg_tmp_",
fh->get_unique_id (), rnd);
if (!(fh_file = build_fh_name (new_path, opt, NULL)))
{
free (new_path);
__leave; /* errno already set */
}
delete fh;
fh = fh_file;
}
if ((fh->is_fs_special () && fh->device_access_denied (flags))
|| !fh->open_with_arch (flags, mode & 07777))
__leave; /* errno already set */