blob: c24f00b68c5ae0a39a07e23bf333ee2918d97b07
1 | /** |
2 | * unix_io.c - Unix style disk io functions. Originated from the Linux-NTFS project. |
3 | * |
4 | * Copyright (c) 2000-2006 Anton Altaparmakov |
5 | * |
6 | * This program/include file is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License as published |
8 | * by the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program/include file is distributed in the hope that it will be |
12 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
13 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program (in the main directory of the NTFS-3G |
18 | * distribution in the file COPYING); if not, write to the Free Software |
19 | * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | */ |
21 | |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" |
24 | #endif |
25 | |
26 | #ifdef HAVE_UNISTD_H |
27 | #include <unistd.h> |
28 | #endif |
29 | #ifdef HAVE_STDLIB_H |
30 | #include <stdlib.h> |
31 | #endif |
32 | #ifdef HAVE_STRING_H |
33 | #include <string.h> |
34 | #endif |
35 | #ifdef HAVE_ERRNO_H |
36 | #include <errno.h> |
37 | #endif |
38 | #ifdef HAVE_STDIO_H |
39 | #include <stdio.h> |
40 | #endif |
41 | #ifdef HAVE_SYS_TYPES_H |
42 | #include <sys/types.h> |
43 | #endif |
44 | #ifdef HAVE_SYS_STAT_H |
45 | #include <sys/stat.h> |
46 | #endif |
47 | #ifdef HAVE_FCNTL_H |
48 | #include <fcntl.h> |
49 | #endif |
50 | #ifdef HAVE_SYS_IOCTL_H |
51 | #include <sys/ioctl.h> |
52 | #endif |
53 | #ifdef HAVE_LINUX_FD_H |
54 | #include <linux/fd.h> |
55 | #endif |
56 | |
57 | #include "types.h" |
58 | #include "mst.h" |
59 | #include "debug.h" |
60 | #include "device.h" |
61 | #include "logging.h" |
62 | #include "misc.h" |
63 | |
64 | |
65 | |
66 | #ifdef __USE_FILE_OFFSET64 |
67 | #include <unistd.h> |
68 | #define lseek lseek64 |
69 | #define pread pread64 |
70 | #define pwrite pwrite64 |
71 | #define fcntl __fcntl64 |
72 | #endif |
73 | |
74 | |
75 | #define DEV_FD(dev) (*(int *)dev->d_private) |
76 | |
77 | /* Define to nothing if not present on this system. */ |
78 | #ifndef O_EXCL |
79 | # define O_EXCL 0 |
80 | #endif |
81 | |
82 | /** |
83 | * fsync replacement which makes every effort to try to get the data down to |
84 | * disk, using different means for different operating systems. Specifically, |
85 | * it issues the proper fcntl for Mac OS X or does fsync where it is available |
86 | * or as a last resort calls the fsync function. Information on this problem |
87 | * was retrieved from: |
88 | * http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf |
89 | */ |
90 | static int ntfs_fsync(int fildes) |
91 | { |
92 | int ret = -1; |
93 | #if defined(__APPLE__) || defined(__DARWIN__) |
94 | # ifndef F_FULLFSYNC |
95 | # error "Mac OS X: F_FULLFSYNC is not defined. Either you didn't include fcntl.h or you're using an older, unsupported version of Mac OS X (pre-10.3)." |
96 | # endif |
97 | /* |
98 | * Apple has disabled fsync() for internal disk drives in OS X. |
99 | * To force a synchronization of disk contents, we use a Mac OS X |
100 | * specific fcntl, F_FULLFSYNC. |
101 | */ |
102 | ret = fcntl(fildes, F_FULLFSYNC, NULL); |
103 | if (ret) { |
104 | /* |
105 | * If we are not on a file system that supports this, |
106 | * then fall back to a plain fsync. |
107 | */ |
108 | ret = fsync(fildes); |
109 | } |
110 | #else |
111 | ret = fsync(fildes); |
112 | #endif |
113 | return ret; |
114 | } |
115 | |
116 | /** |
117 | * ntfs_device_unix_io_open - Open a device and lock it exclusively |
118 | * @dev: |
119 | * @flags: |
120 | * |
121 | * Description... |
122 | * |
123 | * Returns: |
124 | */ |
125 | static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) |
126 | { |
127 | struct flock flk; |
128 | struct stat sbuf; |
129 | int err; |
130 | |
131 | if (NDevOpen(dev)) { |
132 | errno = EBUSY; |
133 | return -1; |
134 | } |
135 | if (stat(dev->d_name, &sbuf)) { |
136 | ntfs_log_perror("Failed to access '%s'", dev->d_name); |
137 | return -1; |
138 | } |
139 | if (S_ISBLK(sbuf.st_mode)) |
140 | NDevSetBlock(dev); |
141 | |
142 | dev->d_private = ntfs_malloc(sizeof(int)); |
143 | if (!dev->d_private) |
144 | return -1; |
145 | /* |
146 | * Open file for exclusive access if mounting r/w. |
147 | * Fuseblk takes care about block devices. |
148 | */ |
149 | if (!NDevBlock(dev) && (flags & O_RDWR) == O_RDWR) |
150 | flags |= O_EXCL; |
151 | *(int*)dev->d_private = open(dev->d_name, flags); |
152 | if (*(int*)dev->d_private == -1) { |
153 | err = errno; |
154 | goto err_out; |
155 | } |
156 | |
157 | if ((flags & O_RDWR) != O_RDWR) |
158 | NDevSetReadOnly(dev); |
159 | |
160 | memset(&flk, 0, sizeof(flk)); |
161 | if (NDevReadOnly(dev)) |
162 | flk.l_type = F_RDLCK; |
163 | else |
164 | flk.l_type = F_WRLCK; |
165 | flk.l_whence = SEEK_SET; |
166 | flk.l_start = flk.l_len = 0LL; |
167 | if (fcntl(DEV_FD(dev), F_SETLK, &flk)) { |
168 | err = errno; |
169 | ntfs_log_perror("Failed to %s lock '%s'", NDevReadOnly(dev) ? |
170 | "read" : "write", dev->d_name); |
171 | if (close(DEV_FD(dev))) |
172 | ntfs_log_perror("Failed to close '%s'", dev->d_name); |
173 | goto err_out; |
174 | } |
175 | |
176 | NDevSetOpen(dev); |
177 | return 0; |
178 | err_out: |
179 | free(dev->d_private); |
180 | dev->d_private = NULL; |
181 | errno = err; |
182 | return -1; |
183 | } |
184 | |
185 | /** |
186 | * ntfs_device_unix_io_close - Close the device, releasing the lock |
187 | * @dev: |
188 | * |
189 | * Description... |
190 | * |
191 | * Returns: |
192 | */ |
193 | static int ntfs_device_unix_io_close(struct ntfs_device *dev) |
194 | { |
195 | struct flock flk; |
196 | |
197 | if (!NDevOpen(dev)) { |
198 | errno = EBADF; |
199 | ntfs_log_perror("Device %s is not open", dev->d_name); |
200 | return -1; |
201 | } |
202 | if (NDevDirty(dev)) |
203 | if (ntfs_fsync(DEV_FD(dev))) { |
204 | ntfs_log_perror("Failed to fsync device %s", dev->d_name); |
205 | return -1; |
206 | } |
207 | |
208 | memset(&flk, 0, sizeof(flk)); |
209 | flk.l_type = F_UNLCK; |
210 | flk.l_whence = SEEK_SET; |
211 | flk.l_start = flk.l_len = 0LL; |
212 | if (fcntl(DEV_FD(dev), F_SETLK, &flk)) |
213 | ntfs_log_perror("Could not unlock %s", dev->d_name); |
214 | if (close(DEV_FD(dev))) { |
215 | ntfs_log_perror("Failed to close device %s", dev->d_name); |
216 | return -1; |
217 | } |
218 | NDevClearOpen(dev); |
219 | free(dev->d_private); |
220 | dev->d_private = NULL; |
221 | return 0; |
222 | } |
223 | |
224 | /** |
225 | * ntfs_device_unix_io_seek - Seek to a place on the device |
226 | * @dev: |
227 | * @offset: |
228 | * @whence: |
229 | * |
230 | * Description... |
231 | * |
232 | * Returns: |
233 | */ |
234 | static s64 ntfs_device_unix_io_seek(struct ntfs_device *dev, s64 offset, |
235 | int whence) |
236 | { |
237 | return lseek(DEV_FD(dev), offset, whence); |
238 | } |
239 | |
240 | /** |
241 | * ntfs_device_unix_io_read - Read from the device, from the current location |
242 | * @dev: |
243 | * @buf: |
244 | * @count: |
245 | * |
246 | * Description... |
247 | * |
248 | * Returns: |
249 | */ |
250 | static s64 ntfs_device_unix_io_read(struct ntfs_device *dev, void *buf, |
251 | s64 count) |
252 | { |
253 | return read(DEV_FD(dev), buf, count); |
254 | } |
255 | |
256 | /** |
257 | * ntfs_device_unix_io_write - Write to the device, at the current location |
258 | * @dev: |
259 | * @buf: |
260 | * @count: |
261 | * |
262 | * Description... |
263 | * |
264 | * Returns: |
265 | */ |
266 | static s64 ntfs_device_unix_io_write(struct ntfs_device *dev, const void *buf, |
267 | s64 count) |
268 | { |
269 | if (NDevReadOnly(dev)) { |
270 | errno = EROFS; |
271 | return -1; |
272 | } |
273 | NDevSetDirty(dev); |
274 | return write(DEV_FD(dev), buf, count); |
275 | } |
276 | |
277 | /** |
278 | * ntfs_device_unix_io_pread - Perform a positioned read from the device |
279 | * @dev: |
280 | * @buf: |
281 | * @count: |
282 | * @offset: |
283 | * |
284 | * Description... |
285 | * |
286 | * Returns: |
287 | */ |
288 | static s64 ntfs_device_unix_io_pread(struct ntfs_device *dev, void *buf, |
289 | s64 count, s64 offset) |
290 | { |
291 | return pread(DEV_FD(dev), buf, count, offset); |
292 | } |
293 | |
294 | /** |
295 | * ntfs_device_unix_io_pwrite - Perform a positioned write to the device |
296 | * @dev: |
297 | * @buf: |
298 | * @count: |
299 | * @offset: |
300 | * |
301 | * Description... |
302 | * |
303 | * Returns: |
304 | */ |
305 | static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf, |
306 | s64 count, s64 offset) |
307 | { |
308 | if (NDevReadOnly(dev)) { |
309 | errno = EROFS; |
310 | return -1; |
311 | } |
312 | NDevSetDirty(dev); |
313 | return pwrite(DEV_FD(dev), buf, count, offset); |
314 | } |
315 | |
316 | /** |
317 | * ntfs_device_unix_io_sync - Flush any buffered changes to the device |
318 | * @dev: |
319 | * |
320 | * Description... |
321 | * |
322 | * Returns: |
323 | */ |
324 | static int ntfs_device_unix_io_sync(struct ntfs_device *dev) |
325 | { |
326 | int res = 0; |
327 | |
328 | if (!NDevReadOnly(dev)) { |
329 | res = ntfs_fsync(DEV_FD(dev)); |
330 | if (res) |
331 | ntfs_log_perror("Failed to sync device %s", dev->d_name); |
332 | else |
333 | NDevClearDirty(dev); |
334 | } |
335 | return res; |
336 | } |
337 | |
338 | /** |
339 | * ntfs_device_unix_io_stat - Get information about the device |
340 | * @dev: |
341 | * @buf: |
342 | * |
343 | * Description... |
344 | * |
345 | * Returns: |
346 | */ |
347 | static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf) |
348 | { |
349 | return fstat(DEV_FD(dev), buf); |
350 | } |
351 | |
352 | /** |
353 | * ntfs_device_unix_io_ioctl - Perform an ioctl on the device |
354 | * @dev: |
355 | * @request: |
356 | * @argp: |
357 | * |
358 | * Description... |
359 | * |
360 | * Returns: |
361 | */ |
362 | static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, int request, |
363 | void *argp) |
364 | { |
365 | return ioctl(DEV_FD(dev), request, argp); |
366 | } |
367 | |
368 | #ifdef __USE_FILE_OFFSET64 |
369 | #define pread pread |
370 | #define pwrite pwrite |
371 | #endif |
372 | /** |
373 | * Device operations for working with unix style devices and files. |
374 | */ |
375 | struct ntfs_device_operations ntfs_device_unix_io_ops = { |
376 | .open = ntfs_device_unix_io_open, |
377 | .close = ntfs_device_unix_io_close, |
378 | .seek = ntfs_device_unix_io_seek, |
379 | .read = ntfs_device_unix_io_read, |
380 | .write = ntfs_device_unix_io_write, |
381 | .pread = ntfs_device_unix_io_pread, |
382 | .pwrite = ntfs_device_unix_io_pwrite, |
383 | .sync = ntfs_device_unix_io_sync, |
384 | .stat = ntfs_device_unix_io_stat, |
385 | .ioctl = ntfs_device_unix_io_ioctl, |
386 | }; |
387 |