Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / afs / LINUX / osi_misc.c
1 /* Copyright 1998 Transarc Corporation - All Rights Reserved.
2  *
3  * osi_misc.c
4  *
5  * Linux support routines.
6  *
7  */
8 #include "../afs/param.h"
9 #include "../afs/sysincludes.h"
10 #include "../afs/afsincludes.h"
11 #include "../afs/afs_stats.h"
12
13 char *crash_addr = 0; /* Induce an oops by writing here. */
14
15 /* Lookup name and return vnode for same. */
16 int osi_lookupname(char *aname, uio_seg_t seg, int followlink,
17                vnode_t **dirvpp, struct dentry **dpp)
18 {
19     struct dentry *dp = NULL;
20     int code;
21
22     code = ENOENT;
23     if (seg == AFS_UIOUSER) {
24         dp = followlink ? namei(aname) : lnamei(aname);
25     }
26     else {
27         dp = lookup_dentry(aname, NULL, followlink ? 1 : 0);
28     }
29     
30     if (dp && !IS_ERR(dp)) {
31         if (dp->d_inode) {
32             *dpp = dp;
33             code = 0;
34         }
35         else
36             dput(dp);
37     }
38             
39     return code;
40 }
41
42 /* Intialize cache device info and fragment size for disk cache partition. */
43 int osi_InitCacheInfo(char *aname)
44 {
45     int code;
46     struct dentry *dp;
47     extern ino_t cacheInode;
48     extern struct osi_dev cacheDev;
49     extern afs_int32 afs_fsfragsize;
50     extern struct super_block *afs_cacheSBp;
51
52     code = osi_lookupname(aname, AFS_UIOSYS, 1, NULL, &dp);
53     if (code) return ENOENT;
54
55     cacheInode = dp->d_inode->i_ino;
56     cacheDev.dev = dp->d_inode->i_dev;
57     afs_fsfragsize = dp->d_inode->i_sb->s_blocksize;
58     afs_cacheSBp = dp->d_inode->i_sb;
59
60     dput(dp);
61
62     return 0;
63 }
64
65
66 #define FOP_READ(F, B, C) (F)->f_op->read(F, B, (size_t)(C), &(F)->f_pos)
67 #define FOP_WRITE(F, B, C) (F)->f_op->write(F, B, (size_t)(C), &(F)->f_pos)
68
69 /* osi_rdwr
70  * Seek, then read or write to an open inode. addrp points to data in
71  * kernel space.
72  */
73 int osi_rdwr(int rw, struct osi_file *file, caddr_t addrp, size_t asize,
74              size_t *resid)
75 {
76     int code = 0;
77     KERNEL_SPACE_DECL;
78     struct file *filp = &file->file;
79     off_t offset = file->offset;
80
81     /* Seek to the desired position. Return -1 on error. */
82     if (filp->f_op->llseek) {
83         if (filp->f_op->llseek(filp, (loff_t)offset, 0) != offset)
84             return -1;
85     }
86     else
87         filp->f_pos = offset;
88
89     /* Read/Write the data. */
90     TO_USER_SPACE();
91     if (rw == UIO_READ)
92         code = FOP_READ(filp, addrp, asize);
93     else if (rw == UIO_WRITE)
94         code = FOP_WRITE(filp, addrp, asize);
95     else /* all is well? */
96         code = asize;
97     TO_KERNEL_SPACE();
98
99     if (code >=0) {
100         *resid = asize - code;
101         return 0;
102     }
103     else
104         return -1;
105 }
106
107 /* This variant is called from AFS read/write routines and takes a uio
108  * struct and, if successful, returns 0.
109  */
110 int osi_file_uio_rdwr(struct osi_file *osifile, uio_t *uiop, int rw)
111 {
112     struct file *filp = &osifile->file;
113     struct inode *ip = FILE_INODE(&osifile->file);
114     KERNEL_SPACE_DECL;
115     int code = 0;
116     struct iovec *iov;
117     int count;
118
119     if (uiop->uio_seg == AFS_UIOSYS)
120         TO_USER_SPACE();
121
122     filp->f_pos = uiop->uio_offset;
123     while (code == 0 && uiop->uio_resid > 0 && uiop->uio_iovcnt > 0) {
124         iov = uiop->uio_iov;
125         count = iov->iov_len;
126         if (count == 0) {
127             uiop->uio_iov++;
128             uiop->uio_iovcnt--;
129             continue;
130         }
131         
132         if (rw == UIO_READ)
133             code = FOP_READ(filp, iov->iov_base, count);
134         else
135             code = FOP_WRITE(filp, iov->iov_base, count);
136
137         if (code < 0) {
138             code = -code;
139             break;
140         }
141         
142         iov->iov_base += code;
143         iov->iov_len -= code;
144         uiop->uio_resid -= code;
145         uiop->uio_offset += code;
146         code = 0;
147     }
148
149     if (uiop->uio_seg == AFS_UIOSYS)
150         TO_KERNEL_SPACE();
151
152     return code;
153 }
154
155 /* setup_uio 
156  * Setup a uio struct.
157  */
158 void setup_uio(uio_t *uiop, struct iovec *iovecp, char *buf,
159                              int pos, int count, uio_flag_t flag,
160                              uio_seg_t seg)
161 {
162     iovecp->iov_base = buf;
163     iovecp->iov_len = count;
164     uiop->uio_iov = iovecp;
165     uiop->uio_iovcnt = 1;
166     uiop->uio_offset = pos;
167     uiop->uio_seg = seg;
168     uiop->uio_resid = count;
169     uiop->uio_flag = flag;
170 }
171
172
173 /* uiomove
174  * UIO_READ : dp -> uio
175  * UIO_WRITE : uio -> dp
176  */
177 int uiomove(char *dp, int length, uio_flag_t rw, uio_t *uiop)
178 {
179     int count, n;
180     struct iovec *iov;
181     int code;
182
183     while (length > 0 && uiop->uio_resid > 0 && uiop->uio_iovcnt > 0) {
184         iov = uiop->uio_iov;
185         count = iov->iov_len;
186
187         if (!count) {
188             uiop->uio_iov++;
189             uiop->uio_iovcnt--;
190             continue;
191         }
192
193         if (count > length)
194             count = length;
195         
196         switch(uiop->uio_seg) {
197         case AFS_UIOSYS:
198             switch(rw) {
199             case UIO_READ:
200                 memcpy(iov->iov_base, dp, count); break;
201             case UIO_WRITE:
202                 memcpy(dp, iov->iov_base, count); break;
203             default:
204                 printf("uiomove: Bad rw = %d\n", rw);
205                 return -EINVAL;
206             }
207             break;
208         case AFS_UIOUSER:
209             switch(rw) {
210             case UIO_READ:
211                 AFS_COPYOUT(dp, iov->iov_base, count, code); break;
212             case UIO_WRITE:
213                 AFS_COPYIN(iov->iov_base, dp, count, code); break;
214             default:
215                 printf("uiomove: Bad rw = %d\n", rw);
216                 return -EINVAL;
217             }
218             break;
219         default:
220             printf("uiomove: Bad seg = %d\n", uiop->uio_seg);
221             return -EINVAL;
222         }
223
224         dp += count;
225         length -= count;
226         iov->iov_base += count;
227         iov->iov_len -= count;
228         uiop->uio_offset += count;
229         uiop->uio_resid -= count;
230     }
231     return 0;
232 }
233
234 void afs_osi_SetTime(osi_timeval_t *tvp)
235 {
236     extern int (*sys_settimeofdayp)(struct timeval *tv, struct timezone *tz);
237     KERNEL_SPACE_DECL;
238
239     AFS_STATCNT(osi_SetTime);
240
241     TO_USER_SPACE();
242     (void) (*sys_settimeofdayp)(tvp, NULL);
243     TO_KERNEL_SPACE();
244 }
245
246 /* Free all the pages on any of the vnodes in the vlru. Must be done before
247  * freeing all memory.
248  */
249 void osi_linux_free_inode_pages(void)
250 {
251     int i;
252     struct vcache *tvc;
253     struct inode *ip;
254     extern struct vcache *afs_vhashT[VCSIZE];
255
256     for (i=0; i<VCSIZE; i++) {
257         for(tvc = afs_vhashT[i]; tvc; tvc=tvc->hnext) {
258             ip = (struct inode*)tvc;
259             if (ip->i_nrpages) {
260                 invalidate_inode_pages(ip);
261                 if (ip->i_nrpages) {
262                     printf("Failed to invalidate all pages on inode 0x%x\n",
263                            ip);
264                 }
265             }
266         }
267     }
268 }
269
270 /* iput an inode. Since we still have a separate inode pool, we don't want
271  * to call iput on AFS inodes, since they would then end up on Linux's
272  * inode_unsed list.
273  */
274 void osi_iput(struct inode *ip)
275 {
276     extern void afs_delete_inode(struct inode *ip);
277     extern struct vfs *afs_globalVFS;
278
279     
280     if (ip->i_count == 0 || ip->i_count & 0xffff0000) {
281         osi_Panic("IPUT Bad refCount %d on inode 0x%x\n",
282                   ip->i_count, ip);
283     }
284     if (afs_globalVFS && afs_globalVFS == ip->i_sb ) {
285         ip->i_count --;
286         if (!ip->i_count)
287             afs_delete_inode(ip);
288     }
289     else { 
290         iput(ip);
291     }
292 }
293
294 /* check_bad_parent() : Checks if this dentry's vcache is a root vcache
295  * that has its mvid (parent dir's fid) pointer set to the wrong directory
296  * due to being mounted in multiple points at once. If so, check_bad_parent()
297  * calls afs_lookup() to correct the vcache's mvid, as well as the volume's
298  * dotdotfid and mtpoint fid members.
299  * Parameters:
300  *  dp - dentry to be checked.
301  * Return Values:
302  *  None.
303  * Sideeffects:
304  *   This dentry's vcache's mvid will be set to the correct parent directory's
305  *   fid.
306  *   This root vnode's volume will have its dotdotfid and mtpoint fids set
307  *    to the correct parent and mountpoint fids.
308  */
309
310 void check_bad_parent(struct dentry *dp)
311 {
312   cred_t *credp;
313   struct vcache *vcp = (struct vcache*)dp->d_inode, *avc = NULL;
314   struct vcache *pvc = (struct vcache *)dp->d_parent->d_inode;
315
316   if (vcp->mvid->Fid.Volume != pvc->fid.Fid.Volume) { /* bad parent */
317     credp = crref();
318
319
320     /* force a lookup, so vcp->mvid is fixed up */
321     afs_lookup(pvc, dp->d_name.name, &avc, credp);
322     if (!avc || vcp != avc) {    /* bad, very bad.. */
323       afs_Trace4(afs_iclSetp, CM_TRACE_TMP_1S3L, ICL_TYPE_STRING,
324                  "afs_linux_revalidate : bad pointer returned from afs_lookup origvc newvc dentry",
325                  ICL_TYPE_POINTER, vcp,
326                  ICL_TYPE_POINTER, avc,
327                  ICL_TYPE_POINTER, dp);
328     }
329     
330   } /* if bad parent */
331  
332   return;
333 }