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