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