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