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