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