-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathfile.hh
432 lines (423 loc) · 12.7 KB
/
file.hh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
/*
* Copyright (c) 2016 Zhao DAI <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any
* later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see accompanying file LICENSE.txt
* or <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Encapsulation for fd (file descriptor) and regular files.
* @author Zhao DAI
*/
#ifndef DOZERG_FILE_H_20120119
#define DOZERG_FILE_H_20120119
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <cstdio>
#include <string>
#include <vector>
#include "tools/system.hh" //ErrorMsg
NS_SERVER_BEGIN
/**
* @brief Abstract interface of fd (file descriptor).
*/
class IFileDesc
{
public:
static const int kInvalidFd = -1;
/**
* @brief Get latest @c errno and error message.
* @return Description of latest error
*/
static std::string ErrMsg(){return tools::ErrorMsg(errno);}
/**
* @brief Construct a default object.
*/
IFileDesc():fd_(kInvalidFd){}
/**
* @brief Destroy this object.
* Release any resources if necessary.
*/
virtual ~IFileDesc(){this->close();}
/**
* @brief Test if this object is a valid file descriptor.
* @return @c true if it's valid; @c false otherwise
*/
bool valid() const{return (fd_ >= 0);}
/**
* @brief Get fd (file descriptor).
* @return fd
*/
int fd() const{return fd_;}
/**
* @brief Get type id of this object.
* Any concrete class derived from IFileDesc shall define a distinct type id named @c kFdType,
* so it's possible to check this id and determine what the object really is.
* @n Currently following `kFdType`s are defined:
* | Type Id | Value |
* | ---: | --- |
* | @ref @c CFile::kFdType | 1 |
* | @ref @c CTcpConnSocket::kFdType | 2 |
* | @ref @c CListenSocket::kFdType | 3 |
* | @ref @c CUdpSocket::kFdType | 4 |
* | @ref @c CPosixShmFile::kFdType | 5 |
* | @ref @c CEpoll::kFdType | 6 |
* @return Type id of this object
* @sa kFdType
*/
virtual int fdType() const = 0;
/**
* @brief Get type name of this object.
* Any concrete class derived from IFileDesc must give a readable name to identify itself.
* @return Readable type name
*/
virtual const char * fdTypeName() const = 0;
/**
* @brief Get file name opened by this object.
* @return A file name, or an empty string if any error occurs
* @sa ErrMsg
*/
std::string filename() const{return tools::GetFilenameByFd(fd_);}
/**
* @brief Get byte size of file opened by this object.
* @return
* @li @c -1: Failed
* @li others: Byte size of file
* @sa ErrMsg
*/
off_t length() const{
if(!valid())
return -1;
struct stat st;
if(0 != ::fstat(fd(), &st))
return -1;
return st.st_size;
}
/**
* @brief Test if file is deleted.
* A file could be removed even if you're reading or writing it. So it's necessary to test this
* status, or you may lost all changes to the file.
* @return @c true if file is deleted; @c false otherwise
*/
bool deleted() const{
if(!valid())
return false;
struct stat st;
if(0 != ::fstat(fd(), &st))
return false;
return (0 == st.st_nlink);
}
/**
* @brief Set block/non-block for operations.
* @param on
* @li @c true: all IO operations will be @em BLOCK by default
* @li @c false: all IO operations will be @em NONBLOCK by default
* @return @c true if succeeded; @c false otherwise
* @sa ErrMsg
*/
bool block(bool on){
if(!valid())
return false;
const int oldflag = ::fcntl(fd_, F_GETFL);
if(-1 == oldflag)
return false;
const int newflag = (on ? oldflag & ~O_NONBLOCK : oldflag | O_NONBLOCK);
if(oldflag == newflag)
return true;
if(::fcntl(fd_, F_SETFL, newflag) < 0)
return false;
return true;
}
/**
* @brief Close this fd.
*/
void close(){
if(valid()){
::close(fd_);
fd_ = kInvalidFd;
}
}
/**
* @brief Get readable description of this object.
* @return Description string
*/
virtual std::string toString() const{
CToString oss;
oss<<"{fd_="<<fd_<<"("<<fdTypeName()<<")"
<<"}";
return oss.str();
}
private:
//NOTE: Subclasses could implement its own copy and assignment operations
IFileDesc(const IFileDesc & other);
IFileDesc & operator =(const IFileDesc & other);
protected:
//member
int fd_;
};
/**
* @brief Regular file operations.
*/
class CFile : public IFileDesc
{
typedef IFileDesc __MyBase;
protected:
static const int kFlagsDefault = O_RDONLY;
static const int kModeDefault = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
public:
static const int kFdType = 1; /**< Type id */
/**
* @brief Remove a file.
* @param pathname File pathname
* @return @c true if succeeded; @c false otherwise
* @sa ErrMsg
*/
static bool Unlink(const std::string & pathname){
if(pathname.empty())
return false;
return (0 == ::unlink(pathname.c_str()));
}
/**
* @brief Rename a file.
* @param oldfile Old pathname
* @param newfile New pathname
* @return @c true if succeeded; @c false otherwise
* @sa ErrMsg
*/
static bool Rename(const std::string & oldfile, const std::string & newfile){
if(oldfile.empty() || newfile.empty())
return false;
return (0 == ::rename(oldfile.c_str(), newfile.c_str()));
}
/**
* @brief Default constructor.
*/
CFile(){}
/**
* @name Open or create a file
* @details @c mode is useful only when a new file is created.
* @param pathname File pathname
* @param flags Flags for @c open(2), e.g. @c O_RDONLY, @c O_WRONLY, or @c O_RDWR
* @param mode Mode for @c open(2) when new file is created, e.g. @c S_IRUSR
* @sa ErrMsg
* @{ */
explicit CFile(const char * pathname, int flags = kFlagsDefault, mode_t mode = kModeDefault){
this->open(pathname, flags, mode);
}
explicit CFile(const std::string & pathname, int flags = kFlagsDefault, mode_t mode = kModeDefault){
this->open(pathname, flags, mode);
}
//TODO: unit test
virtual bool open(const char * pathname, int flags = kFlagsDefault, mode_t mode = kModeDefault){
if(NULL == pathname)
return false;
if(valid())
this->close();
fd_ = ::open(pathname, flags, mode);
return valid();
}
virtual bool open(const std::string & pathname, int flags = kFlagsDefault, mode_t mode = kModeDefault){
return this->open(pathname.c_str(), flags, mode);
}
/** @} */
/**
* @name Duplicate file descriptor
* @details These functions do @em NOT copy file content, it only duplicates fd using @c dup(2).
* @param other Another file object
* @{ */
CFile(const CFile & other){copyFrom(other);}
CFile & operator =(const CFile & other){
copyFrom(other);
return *this;
}
/** @} */
int fdType() const{return kFdType;}
/**
* @brief Get readable description of this object.
* @return @c "CFile"
*/
const char * fdTypeName() const{return "CFile";}
/**
* @name Read from file
* @details These functions read at most @c size bytes from file.
* @param[out] buf A buffer to receive the data
* @param[in] size Size of bytes to read at most
* @param[in] append
* @li @c true: New data will append to @c buf
* @li @c false: New data will overwrite old ones in @c buf
* @sa ErrMsg
* @{ */
/**
* @return
* @li @c -1: Failed
* @li @c 0: The end of file is reached
* @li Positive number: Size of bytes actually read
*/
ssize_t read(char * buf, size_t size){
if(!valid())
return -1;
return ::read(fd(), buf, size);
}
/**
* @return @c true if succeeded; @c false otherwise
*/
bool read(std::vector<char> & buf, size_t size, bool append){
return readData(buf, size, append);
}
/**
* @return @c true if succeeded; @c false otherwise
*/
bool read(std::string & buf, size_t size, bool append){
return readData(buf, size, append);
}
/** @} */
/**
* @name Write to file
* @param buf A buffer to write
* @param size Bytes size of @c buf
* @return
* @li @c -1: Failed
* @li Non-negative number: Size of bytes actually written
* @sa ErrMsg
* @{ */
ssize_t write(const char * buf, size_t size){
if(!valid())
return -1;
return ::write(fd(), buf, size);
}
ssize_t write(const std::vector<char> & buf){
return this->write(&buf[0], buf.size());
}
ssize_t write(const std::string & buf){
return this->write(&buf[0], buf.size());
}
/** @} */
/**
* @name Set cursor
* @details See @c lseek(2) for more information.
* @param offset Offset to move the cursor
* @param whence Directive for @c offset, showing as follows:
* | Directive | Explanation |
* | --- | --- |
* | SEEK_SET | The offset is set to @c offset bytes |
* | SEEK_CUR | The offset is set to its current location plus @c offset bytes |
* | SEEK_END | The offset is set to the size of the file plus @c offset bytes |
* @return @c true if succeeded; @c false otherwise
* @sa ErrMsg
* @{ */
bool seek(off_t offset, int whence){
if(!valid())
return false;
return ((off_t)-1 != ::lseek(fd(), offset, whence));
}
#ifdef __HAS_LSEEK64
bool seek64(off64_t offset, int whence){
if(!valid())
return false;
return ((off_t)-1 != ::lseek64(fd(), offset, whence));
}
#endif
/** @} */
/**
* @name Get cursor
* @{
* @return
* @li @c -1: Failed
* @li Non-negative number: Current cursor position
* @sa ErrMsg
*/
off_t tell() const{
if(!valid())
return -1;
return ::lseek(fd(), 0, SEEK_CUR);
}
#ifdef __HAS_LSEEK64
off64_t tell64() const{
if(!valid())
return -1;
return ::lseek64(fd(), 0, SEEK_CUR);
}
#endif
/** @} */
#ifdef __HAS_FTRUNCATE
/**
* @brief Truncate file.
* The file must be writable.
* @param length Left byte size after truncation
* @return @c true if succeeded; @c false otherwise
* @sa ErrMsg
*/
bool truncate(off_t length){
if(!valid())
return false;
return (0 == ::ftruncate(fd(), length));
}
#endif
/**
* @brief Remove file.
* @return @c true if succeeded; @c false otherwise
* @sa ErrMsg
*/
virtual bool unlink(){
if(!valid())
return false;
return Unlink(__MyBase::filename());
}
/**
* @brief Rename file.
* @param newfile New pathname for the file
* @return @c true if succeeded; @c false otherwise
* @sa ErrMsg
*/
bool rename(const std::string & newfile){
if(!valid() || newfile.empty())
return false;
std::string fname = __MyBase::filename();
if(fname.empty())
return false;
return Rename(fname, newfile);
}
std::string toString() const{
CToString oss;
oss<<"{IFileDesc="<<IFileDesc::toString()
<<", filename="<<filename()
<<"}";
return oss.str();
}
protected:
void copyFrom(const CFile & other){
if(!other.valid()){
this->close();
}else if(!valid()){
fd_ = ::dup(other.fd());
}else
fd_ = ::dup2(other.fd(), fd_);
}
private:
template<class Buf>
bool readData(Buf & buf, size_t size, bool append){
if(!valid())
return false;
const size_t from = (append ? buf.size() : 0);
buf.resize(from + size);
const ssize_t ret = this->read(&buf[from], size);
buf.resize(from + (ret > 0 ? ret : 0));
return (ret >= 0);
}
};
NS_SERVER_END
#endif