/src/poppler/goo/gfile.cc
Line | Count | Source |
1 | | //======================================================================== |
2 | | // |
3 | | // gfile.cc |
4 | | // |
5 | | // Miscellaneous file and directory name manipulation. |
6 | | // |
7 | | // Copyright 1996-2003 Glyph & Cog, LLC |
8 | | // |
9 | | //======================================================================== |
10 | | |
11 | | //======================================================================== |
12 | | // |
13 | | // Modified under the Poppler project - http://poppler.freedesktop.org |
14 | | // |
15 | | // All changes made under the Poppler project to this file are licensed |
16 | | // under GPL version 2 or later |
17 | | // |
18 | | // Copyright (C) 2006 Takashi Iwai <tiwai@suse.de> |
19 | | // Copyright (C) 2006 Kristian Høgsberg <krh@redhat.com> |
20 | | // Copyright (C) 2008 Adam Batkin <adam@batkin.net> |
21 | | // Copyright (C) 2008, 2010, 2012, 2013 Hib Eris <hib@hiberis.nl> |
22 | | // Copyright (C) 2009, 2012, 2014, 2017, 2018, 2021, 2022, 2024-2026 Albert Astals Cid <aacid@kde.org> |
23 | | // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net> |
24 | | // Copyright (C) 2013, 2018 Adam Reichold <adamreichold@myopera.com> |
25 | | // Copyright (C) 2013, 2017 Adrian Johnson <ajohnson@redneon.com> |
26 | | // Copyright (C) 2013 Peter Breitenlohner <peb@mppmu.mpg.de> |
27 | | // Copyright (C) 2013, 2017 Thomas Freitag <Thomas.Freitag@alfa.de> |
28 | | // Copyright (C) 2017 Christoph Cullmann <cullmann@kde.org> |
29 | | // Copyright (C) 2018 Mojca Miklavec <mojca@macports.org> |
30 | | // Copyright (C) 2019, 2021 Christian Persch <chpe@src.gnome.org> |
31 | | // Copyright (C) 2025, 2026 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk> |
32 | | // Copyright (C) 2026 Kleis Auke Wolthuizen <github@kleisauke.nl> |
33 | | // |
34 | | // To see a description of the changes please see the Changelog file that |
35 | | // came with your tarball or type make ChangeLog if you are building from git |
36 | | // |
37 | | //======================================================================== |
38 | | |
39 | | #include <config.h> |
40 | | |
41 | | #ifndef _WIN32 |
42 | | # include <sys/types.h> |
43 | | # include <sys/stat.h> |
44 | | # include <fcntl.h> |
45 | | # include <cstring> |
46 | | # include <pwd.h> |
47 | | #endif // _WIN32 |
48 | | #include <cstdio> |
49 | | #include <limits> |
50 | | #include "gfile.h" |
51 | | |
52 | | #ifndef _WIN32 |
53 | | |
54 | | using namespace std::string_literals; |
55 | | |
56 | | namespace { |
57 | | |
58 | | template<class T> |
59 | | inline constexpr bool has_st_mtim = requires(const T &s) { s.st_mtim; }; |
60 | | |
61 | | template<class T> |
62 | | inline constexpr bool has_st_mtimespec = requires(const T &s) { s.st_mtimespec; }; |
63 | | |
64 | | template<typename Stat> |
65 | | const struct timespec &mtim(const Stat &stbuf) |
66 | 7.13k | { |
67 | 7.13k | static_assert(has_st_mtim<Stat> || has_st_mtimespec<Stat>, "stat must contain st_mtim or st_mtimespec"); |
68 | | |
69 | 7.13k | if constexpr (has_st_mtim<Stat>) { |
70 | 7.13k | return stbuf.st_mtim; |
71 | | } else { |
72 | | // Mac OS X uses a different field name than POSIX and this makes it work |
73 | | return stbuf.st_mtimespec; |
74 | | } |
75 | 7.13k | } |
76 | | |
77 | | } |
78 | | |
79 | | #endif |
80 | | |
81 | | //------------------------------------------------------------------------ |
82 | | |
83 | | #ifndef _WIN32 |
84 | | |
85 | | static bool makeFileDescriptorCloexec(int fd) |
86 | 0 | { |
87 | 0 | # ifdef FD_CLOEXEC |
88 | 0 | int flags = fcntl(fd, F_GETFD); |
89 | 0 | if (flags >= 0 && !(flags & FD_CLOEXEC)) { |
90 | 0 | flags = fcntl(fd, F_SETFD, flags | FD_CLOEXEC); |
91 | 0 | } |
92 | |
|
93 | 0 | return flags >= 0; |
94 | | # else |
95 | | return true; |
96 | | # endif |
97 | 0 | } |
98 | | |
99 | | static int openFileDescriptor(const char *path, int flags) |
100 | 7.13k | { |
101 | 7.13k | # ifdef O_CLOEXEC |
102 | 7.13k | return open(path, flags | O_CLOEXEC); |
103 | | # else |
104 | | int fd = open(path, flags); |
105 | | if (fd == -1) |
106 | | return fd; |
107 | | |
108 | | if (!makeFileDescriptorCloexec(fd)) { |
109 | | close(fd); |
110 | | return -1; |
111 | | } |
112 | | |
113 | | return fd; |
114 | | # endif |
115 | 7.13k | } |
116 | | |
117 | | #endif |
118 | | |
119 | | FILE *openFile(const char *path, const char *mode) |
120 | 0 | { |
121 | | #ifdef _WIN32 |
122 | | OSVERSIONINFO version; |
123 | | wchar_t wPath[MAX_PATH + 1]; |
124 | | char nPath[MAX_PATH + 1]; |
125 | | wchar_t wMode[8]; |
126 | | const char *p; |
127 | | size_t i; |
128 | | |
129 | | // NB: _wfopen is only available in NT |
130 | | version.dwOSVersionInfoSize = sizeof(version); |
131 | | GetVersionEx(&version); |
132 | | if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { |
133 | | for (p = path, i = 0; *p && i < MAX_PATH; ++i) { |
134 | | if ((p[0] & 0xe0) == 0xc0 && p[1] && (p[1] & 0xc0) == 0x80) { |
135 | | wPath[i] = (wchar_t)(((p[0] & 0x1f) << 6) | (p[1] & 0x3f)); |
136 | | p += 2; |
137 | | } else if ((p[0] & 0xf0) == 0xe0 && p[1] && (p[1] & 0xc0) == 0x80 && p[2] && (p[2] & 0xc0) == 0x80) { |
138 | | wPath[i] = (wchar_t)(((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f)); |
139 | | p += 3; |
140 | | } else { |
141 | | wPath[i] = (wchar_t)(p[0] & 0xff); |
142 | | p += 1; |
143 | | } |
144 | | } |
145 | | wPath[i] = (wchar_t)0; |
146 | | for (i = 0; (i < sizeof(mode) - 1) && mode[i]; ++i) { |
147 | | wMode[i] = (wchar_t)(mode[i] & 0xff); |
148 | | } |
149 | | wMode[i] = (wchar_t)0; |
150 | | return _wfopen(wPath, wMode); |
151 | | } else { |
152 | | for (p = path, i = 0; *p && i < MAX_PATH; ++i) { |
153 | | if ((p[0] & 0xe0) == 0xc0 && p[1] && (p[1] & 0xc0) == 0x80) { |
154 | | nPath[i] = (char)(((p[0] & 0x1f) << 6) | (p[1] & 0x3f)); |
155 | | p += 2; |
156 | | } else if ((p[0] & 0xf0) == 0xe0 && p[1] && (p[1] & 0xc0) == 0x80 && p[2] && (p[2] & 0xc0) == 0x80) { |
157 | | nPath[i] = (char)(((p[1] & 0x3f) << 6) | (p[2] & 0x3f)); |
158 | | p += 3; |
159 | | } else { |
160 | | nPath[i] = p[0]; |
161 | | p += 1; |
162 | | } |
163 | | } |
164 | | nPath[i] = '\0'; |
165 | | return fopen(nPath, mode); |
166 | | } |
167 | | #else |
168 | | // First try to atomically open the file with CLOEXEC |
169 | 0 | const std::string modeStr = mode + "e"s; |
170 | 0 | FILE *file = fopen(path, modeStr.c_str()); |
171 | 0 | if (file != nullptr) { |
172 | 0 | return file; |
173 | 0 | } |
174 | | |
175 | | // Fall back to the provided mode and apply CLOEXEC afterwards |
176 | 0 | file = fopen(path, mode); |
177 | 0 | if (file == nullptr) { |
178 | 0 | return nullptr; |
179 | 0 | } |
180 | | |
181 | 0 | if (!makeFileDescriptorCloexec(fileno(file))) { |
182 | 0 | fclose(file); |
183 | 0 | return nullptr; |
184 | 0 | } |
185 | | |
186 | 0 | return file; |
187 | 0 | #endif |
188 | 0 | } |
189 | | |
190 | | char *getLine(char *buf, int size, FILE *f) |
191 | 0 | { |
192 | 0 | int c, i; |
193 | |
|
194 | 0 | i = 0; |
195 | 0 | while (i < size - 1) { |
196 | 0 | if ((c = fgetc(f)) == EOF) { |
197 | 0 | break; |
198 | 0 | } |
199 | 0 | buf[i++] = static_cast<char>(c); |
200 | 0 | if (c == '\x0a') { |
201 | 0 | break; |
202 | 0 | } |
203 | 0 | if (c == '\x0d') { |
204 | 0 | c = fgetc(f); |
205 | 0 | if (c == '\x0a' && i < size - 1) { |
206 | 0 | buf[i++] = static_cast<char>(c); |
207 | 0 | } else if (c != EOF) { |
208 | 0 | ungetc(c, f); |
209 | 0 | } |
210 | 0 | break; |
211 | 0 | } |
212 | 0 | } |
213 | 0 | buf[i] = '\0'; |
214 | 0 | if (i == 0) { |
215 | 0 | return nullptr; |
216 | 0 | } |
217 | 0 | return buf; |
218 | 0 | } |
219 | | |
220 | | int Gfseek(FILE *f, Goffset offset, int whence) |
221 | 0 | { |
222 | 0 | #if HAVE_FSEEKO |
223 | 0 | return fseeko(f, offset, whence); |
224 | | #elif HAVE_FSEEK64 |
225 | | return fseek64(f, offset, whence); |
226 | | #elif defined(__MINGW32__) |
227 | | return fseeko64(f, offset, whence); |
228 | | #elif defined(_WIN32) |
229 | | return _fseeki64(f, offset, whence); |
230 | | #else |
231 | | return fseek(f, offset, whence); |
232 | | #endif |
233 | 0 | } |
234 | | |
235 | | Goffset Gftell(FILE *f) |
236 | 0 | { |
237 | 0 | #if HAVE_FSEEKO |
238 | 0 | return ftello(f); |
239 | | #elif HAVE_FSEEK64 |
240 | | return ftell64(f); |
241 | | #elif defined(__MINGW32__) |
242 | | return ftello64(f); |
243 | | #elif defined(_WIN32) |
244 | | return _ftelli64(f); |
245 | | #else |
246 | | return ftell(f); |
247 | | #endif |
248 | 0 | } |
249 | | |
250 | | Goffset GoffsetMax() |
251 | 0 | { |
252 | 0 | #if HAVE_FSEEKO |
253 | 0 | return (std::numeric_limits<off_t>::max)(); |
254 | | #elif HAVE_FSEEK64 || defined(__MINGW32__) |
255 | | return (std::numeric_limits<off64_t>::max)(); |
256 | | #elif defined(_WIN32) |
257 | | return (std::numeric_limits<__int64>::max)(); |
258 | | #else |
259 | | return (std::numeric_limits<long>::max)(); |
260 | | #endif |
261 | 0 | } |
262 | | |
263 | | //------------------------------------------------------------------------ |
264 | | // GooFile |
265 | | //------------------------------------------------------------------------ |
266 | | |
267 | | #ifdef _WIN32 |
268 | | |
269 | | GooFile::GooFile(HANDLE handleA) : handle(handleA) |
270 | | { |
271 | | GetFileTime(handleA, nullptr, nullptr, &modifiedTimeOnOpen); |
272 | | } |
273 | | |
274 | | int GooFile::read(char *buf, int n, Goffset offset) const |
275 | | { |
276 | | DWORD m; |
277 | | |
278 | | LARGE_INTEGER largeInteger = {}; |
279 | | largeInteger.QuadPart = offset; |
280 | | |
281 | | OVERLAPPED overlapped = {}; |
282 | | overlapped.Offset = largeInteger.LowPart; |
283 | | overlapped.OffsetHigh = largeInteger.HighPart; |
284 | | |
285 | | return FALSE == ReadFile(handle, buf, n, &m, &overlapped) ? -1 : m; |
286 | | } |
287 | | |
288 | | Goffset GooFile::size() const |
289 | | { |
290 | | LARGE_INTEGER size = { (DWORD)-1, -1 }; |
291 | | |
292 | | GetFileSizeEx(handle, &size); |
293 | | |
294 | | return size.QuadPart; |
295 | | } |
296 | | |
297 | | std::unique_ptr<GooFile> GooFile::open(const std::string &fileName) |
298 | | { |
299 | | HANDLE handle = CreateFileA(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); |
300 | | |
301 | | return handle == INVALID_HANDLE_VALUE ? std::unique_ptr<GooFile>() : std::unique_ptr<GooFile>(new GooFile(handle)); |
302 | | } |
303 | | |
304 | | std::unique_ptr<GooFile> GooFile::open(const wchar_t *fileName) |
305 | | { |
306 | | HANDLE handle = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); |
307 | | |
308 | | return handle == INVALID_HANDLE_VALUE ? std::unique_ptr<GooFile>() : std::unique_ptr<GooFile>(new GooFile(handle)); |
309 | | } |
310 | | |
311 | | bool GooFile::modificationTimeChangedSinceOpen() const |
312 | | { |
313 | | struct _FILETIME lastModified; |
314 | | GetFileTime(handle, nullptr, nullptr, &lastModified); |
315 | | |
316 | | return modifiedTimeOnOpen.dwHighDateTime != lastModified.dwHighDateTime || modifiedTimeOnOpen.dwLowDateTime != lastModified.dwLowDateTime; |
317 | | } |
318 | | |
319 | | #else |
320 | | |
321 | | int GooFile::read(char *buf, int n, Goffset offset) const |
322 | 441k | { |
323 | 441k | # if HAVE_PREAD64 |
324 | 441k | return pread64(fd, buf, n, offset); |
325 | | # else |
326 | | return pread(fd, buf, n, offset); |
327 | | # endif |
328 | 441k | } |
329 | | |
330 | | Goffset GooFile::size() const |
331 | 32.2k | { |
332 | 32.2k | # if HAVE_LSEEK64 |
333 | 32.2k | return lseek64(fd, 0, SEEK_END); |
334 | | # else |
335 | | return lseek(fd, 0, SEEK_END); |
336 | | # endif |
337 | 32.2k | } |
338 | | |
339 | | std::unique_ptr<GooFile> GooFile::open(const std::string &fileName) |
340 | 7.13k | { |
341 | 7.13k | int fd = openFileDescriptor(fileName.c_str(), O_RDONLY); |
342 | | |
343 | 7.13k | return GooFile::open(fd); |
344 | 7.13k | } |
345 | | |
346 | | std::unique_ptr<GooFile> GooFile::open(int fdA) |
347 | 7.13k | { |
348 | 7.13k | return fdA < 0 ? std::unique_ptr<GooFile>() : std::unique_ptr<GooFile>(new GooFile(fdA)); |
349 | 7.13k | } |
350 | | |
351 | 7.13k | GooFile::GooFile(int fdA) : fd(fdA) |
352 | 7.13k | { |
353 | 7.13k | struct stat statbuf; |
354 | 7.13k | fstat(fd, &statbuf); |
355 | 7.13k | modifiedTimeOnOpen = mtim(statbuf); |
356 | 7.13k | } |
357 | | |
358 | | bool GooFile::modificationTimeChangedSinceOpen() const |
359 | 0 | { |
360 | 0 | struct stat statbuf; |
361 | 0 | fstat(fd, &statbuf); |
362 | |
|
363 | 0 | return modifiedTimeOnOpen.tv_sec != mtim(statbuf).tv_sec || modifiedTimeOnOpen.tv_nsec != mtim(statbuf).tv_nsec; |
364 | 0 | } |
365 | | |
366 | | #endif // _WIN32 |