aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/vespa/vespalib/io/fileutil.h
blob: 6214bf3e60d49b3201941c0613b69cc35b62ad0e (plain) (blame)
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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
 * @file fileutil.h
 *
 * This class contains file utilities that gives a C++ interface to
 * file operations, and which throws exceptions on failures. The exceptions
 * should contain decent failure messages, such that you don't have to worry
 * about the errno error codes.
 *
 * It provides the following:
 *   - A C++ interface for file IO.
 *   - Exceptions for critical behavior, such that one don't need to handle
 *     critical errors low level in code (if using exceptions that is).
 *   - Functionality for recursive deletion of directories.
 *   - Functionality for copying instead of renaming moving between filesystems.
 *   - Functionality for creating missing parent directories on operations
 *     creating files and directories.
 *   - Functionality for lazy file opening. (Useful when using caches)
 *   - An interface that doesn't expose low level C++ file IO, so it can
 *     hopefully stay static even if we want to change low level implementation.
 *   - Ownership of file descriptor to avoid leaks on exceptions.
 *   - A low level layer that can be used to inject disk errors in tests, and
 *     count operation types and the like. Useful in testing.
 *
 * @author Hakon Humberset
 */

#pragma once

#include <unistd.h>
#include <memory>
#include <vector>
#include <vespa/vespalib/stllike/string.h>
#include <vespa/vespalib/util/memory.h>

namespace vespalib {

/**
 * @brief Simple metadata about a file or directory.
 **/
struct FileInfo {
    using UP = std::unique_ptr<FileInfo>;

    bool  _plainfile;
    bool  _directory;
    bool  _symlink;
    off_t _size;

    bool operator==(const FileInfo&) const;
};

std::ostream& operator<<(std::ostream&, const FileInfo&);

/**
 * @brief A File instance is used to access a single open file.
 *
 * By using this class you get automatic closing of files when the
 * object is destructed, and since the class knows the filename, you
 * will get sensible exceptions containing file names if something
 * goes wrong.
 */
class File {
private:
    int _fd;
    int _flags;
    vespalib::string _filename;
    bool _close;
    mutable int _fileReads; // Tracks number of file reads done on this file
    mutable int _fileWrites; // Tracks number of file writes done in this file

    /**
     * Verify that direct I/O alignment preconditions hold. Triggers assertion
     * failure on violations.
     */
    void verifyDirectIO(uint64_t buf, size_t bufsize, off_t offset) const;

public:
    using UP = std::unique_ptr<File>;

    /**
     * If failing to open file using direct IO it will retry using cached IO.
     */
    enum Flag { READONLY = 1, CREATE = 2, DIRECTIO = 4, TRUNC = 8 };

    /** Create a file instance, without opening the file. */
    File(vespalib::stringref filename);

    /** Create a file instance of an already open file. */
    File(int fileDescriptor, vespalib::stringref filename);

    /** Copying a file instance, moves any open file descriptor. */
    File(File& f);
    File& operator=(File& f);

    /** Closes the file if not instructed to do otherwise. */
    virtual ~File();

    /**
     * Make this instance point at another file.
     * Closes the old file it it was open.
     */
    void setFilename(vespalib::stringref filename);

    const vespalib::string& getFilename() const { return _filename; }

    virtual void open(int flags, bool autoCreateDirectories = false);

    bool isOpen() const { return (_fd != -1); }
    bool isOpenWithDirectIO() const { return ((_flags & DIRECTIO) != 0); }

    /**
     * Whether or not file should be closed when this instance is destructed.
     * By default it will be closed.
     */
    void closeFileWhenDestructed(bool close);

    virtual int getFileDescriptor() const { return _fd; }

    /**
     * Get information about the current file. If file is opened, file descriptor
     * will be used for stat. If file is not open, and the file does not exist
     * yet, you will get fileinfo describing an empty file.
     */
    virtual FileInfo stat() const;

    /**
     * Get the filesize of a file, specified by a file descriptor.
     *
     * @throw IoException If we failed to stat the file.
     */
    virtual off_t getFileSize() const { return stat()._size; }

    /**
     * Resize the currently open file to a given size,
     * truncating or extending file with 0 bytes according to what the former
     * size was.
     *
     * @param size new size of file
     * @throw IoException If we failed to resize the file.
     */
    virtual void resize(off_t size);

    /**
     * Writes data to file.
     *
     * If file is opened in direct I/O mode, arguments buf, bufsize and offset
     * MUST all be 512-byte aligned!
     *
     * @param buf         The buffer to get data from.
     * @param bufsize     Number of bytes to write.
     * @param offset      The position in the file where write should happen.
     * @throw IoException If we failed to write to the file.
     * @return            Always return bufsize.
     */
    virtual off_t write(const void *buf, size_t bufsize, off_t offset);

    /**
     * Read characters from a file.
     *
     * If file is opened in direct I/O mode, arguments buf, bufsize and offset
     * MUST all be 512-byte aligned!
     *
     * @param buf         The buffer into which to read.
     * @param bufsize     Maximum number of bytes to read.
     * @param offset      Position in file to read from.
     * @throw IoException If we failed to read from file.
     * @return            The number of bytes actually read. If less than
     *                    bufsize, this indicates that EOF was reached.
     */
    virtual size_t read(void *buf, size_t bufsize, off_t offset) const;

    /**
     * Read the file into a string.
     *
     * This is a small wrapper around read(), see there for more info.
     *
     * @throw   IoException If we failed to read from file.
     * @return  The content of the file.
     */
    vespalib::string readAll() const;

    /**
     * Read a file into a string.
     *
     * This is a convenience function for the member functions open() and
     * readAll(), see there for more details.
     *
     * @throw   IoException If we failed to read from file.
     * @return  The content of the file.
     */
    static vespalib::string readAll(vespalib::stringref path);

    /**
     * Sync file or directory.
     *
     * This is a convenience function for the member functions open() and
     * sync(), see there for more details.
     *
     * @throw IoException If we failed to sync the file.
     */
    static void sync(vespalib::stringref path);

    virtual void sync();
    virtual bool close();
    virtual bool unlink();

    int getFileReadCount() const { return _fileReads; }
    int getFileWriteCount() const { return _fileWrites; }
};

/**
 * Get the current working directory.
 *
 * @throw IoException On failure.
 */
extern vespalib::string getCurrentDirectory();

/**
 * Change working directory.
 *
 * @param directory   The directory to change to.
 * @throw IoException If we failed to change to the new working directory.
 */
extern void chdir(const vespalib::string & directory);

/**
 * Stat a file.
 *
 * @throw  IoException If we failed to stat the file.
 * @return A file info object if everything went well, a null pointer if the
 *         file was not found.
 */
extern FileInfo::UP stat(const vespalib::string & path);

/**
 * Stat a file. Give info on symlink rather than on file pointed to.
 *
 * @throw  IoException If we failed to stat the file.
 * @return A file info object if everything went well, a null pointer if the
 *         file was not found.
 */
extern FileInfo::UP lstat(const vespalib::string & path);

/**
 * Check if a file exists or not. See also pathExists.
 *
 * @throw IoException If we failed to stat the file.
 */
extern bool fileExists(const vespalib::string & path);

/**
 * Check if a path exists, i.e. whether it's a symbolic link, regular file,
 * directory, etc.
 *
 * This function is the same as fileExists, except if path is a symbolic link:
 * This function returns true, while fileExists returns true only if the path
 * the symbolic link points to exists.
 */
extern inline bool pathExists(const vespalib::string & path) {
    return (lstat(path).get() != 0);
}

/**
 * Get the filesize of the given file. Ignoring if it exists or not.
 * (None-existing files will be reported to have size zero)
 */
extern inline off_t getFileSize(const vespalib::string & path) {
    FileInfo::UP info(stat(path));
    return (info.get() == 0 ? 0 : info->_size);
}

/**
 * Check if a file is a plain file or not.
 *
 * @return True if it is a plain file, false if it don't exist or isn't.
 * @throw IoException If we failed to stat the file.
 */
extern inline bool isPlainFile(const vespalib::string & path) {
    FileInfo::UP info(stat(path));
    return (info.get() && info->_plainfile);
}

/**
 * Check if a file is a directory or not.
 *
 * @return True if it is a directory, false if it don't exist or isn't.
 * @throw IoException If we failed to stat the file.
 */
extern bool isDirectory(const vespalib::string & path);

/**
 * Check whether a path is a symlink.
 *
 * @return True if path exists and is a symbolic link.
 * @throw IoException If there's an unexpected stat failure.
 */
extern inline bool isSymLink(const vespalib::string & path) {
    FileInfo::UP info(lstat(path));
    return (info.get() && info->_symlink);
}

/**
 * List the contents of the given directory.
 */
using DirectoryList = std::vector<vespalib::string>;
extern DirectoryList listDirectory(const vespalib::string & path);

extern MallocAutoPtr getAlignedBuffer(size_t size);

string dirname(stringref name);
string getOpenErrorString(const int osError, stringref name);

} // vespalib