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