/src/brpc/src/butil/file_util.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. |
4 | | |
5 | | #include "butil/file_util.h" |
6 | | |
7 | | #if defined(OS_WIN) |
8 | | #include <io.h> |
9 | | #endif |
10 | | #include <stdio.h> |
11 | | |
12 | | #include <fstream> |
13 | | #include <limits> |
14 | | |
15 | | #include "butil/files/file_enumerator.h" |
16 | | #include "butil/files/file_path.h" |
17 | | #include "butil/logging.h" |
18 | | #include "butil/strings/string_piece.h" |
19 | | #include "butil/strings/string_util.h" |
20 | | #include "butil/strings/stringprintf.h" |
21 | | #include "butil/strings/utf_string_conversions.h" |
22 | | |
23 | | namespace butil { |
24 | | |
25 | | namespace { |
26 | | |
27 | | // The maximum number of 'uniquified' files we will try to create. |
28 | | // This is used when the filename we're trying to download is already in use, |
29 | | // so we create a new unique filename by appending " (nnn)" before the |
30 | | // extension, where 1 <= nnn <= kMaxUniqueFiles. |
31 | | // Also used by code that cleans up said files. |
32 | | static const int kMaxUniqueFiles = 100; |
33 | | |
34 | | } // namespace |
35 | | |
36 | 0 | int64_t ComputeDirectorySize(const FilePath& root_path) { |
37 | 0 | int64_t running_size = 0; |
38 | 0 | FileEnumerator file_iter(root_path, true, FileEnumerator::FILES); |
39 | 0 | while (!file_iter.Next().empty()) |
40 | 0 | running_size += file_iter.GetInfo().GetSize(); |
41 | 0 | return running_size; |
42 | 0 | } |
43 | | |
44 | 0 | bool Move(const FilePath& from_path, const FilePath& to_path) { |
45 | 0 | if (from_path.ReferencesParent() || to_path.ReferencesParent()) |
46 | 0 | return false; |
47 | 0 | return internal::MoveUnsafe(from_path, to_path); |
48 | 0 | } |
49 | | |
50 | 0 | bool CopyFile(const FilePath& from_path, const FilePath& to_path) { |
51 | 0 | if (from_path.ReferencesParent() || to_path.ReferencesParent()) |
52 | 0 | return false; |
53 | 0 | return internal::CopyFileUnsafe(from_path, to_path); |
54 | 0 | } |
55 | | |
56 | 0 | bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { |
57 | | // We open the file in binary format even if they are text files because |
58 | | // we are just comparing that bytes are exactly same in both files and not |
59 | | // doing anything smart with text formatting. |
60 | 0 | std::ifstream file1(filename1.value().c_str(), |
61 | 0 | std::ios::in | std::ios::binary); |
62 | 0 | std::ifstream file2(filename2.value().c_str(), |
63 | 0 | std::ios::in | std::ios::binary); |
64 | | |
65 | | // Even if both files aren't openable (and thus, in some sense, "equal"), |
66 | | // any unusable file yields a result of "false". |
67 | 0 | if (!file1.is_open() || !file2.is_open()) |
68 | 0 | return false; |
69 | | |
70 | 0 | const int BUFFER_SIZE = 2056; |
71 | 0 | char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE]; |
72 | 0 | do { |
73 | 0 | file1.read(buffer1, BUFFER_SIZE); |
74 | 0 | file2.read(buffer2, BUFFER_SIZE); |
75 | |
|
76 | 0 | if ((file1.eof() != file2.eof()) || |
77 | 0 | (file1.gcount() != file2.gcount()) || |
78 | 0 | (memcmp(buffer1, buffer2, file1.gcount()))) { |
79 | 0 | file1.close(); |
80 | 0 | file2.close(); |
81 | 0 | return false; |
82 | 0 | } |
83 | 0 | } while (!file1.eof() || !file2.eof()); |
84 | | |
85 | 0 | file1.close(); |
86 | 0 | file2.close(); |
87 | 0 | return true; |
88 | 0 | } |
89 | | |
90 | 0 | bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { |
91 | 0 | std::ifstream file1(filename1.value().c_str(), std::ios::in); |
92 | 0 | std::ifstream file2(filename2.value().c_str(), std::ios::in); |
93 | | |
94 | | // Even if both files aren't openable (and thus, in some sense, "equal"), |
95 | | // any unusable file yields a result of "false". |
96 | 0 | if (!file1.is_open() || !file2.is_open()) |
97 | 0 | return false; |
98 | | |
99 | 0 | do { |
100 | 0 | std::string line1, line2; |
101 | 0 | getline(file1, line1); |
102 | 0 | getline(file2, line2); |
103 | | |
104 | | // Check for mismatched EOF states, or any error state. |
105 | 0 | if ((file1.eof() != file2.eof()) || |
106 | 0 | file1.bad() || file2.bad()) { |
107 | 0 | return false; |
108 | 0 | } |
109 | | |
110 | | // Trim all '\r' and '\n' characters from the end of the line. |
111 | 0 | std::string::size_type end1 = line1.find_last_not_of("\r\n"); |
112 | 0 | if (end1 == std::string::npos) |
113 | 0 | line1.clear(); |
114 | 0 | else if (end1 + 1 < line1.length()) |
115 | 0 | line1.erase(end1 + 1); |
116 | |
|
117 | 0 | std::string::size_type end2 = line2.find_last_not_of("\r\n"); |
118 | 0 | if (end2 == std::string::npos) |
119 | 0 | line2.clear(); |
120 | 0 | else if (end2 + 1 < line2.length()) |
121 | 0 | line2.erase(end2 + 1); |
122 | |
|
123 | 0 | if (line1 != line2) |
124 | 0 | return false; |
125 | 0 | } while (!file1.eof() || !file2.eof()); |
126 | | |
127 | 0 | return true; |
128 | 0 | } |
129 | | |
130 | | bool ReadFileToString(const FilePath& path, |
131 | | std::string* contents, |
132 | 0 | size_t max_size) { |
133 | 0 | if (contents) |
134 | 0 | contents->clear(); |
135 | 0 | if (path.ReferencesParent()) |
136 | 0 | return false; |
137 | 0 | FILE* file = OpenFile(path, "rb"); |
138 | 0 | if (!file) { |
139 | 0 | return false; |
140 | 0 | } |
141 | | |
142 | 0 | char buf[1 << 16]; |
143 | 0 | size_t len; |
144 | 0 | size_t size = 0; |
145 | 0 | bool read_status = true; |
146 | | |
147 | | // Many files supplied in |path| have incorrect size (proc files etc). |
148 | | // Hence, the file is read sequentially as opposed to a one-shot read. |
149 | 0 | while ((len = fread(buf, 1, sizeof(buf), file)) > 0) { |
150 | 0 | if (contents) |
151 | 0 | contents->append(buf, std::min(len, max_size - size)); |
152 | |
|
153 | 0 | if ((max_size - size) < len) { |
154 | 0 | read_status = false; |
155 | 0 | break; |
156 | 0 | } |
157 | | |
158 | 0 | size += len; |
159 | 0 | } |
160 | 0 | read_status = read_status && !ferror(file); |
161 | 0 | CloseFile(file); |
162 | |
|
163 | 0 | return read_status; |
164 | 0 | } |
165 | | |
166 | 0 | bool ReadFileToString(const FilePath& path, std::string* contents) { |
167 | 0 | return ReadFileToString(path, contents, std::numeric_limits<size_t>::max()); |
168 | 0 | } |
169 | | |
170 | 0 | bool IsDirectoryEmpty(const FilePath& dir_path) { |
171 | 0 | FileEnumerator files(dir_path, false, |
172 | 0 | FileEnumerator::FILES | FileEnumerator::DIRECTORIES); |
173 | 0 | if (files.Next().empty()) |
174 | 0 | return true; |
175 | 0 | return false; |
176 | 0 | } |
177 | | |
178 | 0 | FILE* CreateAndOpenTemporaryFile(FilePath* path) { |
179 | 0 | FilePath directory; |
180 | 0 | if (!GetTempDir(&directory)) |
181 | 0 | return NULL; |
182 | | |
183 | 0 | return CreateAndOpenTemporaryFileInDir(directory, path); |
184 | 0 | } |
185 | | |
186 | 0 | bool CreateDirectory(const FilePath& full_path) { |
187 | 0 | return CreateDirectoryAndGetError(full_path, NULL); |
188 | 0 | } |
189 | | |
190 | 0 | bool CreateDirectory(const FilePath& full_path, bool create_parent) { |
191 | 0 | return CreateDirectoryAndGetError(full_path, NULL, create_parent); |
192 | 0 | } |
193 | | |
194 | | bool CreateDirectoryAndGetError(const FilePath& full_path, |
195 | 0 | File::Error* error) { |
196 | 0 | return CreateDirectoryAndGetError(full_path, error, true); |
197 | 0 | } |
198 | | |
199 | 0 | bool GetFileSize(const FilePath& file_path, int64_t* file_size) { |
200 | 0 | File::Info info; |
201 | 0 | if (!GetFileInfo(file_path, &info)) |
202 | 0 | return false; |
203 | 0 | *file_size = info.size; |
204 | 0 | return true; |
205 | 0 | } |
206 | | |
207 | | bool TouchFile(const FilePath& path, |
208 | | const Time& last_accessed, |
209 | 0 | const Time& last_modified) { |
210 | 0 | int flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES; |
211 | |
|
212 | | #if defined(OS_WIN) |
213 | | // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory. |
214 | | if (DirectoryExists(path)) |
215 | | flags |= File::FLAG_BACKUP_SEMANTICS; |
216 | | #endif // OS_WIN |
217 | |
|
218 | 0 | File file(path, flags); |
219 | 0 | if (!file.IsValid()) |
220 | 0 | return false; |
221 | | |
222 | 0 | return file.SetTimes(last_accessed, last_modified); |
223 | 0 | } |
224 | | |
225 | 0 | bool CloseFile(FILE* file) { |
226 | 0 | if (file == NULL) |
227 | 0 | return true; |
228 | 0 | return fclose(file) == 0; |
229 | 0 | } |
230 | | |
231 | 0 | bool TruncateFile(FILE* file) { |
232 | 0 | if (file == NULL) |
233 | 0 | return false; |
234 | 0 | long current_offset = ftell(file); |
235 | 0 | if (current_offset == -1) |
236 | 0 | return false; |
237 | | #if defined(OS_WIN) |
238 | | int fd = _fileno(file); |
239 | | if (_chsize(fd, current_offset) != 0) |
240 | | return false; |
241 | | #else |
242 | 0 | int fd = fileno(file); |
243 | 0 | if (ftruncate(fd, current_offset) != 0) |
244 | 0 | return false; |
245 | 0 | #endif |
246 | 0 | return true; |
247 | 0 | } |
248 | | |
249 | | int GetUniquePathNumber(const FilePath& path, |
250 | 0 | const FilePath::StringType& suffix) { |
251 | 0 | bool have_suffix = !suffix.empty(); |
252 | 0 | if (!PathExists(path) && |
253 | 0 | (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) { |
254 | 0 | return 0; |
255 | 0 | } |
256 | | |
257 | 0 | FilePath new_path; |
258 | 0 | for (int count = 1; count <= kMaxUniqueFiles; ++count) { |
259 | 0 | new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count)); |
260 | 0 | if (!PathExists(new_path) && |
261 | 0 | (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) { |
262 | 0 | return count; |
263 | 0 | } |
264 | 0 | } |
265 | | |
266 | 0 | return -1; |
267 | 0 | } |
268 | | |
269 | | } // namespace butil |