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