Use intptr_t and uintptr_t for integer/pointer conversions
[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 #include <afs/debug.h>
38 #include "usd.h"
39
40 #ifdef O_LARGEFILE
41 typedef off64_t osi_lloff_t;
42 #define osi_llseek      lseek64
43 #else /* O_LARGEFILE */
44 #ifdef AFS_HAVE_LLSEEK
45 typedef offset_t osi_lloff_t;
46 #define osi_llseek      llseek
47 #else /* AFS_HAVE_LLSEEK */
48 typedef off_t osi_lloff_t;
49 #define osi_llseek      lseek
50 #endif /* AFS_HAVE_LLSEEK */
51 #endif /* O_LARGEFILE */
52
53 /*
54  * This macro should be used inside assertions wherever offset/hyper
55  * conversion occur.  A good compiler in a 64 bit environment will
56  * elide the entire statement if the offset type is 64 bits wide.
57  */
58 #define osi_hFitsInOff(ahyper, noff) \
59     ((sizeof(noff) == 4) ? hfitsin32(ahyper) : 1)
60
61 #define osi_h2off(ahyper, noff)               \
62     ((sizeof(noff) == 4)                      \
63     ? ((noff) = (osi_lloff_t)hgetlo(ahyper))\
64      : ((noff) = ((osi_lloff_t)hgethi(ahyper)<<32) | (osi_lloff_t)hgetlo(ahyper)))
65
66 #define osi_off2h(noff, ahyper)            \
67      ((sizeof(noff) == 4)                   \
68      ? (hset32(ahyper, (int)noff)) \
69      : (hset64(ahyper, (int)((noff>>32)&0xffffffff), ((int)noff&0xffffffff))))
70
71
72 /************ End of osi wrappers ***********************************/
73
74 /* Implementation of user space device I/O for regular POSIX files. */
75
76 static int
77 usd_FileRead(usd_handle_t usd, char *buf, afs_uint32 nbytes,
78              afs_uint32 * xferdP)
79 {
80     int fd = (intptr_t)(usd->handle);
81     int got;
82
83     got = read(fd, buf, nbytes);
84     if (got == -1) {
85         if (xferdP)
86             *xferdP = 0;
87         return errno;
88     }
89     if (xferdP)
90         *xferdP = got;
91     return 0;
92 }
93
94 static int
95 usd_FileWrite(usd_handle_t usd, char *buf, afs_uint32 nbytes,
96               afs_uint32 * xferdP)
97 {
98     int fd = (intptr_t)(usd->handle);
99     int sent;
100
101     sent = write(fd, buf, nbytes);
102     if (sent == -1) {
103         if (xferdP)
104             *xferdP = 0;
105         return errno;
106     }
107     if (xferdP)
108         *xferdP = sent;
109     return 0;
110 }
111
112 extern osi_lloff_t osi_llseek(int, osi_lloff_t, int);
113
114 static int
115 usd_FileSeek(usd_handle_t usd, afs_hyper_t reqOff, int whence,
116              afs_hyper_t * curOffP)
117 {
118     int fd = (intptr_t)(usd->handle);
119     osi_lloff_t lloff;
120
121     if (!osi_hFitsInOff(reqOff, lloff))
122         return EINVAL;
123
124     osi_h2off(reqOff, lloff);
125     lloff = osi_llseek(fd, lloff, whence);
126     if (lloff == (((osi_lloff_t) 0) - 1))
127         return errno;
128     if (curOffP)
129         osi_off2h(lloff, *curOffP);
130
131     return 0;
132 }
133
134 static int
135 usd_FileIoctl(usd_handle_t usd, int req, void *arg)
136 {
137     int fd = (intptr_t)(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 #ifdef AFS_DARWIN100_ENV
215             code = EOPNOTSUPP;
216 #else
217             usd_tapeop_t *tapeOpp = (usd_tapeop_t *) arg;
218 #if defined(AFS_AIX_ENV)
219             struct stop os_tapeop;
220
221             if (tapeOpp->tp_op == USDTAPE_WEOF) {
222                 os_tapeop.st_op = STWEOF;
223             } else if (tapeOpp->tp_op == USDTAPE_REW) {
224                 os_tapeop.st_op = STREW;
225             } else if (tapeOpp->tp_op == USDTAPE_FSF) {
226                 os_tapeop.st_op = STFSF;
227             } else if (tapeOpp->tp_op == USDTAPE_BSF) {
228                 os_tapeop.st_op = STRSF;
229             } else if (tapeOpp->tp_op == USDTAPE_PREPARE) {
230                 return 0;
231             } else if (tapeOpp->tp_op == USDTAPE_SHUTDOWN) {
232                 return 0;
233             } else {
234                 /* unsupported tape operation */
235                 return EINVAL;
236             }
237             os_tapeop.st_count = tapeOpp->tp_count;
238
239             code = ioctl(fd, STIOCTOP, &os_tapeop);
240 #else
241             struct mtop os_tapeop;
242
243             if (tapeOpp->tp_op == USDTAPE_WEOF) {
244                 os_tapeop.mt_op = MTWEOF;
245             } else if (tapeOpp->tp_op == USDTAPE_REW) {
246                 os_tapeop.mt_op = MTREW;
247             } else if (tapeOpp->tp_op == USDTAPE_FSF) {
248                 os_tapeop.mt_op = MTFSF;
249             } else if (tapeOpp->tp_op == USDTAPE_BSF) {
250                 os_tapeop.mt_op = MTBSF;
251             } else if (tapeOpp->tp_op == USDTAPE_PREPARE) {
252                 return 0;
253             } else if (tapeOpp->tp_op == USDTAPE_SHUTDOWN) {
254                 return 0;
255             } else {
256                 /* unsupported tape operation */
257                 return EINVAL;
258             }
259             os_tapeop.mt_count = tapeOpp->tp_count;
260
261             code = ioctl(fd, MTIOCTOP, &os_tapeop);
262 #endif /* AFS_AIX_ENV */
263 #endif
264             if (code == -1) {
265                 code = errno;
266             } else {
267                 code = 0;
268             }
269             return code;
270         }
271
272     case USD_IOCTL_GETBLKSIZE:
273         if (S_ISCHR(info.st_mode) || S_ISBLK(info.st_mode)) {
274             *((long *)arg) = (long)4096;
275             return 0;
276         }
277 #ifdef AFS_AIX_ENV
278         *((long *)arg) = (long)fsinfo.f_bsize;
279 #else /* AFS_AIX_ENV */
280         *((long *)arg) = (long)info.st_blksize;
281 #endif /* AFS_AIX_ENV */
282         break;
283
284     default:
285         return EINVAL;
286     }
287     return code;
288 }
289
290 static int
291 usd_FileClose(usd_handle_t usd)
292 {
293     int fd = (intptr_t)(usd->handle);
294     int code = 0;
295     int ccode;
296
297     /* I can't really believe this is necessary.  On the one hand the user
298      * space code always uses character devices, which aren't supposed to do
299      * any buffering.  On the other, I very much doubt fsyncing a regular file
300      * being salvaged is ever necessary.  But the salvager used to do this
301      * before returning, so... */
302
303     if (usd->openFlags & (O_WRONLY | O_RDWR)) {
304         int mode;
305         code = usd_FileIoctl(usd, USD_IOCTL_GETTYPE, &mode);
306         if (code == 0 && S_ISBLK(mode)) {
307             if (fsync(fd) < 0)
308                 code = errno;
309         }
310     }
311
312     ccode = close(fd);
313     if (!code && ccode)
314         code = errno;
315
316     if (usd->fullPathName)
317         free(usd->fullPathName);
318     free(usd);
319
320     return code;
321 }
322
323 static int
324 usd_FileOpen(const char *path, int flags, int mode, 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 #ifdef O_SYNC                   /* AFS_DARWIN_ENV XXX */
337     if (flags & USD_OPEN_SYNC)
338         oflags |= O_SYNC;
339 #endif
340
341     if (flags & USD_OPEN_CREATE)
342         oflags |= O_CREAT;
343
344 #ifdef O_LARGEFILE
345     fd = open64(path, oflags | O_LARGEFILE, mode);
346 #else /* O_LARGEFILE */
347     fd = open(path, oflags, mode);
348 #endif /* O_LARGEFILE */
349     if (fd == -1)
350         return errno;
351
352     usd = (usd_handle_t) malloc(sizeof(*usd));
353     memset(usd, 0, sizeof(*usd));
354     usd->handle = (void *)(intptr_t)fd;
355     usd->read = usd_FileRead;
356     usd->write = usd_FileWrite;
357     usd->seek = usd_FileSeek;
358     usd->ioctl = usd_FileIoctl;
359     usd->close = usd_FileClose;
360     usd->fullPathName = (char *)malloc(strlen(path) + 1);
361     strcpy(usd->fullPathName, path);
362     usd->openFlags = flags;
363
364     code = 0;
365     if (flags & (USD_OPEN_RLOCK | USD_OPEN_WLOCK)) {
366 #ifdef O_LARGEFILE
367         struct flock64 fl;
368 #else /* O_LARGEFILE */
369         struct flock fl;
370 #endif /* O_LARGEFILE */
371
372         /* make sure both lock bits aren't set */
373         assert(~flags & (USD_OPEN_RLOCK | USD_OPEN_WLOCK));
374
375         fl.l_type = ((flags & USD_OPEN_RLOCK) ? F_RDLCK : F_WRLCK);
376         fl.l_whence = SEEK_SET;
377         fl.l_start = (osi_lloff_t) 0;
378         fl.l_len = (osi_lloff_t) 0;     /* whole file */
379 #ifdef O_LARGEFILE
380         code = fcntl(fd, F_SETLK64, &fl);
381 #else /* O_LARGEFILE */
382         code = fcntl(fd, F_SETLK, &fl);
383 #endif /* O_LARGEFILE */
384         if (code == -1)
385             code = errno;
386
387         /* If we're trying to obtain a write lock on a real disk, then the
388          * aggregate must not be attached by the kernel.  If so, unlock it
389          * and fail. 
390          * WARNING: The code to check for the above has been removed when this
391          * file was ported from DFS src. It should be put back if
392          * this library is used to access hard disks 
393          */
394     }
395
396     if (code == 0 && usdP)
397         *usdP = usd;
398     else
399         usd_FileClose(usd);
400     return code;
401 }
402
403 static int
404 usd_FileDummyClose(usd_handle_t usd)
405 {
406     free(usd);
407     return 0;
408 }
409
410 int
411 usd_Open(const char *path, int oflag, int mode, usd_handle_t * usdP)
412 {
413     return usd_FileOpen(path, oflag, mode, usdP);
414 }
415
416 static int
417 usd_FileStandardInput(usd_handle_t * usdP)
418 {
419     usd_handle_t usd;
420
421     if (usdP)
422         *usdP = NULL;
423
424     usd = (usd_handle_t) malloc(sizeof(*usd));
425     memset(usd, 0, sizeof(*usd));
426     usd->handle = (void *)((unsigned long)0);
427     usd->read = usd_FileRead;
428     usd->write = usd_FileWrite;
429     usd->seek = usd_FileSeek;
430     usd->ioctl = usd_FileIoctl;
431     usd->close = usd_FileDummyClose;
432     usd->fullPathName = "STDIN";
433     usd->openFlags = 0;
434     *usdP = usd;
435
436     return 0;
437 }
438
439 int
440 usd_StandardInput(usd_handle_t * usdP)
441 {
442     return usd_FileStandardInput(usdP);
443 }
444
445 static int
446 usd_FileStandardOutput(usd_handle_t * usdP)
447 {
448     usd_handle_t usd;
449
450     if (usdP)
451         *usdP = NULL;
452
453     usd = (usd_handle_t) malloc(sizeof(*usd));
454     memset(usd, 0, sizeof(*usd));
455     usd->handle = (void *)((unsigned long)1);
456     usd->read = usd_FileRead;
457     usd->write = usd_FileWrite;
458     usd->seek = usd_FileSeek;
459     usd->ioctl = usd_FileIoctl;
460     usd->close = usd_FileDummyClose;
461     usd->fullPathName = "STDOUT";
462     usd->openFlags = 0;
463     *usdP = usd;
464
465     return 0;
466 }
467
468 int
469 usd_StandardOutput(usd_handle_t * usdP)
470 {
471     return usd_FileStandardOutput(usdP);
472 }