blob: 799db0b1b76c9011b3a66a5ee1932cb1ef32dbcc
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 | #ifdef __USE_FILE_OFFSET64 |
65 | #include <unistd.h> |
66 | #endif |
67 | |
68 | #define DEV_FD(dev) (*(int *)dev->d_private) |
69 | |
70 | #ifdef __USE_FILE_OFFSET64 |
71 | #define fcntl __fcntl64 |
72 | #endif |
73 | |
74 | /* Define to nothing if not present on this system. */ |
75 | #ifndef O_EXCL |
76 | # define O_EXCL 0 |
77 | #endif |
78 | |
79 | /** |
80 | * fsync replacement which makes every effort to try to get the data down to |
81 | * disk, using different means for different operating systems. Specifically, |
82 | * it issues the proper fcntl for Mac OS X or does fsync where it is available |
83 | * or as a last resort calls the fsync function. Information on this problem |
84 | * was retrieved from: |
85 | * http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf |
86 | */ |
87 | static int ntfs_fsync(int fildes) |
88 | { |
89 | int ret = -1; |
90 | #if defined(__APPLE__) || defined(__DARWIN__) |
91 | # ifndef F_FULLFSYNC |
92 | # 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)." |
93 | # endif |
94 | /* |
95 | * Apple has disabled fsync() for internal disk drives in OS X. |
96 | * To force a synchronization of disk contents, we use a Mac OS X |
97 | * specific fcntl, F_FULLFSYNC. |
98 | */ |
99 | ret = fcntl(fildes, F_FULLFSYNC, NULL); |
100 | if (ret) { |
101 | /* |
102 | * If we are not on a file system that supports this, |
103 | * then fall back to a plain fsync. |
104 | */ |
105 | ret = fsync(fildes); |
106 | } |
107 | #else |
108 | ret = fsync(fildes); |
109 | #endif |
110 | return ret; |
111 | } |
112 | |
113 | /** |
114 | * ntfs_device_unix_io_open - Open a device and lock it exclusively |
115 | * @dev: |
116 | * @flags: |
117 | * |
118 | * Description... |
119 | * |
120 | * Returns: |
121 | */ |
122 | static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) |
123 | { |
124 | struct flock flk; |
125 | struct stat sbuf; |
126 | int err; |
127 | |
128 | if (NDevOpen(dev)) { |
129 | errno = EBUSY; |
130 | return -1; |
131 | } |
132 | if (stat(dev->d_name, &sbuf)) { |
133 | ntfs_log_perror("Failed to access '%s'", dev->d_name); |
134 | return -1; |
135 | } |
136 | if (S_ISBLK(sbuf.st_mode)) |
137 | NDevSetBlock(dev); |
138 | |
139 | dev->d_private = ntfs_malloc(sizeof(int)); |
140 | if (!dev->d_private) |
141 | return -1; |
142 | /* |
143 | * Open file for exclusive access if mounting r/w. |
144 | * Fuseblk takes care about block devices. |
145 | */ |
146 | if (!NDevBlock(dev) && (flags & O_RDWR) == O_RDWR) |
147 | flags |= O_EXCL; |
148 | *(int*)dev->d_private = open(dev->d_name, flags); |
149 | if (*(int*)dev->d_private == -1) { |
150 | err = errno; |
151 | goto err_out; |
152 | } |
153 | |
154 | if ((flags & O_RDWR) != O_RDWR) |
155 | NDevSetReadOnly(dev); |
156 | |
157 | memset(&flk, 0, sizeof(flk)); |
158 | if (NDevReadOnly(dev)) |
159 | flk.l_type = F_RDLCK; |
160 | else |
161 | flk.l_type = F_WRLCK; |
162 | flk.l_whence = SEEK_SET; |
163 | flk.l_start = flk.l_len = 0LL; |
164 | if (fcntl(DEV_FD(dev), F_SETLK, &flk)) { |
165 | err = errno; |
166 | ntfs_log_perror("Failed to %s lock '%s'", NDevReadOnly(dev) ? |
167 | "read" : "write", dev->d_name); |
168 | if (close(DEV_FD(dev))) |
169 | ntfs_log_perror("Failed to close '%s'", dev->d_name); |
170 | goto err_out; |
171 | } |
172 | |
173 | NDevSetOpen(dev); |
174 | return 0; |
175 | err_out: |
176 | free(dev->d_private); |
177 | dev->d_private = NULL; |
178 | errno = err; |
179 | return -1; |
180 | } |
181 | |
182 | /** |
183 | * ntfs_device_unix_io_close - Close the device, releasing the lock |
184 | * @dev: |
185 | * |
186 | * Description... |
187 | * |
188 | * Returns: |
189 | */ |
190 | static int ntfs_device_unix_io_close(struct ntfs_device *dev) |
191 | { |
192 | struct flock flk; |
193 | |
194 | if (!NDevOpen(dev)) { |
195 | errno = EBADF; |
196 | ntfs_log_perror("Device %s is not open", dev->d_name); |
197 | return -1; |
198 | } |
199 | if (NDevDirty(dev)) |
200 | if (ntfs_fsync(DEV_FD(dev))) { |
201 | ntfs_log_perror("Failed to fsync device %s", dev->d_name); |
202 | return -1; |
203 | } |
204 | |
205 | memset(&flk, 0, sizeof(flk)); |
206 | flk.l_type = F_UNLCK; |
207 | flk.l_whence = SEEK_SET; |
208 | flk.l_start = flk.l_len = 0LL; |
209 | if (fcntl(DEV_FD(dev), F_SETLK, &flk)) |
210 | ntfs_log_perror("Could not unlock %s", dev->d_name); |
211 | if (close(DEV_FD(dev))) { |
212 | ntfs_log_perror("Failed to close device %s", dev->d_name); |
213 | return -1; |
214 | } |
215 | NDevClearOpen(dev); |
216 | free(dev->d_private); |
217 | dev->d_private = NULL; |
218 | return 0; |
219 | } |
220 | |
221 | /** |
222 | * ntfs_device_unix_io_seek - Seek to a place on the device |
223 | * @dev: |
224 | * @offset: |
225 | * @whence: |
226 | * |
227 | * Description... |
228 | * |
229 | * Returns: |
230 | */ |
231 | static s64 ntfs_device_unix_io_seek(struct ntfs_device *dev, s64 offset, |
232 | int whence) |
233 | { |
234 | #ifdef __USE_FILE_OFFSET64 |
235 | return lseek64(DEV_FD(dev), offset, whence); |
236 | #else |
237 | return lseek(DEV_FD(dev), offset, whence); |
238 | #endif |
239 | } |
240 | |
241 | /** |
242 | * ntfs_device_unix_io_read - Read from the device, from the current location |
243 | * @dev: |
244 | * @buf: |
245 | * @count: |
246 | * |
247 | * Description... |
248 | * |
249 | * Returns: |
250 | */ |
251 | static s64 ntfs_device_unix_io_read(struct ntfs_device *dev, void *buf, |
252 | s64 count) |
253 | { |
254 | return read(DEV_FD(dev), buf, count); |
255 | } |
256 | |
257 | /** |
258 | * ntfs_device_unix_io_write - Write to the device, at the current location |
259 | * @dev: |
260 | * @buf: |
261 | * @count: |
262 | * |
263 | * Description... |
264 | * |
265 | * Returns: |
266 | */ |
267 | static s64 ntfs_device_unix_io_write(struct ntfs_device *dev, const void *buf, |
268 | s64 count) |
269 | { |
270 | if (NDevReadOnly(dev)) { |
271 | errno = EROFS; |
272 | return -1; |
273 | } |
274 | NDevSetDirty(dev); |
275 | return write(DEV_FD(dev), buf, count); |
276 | } |
277 | |
278 | /** |
279 | * ntfs_device_unix_io_pread - Perform a positioned read from the device |
280 | * @dev: |
281 | * @buf: |
282 | * @count: |
283 | * @offset: |
284 | * |
285 | * Description... |
286 | * |
287 | * Returns: |
288 | */ |
289 | static s64 ntfs_device_unix_io_pread(struct ntfs_device *dev, void *buf, |
290 | s64 count, s64 offset) |
291 | { |
292 | #ifdef __USE_FILE_OFFSET64 |
293 | return pread64(DEV_FD(dev), buf, count, offset); |
294 | #else |
295 | return pread(DEV_FD(dev), buf, count, offset); |
296 | #endif |
297 | } |
298 | |
299 | /** |
300 | * ntfs_device_unix_io_pwrite - Perform a positioned write to the device |
301 | * @dev: |
302 | * @buf: |
303 | * @count: |
304 | * @offset: |
305 | * |
306 | * Description... |
307 | * |
308 | * Returns: |
309 | */ |
310 | static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf, |
311 | s64 count, s64 offset) |
312 | { |
313 | if (NDevReadOnly(dev)) { |
314 | errno = EROFS; |
315 | return -1; |
316 | } |
317 | NDevSetDirty(dev); |
318 | #ifdef __USE_FILE_OFFSET64 |
319 | return pwrite64(DEV_FD(dev), buf, count, offset); |
320 | #else |
321 | return pwrite(DEV_FD(dev), buf, count, offset); |
322 | #endif |
323 | } |
324 | |
325 | /** |
326 | * ntfs_device_unix_io_sync - Flush any buffered changes to the device |
327 | * @dev: |
328 | * |
329 | * Description... |
330 | * |
331 | * Returns: |
332 | */ |
333 | static int ntfs_device_unix_io_sync(struct ntfs_device *dev) |
334 | { |
335 | int res = 0; |
336 | |
337 | if (!NDevReadOnly(dev)) { |
338 | res = ntfs_fsync(DEV_FD(dev)); |
339 | if (res) |
340 | ntfs_log_perror("Failed to sync device %s", dev->d_name); |
341 | else |
342 | NDevClearDirty(dev); |
343 | } |
344 | return res; |
345 | } |
346 | |
347 | /** |
348 | * ntfs_device_unix_io_stat - Get information about the device |
349 | * @dev: |
350 | * @buf: |
351 | * |
352 | * Description... |
353 | * |
354 | * Returns: |
355 | */ |
356 | static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf) |
357 | { |
358 | return fstat(DEV_FD(dev), buf); |
359 | } |
360 | |
361 | /** |
362 | * ntfs_device_unix_io_ioctl - Perform an ioctl on the device |
363 | * @dev: |
364 | * @request: |
365 | * @argp: |
366 | * |
367 | * Description... |
368 | * |
369 | * Returns: |
370 | */ |
371 | static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, int request, |
372 | void *argp) |
373 | { |
374 | return ioctl(DEV_FD(dev), request, argp); |
375 | } |
376 | |
377 | /** |
378 | * Device operations for working with unix style devices and files. |
379 | */ |
380 | struct ntfs_device_operations ntfs_device_unix_io_ops = { |
381 | .open = ntfs_device_unix_io_open, |
382 | .close = ntfs_device_unix_io_close, |
383 | .seek = ntfs_device_unix_io_seek, |
384 | .read = ntfs_device_unix_io_read, |
385 | .write = ntfs_device_unix_io_write, |
386 | .pread = ntfs_device_unix_io_pread, |
387 | .pwrite = ntfs_device_unix_io_pwrite, |
388 | .sync = ntfs_device_unix_io_sync, |
389 | .stat = ntfs_device_unix_io_stat, |
390 | .ioctl = ntfs_device_unix_io_ioctl, |
391 | }; |
392 |