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