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