E-MailRelay
gfile_win32.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2023 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gfile_win32.cpp
19///
20
21#include "gdef.h"
22#include "gfile.h"
23#include "gassert.h"
24#include "gprocess.h"
25#include <sys/stat.h>
26#include <fcntl.h>
27#include <io.h>
28#include <cstdio>
29#include <direct.h>
30#include <share.h>
31#include <iomanip>
32#include <sstream>
33#include <streambuf>
34#include <limits>
35#include <array>
36
37namespace G
38{
39 namespace FileImp
40 {
41 template <typename T>
42 void open( T & io , const char * path , std::ios_base::openmode mode )
43 {
44 #if GCONFIG_HAVE_EXTENDED_OPEN
45 io.open( path , mode , _SH_DENYNO ) ; // _fsopen()
46 #else
47 io.open( path , mode ) ;
48 #endif
49 }
50 int open( const char * path , int flags , int pmode ) noexcept
51 {
52 #if GCONFIG_HAVE_SOPEN_S
53 _set_errno( 0 ) ; // mingw bug
54 int fd = -1 ;
55 errno_t rc = _sopen_s( &fd , path , flags | _O_NOINHERIT , _SH_DENYNO , pmode ) ;
56 return rc == 0 ? fd : -1 ;
57 #else
58 #if GCONFIG_HAVE_SOPEN
59 return _sopen( path , flags | _O_NOINHERIT , _SH_DENYNO , pmode ) ;
60 #else
61 return _open( path , flags | _O_NOINHERIT , pmode ) ;
62 #endif
63 #endif
64 }
65 void uninherited( HANDLE h )
66 {
67 if( h )
68 SetHandleInformation( h , HANDLE_FLAG_INHERIT , 0 ) ;
69 }
70 HANDLE handle( int fd )
71 {
72 return fd >= 0 ? reinterpret_cast<HANDLE>( _get_osfhandle(fd) ) : HNULL ;
73 }
74 int fd( std::FILE * fp )
75 {
76 return fp ? _fileno( fp ) : -1 ;
77 }
78 std::FILE * fopen( const char * path , const char * mode ) noexcept
79 {
80 std::FILE * fp = nullptr ;
81 #if GCONFIG_HAVE_FSOPEN
82 fp = _fsopen( path , mode , _SH_DENYNO ) ;
83 #else
84 #if GCONFIG_HAVE_FOPEN_S
85 errno_t e = fopen_s( &fp , path , mode ) ;
86 if( e )
87 fp = nullptr ;
88 #else
89 fp = std::fopen( path , mode ) ;
90 #endif
91 #endif
92 uninherited( handle(fd(fp)) ) ; // or add "N" to mode
93 return fp ;
94 }
95 }
96}
97
98void G::File::open( std::ofstream & ofstream , const Path & path )
99{
100 FileImp::open( ofstream , path.cstr() , std::ios_base::out | std::ios_base::binary ) ;
101}
102
103void G::File::open( std::ofstream & ofstream , const Path & path , Text )
104{
105 FileImp::open( ofstream , path.cstr() , std::ios_base::out ) ;
106}
107
108void G::File::open( std::ofstream & ofstream , const Path & path , Append )
109{
110 FileImp::open( ofstream , path.cstr() , std::ios_base::app | std::ios_base::binary ) ;
111}
112
113void G::File::open( std::ifstream & ifstream , const Path & path )
114{
115 FileImp::open( ifstream , path.cstr() , std::ios_base::in | std::ios_base::binary ) ;
116}
117
118void G::File::open( std::ifstream & ifstream , const Path & path , Text )
119{
120 FileImp::open( ifstream , path.cstr() , std::ios_base::in ) ;
121}
122
123std::filebuf * G::File::open( std::filebuf & fb , const Path & path , InOut inout )
124{
125 inout == InOut::In ?
126 FileImp::open( fb , path.cstr() , std::ios_base::in | std::ios_base::binary ) :
127 FileImp::open( fb , path.cstr() , std::ios_base::out | std::ios_base::binary ) ;
128 return fb.is_open() ? &fb : nullptr ;
129}
130
131int G::File::open( const char * path , InOutAppend mode ) noexcept
132{
133 int pmode = _S_IREAD | _S_IWRITE ;
134 if( mode == InOutAppend::In )
135 return FileImp::open( path , _O_RDONLY|_O_BINARY , pmode ) ;
136 else if( mode == InOutAppend::Out )
137 return FileImp::open( path , _O_WRONLY|_O_CREAT|_O_TRUNC|_O_BINARY , pmode ) ;
138 else
139 return FileImp::open( path , _O_WRONLY|_O_CREAT|_O_APPEND|_O_BINARY , pmode ) ;
140}
141
142int G::File::open( const char * path , CreateExclusive ) noexcept
143{
144 int pmode = _S_IREAD | _S_IWRITE ;
145 return FileImp::open( path , _O_WRONLY|_O_CREAT|_O_EXCL|_O_BINARY , pmode ) ;
146}
147
148std::FILE * G::File::fopen( const char * path , const char * mode ) noexcept
149{
150 G_ASSERT( path && mode ) ;
151 return FileImp::fopen( path , mode ) ;
152}
153
154bool G::File::probe( const char * path ) noexcept
155{
156 int pmode = _S_IREAD | _S_IWRITE ;
157 int fd = FileImp::open( path , _O_WRONLY|_O_CREAT|_O_EXCL|O_TEMPORARY|_O_BINARY , pmode ) ;
158 if( fd >= 0 )
159 _close( fd ) ; // also deletes
160 return fd >= 0 ;
161}
162
163void G::File::create( const Path & path )
164{
165 int fd = FileImp::open( path.cstr() , _O_RDONLY|_O_CREAT , _S_IREAD|_S_IWRITE ) ;
166 if( fd < 0 )
167 throw CannotCreate( path.str() ) ;
168 _close( fd ) ;
169}
170
171bool G::File::renameOnto( const Path & from , const Path & to , std::nothrow_t ) noexcept
172{
173 bool ok = 0 == std::rename( from.cstr() , to.cstr() ) ;
174 int error = Process::errno_() ;
175 if( !ok && error == EEXIST ) // MS documentation says EACCES :-<
176 {
177 std::remove( to.cstr() ) ;
178 ok = 0 == std::rename( from.cstr() , to.cstr() ) ;
179 }
180 return ok ;
181}
182
183ssize_t G::File::read( int fd , char * p , std::size_t n ) noexcept
184{
185 constexpr std::size_t limit = std::numeric_limits<unsigned int>::max() ;
186 unsigned int un = static_cast<unsigned int>(std::min(limit,n)) ;
187 return _read( fd , p , un ) ;
188}
189
190ssize_t G::File::write( int fd , const char * p , std::size_t n ) noexcept
191{
192 constexpr std::size_t limit = std::numeric_limits<unsigned int>::max() ;
193 unsigned int un = static_cast<unsigned int>(std::min(limit,n)) ;
194 return _write( fd , p , un ) ;
195}
196
197void G::File::close( int fd ) noexcept
198{
199 _close( fd ) ;
200}
201
202int G::File::mkdirImp( const Path & dir ) noexcept
203{
204 int rc = _mkdir( dir.cstr() ) ;
205 if( rc == 0 )
206 {
207 return 0 ;
208 }
209 else
210 {
211 int e = G::Process::errno_() ;
212 if( e == 0 ) e = EINVAL ;
213 return e ;
214 }
215}
216
217G::File::Stat G::File::statImp( const char * path , bool ) noexcept
218{
219 Stat s ;
220 struct _stat64 statbuf {} ;
221 if( 0 == _stat64( path , &statbuf ) )
222 {
223 s.error = 0 ;
224 s.enoent = false ;
225 s.eaccess = false ;
226 s.is_dir = (statbuf.st_mode & S_IFDIR) ;
227 s.is_link = !s.is_dir ; // good enough for now
228 s.is_executable = (statbuf.st_mode & _S_IEXEC) ; // based on filename extension
229 s.is_empty = statbuf.st_size == 0 ;
230 s.mtime_s = static_cast<std::time_t>(statbuf.st_mtime) ;
231 s.mtime_us = 0 ;
232 s.mode = static_cast<unsigned long>( statbuf.st_mode & 07777 ) ;
233 s.size = static_cast<unsigned long long>( statbuf.st_size ) ;
234 s.blocks = static_cast<unsigned long long>( statbuf.st_size >> 24 ) ;
235 }
236 else
237 {
238 int error = Process::errno_() ;
239 s.error = error ? error : EINVAL ;
240 s.enoent = true ; // could do better
241 s.eaccess = false ;
242 }
243 return s ;
244}
245
246bool G::File::existsImp( const char * path , bool & enoent , bool & eaccess ) noexcept
247{
248 Stat s = statImp( path ) ;
249 if( s.error )
250 {
251 enoent = s.enoent ;
252 eaccess = s.eaccess ;
253 }
254 return s.error == 0 ;
255}
256
257bool G::File::chmodx( const Path & , bool )
258{
259 return true ; // no-op
260}
261
262void G::File::chmod( const Path & , const std::string & )
263{
264}
265
266void G::File::chgrp( const Path & , const std::string & )
267{
268}
269
270bool G::File::chgrp( const Path & , const std::string & , std::nothrow_t )
271{
272 return true ; // no-op
273}
274
275bool G::File::chgrp( const Path & , gid_t , std::nothrow_t )
276{
277 return true ; // no-op
278}
279
280bool G::File::hardlink( const Path & , const Path & , std::nothrow_t )
281{
282 return false ;
283}
284
285G::Path G::File::readlink( const Path & )
286{
287 return Path() ;
288}
289
290void G::File::link( const Path & , const Path & new_link )
291{
292 throw CannotLink( new_link.str() , "not supported" ) ;
293}
294
295bool G::File::link( const Path & , const Path & , std::nothrow_t )
296{
297 return false ; // not supported
298}
299
300std::streamoff G::File::seek( int fd , std::streamoff offset , Seek origin ) noexcept
301{
302 off_t rc = _lseek( fd , static_cast<off_t>(offset) ,
303 origin == Seek::Start ? SEEK_SET : ( origin == Seek::End ? SEEK_END : SEEK_CUR ) ) ;
304 return static_cast<std::streamoff>(rc) ;
305}
306
static std::FILE * fopen(const char *, const char *mode) noexcept
Calls std::fopen().
Definition: gfile_unix.cpp:108
static bool probe(const char *) noexcept
Creates and deletes a temporary probe file.
Definition: gfile_unix.cpp:114
static void open(std::ofstream &, const Path &)
Calls open() on the given output file stream.
Definition: gfile_unix.cpp:55
static void close(int fd) noexcept
Calls ::close() or equivalent.
Definition: gfile_unix.cpp:149
static void link(const Path &target, const Path &new_link)
Creates a symlink.
Definition: gfile_unix.cpp:356
static std::streamoff seek(int fd, std::streamoff offset, Seek) noexcept
Does ::lseek() or equivalent.
Definition: gfile_unix.cpp:432
static ssize_t write(int fd, const char *, std::size_t) noexcept
Calls ::write() or equivalent.
Definition: gfile_unix.cpp:144
static void chmod(const Path &file, const std::string &spec)
Sets the file permissions.
Definition: gfile_unix.cpp:232
static void chmodx(const Path &file)
Makes the file executable. Throws on error.
Definition: gfile.cpp:239
static ssize_t read(int fd, char *, std::size_t) noexcept
Calls ::read() or equivalent.
Definition: gfile_unix.cpp:139
static bool renameOnto(const Path &from, const Path &to, std::nothrow_t) noexcept
Renames the file, deleting 'to' first if necessary.
Definition: gfile_unix.cpp:134
static void create(const Path &)
Creates the file if it does not exist.
Definition: gfile_unix.cpp:125
static G::Path readlink(const Path &link)
Reads a symlink. Throws on error.
Definition: gfile_unix.cpp:396
static bool hardlink(const Path &src, const Path &dst, std::nothrow_t)
Creates a hard link.
Definition: gfile_unix.cpp:350
static void chgrp(const Path &file, const std::string &group)
Sets the file group ownership. Throws on error.
Definition: gfile_unix.cpp:330
A Path object represents a file system path.
Definition: gpath.h:73
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
Low-level classes.
Definition: garg.h:30
STL namespace.
A portable 'struct stat'.
Definition: gfile.h:67