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