1 #include <precomp.h>
2 #include "autofd.h"
3 #include "autodir.h"
4
5 // Due to common implementation considerations, autofd and autodir's implementation are both in this file
6
7 // Translate utf-8 to wide characters
a2u(const char * str)8 static auto_array<wchar_t> a2u( const char *str )
9 {
10 int newlen=MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, NULL, 0 );
11
12 if( newlen==0 ) {
13 throw( rscerror( _T("Ansi to Unicode conversion error"), Error2errno(GetLastError())) );
14 }
15
16 auto_array<wchar_t> buffer(new wchar_t[newlen+1]);
17
18 if( MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, buffer.get(), newlen )==0 ) {
19 throw( rscerror( _T("Ansi to Unicode conversion error"), Error2errno(GetLastError())) );
20 }
21
22 buffer[newlen]=L'\0';
23
24 return buffer;
25 }
26
27 // Unconditionally convert from Wide to ANSI
u2a(const wchar_t * str)28 static auto_array<char> u2a( const wchar_t *str )
29 {
30 int newlen=WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str, -1, NULL, 0, NULL, NULL );
31
32 if( newlen==0 ) {
33 throw( rscerror( _T("Unicode to Ansi conversion error"), GetLastError()) );
34 }
35
36 auto_array<char> buffer(new char[newlen+1]);
37
38 if( WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, str, -1, buffer.get(), newlen, NULL, NULL )==0 ) {
39 throw( rscerror( _T("Ansi to Unicode conversion error"), GetLastError()) );
40 }
41
42 buffer[newlen]='\0';
43
44 return buffer;
45 }
46
47
ut2ft(time_t unixtime)48 FILETIME autofd::ut2ft( time_t unixtime )
49 {
50 ULARGE_INTEGER res;
51 static const ULONGLONG epoch_start=116444736000000000;
52
53 res.QuadPart=unixtime;
54 res.QuadPart*=10*1000*1000;
55 res.QuadPart+=epoch_start;
56
57 FILETIME ret;
58
59 ret.dwHighDateTime=res.HighPart;
60 ret.dwLowDateTime=res.LowPart;
61
62 return ret;
63 }
64
ft2ut(FILETIME ft)65 time_t autofd::ft2ut( FILETIME ft )
66 {
67 // Converts FILETIME to time_t
68 static const ULONGLONG epoch_start=116444736000000000;
69 ULARGE_INTEGER unified_ft;
70 unified_ft.LowPart=ft.dwLowDateTime;
71 unified_ft.HighPart=ft.dwHighDateTime;
72 if( unified_ft.QuadPart<epoch_start )
73 return -1;
74
75 unified_ft.QuadPart-=epoch_start;
76 unified_ft.QuadPart/=10*1000*1000;
77 if( unified_ft.HighPart!=0 )
78 return -1;
79
80 return unified_ft.LowPart;
81 }
82
autofd(const char * pathname,int flags,mode_t mode,bool utf8)83 autofd::autofd( const char *pathname, int flags, mode_t mode, bool utf8 ) : f_eof(false)
84 {
85 DWORD access=0, disposition=0;
86
87 WIN32_FILE_ATTRIBUTE_DATA file_attr;
88 if( GetFileAttributesEx( pathname, GetFileExInfoStandard, &file_attr ) ) {
89 if( file_attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
90 // Cannot open a directory
91 throw EXCEPT_CLASS("file open failed", EISDIR, pathname );
92 }
93 }
94
95 #define CHECK_MASK(a) ((flags&(a))==(a))
96 if( CHECK_MASK(O_CREAT|O_EXCL) )
97 disposition=CREATE_NEW;
98 else if( CHECK_MASK(O_CREAT|O_TRUNC) )
99 disposition=CREATE_ALWAYS;
100 else if( CHECK_MASK(O_CREAT) )
101 disposition=OPEN_ALWAYS;
102 else if( CHECK_MASK(O_TRUNC) )
103 disposition=TRUNCATE_EXISTING;
104 else
105 disposition=OPEN_EXISTING;
106
107 switch( flags&O_ACCMODE ) {
108 case O_RDONLY:
109 access=GENERIC_READ;
110 break;
111 case O_WRONLY:
112 access=GENERIC_WRITE;
113 break;
114 case O_RDWR:
115 access=GENERIC_READ|GENERIC_WRITE;
116 break;
117 }
118
119 HANDLE newfile;
120 if( !utf8 )
121 newfile=CreateFileA(pathname, access,
122 FILE_SHARE_READ|FILE_SHARE_WRITE, // We are a unix program at heart
123 NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL );
124 else {
125 // Need to translate the file name to unicode
126 newfile=CreateFileW(a2u(pathname).get(), access,
127 FILE_SHARE_READ|FILE_SHARE_WRITE, // We are a unix program at heart
128 NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL );
129 }
130 static_cast<autohandle &>(*this)=autohandle(newfile);
131
132 if( *this==INVALID_HANDLE_VALUE )
133 throw EXCEPT_CLASS("file open failed", Error2errno(GetLastError()), pathname );
134 }
135
read(file_t fd,void * buf,size_t count)136 ssize_t autofd::read( file_t fd, void *buf, size_t count )
137 {
138 DWORD ures;
139 if( !ReadFile( fd, buf, count, &ures, NULL ) && GetLastError()!=ERROR_BROKEN_PIPE )
140 throw rscerror("read failed", Error2errno(GetLastError()));
141
142 return ures;
143 }
144
read(void * buf,size_t count) const145 ssize_t autofd::read( void *buf, size_t count ) const
146 {
147 ssize_t num=read( *static_cast<const autohandle *>(this), buf, count );
148
149 if( num==0 )
150 f_eof=true;
151
152 return num;
153 }
154
write(file_t fd,const void * buf,size_t count)155 ssize_t autofd::write( file_t fd, const void *buf, size_t count )
156 {
157 DWORD written;
158 if( !WriteFile( fd, buf, count, &written, NULL ) )
159 throw rscerror("write failed", Error2errno(GetLastError()));
160
161 return written;
162 }
163
stat(const char * file_name,bool utf8)164 struct stat autofd::stat( const char *file_name, bool utf8 )
165 {
166 struct stat ret;
167 WIN32_FILE_ATTRIBUTE_DATA data;
168
169 if( !utf8 ) {
170 if( !GetFileAttributesExA( file_name, GetFileExInfoStandard, &data ) )
171 throw rscerror("stat failed", Error2errno(GetLastError()), file_name);
172 } else {
173 if( !GetFileAttributesExW( a2u(file_name).get(), GetFileExInfoStandard, &data ) )
174 throw rscerror("stat failed", Error2errno(GetLastError()), file_name);
175 }
176
177 ZeroMemory( &ret, sizeof(ret) );
178 ret.st_atime=ft2ut(data.ftLastAccessTime);
179 ret.st_ctime=ft2ut(data.ftCreationTime);
180 ret.st_mtime=ft2ut(data.ftLastWriteTime);
181 ret.st_dev=0;
182 ret.st_rdev=0;
183
184 if( data.dwFileAttributes&FILE_ATTRIBUTE_REPARSE_POINT ) {
185 // The Vista equivalent of a symbolic link, more or less
186 ret.st_mode=S_IFLNK;
187 } else if( data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY ) {
188 ret.st_mode=S_IFDIR;
189 } else {
190 ret.st_mode=S_IFREG;
191 }
192
193 ret.st_nlink=1;
194 ret.st_size=data.nFileSizeHigh;
195 ret.st_size<<=32;
196 ret.st_size|=data.nFileSizeLow;
197
198 return ret;
199 }
200
fstat() const201 struct stat autofd::fstat() const
202 {
203 struct stat ret;
204 BY_HANDLE_FILE_INFORMATION info;
205
206 if( !GetFileInformationByHandle(*this, &info ) )
207 throw rscerror("stat failed", Error2errno(GetLastError()));
208
209 ZeroMemory(&ret, sizeof(ret));
210 ret.st_atime=ft2ut(info.ftLastAccessTime);
211 ret.st_ctime=ft2ut(info.ftCreationTime);
212 ret.st_dev=0; // For a device - handle. Otherwise 0
213 ret.st_mode=S_IFREG; // unix mode
214 ret.st_mtime=ft2ut(info.ftLastWriteTime);
215 ret.st_nlink=static_cast<short>(info.nNumberOfLinks); // nlink
216 ret.st_rdev=0; // same as dev
217 ret.st_size=info.nFileSizeHigh;
218 ret.st_size<<=32;
219 ret.st_size|=info.nFileSizeLow;
220
221 return ret;
222 }
223
lseek(file_t file,off_t offset,int whence)224 off_t autofd::lseek( file_t file, off_t offset, int whence )
225 {
226 DWORD dwMoveMethod=0;
227
228 switch( whence ) {
229 case SEEK_SET:
230 dwMoveMethod=FILE_BEGIN;
231 break;
232 case SEEK_CUR:
233 dwMoveMethod=FILE_CURRENT;
234 break;
235 case SEEK_END:
236 dwMoveMethod=FILE_END;
237 break;
238 default:
239 throw rscerror("Invalid whence given", EINVAL);
240 }
241
242 LONG offsethigh, offsetlow;
243 offsetlow=static_cast<LONG>(offset);
244 offsethigh=static_cast<LONG>(offset>>32);
245 offsetlow=SetFilePointer( file, offsetlow, &offsethigh, dwMoveMethod );
246
247 offset=offsethigh;
248 offset<<=32;
249 offset|=offsetlow;
250
251 return offset;
252 }
253
utimes(const char * filename,const struct timeval tv[2],bool utf8)254 int autofd::utimes( const char *filename, const struct timeval tv[2], bool utf8)
255 {
256 FILETIME modtime, accesstime;
257
258 accesstime=ut2ft(tv[0].tv_sec);
259 modtime=ut2ft(tv[1].tv_sec);
260
261 // The only function in Windows that sets file modification/access times does so for
262 // open files only, so we have no choice but to open the file for write access
263 autofd file(filename, O_WRONLY, utf8);
264 if( SetFileTime(file, NULL, &accesstime, &modtime ) )
265 return 0;
266 else {
267 errno=Error2errno(GetLastError());
268 return -1;
269 }
270 }
271
dup(int filedes)272 autofd autofd::dup( int filedes )
273 {
274 autofd orig_std(GetStdHandle(filedes));
275 autofd ret(orig_std.Duplicate(false));
276 orig_std.release();
277 return ret;
278 }
279
rmdir(const char * pathname,bool utf8)280 void autofd::rmdir( const char *pathname, bool utf8 )
281 {
282 if( !(utf8?RemoveDirectoryW(a2u(pathname).get()):RemoveDirectoryA(pathname)) ) {
283 DWORD error=GetLastError();
284
285 if( error!=ERROR_FILE_NOT_FOUND && error!=ERROR_PATH_NOT_FOUND )
286 throw rscerror("Error removing directory", Error2errno(error), pathname);
287 }
288 }
289
mv(const char * src,const char * dst,bool utf8)290 void autofd::mv( const char *src, const char *dst, bool utf8 ) {
291 if( !(utf8?
292 MoveFileExW( a2u(src).get(), a2u(dst).get(), MOVEFILE_REPLACE_EXISTING):
293 MoveFileExA( src, dst, MOVEFILE_REPLACE_EXISTING)) )
294 {
295 throw rscerror("rename failed", Error2errno(GetLastError()), dst );
296 }
297 }
298
unlink(const char * pathname,bool utf8)299 int autofd::unlink(const char *pathname, bool utf8)
300 {
301 DWORD error=ERROR_SUCCESS;
302 if( !(utf8?DeleteFileW( a2u(pathname).get() ):DeleteFileA( pathname ))
303 && (error=GetLastError())!=ERROR_FILE_NOT_FOUND )
304 throw rscerror("Erasing file", Error2errno(GetLastError()), pathname );
305
306 if( error!=ERROR_SUCCESS ) {
307 errno=Error2errno(error);
308
309 return -1;
310 }
311
312 return 0;
313 }
314
315 // Recursively create directories
316 // mode is the permissions of the end directory
317 // int_mode is the permissions of all intermediately created dirs
mkpath_actual(const std::string & path,mode_t mode,bool utf8)318 static void mkpath_actual(const std::string &path, mode_t mode, bool utf8)
319 {
320 BOOL res;
321 if( utf8 )
322 res=CreateDirectoryW( a2u(path.c_str()).get(), NULL );
323 else
324 res=CreateDirectoryA( path.c_str(), NULL );
325
326 if( !res && GetLastError()!=ERROR_ALREADY_EXISTS ) {
327 // "Creating" a drive letter may fail for a whole host of reasons while actually succeeding
328 if( path.length()!=2 || path[1]!=':' ||
329 // Got this far in the "if" only if we tried to create something of the form C:
330 // Only a ERROR_INVALID_DRIVE actually means an error
331 GetLastError()==ERROR_INVALID_DRIVE )
332
333 throw rscerror("mkdir failed", Error2errno(GetLastError()), path.c_str() );
334 }
335 }
336
mkpath(const char * path,mode_t mode,bool utf8)337 void autofd::mkpath(const char *path, mode_t mode, bool utf8)
338 {
339 if( path[0]!='\0' ) {
340 for( int sublen=0; path[sublen]!='\0'; sublen++ ) {
341 if( sublen>0 && path[sublen]==DIRSEP_C && path[sublen+1]!=DIRSEP_C ) {
342 std::string subpath(path, sublen);
343 mkpath_actual(subpath, mode, utf8);
344 }
345 }
346
347 mkpath_actual(path, mode, utf8);
348 }
349 }
350
351 // Return the dir part of the name
dirpart(const char * path,bool utf8)352 int autofd::dirpart( const char *path, bool utf8 )
353 {
354 int i, last=0;
355
356 for( i=0; path[i]!='\0'; ++i ) {
357 if( path[i]==DIRSEP_C )
358 last=i;
359 }
360
361 return last;
362 }
363
combine_paths(const char * left,const char * right,bool utf8)364 std::string autofd::combine_paths( const char *left, const char *right, bool utf8 )
365 {
366 std::string ret(left);
367
368 int i;
369 // Trim trailing slashes
370 for( i=ret.length()-1; i>0 && ret[i]==DIRSEP_C; --i )
371 ;
372
373 ret.resize(++i);
374 if( i>0 )
375 ret+=DIRSEP_S;
376
377 // Trim leading slashes
378 for( i=0; right[i]==DIRSEP_C; ++i )
379 ;
380 ret+=right+i;
381
382 return ret;
383 }
384
readline() const385 std::string autofd::readline() const
386 {
387 std::string ret;
388 char ch;
389
390 while( read( &ch, 1 )==1 && ch!='\n' ) {
391 ret+=ch;
392 }
393
394 if( ch=='\n' && ret.length()>0 && ret[ret.length()-1]=='\r' )
395 ret.resize(ret.length()-1);
396
397 return ret;
398 }
399
400 // autodir implementation
autodir(const char * dirname,bool _utf8)401 autodir::autodir( const char *dirname, bool _utf8 ) : eof(false), utf8(_utf8)
402 {
403 WIN32_FILE_ATTRIBUTE_DATA file_attr;
404 if( GetFileAttributesEx( dirname, GetFileExInfoStandard, &file_attr ) ) {
405 if( !(file_attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
406 // Cannot open a directory
407 throw EXCEPT_CLASS("opendir failed", ENOTDIR, dirname );
408 }
409 }
410
411 if( utf8 ) {
412 WIN32_FIND_DATAW wide_find_data;
413 h_dirscan=FindFirstFileW((std::basic_string<wchar_t>(a2u(dirname).get())+L"\\*").c_str(), &wide_find_data );
414
415 if( h_dirscan!=INVALID_HANDLE_VALUE )
416 data_w2a(&wide_find_data);
417 } else
418 h_dirscan=FindFirstFile((std::string(dirname)+"\\*").c_str(), &finddata );
419
420 #if defined(EXCEPT_CLASS)
421 if( h_dirscan==INVALID_HANDLE_VALUE )
422 throw rscerror("opendir failed", Error2errno(GetLastError()), dirname);
423 #endif
424 }
425
read()426 struct dirent *autodir::read()
427 {
428 if( !eof ) {
429 strcpy_s(posixdir.d_name, finddata.cFileName);
430
431 BOOL res;
432 if( utf8 ) {
433 WIN32_FIND_DATAW wide_data;
434 res=FindNextFileW(h_dirscan, &wide_data);
435
436 if( res )
437 data_w2a(&wide_data);
438 } else {
439 res=FindNextFileA(h_dirscan, &finddata);
440 }
441
442 if( !res ) {
443 eof=true;
444 DWORD error=GetLastError();
445 if( error!=ERROR_NO_MORE_FILES ) {
446 throw rscerror("Error getting directory listing", Error2errno(error));
447 }
448 }
449 return &posixdir;
450 } else {
451 return NULL;
452 }
453 }
454
data_w2a(const WIN32_FIND_DATAW * data)455 void autodir::data_w2a( const WIN32_FIND_DATAW *data )
456 {
457 // Copy all identical file elements
458 finddata.dwFileAttributes=data->dwFileAttributes;
459 finddata.ftCreationTime=data->ftCreationTime;
460 finddata.ftLastAccessTime=data->ftLastAccessTime;
461 finddata.ftLastWriteTime=data->ftLastWriteTime;
462 finddata.nFileSizeHigh=data->nFileSizeHigh;
463 finddata.nFileSizeLow=data->nFileSizeLow;
464 finddata.dwReserved0=data->dwReserved0;
465 finddata.dwReserved1=data->dwReserved1;
466
467 // XXX What happens if the name is too long?
468 strncpy( finddata.cFileName, u2a(data->cFileName).get(), MAX_PATH );
469
470 for( int i=0; i<sizeof(finddata.cAlternateFileName); ++i ) {
471 // The short path is only ASCII characters
472 finddata.cAlternateFileName[i]=static_cast<CHAR>(data->cAlternateFileName[i]);
473 }
474 }
475