1 // Copyright (c) 1999-2018 David Muse
2 // See the COPYING file for more information
3 
4 
5 #include <rudiments/file.h>
6 #include <rudiments/userentry.h>
7 #include <rudiments/groupentry.h>
8 #include <rudiments/charstring.h>
9 #include <rudiments/bytestring.h>
10 #include <rudiments/error.h>
11 #include <rudiments/stringbuffer.h>
12 #include <rudiments/permissions.h>
13 #include <rudiments/directory.h>
14 #include <rudiments/stdio.h>
15 #ifndef RUDIMENTS_HAVE_BLKSIZE_T
16 	#include <rudiments/filesystem.h>
17 #endif
18 
19 #ifdef RUDIMENTS_HAVE_WINDOWS_H
20 	#include <windows.h>
21 #endif
22 
23 // for struct stat
24 #ifdef RUDIMENTS_HAVE_SYS_STAT_H
25 	#include <sys/stat.h>
26 #endif
27 
28 #if !defined(RUDIMENTS_HAVE_UTIMES_CONST_CHAR) && \
29 		!defined(RUDIMENTS_HAVE_UTIMES_CHAR)
30 	#include <rudiments/datetime.h>
31 #endif
32 #ifndef RUDIMENTS_HAVE_MKSTEMP
33 	#include <rudiments/datetime.h>
34 	#include <rudiments/randomnumber.h>
35 	#include <rudiments/permissions.h>
36 #endif
37 
38 #include <stdio.h>
39 #ifdef RUDIMENTS_HAVE_STDLIB_H
40 	#include <stdlib.h>
41 #endif
42 #ifdef RUDIMENTS_HAVE_UNISTD_H
43 	#include <unistd.h>
44 #endif
45 #ifdef RUDIMENTS_HAVE_SYS_TIME_H
46 	#include <sys/time.h>
47 #endif
48 #ifdef RUDIMENTS_HAVE_UTIME_H
49 	#include <utime.h>
50 #endif
51 #ifdef RUDIMENTS_HAVE_IO_H
52 	#undef _POSIX_
53 	#include <io.h>
54 #endif
55 #ifdef RUDIMENTS_HAVE_ACLAPI_H
56 	#include <aclapi.h>
57 #endif
58 #ifdef RUDIMENTS_HAVE_ACCCTRL_H
59 	#include <accctrl.h>
60 #endif
61 
62 // windows doesn't define these, but we need them
63 // internally to this file
64 #ifndef F_GETLK
65 	#define F_GETLK		0
66 #endif
67 #ifndef F_SETLK
68 	#define F_SETLK		1
69 #endif
70 #ifndef F_SETLKW
71 	#define F_SETLKW	2
72 #endif
73 #ifndef _PC_LINK_MAX
74 	#define _PC_LINK_MAX		0
75 #endif
76 #ifndef _PC_CHOWN_RESTRICTED
77 	#define _PC_CHOWN_RESTRICTED	1
78 #endif
79 #ifndef F_OK
80 	#define F_OK	0
81 #endif
82 #ifndef W_OK
83 	#define W_OK	2
84 #endif
85 #ifndef R_OK
86 	#define R_OK	4
87 #endif
88 #ifndef X_OK
89 	// no such thing on windows, so we'll
90 	// just set this to be the same as F_OK
91 	#define X_OK	0
92 #endif
93 
94 #ifdef RUDIMENTS_HAVE_UNDEFINED_FSYNC
95 extern "C" int fsync(int);
96 #endif
97 
98 #ifdef RUDIMENTS_HAVE_UNDEFINED_FTRUNCATE
99 extern "C" int ftruncate(int,int);
100 #endif
101 
102 class fileprivate {
103 	friend class file;
104 	private:
105 		struct	stat	_st;
106 		#if defined(RUDIMENTS_HAVE_GETFILETYPE)
107 			DWORD		_filetype;
108 		#endif
109 		#if defined(RUDIMENTS_HAVE_GETFILEINFORMATIONBYHANDLE)
110 			uint64_t	_inode;
111 		#endif
112 		#ifndef RUDIMENTS_HAVE_BLKSIZE_T
113 			char		*_name;
114 			blksize_t	_blocksize;
115 		#endif
116 		bool	_getcurrentpropertiesonopen;
117 };
118 
file()119 file::file() : filedescriptor() {
120 	pvt=new fileprivate;
121 	bytestring::zero(&pvt->_st,sizeof(pvt->_st));
122 	#if defined(RUDIMENTS_HAVE_GETFILETYPE)
123 		pvt->_filetype=0;
124 	#endif
125 	#if defined(RUDIMENTS_HAVE_GETFILEINFORMATIONBYHANDLE)
126 		pvt->_inode=0;
127 	#endif
128 	#ifndef RUDIMENTS_HAVE_BLKSIZE_T
129 		pvt->_name=NULL;
130 		pvt->_blocksize=0;
131 	#endif
132 	pvt->_getcurrentpropertiesonopen=true;
133 	type("file");
134 }
135 
file(const file & f)136 file::file(const file &f) : filedescriptor(f) {
137 	pvt=new fileprivate;
138 	fileClone(f);
139 	type("file");
140 }
141 
operator =(const file & f)142 file &file::operator=(const file &f) {
143 	if (this!=&f) {
144 		filedescriptor::operator=(f);
145 		fileClone(f);
146 	}
147 	return *this;
148 }
149 
fileClone(const file & f)150 void file::fileClone(const file &f) {
151 	pvt->_st=f.pvt->_st;
152 	#if defined(RUDIMENTS_HAVE_GETFILETYPE)
153 		pvt->_filetype=f.pvt->_filetype;
154 	#endif
155 	#if defined(RUDIMENTS_HAVE_GETFILEINFORMATIONBYHANDLE)
156 		pvt->_inode=f.pvt->_inode;
157 	#endif
158 	#ifndef RUDIMENTS_HAVE_BLKSIZE_T
159 		pvt->_name=charstring::duplicate(f.pvt->_name);
160 		pvt->_blocksize=f.pvt->_blocksize;
161 	#endif
162 	pvt->_getcurrentpropertiesonopen=f.pvt->_getcurrentpropertiesonopen;
163 }
164 
~file()165 file::~file() {
166 
167 	// set NOTE in ~threadmutex()
168 
169 	if (!pvt) {
170 		return;
171 	}
172 
173 	#ifndef RUDIMENTS_HAVE_BLKSIZE_T
174 		if (pvt->_name) {
175 			char	*tmpname=pvt->_name;
176 			pvt->_name=NULL;
177 			delete[] tmpname;
178 		}
179 	#endif
180 
181 	fileprivate	*tmppvt=pvt;
182 	pvt=NULL;
183 	delete tmppvt;
184 }
185 
create(const char * name,mode_t perms)186 bool file::create(const char *name, mode_t perms) {
187 	return open(name,O_CREAT|O_TRUNC|O_RDWR|O_BINARY,perms);
188 }
189 
createFile(const char * name,mode_t perms)190 bool file::createFile(const char *name, mode_t perms) {
191 	file	fl;
192 	return fl.create(name,perms);
193 }
194 
lowLevelOpen(const char * name,int32_t flags,mode_t perms,bool useperms)195 bool file::lowLevelOpen(const char *name, int32_t flags,
196 				mode_t perms, bool useperms) {
197 
198 	// don't allow create without perms
199 	if (flags&O_CREAT && (!perms || !useperms)) {
200 		error::setErrorNumber(EINVAL);
201 		return false;
202 	}
203 
204 	#ifndef RUDIMENTS_HAVE_BLKSIZE_T
205 		delete[] pvt->_name;
206 		pvt->_name=charstring::duplicate(name);
207 	#endif
208 
209 	#ifdef RUDIMENTS_HAVE_CREATEFILE
210 
211 		// On Windows, when creating a file, in order to set permissions
212 		// other than just owner read/write, the CreateFile method must
213 		// be used rather than just plain _open.
214 		// Also, on Windows, if you want to be able to set file
215 		// ownership and permissions, you have to use CreateFile just to
216 		// open the file so that the access mode can include WRITE_DAC
217 		// and WRITE_OWNER, which are not set when using _open.
218 
219 		PSECURITY_DESCRIPTOR	psd=NULL;
220 		void			*dacl=NULL;
221 		PSECURITY_ATTRIBUTES	psatt=NULL;
222 
223 		if (flags&O_CREAT) {
224 
225 			// create security descriptor
226 			psd=(PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
227 					SECURITY_DESCRIPTOR_MIN_LENGTH);
228 			if (!InitializeSecurityDescriptor(psd,
229 					SECURITY_DESCRIPTOR_REVISION)) {
230 				fd(-1);
231 				LocalFree(psd);
232 				return false;
233 			}
234 			dacl=permissions::permOctalToDacl(perms,true);
235 			if (!SetSecurityDescriptorDacl(psd,TRUE,
236 						(PACL)dacl,FALSE)
237 				#if _WIN32_WINNT>=0x0500
238 				|| !SetSecurityDescriptorControl(psd,
239 						SE_DACL_PROTECTED,
240 						SE_DACL_PROTECTED)
241 				#endif
242 				) {
243 				fd(-1);
244 				LocalFree(dacl);
245 				LocalFree(psd);
246 				return false;
247 			}
248 
249 			// create security attributes
250 			SECURITY_ATTRIBUTES	satt;
251 			satt.nLength=sizeof(SECURITY_ATTRIBUTES);
252 			satt.lpSecurityDescriptor=psd;
253 			satt.bInheritHandle=TRUE;
254 			psatt=&satt;
255 		}
256 
257 		// Determine the access and share modes.
258 		// O_RDONLY, O_WRONLY and O_RDWR are usually 0, 1 and 2.
259 		// We can use & to test for O_WRONLY and O_RDWR but we can't
260 		// effectively test for O_RDONLY at all, except by testing for
261 		// the lack of the other two.  It's easier to default to
262 		// read-only and just update the access/share modes if we
263 		// detect O_WRONLY/O_RDWR.
264 		DWORD	accessmode=GENERIC_READ|READ_CONTROL;
265 		if (flags&O_WRONLY) {
266 			accessmode=GENERIC_WRITE|DELETE|WRITE_DAC|WRITE_OWNER;
267 		}
268 		if (flags&O_RDWR) {
269 			accessmode=GENERIC_WRITE|DELETE|WRITE_DAC|WRITE_OWNER|
270 						GENERIC_READ|READ_CONTROL;
271 		}
272 
273 		// determine the creation disposition
274 		DWORD	cdisp=0;
275 		if (flags&O_CREAT) {
276 			if (flags&O_EXCL) {
277 				cdisp=CREATE_NEW;
278 			} else {
279 				// O_CREAT without O_EXCL should create the
280 				// file if it doesn't exist but otherwise just
281 				// open it normally.
282 				// CREATE_ALWAYS creates the file if it doesn't
283 				// exist, but otherwise "replaces" it - ie. it
284 				// opens and truncates it.
285 				// We need to open the file normally if it
286 				// exists (OPEN_EXISTING) and otherwise create
287 				// it (CREATE_ALWAYS).
288 				// Unfortunately we can't do all of that
289 				// atomically.
290 				cdisp=(exists(name))?
291 					OPEN_EXISTING:CREATE_ALWAYS;
292 			}
293 		} else {
294 			cdisp=OPEN_EXISTING;
295 		}
296 
297 		// determine the attrs
298 		// FILE_FLAG_BACKUP_SEMANTICS must be used when opening a
299 		// directory, for some reason
300 		DWORD	attrs=FILE_ATTRIBUTE_NORMAL;
301 		DWORD	fileattr=GetFileAttributes(name);
302 		if (fileattr&FILE_ATTRIBUTE_DIRECTORY) {
303 			attrs=FILE_FLAG_BACKUP_SEMANTICS;
304 		}
305 
306 		// create/open the file
307 		HANDLE	fh=CreateFile(name,accessmode,
308 					FILE_SHARE_DELETE|
309 					FILE_SHARE_READ|
310 					FILE_SHARE_WRITE,
311 					#if _WIN32_WINNT>=0x0500
312 					psatt,
313 					#else
314 					NULL,
315 					#endif
316 					cdisp,attrs,NULL);
317 		LocalFree(psd);
318 		LocalFree(dacl);
319 
320 		if (fh==INVALID_HANDLE_VALUE) {
321 			fd(-1);
322 
323 			// handle file/directory not found
324 			// FIXME: really there ought to be a generic
325 			// Windows-Posix error mapping function call here
326 			int32_t	err=error::getNativeErrorNumber();
327 			if (err==2 || err==3) {
328 				error::setErrorNumber(ENOENT);
329 			}
330 
331 			return false;
332 		}
333 
334 		// get the file descriptor from the handle
335 		fd(_open_osfhandle((long)fh,flags&~(O_CREAT|O_TRUNC)));
336 
337 		// truncate if necessary
338 		if (flags&O_TRUNC) {
339 			truncate();
340 		}
341 
342 		// append if specified
343 		if (flags&O_APPEND) {
344 			setPositionRelativeToEnd(0);
345 		}
346 	#else
347 
348 		int32_t	result;
349 		error::clearError();
350 		do {
351 			if (useperms) {
352 				#if defined(RUDIMENTS_HAVE__OPEN)
353 					result=_open(name,flags,perms);
354 				#elif defined(RUDIMENTS_HAVE_OPEN)
355 					result=::open(name,flags,perms);
356 				#else
357 					#error no open or anything like it
358 				#endif
359 			} else {
360 				#if defined(RUDIMENTS_HAVE__OPEN)
361 					result=_open(name,flags);
362 				#elif defined(RUDIMENTS_HAVE_OPEN)
363 					result=::open(name,flags);
364 				#else
365 					#error no open or anything like it
366 				#endif
367 			}
368 		} while (result==-1 && error::getErrorNumber()==EINTR);
369 		fd(result);
370 	#endif
371 
372 	return (fd()!=-1);
373 }
374 
open(const char * name,int32_t flags)375 bool file::open(const char *name, int32_t flags) {
376 	return (lowLevelOpen(name,flags,0,false) &&
377 		((pvt->_getcurrentpropertiesonopen)?
378 				getCurrentProperties():true));
379 }
380 
open(const char * name,int32_t flags,mode_t perms)381 bool file::open(const char *name, int32_t flags, mode_t perms) {
382 	return (lowLevelOpen(name,flags,perms,true) &&
383 		((pvt->_getcurrentpropertiesonopen)?
384 				getCurrentProperties():true));
385 }
386 
getContents()387 char *file::getContents() {
388 	if (fd()==-1) {
389 		return NULL;
390 	}
391 	getCurrentProperties();
392 	off64_t	curpos=getCurrentPosition();
393 	setPositionRelativeToBeginning(0);
394 	char	*contents=NULL;
395 	if (pvt->_st.st_size) {
396 		off64_t	size=pvt->_st.st_size;
397 		contents=new char[size+1];
398 		contents[size]='\0';
399 		if (size && read(contents,size)!=size) {
400 			delete[] contents;
401 			contents=NULL;
402 		}
403 	} else {
404 		stringbuffer	cts;
405 		// FIXME: this is fine for url's but for actual files that
406 		// we don't know the size of, we ought to use a more
407 		// intelligent size.
408 		char		buffer[16*1024];
409 		for (;;) {
410 			ssize_t	s=read(buffer,sizeof(buffer));
411 			cts.append(buffer,s);
412 			if (s<(ssize_t)sizeof(buffer)) {
413 				contents=cts.detachString();
414 				break;
415 			}
416 		}
417 	}
418 	setPositionRelativeToBeginning(curpos);
419 	return contents;
420 }
421 
getContents(const char * name)422 char *file::getContents(const char *name) {
423 	file	fl;
424 	fl.open(name,O_RDONLY|O_BINARY);
425 	char	*contents=fl.getContents();
426 	fl.close();
427 	return contents;
428 }
429 
getContents(unsigned char * buffer,size_t buffersize)430 ssize_t file::getContents(unsigned char *buffer, size_t buffersize) {
431 	return read(buffer,buffersize);
432 }
433 
getContents(const char * name,unsigned char * buffer,size_t buffersize)434 ssize_t file::getContents(const char *name, unsigned char *buffer,
435 						size_t buffersize) {
436 	file	fl;
437 	fl.open(name,O_RDONLY|O_BINARY);
438 	ssize_t	bytes=fl.getContents(buffer,buffersize);
439 	fl.close();
440 	return bytes;
441 }
442 
443 
tryLockFile(int16_t type) const444 bool file::tryLockFile(int16_t type) const {
445 	return tryLockRegion(type,0,0);
446 }
447 
lockFile(int16_t type) const448 bool file::lockFile(int16_t type) const {
449 	return lockRegion(type,0,0);
450 }
451 
checkLockFile(int16_t type,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const452 bool file::checkLockFile(int16_t type,
453 			int16_t *conftype, int16_t *confwhence,
454 			off64_t *confstart, off64_t *conflen) const {
455 	return checkLockRegion(type,0,0,conftype,confwhence,confstart,conflen);
456 }
457 
unlockFile() const458 bool file::unlockFile() const {
459 	return unlockRegion(0,0);
460 }
461 
tryLockRegion(int16_t type,off64_t start,off64_t len) const462 bool file::tryLockRegion(int16_t type, off64_t start, off64_t len) const {
463 	return lock(F_SETLK,type,SEEK_SET,start,len);
464 }
465 
lockRegion(int16_t type,off64_t start,off64_t len) const466 bool file::lockRegion(int16_t type, off64_t start, off64_t len) const {
467 	return lock(F_SETLKW,type,SEEK_SET,start,len);
468 }
469 
checkLockRegion(int16_t type,off64_t start,off64_t len,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const470 bool file::checkLockRegion(int16_t type, off64_t start, off64_t len,
471 				int16_t *conftype, int16_t *confwhence,
472 				off64_t *confstart, off64_t *conflen) const {
473 	return checkLock(type,SEEK_SET,start,len,
474 				conftype,confwhence,confstart,conflen);
475 }
476 
unlockRegion(off64_t start,off64_t len) const477 bool file::unlockRegion(off64_t start, off64_t len) const {
478 	return unlock(SEEK_SET,start,len);
479 }
480 
tryLockFromCurrent(int16_t type,off64_t len) const481 bool file::tryLockFromCurrent(int16_t type, off64_t len) const {
482 	return tryLockFromCurrent(type,0,len);
483 }
484 
tryLockFromCurrent(int16_t type,off64_t start,off64_t len) const485 bool file::tryLockFromCurrent(int16_t type, off64_t start, off64_t len) const {
486 	return lock(F_SETLK,type,SEEK_CUR,start,len);
487 }
488 
lockFromCurrent(int16_t type,off64_t len) const489 bool file::lockFromCurrent(int16_t type, off64_t len) const {
490 	return lockFromCurrent(type,0,len);
491 }
492 
lockFromCurrent(int16_t type,off64_t start,off64_t len) const493 bool file::lockFromCurrent(int16_t type, off64_t start, off64_t len) const {
494 	return lock(F_SETLKW,type,SEEK_CUR,start,len);
495 }
496 
checkLockFromCurrent(int16_t type,off64_t len,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const497 bool file::checkLockFromCurrent(int16_t type, off64_t len,
498 				int16_t *conftype, int16_t *confwhence,
499 				off64_t *confstart, off64_t *conflen) const {
500 	return checkLockFromCurrent(type,0,len,
501 				conftype,confwhence,confstart,conflen);
502 }
503 
checkLockFromCurrent(int16_t type,off64_t start,off64_t len,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const504 bool file::checkLockFromCurrent(int16_t type, off64_t start, off64_t len,
505 				int16_t *conftype, int16_t *confwhence,
506 				off64_t *confstart, off64_t *conflen) const {
507 	return checkLock(type,SEEK_CUR,start,len,
508 				conftype,confwhence,confstart,conflen);
509 }
510 
unlockFromCurrent(off64_t len) const511 bool file::unlockFromCurrent(off64_t len) const {
512 	return unlockFromCurrent(0,len);
513 }
514 
unlockFromCurrent(off64_t start,off64_t len) const515 bool file::unlockFromCurrent(off64_t start, off64_t len) const {
516 	return unlock(SEEK_CUR,start,len);
517 }
518 
tryLockFromEnd(int16_t type,off64_t len) const519 bool file::tryLockFromEnd(int16_t type, off64_t len) const {
520 	return tryLockFromEnd(type,0,len);
521 }
522 
tryLockFromEnd(int16_t type,off64_t start,off64_t len) const523 bool file::tryLockFromEnd(int16_t type, off64_t start, off64_t len) const {
524 	return lock(F_SETLK,type,SEEK_END,start,len);
525 }
526 
lockFromEnd(int16_t type,off64_t len) const527 bool file::lockFromEnd(int16_t type, off64_t len) const {
528 	return lockFromEnd(type,0,len);
529 }
530 
lockFromEnd(int16_t type,off64_t start,off64_t len) const531 bool file::lockFromEnd(int16_t type, off64_t start, off64_t len) const {
532 	return lock(F_SETLKW,type,SEEK_END,start,len);
533 }
534 
checkLockFromEnd(int16_t type,off64_t len,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const535 bool file::checkLockFromEnd(int16_t type, off64_t len,
536 				int16_t *conftype, int16_t *confwhence,
537 				off64_t *confstart, off64_t *conflen) const {
538 	return checkLockFromEnd(type,0,len,
539 				conftype,confwhence,confstart,conflen);
540 }
541 
checkLockFromEnd(int16_t type,off64_t start,off64_t len,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const542 bool file::checkLockFromEnd(int16_t type, off64_t start, off64_t len,
543 				int16_t *conftype, int16_t *confwhence,
544 				off64_t *confstart, off64_t *conflen) const {
545 	return checkLock(type,SEEK_END,start,len,
546 				conftype,confwhence,confstart,conflen);
547 }
548 
unlockFromEnd(off64_t len) const549 bool file::unlockFromEnd(off64_t len) const {
550 	return unlockFromEnd(0,len);
551 }
552 
unlockFromEnd(off64_t start,off64_t len) const553 bool file::unlockFromEnd(off64_t start, off64_t len) const {
554 	return unlock(SEEK_END,start,len);
555 }
556 
tryLockRemainder(int16_t type,off64_t start) const557 bool file::tryLockRemainder(int16_t type, off64_t start) const {
558 	return lock(F_SETLK,type,SEEK_SET,start,0);
559 }
560 
lockRemainder(int16_t type,off64_t start) const561 bool file::lockRemainder(int16_t type, off64_t start) const {
562 	return lock(F_SETLKW,type,SEEK_SET,start,0);
563 }
564 
checkLockRemainder(int16_t type,off64_t start,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const565 bool file::checkLockRemainder(int16_t type, off64_t start,
566 				int16_t *conftype, int16_t *confwhence,
567 				off64_t *confstart, off64_t *conflen) const {
568 	return checkLock(type,SEEK_SET,start,0,
569 				conftype,confwhence,confstart,conflen);
570 }
571 
unlockRemainder(off64_t start) const572 bool file::unlockRemainder(off64_t start) const {
573 	return unlock(SEEK_SET,start,0);
574 }
575 
tryLockRemainderFromCurrent(int16_t type) const576 bool file::tryLockRemainderFromCurrent(int16_t type) const {
577 	return tryLockRemainderFromCurrent(type,0);
578 }
579 
tryLockRemainderFromCurrent(int16_t type,off64_t start) const580 bool file::tryLockRemainderFromCurrent(int16_t type, off64_t start) const {
581 	return lock(F_SETLK,type,SEEK_CUR,start,0);
582 }
583 
lockRemainderFromCurrent(int16_t type) const584 bool file::lockRemainderFromCurrent(int16_t type) const {
585 	return lockRemainderFromCurrent(type,0);
586 }
587 
lockRemainderFromCurrent(int16_t type,off64_t start) const588 bool file::lockRemainderFromCurrent(int16_t type, off64_t start) const {
589 	return lock(F_SETLKW,type,SEEK_CUR,start,0);
590 }
591 
checkLockRemainderFromCurrent(int16_t type,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const592 bool file::checkLockRemainderFromCurrent(int16_t type,
593 				int16_t *conftype, int16_t *confwhence,
594 				off64_t *confstart, off64_t *conflen) const {
595 	return checkLockRemainderFromCurrent(type,0,
596 				conftype,confwhence,confstart,conflen);
597 }
598 
checkLockRemainderFromCurrent(int16_t type,off64_t start,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const599 bool file::checkLockRemainderFromCurrent(int16_t type, off64_t start,
600 				int16_t *conftype, int16_t *confwhence,
601 				off64_t *confstart, off64_t *conflen) const {
602 	return checkLock(type,SEEK_CUR,start,0,
603 				conftype,confwhence,confstart,conflen);
604 }
605 
unlockRemainderFromCurrent() const606 bool file::unlockRemainderFromCurrent() const {
607 	return unlockRemainderFromCurrent(0);
608 }
609 
unlockRemainderFromCurrent(off64_t start) const610 bool file::unlockRemainderFromCurrent(off64_t start) const {
611 	return unlock(SEEK_CUR,start,0);
612 }
613 
tryLockRemainderFromEnd(int16_t type) const614 bool file::tryLockRemainderFromEnd(int16_t type) const {
615 	return tryLockRemainderFromEnd(type,0);
616 }
617 
tryLockRemainderFromEnd(int16_t type,off64_t start) const618 bool file::tryLockRemainderFromEnd(int16_t type, off64_t start) const {
619 	return lock(F_SETLK,type,SEEK_END,start,0);
620 }
621 
lockRemainderFromEnd(int16_t type) const622 bool file::lockRemainderFromEnd(int16_t type) const {
623 	return lockRemainderFromEnd(type,0);
624 }
625 
lockRemainderFromEnd(int16_t type,off64_t start) const626 bool file::lockRemainderFromEnd(int16_t type, off64_t start) const {
627 	return lock(F_SETLKW,type,SEEK_END,start,0);
628 }
629 
checkLockRemainderFromEnd(int16_t type,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const630 bool file::checkLockRemainderFromEnd(int16_t type,
631 				int16_t *conftype, int16_t *confwhence,
632 				off64_t *confstart, off64_t *conflen) const {
633 	return checkLockRemainderFromEnd(type,0,
634 				conftype,confwhence,confstart,conflen);
635 }
636 
checkLockRemainderFromEnd(int16_t type,off64_t start,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const637 bool file::checkLockRemainderFromEnd(int16_t type, off64_t start,
638 				int16_t *conftype, int16_t *confwhence,
639 				off64_t *confstart, off64_t *conflen) const {
640 	return checkLock(type,SEEK_END,start,0,
641 				conftype,confwhence,confstart,conflen);
642 }
643 
sequentialAccess(off64_t start,size_t len) const644 bool file::sequentialAccess(off64_t start, size_t len) const {
645 	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
646 			defined(POSIX_FADV_SEQUENTIAL)
647 		return posixFadvise(start,len,POSIX_FADV_SEQUENTIAL);
648 	#else
649 		return true;
650 	#endif
651 }
652 
randomAccess(off64_t start,size_t len) const653 bool file::randomAccess(off64_t start, size_t len) const {
654 	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
655 			defined(POSIX_FADV_RANDOM)
656 		return posixFadvise(start,len,POSIX_FADV_RANDOM);
657 	#else
658 		return true;
659 	#endif
660 }
661 
onlyOnce(off64_t start,size_t len) const662 bool file::onlyOnce(off64_t start, size_t len) const {
663 	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
664 			defined(POSIX_FADV_NOREUSE)
665 		return posixFadvise(start,len,POSIX_FADV_NOREUSE);
666 	#else
667 		return true;
668 	#endif
669 }
670 
willNeed(off64_t start,size_t len) const671 bool file::willNeed(off64_t start, size_t len) const {
672 	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
673 			defined(POSIX_FADV_WILLNEED)
674 		return posixFadvise(start,len,POSIX_FADV_WILLNEED);
675 	#else
676 		return true;
677 	#endif
678 }
679 
wontNeed(off64_t start,size_t len) const680 bool file::wontNeed(off64_t start, size_t len) const {
681 	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
682 			defined(POSIX_FADV_DONTNEED)
683 		return posixFadvise(start,len,POSIX_FADV_DONTNEED);
684 	#else
685 		return true;
686 	#endif
687 }
688 
normalAccess(off64_t start,size_t len) const689 bool file::normalAccess(off64_t start, size_t len) const {
690 	#if defined(RUDIMENTS_HAVE_POSIX_FADVISE) && \
691 			defined(POSIX_FADV_NORMAL)
692 		return posixFadvise(start,len,POSIX_FADV_NORMAL);
693 	#else
694 		return true;
695 	#endif
696 }
697 
reserve(off64_t start,size_t len) const698 bool file::reserve(off64_t start, size_t len) const {
699 	#ifdef RUDIMENTS_HAVE_POSIX_FALLOCATE
700 		int32_t	result;
701 		error::clearError();
702 		do {
703 			result=posix_fallocate(fd(),start,len);
704 		} while (result==-1 && error::getErrorNumber()==EINTR);
705 		return !result;
706 	#else
707 		return false;
708 	#endif
709 }
710 
truncate(const char * filename)711 bool file::truncate(const char *filename) {
712 	return truncate(filename,0);
713 }
714 
truncate(const char * filename,off64_t length)715 bool file::truncate(const char *filename, off64_t length) {
716 	#ifdef RUDIMENTS_HAVE_TRUNCATE
717 		int32_t	result;
718 		do {
719 			result=::truncate(filename,length);
720 		} while (result==-1 && error::getErrorNumber()==-1);
721 		return !result;
722 	#else
723 		file	f;
724 		return f.open(filename,O_WRONLY) && f.truncate(length);
725 	#endif
726 }
727 
truncate() const728 bool file::truncate() const {
729 	return truncate((off64_t)0);
730 }
731 
truncate(off64_t length) const732 bool file::truncate(off64_t length) const {
733 	int32_t	result;
734 	do {
735 		#if defined(RUDIMENTS_HAVE_FTRUNCATE) || \
736 			defined(RUDIMENTS_HAVE_UNDEFINED_FTRUNCATE)
737 			result=::ftruncate(fd(),length);
738 		#elif defined(RUDIMENTS_HAVE__CHSIZE_S)
739 			result=_chsize_s(fd(),length);
740 		#elif defined(RUDIMENTS_HAVE_SETENDOFFILE)
741 			return (setPositionRelativeToBeginning(0)!=-1 &&
742 				SetEndOfFile((HANDLE)
743 					getHandleFromFileDescriptor(fd()))==
744 					TRUE);
745 		#else
746 			#error no ftruncate or anything like it
747 		#endif
748 	} while (result==-1 && error::getErrorNumber()==-1);
749 	return !result;
750 }
751 
unlockRemainderFromEnd() const752 bool file::unlockRemainderFromEnd() const {
753 	return unlockRemainderFromEnd(0);
754 }
755 
unlockRemainderFromEnd(off64_t start) const756 bool file::unlockRemainderFromEnd(off64_t start) const {
757 	return unlock(SEEK_END,start,0);
758 }
759 
setPositionRelativeToBeginning(off64_t offset) const760 off64_t file::setPositionRelativeToBeginning(off64_t offset) const {
761 	return lseek(offset,SEEK_SET);
762 }
763 
setPositionRelativeToCurrent(off64_t offset) const764 off64_t file::setPositionRelativeToCurrent(off64_t offset) const {
765 	return lseek(offset,SEEK_CUR);
766 }
767 
setPositionRelativeToEnd(off64_t offset) const768 off64_t file::setPositionRelativeToEnd(off64_t offset) const {
769 	return lseek(offset,SEEK_END);
770 }
771 
getCurrentPosition() const772 off64_t file::getCurrentPosition() const {
773 	return lseek(0,SEEK_CUR);
774 }
775 
lseek(off64_t offset,int32_t whence) const776 off64_t file::lseek(off64_t offset, int32_t whence) const {
777 	int32_t	result;
778 	error::clearError();
779 	do {
780 		#if defined(RUDIMENTS_HAVE__LSEEK)
781 			result=_lseek(fd(),offset,whence);
782 		#elif defined(RUDIMENTS_HAVE_LSEEK)
783 			result=::lseek(fd(),offset,whence);
784 		#else
785 			#error no lseek or anything like it
786 		#endif
787 	} while (result==-1 && error::getErrorNumber()==EINTR);
788 	return result;
789 }
790 
exists(const char * filename)791 bool file::exists(const char *filename) {
792 	return accessible(filename,F_OK);
793 }
794 
readable(const char * filename)795 bool file::readable(const char *filename) {
796 	return accessible(filename,R_OK);
797 }
798 
writeable(const char * filename)799 bool file::writeable(const char *filename) {
800 	return accessible(filename,W_OK);
801 }
802 
executable(const char * filename)803 bool file::executable(const char *filename) {
804 	return accessible(filename,X_OK);
805 }
806 
accessible(const char * filename,int32_t mode)807 bool file::accessible(const char *filename, int32_t mode) {
808 	int32_t	result;
809 	error::clearError();
810 	do {
811 		#if defined(RUDIMENTS_HAVE__ACCESS_S)
812 			result=(!charstring::isNullOrEmpty(filename))?
813 						_access_s(filename,mode):-1;
814 		#elif defined(RUDIMENTS_HAVE_ACCESS)
815 			result=access(filename,mode);
816 		#else
817 			#error no access or anything like it
818 		#endif
819 	} while (result==-1 && error::getErrorNumber()==EINTR);
820 	return !result;
821 }
822 
getCurrentProperties()823 bool file::getCurrentProperties() {
824 	int32_t	result;
825 	error::clearError();
826 	do {
827 		result=fstat(fd(),&pvt->_st);
828 	} while (result==-1 && error::getErrorNumber()==EINTR);
829 	if (result) {
830 		return false;
831 	}
832 	#if defined(RUDIMENTS_HAVE_GETFILETYPE)
833 		pvt->_filetype=GetFileType(
834 				(HANDLE)getHandleFromFileDescriptor(fd()));
835 	#endif
836 	#ifndef RUDIMENTS_HAVE_BLKSIZE_T
837 		filesystem	fs;
838 		pvt->_blocksize=0;
839 		if (fs.open(pvt->_name)) {
840 			pvt->_blocksize=fs.getBlockSize();
841 		}
842 	#endif
843 	#if defined(RUDIMENTS_HAVE_GETFILEINFORMATIONBYHANDLE)
844 		// On Windows, the st_ino and st_dev members aren't set.
845 		// Get the file index and volume serial numbers for these.
846 
847 		BY_HANDLE_FILE_INFORMATION	bhfi;
848 		if (GetFileInformationByHandle(
849 				(HANDLE)getHandleFromFileDescriptor(fd()),
850 				&bhfi)==TRUE) {
851 
852 			pvt->_inode=(((uint64_t)bhfi.nFileIndexHigh)<<32)|
853 							bhfi.nFileIndexLow;
854 			pvt->_st.st_dev=bhfi.dwVolumeSerialNumber;
855 		}
856 	#endif
857 	#if defined(RUDIMENTS_HAVE_GETSECURITYINFO)
858 
859 		// On Windows, the st_mode isn't set correctly.  Get the DACL
860 		// of the file and convert it to a mode_t...
861 
862 		// get the security information
863 		PACL			dacl=NULL;
864 		PSECURITY_DESCRIPTOR	ppsd=NULL;
865 		if (GetSecurityInfo((HANDLE)getHandleFromFileDescriptor(fd()),
866 					SE_FILE_OBJECT,
867 					DACL_SECURITY_INFORMATION,
868 					NULL,NULL,&dacl,NULL,&ppsd)!=
869 							ERROR_SUCCESS) {
870 			return false;
871 		}
872 
873 		// convert the dacl to perms
874 		pvt->_st.st_mode=permissions::daclToPermOctal((void *)dacl);
875 
876 		// clean up
877 		LocalFree(ppsd);
878 	#endif
879 	return true;
880 }
881 
stat(const char * filename,void * st)882 bool file::stat(const char *filename, void *st) {
883 	int32_t	result;
884 	error::clearError();
885 	do {
886 		result=::stat(filename,(struct stat *)st);
887 	} while (result==-1 && error::getErrorNumber()==EINTR);
888 	return (result!=-1);
889 }
890 
getPermissions() const891 mode_t file::getPermissions() const {
892 	return pvt->_st.st_mode;
893 }
894 
getOwnerUserId() const895 uid_t file::getOwnerUserId() const {
896 	return pvt->_st.st_uid;
897 }
898 
getOwnerGroupId() const899 gid_t file::getOwnerGroupId() const {
900 	return pvt->_st.st_gid;
901 }
902 
getSize() const903 off64_t file::getSize() const {
904 	return pvt->_st.st_size;
905 }
906 
getBlockSize() const907 blksize_t file::getBlockSize() const {
908 	#ifdef RUDIMENTS_HAVE_BLKSIZE_T
909 		return pvt->_st.st_blksize;
910 	#else
911 		return pvt->_blocksize;
912 	#endif
913 }
914 
getBlockCount() const915 blkcnt_t file::getBlockCount() const {
916 	#ifdef RUDIMENTS_HAVE_BLKCNT_T
917 		return pvt->_st.st_blocks;
918 	#else
919 		off64_t		size=getSize();
920 		blksize_t	blksize=getBlockSize();
921 		return (size && blksize)?(size/blksize+1):0;
922 	#endif
923 }
924 
925 #ifndef RUDIMENTS_HAVE_S_ISSOCK
926 	#define S_ISSOCK(m) (((m&0140000)==0140000)?1:0)
927 #endif
928 
isSocket() const929 int32_t file::isSocket() const {
930 	#if defined(_S_IFSOCK)
931 		return ((pvt->_st.st_mode&_S_IFSOCK)==_S_IFSOCK);
932 	#elif defined(S_IFSOCK)
933 		return ((pvt->_st.st_mode&S_IFSOCK)==S_IFSOCK);
934 	#else
935 		return S_ISSOCK(pvt->_st.st_mode);
936 	#endif
937 }
938 
939 #ifndef RUDIMENTS_HAVE_S_ISLNK
940 	#define S_ISLNK(m) (((m&0120000)==0120000)?1:0)
941 #endif
942 
isSymbolicLink() const943 int32_t file::isSymbolicLink() const {
944 	#if defined(_S_IFLNK)
945 		return ((pvt->_st.st_mode&_S_IFLNK)==_S_IFLNK);
946 	#elif defined(S_IFLNK)
947 		return ((pvt->_st.st_mode&S_IFLNK)==S_IFLNK);
948 	#else
949 		return S_ISLNK(pvt->_st.st_mode);
950 	#endif
951 }
952 
isRegularFile() const953 int32_t file::isRegularFile() const {
954 	#if defined(RUDIMENTS_HAVE_GETFILETYPE)
955 		return (pvt->_filetype==FILE_TYPE_DISK);
956 	#elif defined(_S_IFREG)
957 		return ((pvt->_st.st_mode&_S_IFREG)==_S_IFREG);
958 	#elif defined(S_IFREG)
959 		return ((pvt->_st.st_mode&S_IFREG)==S_IFREG);
960 	#else
961 		return S_ISREG(pvt->_st.st_mode);
962 	#endif
963 }
964 
965 #ifndef RUDIMENTS_HAVE_S_ISBLK
966 	#define S_ISBLK(m) (((m&0060000)==0060000)?1:0)
967 #endif
968 
isBlockDevice() const969 int32_t file::isBlockDevice() const {
970 	#if defined(_S_IFBLK)
971 		return ((pvt->_st.st_mode&_S_IFBLK)==_S_IFBLK);
972 	#elif defined(S_IFBLK)
973 		return ((pvt->_st.st_mode&S_IFBLK)==S_IFBLK);
974 	#else
975 		return S_ISBLK(pvt->_st.st_mode);
976 	#endif
977 }
978 
isDirectory() const979 int32_t file::isDirectory() const {
980 	#if defined(_S_IFDIR)
981 		return ((pvt->_st.st_mode&_S_IFDIR)==_S_IFDIR);
982 	#elif defined(S_IFDIR)
983 		return ((pvt->_st.st_mode&S_IFDIR)==S_IFDIR);
984 	#else
985 		return S_ISDIR(pvt->_st.st_mode);
986 	#endif
987 }
988 
isCharacterDevice() const989 int32_t file::isCharacterDevice() const {
990 	#if defined(RUDIMENTS_HAVE_GETFILETYPE)
991 		return (pvt->_filetype==FILE_TYPE_CHAR);
992 	#elif defined(_S_IFCHR)
993 		return ((pvt->_st.st_mode&_S_IFCHR)==_S_IFCHR);
994 	#elif defined(S_IFCHR)
995 		return ((pvt->_st.st_mode&S_IFCHR)==S_IFCHR);
996 	#else
997 		return S_ISCHR(pvt->_st.st_mode);
998 	#endif
999 }
1000 
isFifo() const1001 int32_t file::isFifo() const {
1002 	#if defined(RUDIMENTS_HAVE_GETFILETYPE)
1003 		return (pvt->_filetype==FILE_TYPE_PIPE);
1004 	#elif defined(_S_IFIFO)
1005 		return ((pvt->_st.st_mode&_S_IFIFO)==_S_IFIFO);
1006 	#elif defined(S_IFIFO)
1007 		return ((pvt->_st.st_mode&S_IFIFO)==S_IFIFO);
1008 	#else
1009 		return S_ISFIFO(pvt->_st.st_mode);
1010 	#endif
1011 }
1012 
getLastAccessTime() const1013 time_t file::getLastAccessTime() const {
1014 	return pvt->_st.st_atime;
1015 }
1016 
getLastModificationTime() const1017 time_t file::getLastModificationTime() const {
1018 	return pvt->_st.st_mtime;
1019 }
1020 
getLastChangeTime() const1021 time_t file::getLastChangeTime() const {
1022 	return pvt->_st.st_ctime;
1023 }
1024 
1025 
getDevice() const1026 dev_t file::getDevice() const {
1027 	return pvt->_st.st_dev;
1028 }
1029 
getDeviceType() const1030 dev_t file::getDeviceType() const {
1031 	return pvt->_st.st_rdev;
1032 }
1033 
getInode() const1034 uint64_t file::getInode() const {
1035 	#if defined(RUDIMENTS_HAVE_GETFILEINFORMATIONBYHANDLE)
1036 		return pvt->_inode;
1037 	#else
1038 		// some platforms (OpenVMS) require this cast
1039 		return (uint64_t)pvt->_st.st_ino;
1040 	#endif
1041 }
1042 
getNumberOfHardLinks() const1043 nlink_t file::getNumberOfHardLinks() const {
1044 	return pvt->_st.st_nlink;
1045 }
1046 
getCurrentPropertiesOnOpen()1047 void file::getCurrentPropertiesOnOpen() {
1048 	pvt->_getcurrentpropertiesonopen=true;
1049 }
1050 
dontGetCurrentPropertiesOnOpen()1051 void file::dontGetCurrentPropertiesOnOpen() {
1052 	pvt->_getcurrentpropertiesonopen=false;
1053 }
1054 
lock(int32_t method,int16_t type,int16_t whence,off64_t start,off64_t len) const1055 bool file::lock(int32_t method, int16_t type,
1056 			int16_t whence, off64_t start, off64_t len) const {
1057 	#if defined(RUDIMENTS_HAVE_FCNTL)
1058 		struct flock	lck;
1059 		lck.l_type=type;
1060 		lck.l_whence=whence;
1061 		lck.l_start=start;
1062 		lck.l_len=len;
1063 		return !fCntl(method,reinterpret_cast<long>(&lck));
1064 	#elif defined(RUDIMENTS_HAVE_LOCKFILEEX)
1065 		off64_t	cur=getCurrentPosition();
1066 		if (cur==-1) {
1067 			return false;
1068 		}
1069 		LARGE_INTEGER	lockstart;
1070 		lockstart.QuadPart=lseek(start,whence);
1071 		if (lockstart.QuadPart==-1) {
1072 			return false;
1073 		}
1074 		if (setPositionRelativeToBeginning(cur)==-1) {
1075 			return false;
1076 		}
1077 		OVERLAPPED	ol;
1078 		bytestring::zero((void *)&ol,sizeof(ol));
1079 		ol.Offset=lockstart.LowPart;
1080 		ol.OffsetHigh=lockstart.HighPart;
1081 		LARGE_INTEGER	locklength;
1082 		locklength.QuadPart=(len)?len:MAXDWORD;
1083 		DWORD	flags=
1084 			((type&F_WRLCK)?LOCKFILE_EXCLUSIVE_LOCK:0)|
1085 			((method==F_SETLK)?LOCKFILE_FAIL_IMMEDIATELY:0);
1086 		return LockFileEx((HANDLE)getHandleFromFileDescriptor(fd()),
1087 					flags,
1088 					0,
1089 					locklength.LowPart,
1090 					locklength.HighPart,
1091 					&ol)!=FALSE;
1092 	#else
1093 		#error no fcntl, LockFile or anything like it
1094 	#endif
1095 }
1096 
checkLock(int16_t type,int16_t whence,off64_t start,off64_t len,int16_t * conftype,int16_t * confwhence,off64_t * confstart,off64_t * conflen) const1097 bool file::checkLock(int16_t type, int16_t whence,
1098 			off64_t start, off64_t len,
1099 			int16_t *conftype, int16_t *confwhence,
1100 			off64_t *confstart, off64_t *conflen) const {
1101 	#if defined(RUDIMENTS_HAVE_FCNTL)
1102 		struct flock	lck;
1103 		lck.l_type=type;
1104 		lck.l_whence=whence;
1105 		lck.l_start=start;
1106 		lck.l_len=len;
1107 		int32_t	result=fCntl(F_GETLK,reinterpret_cast<long>(&lck));
1108 		*conftype=lck.l_type;
1109 		*confwhence=lck.l_whence;
1110 		*confstart=lck.l_start;
1111 		*conflen=lck.l_len;
1112 		return !result;
1113 	#elif defined(RUDIMENTS_HAVE_LOCKFILEEX)
1114 		// Windows doesn't appear to support this at all.
1115 		// I guess we'll return false, meaning not locked.
1116 		return false;
1117 	#else
1118 		#error no fcntl(F_GETLK), LockFile or anything like it
1119 	#endif
1120 }
1121 
unlock(int16_t whence,off64_t start,off64_t len) const1122 bool file::unlock(int16_t whence, off64_t start, off64_t len) const {
1123 	#if defined(RUDIMENTS_HAVE_FCNTL)
1124 		struct flock	lck;
1125 		lck.l_type=F_UNLCK;
1126 		lck.l_whence=whence;
1127 		lck.l_start=start;
1128 		lck.l_len=len;
1129 		return !fCntl(F_SETLK,reinterpret_cast<long>(&lck));
1130 	#elif defined(RUDIMENTS_HAVE_LOCKFILEEX)
1131 		off64_t	cur=getCurrentPosition();
1132 		if (cur==-1) {
1133 			return false;
1134 		}
1135 		LARGE_INTEGER	lockstart;
1136 		lockstart.QuadPart=lseek(start,whence);
1137 		if (lockstart.QuadPart==-1) {
1138 			return false;
1139 		}
1140 		if (setPositionRelativeToBeginning(cur)==-1) {
1141 			return false;
1142 		}
1143 		LARGE_INTEGER	locklength;
1144 		locklength.QuadPart=(len)?len:MAXDWORD;
1145 		return UnlockFile((HANDLE)getHandleFromFileDescriptor(fd()),
1146 					lockstart.LowPart,
1147 					lockstart.HighPart,
1148 					locklength.LowPart,
1149 					locklength.HighPart)!=FALSE;
1150 	#else
1151 		#error no fcntl, UnlockFile or anything like it
1152 	#endif
1153 }
1154 
setPermissions(mode_t perms) const1155 bool file::setPermissions(mode_t perms) const {
1156 	return permissions::setFilePermissions(fd(),perms);
1157 }
1158 
changeOwner(const char * newuser,const char * newgroup) const1159 bool file::changeOwner(const char *newuser, const char *newgroup) const {
1160 	return changeOwner(userentry::getUserId(newuser),
1161 				groupentry::getGroupId(newgroup));
1162 }
1163 
changeOwner(uid_t uid,gid_t gid) const1164 bool file::changeOwner(uid_t uid, gid_t gid) const {
1165 	#if defined(RUDIMENTS_HAVE_FCHOWN)
1166 
1167 		int32_t	result;
1168 		error::clearError();
1169 		do {
1170 			result=fchown(fd(),uid,gid);
1171 		} while (result==-1 && error::getErrorNumber()==EINTR);
1172 		return !result;
1173 
1174 	#elif defined(RUDIMENTS_HAVE_SETSECURITYINFO)
1175 
1176 		// get the file handle
1177 		HANDLE	fh=(HANDLE)getHandleFromFileDescriptor(fd());
1178 		if (fh==INVALID_HANDLE_VALUE) {
1179 			return false;
1180 		}
1181 
1182 		// get the user and group sid's
1183 		userentry	ue;
1184 		groupentry	ge;
1185 		if (!ue.initialize(uid) || !ge.initialize(gid)) {
1186 			return false;
1187 		}
1188 
1189 		// adjust my privileges so I can set the owner
1190 		HANDLE	th=NULL;
1191 		if (!OpenProcessToken(GetCurrentProcess(),
1192 					TOKEN_ADJUST_PRIVILEGES,&th)) {
1193 			return false;
1194 		}
1195 		TOKEN_PRIVILEGES	priv;
1196 		priv.PrivilegeCount=1;
1197 		priv.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
1198 		const char *privs[5]={
1199 			SE_BACKUP_NAME,
1200 			SE_RESTORE_NAME,
1201 			SE_SECURITY_NAME,
1202 			SE_TAKE_OWNERSHIP_NAME,
1203 			NULL
1204 		};
1205 		for (const char * const *p=privs; *p; p++) {
1206 			LUID	luid;
1207 			if (!LookupPrivilegeValue(NULL,*p,&luid)) {
1208 				CloseHandle(th);
1209 				return false;
1210 			}
1211 			priv.Privileges[0].Luid=luid;
1212 			if (!AdjustTokenPrivileges(th,FALSE,&priv,
1213 					sizeof(TOKEN_PRIVILEGES),NULL,NULL)) {
1214 				CloseHandle(th);
1215 				return false;
1216 			}
1217 		}
1218 
1219 		// set the owner/group
1220 		if (SetSecurityInfo(fh,
1221 				SE_FILE_OBJECT,
1222 				OWNER_SECURITY_INFORMATION|
1223 				GROUP_SECURITY_INFORMATION,
1224 				(PSID)ue.getSid(),
1225 				(PSID)ge.getSid(),
1226 				NULL,NULL)!=ERROR_SUCCESS) {
1227 			CloseHandle(th);
1228 			return false;
1229 		}
1230 
1231 		// Below is the original way I implemented this.
1232 		// BuildSecurityDescriptor crashes on Windows 8 though.
1233 		// There are other issues too.  Files are kernel objects, so I
1234 		// should probably use Get/SetKernelObjectSecurity instead of
1235 		// Get/SetUserObjectSecurity.  Though the docs also say to use
1236 		// SetSecurityInfo instead of SetKernelObjectSecurity for
1237 		// filesystem objects, which is what I changed it to.
1238 #if 0
1239 		// FIXME: do these need to be freed?
1240 		PSID	usid=(PSID)bytestring::duplicate(ue.getSid(),
1241 							ue.getSidSize());
1242 		PSID	gsid=(PSID)bytestring::duplicate(ge.getSid(),
1243 							ge.getSidSize());
1244 
1245 		// build trustees
1246 		TRUSTEE ut;
1247 		bytestring::zero(&ut,sizeof(ut));
1248 		BuildTrusteeWithSid(&ut,usid);
1249 		TRUSTEE gt;
1250 		bytestring::zero(&gt,sizeof(gt));
1251 		BuildTrusteeWithSid(&gt,gsid);
1252 
1253 		// get the existing owner/group security info for the file
1254 		// into a security descriptor
1255 		DWORD	sdsize=0;
1256 		SECURITY_INFORMATION	sinfo=
1257 				OWNER_SECURITY_INFORMATION|
1258 				GROUP_SECURITY_INFORMATION;
1259 		GetUserObjectSecurity(fh,&sinfo,NULL,0,&sdsize);
1260 		PSECURITY_DESCRIPTOR	sdesc=
1261 			(PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,sdsize);
1262 		if (!sdesc) {
1263 			CloseHandle(th);
1264 			return false;
1265 		}
1266 		bytestring::zero(sdesc,sdsize);
1267 		if (!GetUserObjectSecurity(fh,&sinfo,sdesc,sdsize,&sdsize)) {
1268 			LocalFree(sdesc);
1269 			CloseHandle(th);
1270 			return false;
1271 		}
1272 
1273 		// build a new security descriptor, starting with the existing
1274 		// security descriptor, but updating the user/group info
1275 		PSECURITY_DESCRIPTOR	newsdesc=NULL;
1276 		DWORD	result=BuildSecurityDescriptor(
1277 						&ut,&gt,
1278 						0,NULL,0,NULL,
1279 						sdesc,&sdsize,&newsdesc);
1280 		if (result!=ERROR_SUCCESS) {
1281 			LocalFree(sdesc);
1282 			LocalFree(newsdesc);
1283 			CloseHandle(th);
1284 			return false;
1285 		}
1286 
1287 		// apply the new security descriptor to the file
1288 		if (!SetUserObjectSecurity(fh,&sinfo,newsdesc)) {
1289 			LocalFree(sdesc);
1290 			LocalFree(newsdesc);
1291 			CloseHandle(th);
1292 			return false;
1293 		}
1294 
1295 		// clean up
1296 		LocalFree(sdesc);
1297 		LocalFree(newsdesc);
1298 #endif
1299 		CloseHandle(th);
1300 		return true;
1301 	#else
1302 		RUDIMENTS_SET_ENOSYS
1303 		return false;
1304 	#endif
1305 }
1306 
setPermissions(const char * filename,mode_t perms)1307 bool file::setPermissions(const char *filename, mode_t perms) {
1308 	return permissions::setFilePermissions(filename,perms);
1309 }
1310 
changeOwner(const char * filename,const char * newuser,const char * newgroup)1311 bool file::changeOwner(const char *filename, const char *newuser,
1312 						const char *newgroup) {
1313 	file	fl;
1314 	return (fl.open(filename,O_RDWR) && fl.changeOwner(newuser,newgroup));
1315 }
1316 
changeOwner(const char * filename,uid_t uid,gid_t gid)1317 bool file::changeOwner(const char *filename, uid_t uid, gid_t gid) {
1318 	file	fl;
1319 	return (fl.open(filename,O_RDWR) && fl.changeOwner(uid,gid));
1320 }
1321 
canChangeOwner(const char * filename)1322 bool file::canChangeOwner(const char *filename) {
1323 	return !pathConf(filename,_PC_CHOWN_RESTRICTED);
1324 }
1325 
canChangeOwner() const1326 bool file::canChangeOwner() const {
1327 	return !fpathConf(_PC_CHOWN_RESTRICTED);
1328 }
1329 
rename(const char * oldpath,const char * newpath)1330 bool file::rename(const char *oldpath, const char *newpath) {
1331 	int32_t	result;
1332 	error::clearError();
1333 	do {
1334 		result=::rename(oldpath,newpath);
1335 	} while (result==-1 && error::getErrorNumber()==EINTR);
1336 	return !result;
1337 }
1338 
remove(const char * filename)1339 bool file::remove(const char *filename) {
1340 	int32_t	result;
1341 	error::clearError();
1342 	do {
1343 		#if defined(RUDIMENTS_HAVE__UNLINK)
1344 			result=_unlink(filename);
1345 		#elif defined(RUDIMENTS_HAVE_UNLINK)
1346 			result=unlink(filename);
1347 		#else
1348 			#error no unlink or anything like it
1349 		#endif
1350 	} while (result==-1 && error::getErrorNumber()==EINTR);
1351 	return !result;
1352 }
1353 
createHardLink(const char * oldpath,const char * newpath)1354 bool file::createHardLink(const char *oldpath, const char *newpath) {
1355 	#if defined(RUDIMENTS_HAVE_LINK)
1356 		int32_t	result;
1357 		error::clearError();
1358 		do {
1359 			result=link(oldpath,newpath);
1360 		} while (result==-1 && error::getErrorNumber()==EINTR);
1361 		return !result;
1362 	#elif defined(RUDIMENTS_HAVE_LOADLIBRARY)
1363 		bool	retval=false;
1364 		HMODULE	lib=LoadLibrary("KERNEL32");
1365 		if (lib) {
1366 			bool	(*proc)(LPCSTR,LPCSTR,
1367 					LPSECURITY_ATTRIBUTES)=
1368 				(bool (*)(LPCSTR,LPCSTR,
1369 					LPSECURITY_ATTRIBUTES))
1370 					GetProcAddress(lib,"CreateHardLinkA");
1371 			if (proc) {
1372 				retval=proc(newpath,oldpath,NULL);
1373 			}
1374 			FreeLibrary(lib);
1375 		}
1376 		return retval;
1377 	#else
1378 		RUDIMENTS_SET_ENOSYS
1379 		return false;
1380 	#endif
1381 }
1382 
createSymbolicLink(const char * oldpath,const char * newpath)1383 bool file::createSymbolicLink(const char *oldpath, const char *newpath) {
1384 	#if defined(RUDIMENTS_HAVE_SYMLINK)
1385 		int32_t	result;
1386 		error::clearError();
1387 		do {
1388 			result=symlink(oldpath,newpath);
1389 		} while (result==-1 && error::getErrorNumber()==EINTR);
1390 		return !result;
1391 	#else
1392 		RUDIMENTS_SET_ENOSYS
1393 		return false;
1394 	#endif
1395 }
1396 
resolveSymbolicLink(const char * filename)1397 char *file::resolveSymbolicLink(const char *filename) {
1398 
1399 	#if defined(RUDIMENTS_HAVE_READLINK)
1400 		size_t	inc=directory::maxPathLength(filename);
1401 		size_t	max=inc*10;
1402 		for (size_t size=inc; size<max; size=size+inc) {
1403 
1404 			// create a buffer to store the path
1405 			char	*buffer=new char[size];
1406 
1407 			// read the path into the buffer
1408 			int32_t	len;
1409 			error::clearError();
1410 			do {
1411 				len=::readlink(filename,buffer,size);
1412 			} while (len==-1 && error::getErrorNumber()==EINTR);
1413 
1414 			if (len==-1) {
1415 
1416 				// if the call to readlink failed, delete the
1417 				// buffer and return NULL
1418 				delete[] buffer;
1419 				return NULL;
1420 
1421 			} else if ((size_t)len==size) {
1422 
1423 				// if the length of the path was the same as
1424 				// the buffer size the we didn't get the entire
1425 				// path, loop back, increase the size of the
1426 				// buffer and try again
1427 				delete[] buffer;
1428 
1429 			} else {
1430 				// NULL-terminate the buffer,
1431 				// readlink() doesn't do this for us
1432 				buffer[len]='\0';
1433 				return buffer;
1434 			}
1435 		}
1436 		return NULL;
1437 	#else
1438 		RUDIMENTS_SET_ENOSYS
1439 		return NULL;
1440 	#endif
1441 }
1442 
sync() const1443 bool file::sync() const {
1444 	#if defined(RUDIMENTS_HAVE_FSYNC) || \
1445 		defined(RUDIMENTS_HAVE_UNDEFINED_FSYNC)
1446 		int32_t	result;
1447 		error::clearError();
1448 		do {
1449 			result=fsync(fd());
1450 		} while (result==-1 && error::getErrorNumber()==EINTR);
1451 		return !result;
1452 	#elif defined(RUDIMENTS_HAVE_FLUSHFILEBUFFERS)
1453 		return (FlushFileBuffers(
1454 			(HANDLE)getHandleFromFileDescriptor(fd()))!=0);
1455 	#elif defined(RUDIMENTS_HAVE_COMMIT)
1456 		return _commit(fd())==0;
1457 	#else
1458 		#error no fsync or anything like it
1459 	#endif
1460 }
1461 
dataSync() const1462 bool file::dataSync() const {
1463 	#ifdef RUDIMENTS_HAVE_FDATASYNC
1464 		int32_t	result;
1465 		error::clearError();
1466 		do {
1467 			result=fdatasync(fd());
1468 		} while (result==-1 && error::getErrorNumber()==EINTR);
1469 		return !result;
1470 	#else
1471 		return sync();
1472 	#endif
1473 }
1474 
setLastAccessTime(const char * filename,time_t lastaccesstime)1475 bool file::setLastAccessTime(const char *filename,
1476 					time_t lastaccesstime) {
1477 	struct stat	st;
1478 	if (!stat(filename,&st)) {
1479 		return false;
1480 	}
1481 	return setLastAccessAndModificationTimes(filename,
1482 						lastaccesstime,
1483 						st.st_mtime);
1484 }
1485 
setLastModificationTime(const char * filename,time_t lastmodtime)1486 bool file::setLastModificationTime(const char *filename,
1487 					time_t lastmodtime) {
1488 	struct stat	st;
1489 	if (!stat(filename,&st)) {
1490 		return false;
1491 	}
1492 	return setLastAccessAndModificationTimes(filename,
1493 						st.st_atime,
1494 						lastmodtime);
1495 }
1496 
setLastAccessAndModificationTimes(const char * filename,time_t lastaccesstime,time_t lastmodtime)1497 bool file::setLastAccessAndModificationTimes(const char *filename,
1498 						time_t lastaccesstime,
1499 						time_t lastmodtime) {
1500 	#if defined(RUDIMENTS_HAVE_UTIMES_CONST_CHAR) || \
1501 			defined(RUDIMENTS_HAVE_UTIMES_CHAR)
1502 		timeval	tv[2];
1503 		tv[0].tv_sec=static_cast<long>(lastaccesstime);
1504 		tv[0].tv_usec=0;
1505 		tv[1].tv_sec=static_cast<long>(lastmodtime);
1506 		tv[1].tv_usec=0;
1507 		int32_t	result;
1508 		error::clearError();
1509 		do {
1510 			#if defined(RUDIMENTS_HAVE_UTIMES_CONST_CHAR)
1511 				result=utimes(filename,tv);
1512 			#elif defined(RUDIMENTS_HAVE_UTIMES_CHAR)
1513 				result=utimes((char *)filename,tv);
1514 			#endif
1515 		} while (result==-1 && error::getErrorNumber()==EINTR);
1516 		return !result;
1517 	#elif defined(RUDIMENTS_HAVE_UTIME)
1518 		utimbuf	tb;
1519 		tb.actime=static_cast<time_t>(lastaccesstime);
1520 		tb.modtime=static_cast<time_t>(lastmodtime);
1521 		int32_t	result;
1522 		error::clearError();
1523 		do {
1524 			result=utime(filename,&tb);
1525 		} while (result==-1 && error::getErrorNumber()==EINTR);
1526 		return !result;
1527 	#elif defined(RUDIMENTS_HAVE_SETFILETIME)
1528 
1529 		// open the file
1530 		HANDLE	handle=CreateFile(filename,
1531 						FILE_WRITE_ATTRIBUTES,
1532 						FILE_SHARE_DELETE|
1533 						FILE_SHARE_READ|
1534 						FILE_SHARE_WRITE,
1535 						NULL,
1536 						OPEN_EXISTING,
1537 						FILE_ATTRIBUTE_NORMAL|
1538 						FILE_FLAG_BACKUP_SEMANTICS,
1539 						NULL);
1540 		if (handle==INVALID_HANDLE_VALUE) {
1541 			return false;
1542 		}
1543 
1544 		// windows time starts at 1/1/1601
1545 		// unix time starts at 1/1/1970
1546 		// this is the number of seconds between those dates
1547 		#define	timediff	11644473600
1548 
1549 		// convert the times from unix to windows format
1550 		ULARGE_INTEGER	lastaccessfiletime;
1551 		lastaccessfiletime.QuadPart=(lastaccesstime+timediff)*10000000;
1552 		ULARGE_INTEGER	lastmodfiletime;
1553 		lastmodfiletime.QuadPart=(lastmodtime+timediff)*10000000;
1554 
1555 		// set the file times
1556 		bool	retval=SetFileTime(handle,NULL,
1557 						(FILETIME *)&lastaccesstime,
1558 						(FILETIME *)&lastmodtime)!=0;
1559 
1560 		// close the file
1561 		CloseHandle(handle);
1562 
1563 		// return the result
1564 		return retval;
1565 	#else
1566 		RUDIMENTS_SET_ENOSYS
1567 		return false;
1568 	#endif
1569 }
1570 
setLastAccessAndModificationTimes(const char * filename)1571 bool file::setLastAccessAndModificationTimes(const char *filename) {
1572 	#if defined(RUDIMENTS_HAVE_UTIMES_CONST_CHAR) || \
1573 			defined(RUDIMENTS_HAVE_UTIMES_CHAR)
1574 		int32_t	result;
1575 		error::clearError();
1576 		do {
1577 			#if defined(RUDIMENTS_HAVE_UTIMES_CONST_CHAR)
1578 				result=utimes(filename,NULL);
1579 			#elif defined(RUDIMENTS_HAVE_UTIMES_CHAR)
1580 				result=utimes((char *)filename,NULL);
1581 			#endif
1582 		} while (result==-1 && error::getErrorNumber()==EINTR);
1583 		return !result;
1584 	#else
1585 		datetime	dt;
1586 		dt.getSystemDateAndTime();
1587 		return setLastAccessAndModificationTimes(filename,
1588 							dt.getEpoch(),
1589 							dt.getEpoch());
1590 	#endif
1591 }
1592 
createFifo(const char * filename,mode_t perms)1593 bool file::createFifo(const char *filename, mode_t perms) {
1594 	#if defined(RUDIMENTS_HAVE_MKFIFO)
1595 		int32_t	result;
1596 		error::clearError();
1597 		do {
1598 			result=mkfifo(filename,perms);
1599 		} while (result==-1 && error::getErrorNumber()==EINTR);
1600 		return !result;
1601 	#elif defined(RUDIMENTS_HAVE_CREATENAMEDPIPE)
1602 		HANDLE	handle=CreateNamedPipe(filename,0,0,1,
1603 						1024,1024,5000,NULL);
1604 		if (handle==INVALID_HANDLE_VALUE) {
1605 			return false;
1606 		}
1607 		CloseHandle(handle);
1608 		return true;
1609 	#else
1610 		RUDIMENTS_SET_ENOSYS
1611 		return false;
1612 	#endif
1613 }
1614 
createTemporaryFile(char * templatefilename)1615 int32_t file::createTemporaryFile(char *templatefilename) {
1616 	#if defined(RUDIMENTS_HAVE_MKSTEMP)
1617 		int32_t	result;
1618 		error::clearError();
1619 		do {
1620 			result=mkstemp(templatefilename);
1621 		} while (result==-1 && error::getErrorNumber()==EINTR);
1622 		return result;
1623 	#else
1624 		// sanity check on templatefilename
1625 		char	*lastsix=templatefilename+
1626 				charstring::length(templatefilename)-6;
1627 		if (charstring::compare(lastsix,"XXXXXX")) {
1628 			error::setErrorNumber(EINVAL);
1629 			return -1;
1630 		}
1631 
1632 		// replace X's with random characters...
1633 
1634 		// seed the random number
1635 		uint32_t	seed=randomnumber::getSeed();
1636 
1637 		// for each of the 6 characters...
1638 		for (uint8_t i=0; i<6; i++) {
1639 
1640 			// get a random number, scale it to 0-60
1641 			seed=randomnumber::generateNumber(seed);
1642 			char	ch=(char)randomnumber::scaleNumber(seed,0,59);
1643 
1644 			// translate...
1645 			//  0-9  -> '0' - '9'
1646 			// 10-34 -> 'A' - 'Z'
1647 			// 35-59 -> 'a' - 'z'
1648 			if (ch<10) {
1649 				ch=ch+'0';
1650 			} else if (ch<35) {
1651 				ch=ch-10+'A';
1652 			} else {
1653 				ch=ch-35+'a';
1654 			}
1655 
1656 			// set character
1657 			lastsix[i]=ch;
1658 		}
1659 
1660 		// create the file
1661 		file	f;
1662 		if (!f.create(templatefilename,
1663 				permissions::evalPermString("rw-r--r--"))) {
1664 			return -1;
1665 		}
1666 
1667 		// fake it out so the file won't get
1668 		// closed when f is deallocated
1669 		int32_t	fdesc=f.getFileDescriptor();
1670 		f.setFileDescriptor(-1);
1671 
1672 		// return the file descriptor
1673 		return fdesc;
1674 	#endif
1675 }
1676 
createTemporaryFile(char * templatefilename,mode_t perms)1677 int32_t file::createTemporaryFile(char *templatefilename, mode_t perms) {
1678 	int32_t	result=createTemporaryFile(templatefilename);
1679 	if (result!=1) {
1680 		if (!permissions::setFilePermissions(result,perms)) {
1681 			remove(templatefilename);
1682 			return -1;
1683 		}
1684 	}
1685 	return result;
1686 }
1687 
getInternalFileStatisticsStructure()1688 void *file::getInternalFileStatisticsStructure() {
1689 	return (void *)&(pvt->_st);
1690 }
1691 
dirname(const char * filename)1692 char *file::dirname(const char *filename) {
1693 
1694 	if (!filename) {
1695 		return NULL;
1696 	} else if (!charstring::contains(filename,'/') ||
1697 			!charstring::compare(filename,".")) {
1698 		return charstring::duplicate(".");
1699 	} else if (!charstring::compare(filename,"..")) {
1700 		return charstring::duplicate("..");
1701 	} else if (!charstring::compare(filename,"/")) {
1702 		return charstring::duplicate("/");
1703 	}
1704 
1705 	char	*retval=charstring::duplicate(filename);
1706 	charstring::rightTrim(retval,'/');
1707 	char	*lastslash=charstring::findLast(retval,'/');
1708 	if (lastslash==retval) {
1709 		(*(lastslash+1))='\0';
1710 	} else {
1711 		(*lastslash)='\0';
1712 	}
1713 	return retval;
1714 }
1715 
basename(const char * filename)1716 char *file::basename(const char *filename) {
1717 
1718 	if (!filename) {
1719 		return NULL;
1720 	}
1721 
1722 	char	*copy=charstring::duplicate(filename);
1723 	charstring::rightTrim(copy,'/');
1724 
1725 	char	*retval;
1726 	char	*ptr=charstring::findLast(copy,'/');
1727 	if (!ptr) {
1728 		retval=charstring::duplicate(copy);
1729 	} else {
1730 		retval=charstring::duplicate(ptr+1);
1731 	}
1732 	delete[] copy;
1733 	return retval;
1734 }
1735 
basename(const char * filename,const char * suffix)1736 char *file::basename(const char *filename, const char *suffix) {
1737 	char	*retval=basename(filename);
1738 	char	*ptr=charstring::findLast(retval,suffix);
1739 	if (!(*(ptr+charstring::length(suffix)))) {
1740 		(*ptr)='\0';
1741 	}
1742 	return retval;
1743 }
1744 
eightDotThree(const char * filename)1745 char *file::eightDotThree(const char *filename) {
1746 
1747 	// FIXME: I'm sure there's an infinitely more efficient way of
1748 	// doing this using a static buffer...
1749 
1750 	// get the base name
1751 	char	*base=basename(filename);
1752 
1753 	// get the directory name unless no directory was specified
1754 	char	*dir=NULL;
1755 	if (charstring::compare(base,filename)) {
1756 		dir=dirname(filename);
1757 	}
1758 
1759 	// get the suffix and terminate the base at the final dot
1760 	char	*suffix=NULL;
1761 	char	*finaldot=charstring::findLast(base,'.');
1762 	if (finaldot) {
1763 		suffix=charstring::duplicate(finaldot+1);
1764 		*finaldot='\0';
1765 	}
1766 
1767 	// Strip illegal characters from the base.  If we had to strip any
1768 	// characters from the base then we need to force the loop below to
1769 	// put a ~# at the end.
1770 	bool	tryunique=charstring::stripSet(base," .\"/\\[]:;=,");
1771 
1772 	// strip illegal characters from the suffix
1773 	charstring::stripSet(suffix," .\"/\\[]:;=,");
1774 
1775 	// truncate the base at 8 characters if necessary
1776 	if (charstring::length(base)>8) {
1777 		*(base+8)='\0';
1778 	}
1779 
1780 	// truncate the suffix at 3 characters if necessary
1781 	if (charstring::length(suffix)>3) {
1782 		*(suffix+3)='\0';
1783 	}
1784 
1785 	// convert the base and suffix to upper case
1786 	charstring::upper(base);
1787 	charstring::upper(suffix);
1788 
1789 	// verify that the filename is unique
1790 	uint8_t		counter=1;
1791 	stringbuffer	fullname;
1792 	char		newbase[9];
1793 	charstring::copy(newbase,base);
1794 	for (;;) {
1795 
1796 		// try to come up with a unique name
1797 		if (tryunique) {
1798 			size_t	baselen=charstring::length(base);
1799 			if (baselen>6) {
1800 				baselen=6;
1801 			}
1802 			charstring::copy(newbase,base,baselen);
1803 			newbase[baselen]='~';
1804 			newbase[baselen+1]='0'+counter;
1805 			fullname.clear();
1806 			counter++;
1807 		}
1808 
1809 		// build the test file name
1810 		if (dir) {
1811 			fullname.append(dir)->append('/');
1812 		}
1813 		fullname.append(newbase);
1814 		if (suffix) {
1815 			fullname.append('.')->append(suffix);
1816 		}
1817 
1818 		// if the name we've come up with is the same as the original
1819 		// filename or if a file with the name we've come up with
1820 		// doesn't exist, then go ahead and use the name
1821 		if (!charstring::compare(fullname.getString(),filename) ||
1822 						!exists(fullname.getString())) {
1823 			break;
1824 		}
1825 
1826 		// if the counter hits 10 digits then it must not be possible
1827 		// to uniquely name this file
1828 		if (counter==10) {
1829 			delete[] base;
1830 			delete[] dir;
1831 			delete[] suffix;
1832 			return NULL;
1833 		}
1834 
1835 		// try for a unique name the next round
1836 		tryunique=true;
1837 	}
1838 
1839 	// get the final name
1840 	char	*retval=fullname.detachString();
1841 
1842 	// clean up
1843 	delete[] dir;
1844 	delete[] base;
1845 	delete[] suffix;
1846 
1847 	// return the name
1848 	return retval;
1849 }
1850 
generateKey(const char * filename,int32_t id)1851 key_t file::generateKey(const char *filename, int32_t id) {
1852 #if defined(RUDIMENTS_HAVE_FTOK)
1853 	#ifdef RUDIMENTS_HAVE_CONST_CHAR_FTOK
1854 		return ::ftok(filename,id);
1855 	#else
1856 		return ::ftok(const_cast<char *>(filename),id);
1857 	#endif
1858 #elif !defined( __VMS)
1859 	file	f;
1860 	if (!f.open(filename,O_RDONLY)) {
1861 		return -1;
1862 	}
1863 	return (key_t)((f.getInode() & 0xffff) |
1864 			((f.getDevice() & 0xff) << 16) |
1865 			((id & 0xff)  << 24));
1866 #else
1867 	RUDIMENTS_SET_ENOSYS
1868 	return -1;
1869 #endif
1870 }
1871 
maxLinks(const char * filename)1872 int64_t file::maxLinks(const char *filename) {
1873 	return pathConf(filename,_PC_LINK_MAX);
1874 }
1875 
maxLinks() const1876 int64_t file::maxLinks() const {
1877 	return fpathConf(_PC_LINK_MAX);
1878 }
1879 
posixFadvise(off64_t offset,off64_t len,int32_t advice) const1880 bool file::posixFadvise(off64_t offset, off64_t len, int32_t advice) const {
1881 	#ifdef RUDIMENTS_HAVE_POSIX_FADVISE
1882 		int32_t	result;
1883 		error::clearError();
1884 		do {
1885 			result=posix_fadvise(fd(),offset,len,advice);
1886 		} while (result==-1 && error::getErrorNumber()==EINTR);
1887 		return !result;
1888 	#else
1889 		// on platforms that don't support this, it's ok to just return
1890 		// success since really it only gives the filesystem hints
1891 		return true;
1892 	#endif
1893 }
1894 
pathConf(const char * path,int32_t name)1895 int64_t file::pathConf(const char *path, int32_t name) {
1896 	#if defined(RUDIMENTS_HAVE_PATHCONF)
1897 		int64_t	result;
1898 		error::clearError();
1899 		do {
1900 			result=pathconf(path,name);
1901 		} while (result==-1 && error::getErrorNumber()==EINTR);
1902 		return result;
1903 	#else
1904 		RUDIMENTS_SET_ENOSYS
1905 		return -1;
1906 	#endif
1907 }
1908 
fpathConf(int32_t name) const1909 int64_t file::fpathConf(int32_t name) const {
1910 	#if defined(RUDIMENTS_HAVE_FPATHCONF)
1911 		int64_t	result;
1912 		error::clearError();
1913 		do {
1914 			result=fpathconf(fd(),name);
1915 		} while (result==-1 && error::getErrorNumber()==EINTR);
1916 		return result;
1917 	#else
1918 		RUDIMENTS_SET_ENOSYS
1919 		return -1;
1920 	#endif
1921 }
1922