libafs: Tidy up iovec allocation and trimming
[openafs.git] / src / afs / afs_osi_uio.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 "afs/sysincludes.h"    /* Standard vendor system headers */
15 #include "afsincludes.h"        /* Afs-based standard headers */
16 #include "afs/afs_stats.h"      /* statistics */
17 #include "afs/afs_cbqueue.h"
18 #include "afs/nfsclient.h"
19 #include "afs/afs_osidnlc.h"
20
21
22 /*
23  * UIO routines
24  */
25
26 #ifndef AFS_DARWIN80_ENV
27 /* routine to make copy of uio structure in ainuio, using aoutvec for space */
28 int
29 afsio_copy(struct uio *ainuio, struct uio *aoutuio,
30            struct iovec *aoutvec)
31 {
32     int i;
33     struct iovec *tvec;
34
35     AFS_STATCNT(afsio_copy);
36     if (ainuio->afsio_iovcnt > AFS_MAXIOVCNT)
37         return EINVAL;
38     memcpy((char *)aoutuio, (char *)ainuio, sizeof(struct uio));
39     tvec = ainuio->afsio_iov;
40     aoutuio->afsio_iov = aoutvec;
41     for (i = 0; i < ainuio->afsio_iovcnt; i++) {
42         memcpy((char *)aoutvec, (char *)tvec, sizeof(struct iovec));
43         tvec++;                 /* too many compiler bugs to do this as one expr */
44         aoutvec++;
45     }
46     return 0;
47 }
48
49 /* trim the uio structure to the specified size */
50 int
51 afsio_trim(struct uio *auio, afs_int32 asize)
52 {
53     int i;
54     struct iovec *tv;
55
56     AFS_STATCNT(afsio_trim);
57     auio->afsio_resid = asize;
58     tv = auio->afsio_iov;
59     /* It isn't clear that multiple iovecs work ok (hasn't been tested!) */
60     for (i = 0;; i++, tv++) {
61         if (i >= auio->afsio_iovcnt || asize <= 0) {
62             /* we're done */
63             auio->afsio_iovcnt = i;
64             break;
65         }
66         if (tv->iov_len <= asize)
67             /* entire iovec is included */
68             asize -= tv->iov_len;       /* this many fewer bytes */
69         else {
70             /* this is the last one */
71             tv->iov_len = asize;
72             auio->afsio_iovcnt = i + 1;
73             break;
74         }
75     }
76     return 0;
77 }
78
79 /* Allocate space for, then partially copy, over an existing iovec up to the
80  * length given in len.
81  *
82  * This requires that SmallSpace can alloc space big enough to hold a struct
83  * UIO, plus 16 iovecs
84  */
85
86 struct uio *
87 afsio_partialcopy(struct uio *auio, size_t len) {
88     char *space;
89     struct uio *newuio;
90     struct iovec *newvec;
91
92     /* Allocate a block that can contain both the UIO and the iovec */
93     space = osi_AllocSmallSpace(sizeof(struct uio) +
94                                 sizeof(struct iovec) * AFS_MAXIOVCNT);
95
96     newuio = (struct uio *) space;
97     newvec = (struct iovec *) (space + sizeof(struct uio));
98
99     afsio_copy(auio, newuio, newvec);
100     afsio_trim(newuio, len);
101
102     return newuio;
103 }
104
105 void
106 afsio_free(struct uio *uio) {
107     osi_FreeSmallSpace(uio);
108 }
109 #endif
110
111 /* skip asize bytes in the current uio structure */
112 int
113 afsio_skip(struct uio *auio, afs_int32 asize)
114 {
115     struct iovec *tv;   /* pointer to current iovec */
116     int cnt;
117
118     AFS_STATCNT(afsio_skip);
119 #ifdef AFS_DARWIN80_ENV
120     uio_update(auio, asize);
121 #else
122     /* It isn't guaranteed that multiple iovecs work ok (hasn't been tested!) */
123     while (asize > 0 && auio->afsio_resid) {
124         tv = auio->afsio_iov;
125         cnt = tv->iov_len;
126         if (cnt == 0) {
127             auio->afsio_iov++;
128             auio->afsio_iovcnt--;
129             continue;
130         }
131         if (cnt > asize)
132             cnt = asize;
133         tv->iov_base = (char *)(tv->iov_base) + cnt;
134         tv->iov_len -= cnt;
135         auio->uio_resid -= cnt;
136         auio->afsio_offset += cnt;
137         asize -= cnt;
138     }
139 #endif
140     return 0;
141 }