Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / usd / usd_file.c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <roken.h>
14 #include <afs/opr.h>
15
16 #if defined(AFS_AIX_ENV)
17 #include <sys/tape.h>
18 #include <sys/statfs.h>
19 #else
20 #ifdef AFS_DARWIN_ENV
21 #include <sys/ioccom.h>
22 #endif
23 #ifndef AFS_DARWIN100_ENV
24 #include <sys/mtio.h>
25 #endif
26 #endif /* AFS_AIX_ENV */
27
28 #include "usd.h"
29
30 #ifdef O_LARGEFILE
31 typedef off64_t osi_lloff_t;
32 #define osi_llseek lseek64
33 #else /* O_LARGEFILE */
34 #ifdef AFS_HAVE_LLSEEK
35 typedef offset_t osi_lloff_t;
36 #define osi_llseek llseek
37 #else /* AFS_HAVE_LLSEEK */
38 typedef off_t osi_lloff_t;
39 #define osi_llseek lseek
40 #endif /* AFS_HAVE_LLSEEK */
41 #endif /* O_LARGEFILE */
42
43 /*
44 * This macro should be used inside assertions wherever offset/hyper
45 * conversion occur. A good compiler in a 64 bit environment will
46 * elide the entire statement if the offset type is 64 bits wide.
47 */
48 #define osi_hFitsInOff(val) \
49 ((sizeof(osi_lloff_t) == 4) ? (((val) & 0xffffffff00000000LL) == 0) : 1)
50
51 /************ End of osi wrappers ***********************************/
52
53 /* Implementation of user space device I/O for regular POSIX files. */
54
55 static int
56 usd_FileRead(usd_handle_t usd, char *buf, afs_uint32 nbytes,
57 afs_uint32 * xferdP)
58 {
59 int fd = (intptr_t)(usd->handle);
60 int got;
61
62 got = read(fd, buf, nbytes);
63 if (got == -1) {
64 if (xferdP)
65 *xferdP = 0;
66 return errno;
67 }
68 if (xferdP)
69 *xferdP = got;
70 return 0;
71 }
72
73 static int
74 usd_FileWrite(usd_handle_t usd, char *buf, afs_uint32 nbytes,
75 afs_uint32 * xferdP)
76 {
77 int fd = (intptr_t)(usd->handle);
78 int sent;
79
80 sent = write(fd, buf, nbytes);
81 if (sent == -1) {
82 if (xferdP)
83 *xferdP = 0;
84 return errno;
85 }
86 if (xferdP)
87 *xferdP = sent;
88 return 0;
89 }
90
91 extern osi_lloff_t osi_llseek(int, osi_lloff_t, int);
92
93 static int
94 usd_FileSeek(usd_handle_t usd, afs_int64 reqOff, int whence,
95 afs_int64 * curOffP)
96 {
97 int fd = (intptr_t)(usd->handle);
98 osi_lloff_t lloff;
99
100 if (!osi_hFitsInOff(reqOff))
101 return EINVAL;
102
103 lloff = osi_llseek(fd, reqOff, whence);
104 if (lloff == (((osi_lloff_t) 0) - 1))
105 return errno;
106 if (curOffP)
107 *curOffP = lloff;
108
109 return 0;
110 }
111
112 static int
113 usd_FileIoctl(usd_handle_t usd, int req, void *arg)
114 {
115 int fd = (intptr_t)(usd->handle);
116 #ifdef O_LARGEFILE
117 struct stat64 info;
118 #else /* O_LARGEFILE */
119 struct stat info;
120 #endif /* O_LARGEFILE */
121 #ifdef AFS_AIX_ENV
122 struct statfs fsinfo; /* AIX stat structure doesn't have st_blksize */
123 #endif /* AFS_AIX_ENV */
124 afs_int64 size;
125 int code = 0;
126
127 switch (req) {
128 case USD_IOCTL_GETBLKSIZE:
129 #ifdef AFS_AIX_ENV
130 code = fstatfs(fd, &fsinfo);
131 if (code) {
132 *((long *)arg) = (long)4096;
133 return 0;
134 }
135 break;
136 #endif /* AFS_AIX_ENV */
137 case USD_IOCTL_GETTYPE:
138 case USD_IOCTL_GETDEV:
139 case USD_IOCTL_GETSIZE:
140 #ifdef O_LARGEFILE
141 code = fstat64(fd, &info);
142 #else /* O_LARGEFILE */
143 code = fstat(fd, &info);
144 #endif /* O_LARGEFILE */
145 if (code)
146 return errno;
147 break;
148 }
149
150 switch (req) {
151 case USD_IOCTL_GETTYPE:
152 *(int *)arg = info.st_mode & S_IFMT;
153 break;
154 case USD_IOCTL_GETDEV:
155 if (!(S_ISCHR(info.st_mode) || S_ISBLK(info.st_mode)))
156 return ENODEV; /* not a device */
157 *(dev_t *) arg = info.st_rdev;
158 break;
159 case USD_IOCTL_GETSIZE:
160 if (S_ISCHR(info.st_mode) || S_ISBLK(info.st_mode))
161 return ENOTTY; /* shouldn't be a device */
162 *(afs_int64 *)arg = info.st_size;
163 break;
164 case USD_IOCTL_GETFULLNAME:
165 *(char **)arg = usd->fullPathName;
166 break;
167
168 case USD_IOCTL_SETSIZE:
169
170 /* We could just use ftruncate in all cases. (This even works on AIX;
171 * I tried it). -blake 931118 */
172
173 /* However, I'm pretty sure this doesn't work on Ultrix so I am
174 * unsure about OSF/1 and HP/UX. 931118 */
175
176 size = *(afs_int64 *) arg;
177 if (!osi_hFitsInOff(size))
178 return EFBIG;
179 #ifdef O_LARGEFILE
180 code = ftruncate64(fd, size);
181 #else /* O_LARGEFILE */
182 code = ftruncate(fd, size);
183 #endif /* O_LARGEFILE */
184 if (code == -1)
185 code = errno;
186 return code;
187
188 case USD_IOCTL_TAPEOPERATION:
189 {
190 #ifdef AFS_DARWIN100_ENV
191 code = EOPNOTSUPP;
192 #else
193 usd_tapeop_t *tapeOpp = (usd_tapeop_t *) arg;
194 #if defined(AFS_AIX_ENV)
195 struct stop os_tapeop;
196
197 if (tapeOpp->tp_op == USDTAPE_WEOF) {
198 os_tapeop.st_op = STWEOF;
199 } else if (tapeOpp->tp_op == USDTAPE_REW) {
200 os_tapeop.st_op = STREW;
201 } else if (tapeOpp->tp_op == USDTAPE_FSF) {
202 os_tapeop.st_op = STFSF;
203 } else if (tapeOpp->tp_op == USDTAPE_BSF) {
204 os_tapeop.st_op = STRSF;
205 } else if (tapeOpp->tp_op == USDTAPE_PREPARE) {
206 return 0;
207 } else if (tapeOpp->tp_op == USDTAPE_SHUTDOWN) {
208 return 0;
209 } else {
210 /* unsupported tape operation */
211 return EINVAL;
212 }
213 os_tapeop.st_count = tapeOpp->tp_count;
214
215 code = ioctl(fd, STIOCTOP, &os_tapeop);
216 #else
217 struct mtop os_tapeop;
218
219 if (tapeOpp->tp_op == USDTAPE_WEOF) {
220 os_tapeop.mt_op = MTWEOF;
221 } else if (tapeOpp->tp_op == USDTAPE_REW) {
222 os_tapeop.mt_op = MTREW;
223 } else if (tapeOpp->tp_op == USDTAPE_FSF) {
224 os_tapeop.mt_op = MTFSF;
225 } else if (tapeOpp->tp_op == USDTAPE_BSF) {
226 os_tapeop.mt_op = MTBSF;
227 } else if (tapeOpp->tp_op == USDTAPE_PREPARE) {
228 return 0;
229 } else if (tapeOpp->tp_op == USDTAPE_SHUTDOWN) {
230 return 0;
231 } else {
232 /* unsupported tape operation */
233 return EINVAL;
234 }
235 os_tapeop.mt_count = tapeOpp->tp_count;
236
237 code = ioctl(fd, MTIOCTOP, &os_tapeop);
238 #endif /* AFS_AIX_ENV */
239 #endif
240 if (code == -1) {
241 code = errno;
242 } else {
243 code = 0;
244 }
245 return code;
246 }
247
248 case USD_IOCTL_GETBLKSIZE:
249 if (S_ISCHR(info.st_mode) || S_ISBLK(info.st_mode)) {
250 *((long *)arg) = (long)4096;
251 return 0;
252 }
253 #ifdef AFS_AIX_ENV
254 *((long *)arg) = (long)fsinfo.f_bsize;
255 #else /* AFS_AIX_ENV */
256 *((long *)arg) = (long)info.st_blksize;
257 #endif /* AFS_AIX_ENV */
258 break;
259
260 default:
261 return EINVAL;
262 }
263 return code;
264 }
265
266 static int
267 usd_FileClose(usd_handle_t usd)
268 {
269 int fd = (intptr_t)(usd->handle);
270 int code = 0;
271 int ccode;
272
273 /* I can't really believe this is necessary. On the one hand the user
274 * space code always uses character devices, which aren't supposed to do
275 * any buffering. On the other, I very much doubt fsyncing a regular file
276 * being salvaged is ever necessary. But the salvager used to do this
277 * before returning, so... */
278
279 if (usd->openFlags & (O_WRONLY | O_RDWR)) {
280 int mode;
281 code = usd_FileIoctl(usd, USD_IOCTL_GETTYPE, &mode);
282 if (code == 0 && S_ISBLK(mode)) {
283 if (fsync(fd) < 0)
284 code = errno;
285 }
286 }
287
288 ccode = close(fd);
289 if (!code && ccode)
290 code = errno;
291
292 if (usd->fullPathName)
293 free(usd->fullPathName);
294 free(usd);
295
296 return code;
297 }
298
299 static int
300 usd_FileOpen(const char *path, int flags, int mode, usd_handle_t * usdP)
301 {
302 int fd;
303 int oflags;
304 usd_handle_t usd;
305 int code;
306
307 if (usdP)
308 *usdP = NULL;
309
310 oflags = (flags & USD_OPEN_RDWR) ? O_RDWR : O_RDONLY;
311
312 #ifdef O_SYNC /* AFS_DARWIN_ENV XXX */
313 if (flags & USD_OPEN_SYNC)
314 oflags |= O_SYNC;
315 #endif
316
317 if (flags & USD_OPEN_CREATE)
318 oflags |= O_CREAT;
319
320 #ifdef O_LARGEFILE
321 fd = open64(path, oflags | O_LARGEFILE, mode);
322 #else /* O_LARGEFILE */
323 fd = open(path, oflags, mode);
324 #endif /* O_LARGEFILE */
325 if (fd == -1)
326 return errno;
327
328 usd = calloc(1, sizeof(*usd));
329 usd->handle = (void *)(intptr_t)fd;
330 usd->read = usd_FileRead;
331 usd->write = usd_FileWrite;
332 usd->seek = usd_FileSeek;
333 usd->ioctl = usd_FileIoctl;
334 usd->close = usd_FileClose;
335 usd->fullPathName = strdup(path);
336 usd->openFlags = flags;
337
338 code = 0;
339 if (flags & (USD_OPEN_RLOCK | USD_OPEN_WLOCK)) {
340 #ifdef O_LARGEFILE
341 struct flock64 fl;
342 #else /* O_LARGEFILE */
343 struct flock fl;
344 #endif /* O_LARGEFILE */
345
346 /* make sure both lock bits aren't set */
347 opr_Assert(~flags & (USD_OPEN_RLOCK | USD_OPEN_WLOCK));
348
349 fl.l_type = ((flags & USD_OPEN_RLOCK) ? F_RDLCK : F_WRLCK);
350 fl.l_whence = SEEK_SET;
351 fl.l_start = (osi_lloff_t) 0;
352 fl.l_len = (osi_lloff_t) 0; /* whole file */
353 #ifdef O_LARGEFILE
354 code = fcntl(fd, F_SETLK64, &fl);
355 #else /* O_LARGEFILE */
356 code = fcntl(fd, F_SETLK, &fl);
357 #endif /* O_LARGEFILE */
358 if (code == -1)
359 code = errno;
360
361 /* If we're trying to obtain a write lock on a real disk, then the
362 * aggregate must not be attached by the kernel. If so, unlock it
363 * and fail.
364 * WARNING: The code to check for the above has been removed when this
365 * file was ported from DFS src. It should be put back if
366 * this library is used to access hard disks
367 */
368 }
369
370 if (code == 0 && usdP)
371 *usdP = usd;
372 else
373 usd_FileClose(usd);
374 return code;
375 }
376
377 static int
378 usd_FileDummyClose(usd_handle_t usd)
379 {
380 free(usd);
381 return 0;
382 }
383
384 int
385 usd_Open(const char *path, int oflag, int mode, usd_handle_t * usdP)
386 {
387 return usd_FileOpen(path, oflag, mode, usdP);
388 }
389
390 static int
391 usd_FileStandardInput(usd_handle_t * usdP)
392 {
393 usd_handle_t usd;
394
395 usd = calloc(1, sizeof(*usd));
396 usd->handle = (void *)((unsigned long)0);
397 usd->read = usd_FileRead;
398 usd->write = usd_FileWrite;
399 usd->seek = usd_FileSeek;
400 usd->ioctl = usd_FileIoctl;
401 usd->close = usd_FileDummyClose;
402 usd->fullPathName = "STDIN";
403 usd->openFlags = 0;
404 *usdP = usd;
405
406 return 0;
407 }
408
409 int
410 usd_StandardInput(usd_handle_t * usdP)
411 {
412 return usd_FileStandardInput(usdP);
413 }
414
415 static int
416 usd_FileStandardOutput(usd_handle_t * usdP)
417 {
418 usd_handle_t usd;
419
420 usd = calloc(1, sizeof(*usd));
421 usd->handle = (void *)((unsigned long)1);
422 usd->read = usd_FileRead;
423 usd->write = usd_FileWrite;
424 usd->seek = usd_FileSeek;
425 usd->ioctl = usd_FileIoctl;
426 usd->close = usd_FileDummyClose;
427 usd->fullPathName = "STDOUT";
428 usd->openFlags = 0;
429 *usdP = usd;
430
431 return 0;
432 }
433
434 int
435 usd_StandardOutput(usd_handle_t * usdP)
436 {
437 return usd_FileStandardOutput(usdP);
438 }