E-MailRelay
gfile_win32.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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 "gnowide.h"
23#include "gfile.h"
24#include "gconvert.h"
25#include "gprocess.h"
26#include "gassert.h"
27#include "glog.h"
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <io.h>
31#include <cstdio>
32#include <direct.h>
33#include <share.h>
34#include <iomanip>
35#include <sstream>
36#include <streambuf>
37#include <limits>
38#include <array>
39
40namespace G
41{
42 namespace FileImp
43 {
44 template <typename T>
45 void open( T & io , const Path & path , std::ios_base::openmode mode )
46 {
47 nowide::open( io , path , mode ) ;
48 }
49 int open( const Path & path , int flags , int pmode , bool inherit )
50 {
51 return nowide::open( path , flags , pmode , inherit ) ;
52 }
53 void uninherited( HANDLE h )
54 {
55 if( h )
56 SetHandleInformation( h , HANDLE_FLAG_INHERIT , 0 ) ;
57 }
58 HANDLE handle( int fd )
59 {
60 return fd >= 0 ? reinterpret_cast<HANDLE>( _get_osfhandle(fd) ) : HNULL ;
61 }
62 int fd( std::FILE * fp )
63 {
64 return fp ? _fileno( fp ) : -1 ;
65 }
66 std::FILE * fopen( const Path & path , const char * mode )
67 {
68 std::FILE * fp = nowide::fopen( path , mode ) ;
69 uninherited( handle(fd(fp)) ) ; // or add "N" to mode
70 return fp ;
71 }
72 }
73}
74
75void G::File::open( std::ofstream & ofstream , const Path & path )
76{
77 FileImp::open( ofstream , path , std::ios_base::out | std::ios_base::binary ) ;
78}
79
80void G::File::open( std::ofstream & ofstream , const Path & path , Text )
81{
82 FileImp::open( ofstream , path , std::ios_base::out ) ;
83}
84
85void G::File::open( std::ofstream & ofstream , const Path & path , Append )
86{
87 FileImp::open( ofstream , path , std::ios_base::app | std::ios_base::binary ) ;
88}
89
90void G::File::open( std::ifstream & ifstream , const Path & path )
91{
92 FileImp::open( ifstream , path , std::ios_base::in | std::ios_base::binary ) ;
93}
94
95void G::File::open( std::ifstream & ifstream , const Path & path , Text )
96{
97 FileImp::open( ifstream , path , std::ios_base::in ) ;
98}
99
100std::filebuf * G::File::open( std::filebuf & fb , const Path & path , InOut inout )
101{
102 inout == InOut::In ?
103 FileImp::open( fb , path , std::ios_base::in | std::ios_base::binary ) :
104 FileImp::open( fb , path , std::ios_base::out | std::ios_base::binary ) ;
105 return fb.is_open() ? &fb : nullptr ;
106}
107
108int G::File::open( const Path & path , InOutAppend mode , bool inherit ) noexcept
109{
110 try
111 {
112 const int pmode = _S_IREAD | _S_IWRITE ;
113 if( mode == InOutAppend::In )
114 return FileImp::open( path , _O_RDONLY|_O_BINARY , pmode , inherit ) ;
115 else if( mode == InOutAppend::Out )
116 return FileImp::open( path , _O_WRONLY|_O_CREAT|_O_TRUNC|_O_BINARY , pmode , inherit ) ;
117 else if( mode == InOutAppend::OutNoCreate )
118 return FileImp::open( path , _O_WRONLY|_O_BINARY , pmode , inherit ) ;
119 else
120 return FileImp::open( path , _O_WRONLY|_O_CREAT|_O_APPEND|_O_BINARY , pmode , inherit ) ;
121 }
122 catch(...)
123 {
124 return -1 ;
125 }
126}
127
128int G::File::open( const Path & path , CreateExclusive ) noexcept
129{
130 try
131 {
132 const int pmode = _S_IREAD | _S_IWRITE ;
133 const bool inherit = false ;
134 return FileImp::open( path , _O_WRONLY|_O_CREAT|_O_EXCL|_O_BINARY , pmode , inherit ) ;
135 }
136 catch(...)
137 {
138 return -1 ;
139 }
140}
141
142std::FILE * G::File::fopen( const Path & path , const char * mode ) noexcept
143{
144 try
145 {
146 G_ASSERT( mode ) ;
147 return FileImp::fopen( path , mode ) ;
148 }
149 catch(...)
150 {
151 return nullptr ;
152 }
153}
154
155bool G::File::probe( const Path & path ) noexcept
156{
157 try
158 {
159 const int pmode = _S_IREAD | _S_IWRITE ;
160 const bool inherit = false ;
161 int fd = FileImp::open( path , _O_WRONLY|_O_CREAT|_O_EXCL|O_TEMPORARY|_O_BINARY , pmode , inherit ) ;
162 if( fd >= 0 )
163 _close( fd ) ; // also deletes
164 return fd >= 0 ;
165 }
166 catch(...)
167 {
168 return false ;
169 }
170}
171
172void G::File::create( const Path & path )
173{
174 const int pmode = _S_IREAD | _S_IWRITE ;
175 const bool inherit = false ;
176 int fd = FileImp::open( path , _O_RDONLY|_O_CREAT , pmode , inherit ) ;
177 if( fd < 0 )
178 throw CannotCreate( path.str() ) ;
179 _close( fd ) ;
180}
181
182bool G::File::renameOnto( const Path & from , const Path & to , std::nothrow_t ) noexcept
183{
184 try
185 {
186 bool ok = nowide::rename( from , to ) ;
187 int error = Process::errno_() ;
188 if( !ok && error == EEXIST ) // MS documentation says EACCES :-<
189 {
190 File::remove( to , std::nothrow ) ;
191 ok = nowide::rename( from , to ) ;
192 }
193 return ok ;
194 }
195 catch(...)
196 {
197 return false ;
198 }
199}
200
201ssize_t G::File::read( int fd , char * p , std::size_t n ) noexcept
202{
203 constexpr std::size_t limit = std::numeric_limits<unsigned int>::max() ;
204 unsigned int un = static_cast<unsigned int>(std::min(limit,n)) ;
205 return _read( fd , p , un ) ;
206}
207
208ssize_t G::File::write( int fd , const char * p , std::size_t n ) noexcept
209{
210 constexpr std::size_t limit = std::numeric_limits<unsigned int>::max() ;
211 unsigned int un = static_cast<unsigned int>(std::min(limit,n)) ;
212 return _write( fd , p , un ) ;
213}
214
215void G::File::close( int fd ) noexcept
216{
217 _close( fd ) ;
218}
219
220bool G::File::remove( const Path & path , std::nothrow_t ) noexcept
221{
222 bool ok = nowide::remove( path ) ;
223 if( !ok )
224 {
225 int e = Process::errno_() ;
226 if( e == EACCES )
227 ok = nowide::rmdir( path ) ;
228 }
229 return ok ;
230}
231
232void G::File::remove( const Path & path )
233{
234 bool ok = remove( path , std::nothrow ) ;
235 if( !ok )
236 {
237 int e = Process::errno_() ;
238 G_WARNING( "G::File::remove: cannot remove [" << path << "]: " << Process::strerror(e) ) ;
239 throw CannotRemove( path.str() , Process::strerror(e) ) ;
240 }
241}
242
243bool G::File::cleanup( const Cleanup::Arg & arg ) noexcept
244{
245 return nowide::remove( Path(arg.str()) ) ; // never gets here
246}
247
248int G::File::mkdirImp( const Path & dir ) noexcept
249{
250 try
251 {
252 int rc = nowide::mkdir( dir ) ;
253 if( rc == 0 )
254 {
255 return 0 ;
256 }
257 else
258 {
259 int e = G::Process::errno_() ;
260 if( e == 0 ) e = EINVAL ;
261 return e ;
262 }
263 }
264 catch(...)
265 {
266 return EINVAL ;
267 }
268}
269
270G::File::Stat G::File::statImp( const char * path , bool /*symlink_nofollow*/ ) noexcept
271{
272 try
273 {
274 Stat s ;
275 nowide::statbuf_type statbuf {} ;
276 if( 0 == nowide::stat( path , &statbuf ) )
277 {
278 s.error = 0 ;
279 s.enoent = false ;
280 s.eaccess = false ;
281 s.is_dir = (statbuf.st_mode & S_IFDIR) ;
282 s.is_link = !s.is_dir ; // good enough for now
283 s.is_executable = (statbuf.st_mode & _S_IEXEC) ; // based on filename extension
284 s.is_empty = statbuf.st_size == 0 ;
285 s.mtime_s = static_cast<std::time_t>(statbuf.st_mtime) ;
286 s.mtime_us = 0 ;
287 s.mode = static_cast<unsigned long>( statbuf.st_mode & 07777 ) ;
288 s.size = static_cast<unsigned long long>( statbuf.st_size ) ;
289 s.blocks = static_cast<unsigned long long>( statbuf.st_size >> 24 ) ;
290 }
291 else
292 {
293 int error = Process::errno_() ;
294 s.error = error ? error : EINVAL ;
295 s.enoent = true ; // could do better
296 s.eaccess = false ;
297 }
298 return s ;
299 }
300 catch(...)
301 {
302 Stat s ;
303 s.error = EINVAL ;
304 s.enoent = true ;
305 s.eaccess = false ;
306 return s ;
307 }
308}
309
310bool G::File::existsImp( const char * path , bool & enoent , bool & eaccess ) noexcept
311{
312 try
313 {
314 Stat s = statImp( path ) ;
315 if( s.error )
316 {
317 enoent = s.enoent ;
318 eaccess = s.eaccess ;
319 }
320 return s.error == 0 ;
321 }
322 catch(...)
323 {
324 return false ;
325 }
326}
327
328bool G::File::chmodx( const Path & , bool )
329{
330 return true ; // no-op
331}
332
333void G::File::chmod( const Path & , const std::string & )
334{
335}
336
337void G::File::chgrp( const Path & , const std::string & )
338{
339}
340
341bool G::File::chgrp( const Path & , const std::string & , std::nothrow_t )
342{
343 return true ; // no-op
344}
345
346bool G::File::chgrp( const Path & , gid_t , std::nothrow_t )
347{
348 return true ; // no-op
349}
350
351bool G::File::hardlink( const Path & , const Path & , std::nothrow_t )
352{
353 return false ;
354}
355
356G::Path G::File::readlink( const Path & )
357{
358 return Path() ;
359}
360
361void G::File::link( const Path & , const Path & new_link )
362{
363 throw CannotLink( new_link.str() , "not supported" ) ;
364}
365
366bool G::File::link( const Path & , const Path & , std::nothrow_t )
367{
368 return false ; // not supported
369}
370
371std::streamoff G::File::seek( int fd , std::streamoff offset , Seek origin ) noexcept
372{
373 off_t rc = _lseek( fd , static_cast<off_t>(offset) ,
374 origin == Seek::Start ? SEEK_SET : ( origin == Seek::End ? SEEK_END : SEEK_CUR ) ) ;
375 return static_cast<std::streamoff>(rc) ;
376}
377
static void open(std::ofstream &, const Path &)
Calls open() on the given output file stream.
Definition: gfile_unix.cpp:56
static void close(int fd) noexcept
Calls ::close() or equivalent.
Definition: gfile_unix.cpp:159
static void link(const Path &target, const Path &new_link)
Creates a symlink.
Definition: gfile_unix.cpp:396
static std::streamoff seek(int fd, std::streamoff offset, Seek) noexcept
Does ::lseek() or equivalent.
Definition: gfile_unix.cpp:472
static ssize_t write(int fd, const char *, std::size_t) noexcept
Calls ::write() or equivalent.
Definition: gfile_unix.cpp:154
static void chmod(const Path &file, const std::string &spec)
Sets the file permissions.
Definition: gfile_unix.cpp:272
static bool remove(const Path &path, std::nothrow_t) noexcept
Deletes the file or directory. Returns false on error.
Definition: gfile_unix.cpp:177
static void chmodx(const Path &file)
Makes the file executable. Throws on error.
Definition: gfile.cpp:229
static ssize_t read(int fd, char *, std::size_t) noexcept
Calls read() or equivalent.
Definition: gfile_unix.cpp:149
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:144
static void create(const Path &)
Creates the file if it does not exist.
Definition: gfile_unix.cpp:135
static G::Path readlink(const Path &link)
Reads a symlink. Throws on error.
Definition: gfile_unix.cpp:436
static std::FILE * fopen(const Path &, const char *mode) noexcept
Calls std::fopen().
Definition: gfile_unix.cpp:114
static bool hardlink(const Path &src, const Path &dst, std::nothrow_t)
Creates a hard link.
Definition: gfile_unix.cpp:390
static bool probe(const Path &) noexcept
Creates and deletes a temporary probe file.
Definition: gfile_unix.cpp:120
static bool cleanup(const Cleanup::Arg &path_arg) noexcept
Deletes the file.
Definition: gfile_unix.cpp:172
static void chgrp(const Path &file, const std::string &group)
Sets the file group ownership. Throws on error.
Definition: gfile_unix.cpp:370
A Path object represents a file system path.
Definition: gpath.h:82
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
Contains inline functions that convert to and from UTF-8 strings in order to call wide-character "W()...
Low-level classes.
Definition: garg.h:36
STL namespace.
A portable 'struct stat'.
Definition: gfile.h:68