linux-2625-update-20080322
[openafs.git] / src / afs / LINUX / osi_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 RCSID
14     ("$Header$");
15
16 #ifdef AFS_LINUX24_ENV
17 #include "h/module.h" /* early to avoid printf->printk mapping */
18 #endif
19 #include "afs/sysincludes.h"    /* Standard vendor system headers */
20 #include "afsincludes.h"        /* Afs-based standard headers */
21 #include "afs/afs_stats.h"      /* afs statistics */
22 #include "h/smp_lock.h"
23 #if defined(AFS_LINUX26_ENV)
24 #include "h/namei.h"
25 #endif
26 #if !defined(HAVE_IGET)
27 #include "h/exportfs.h"
28 #endif
29
30 afs_lock_t afs_xosi;            /* lock is for tvattr */
31 extern struct osi_dev cacheDev;
32 #if defined(AFS_LINUX24_ENV)
33 extern struct vfsmount *afs_cacheMnt;
34 #endif
35 extern struct super_block *afs_cacheSBp;
36
37 #if defined(AFS_LINUX26_ENV) 
38 void *
39 osi_UFSOpen(afs_int32 ainode)
40 {
41     register struct osi_file *afile = NULL;
42     extern int cacheDiskType;
43     struct inode *tip = NULL;
44     struct dentry *dp = NULL;
45     struct file *filp = NULL;
46 #if !defined(HAVE_IGET)
47     struct fid fid;
48 #endif
49     AFS_STATCNT(osi_UFSOpen);
50     if (cacheDiskType != AFS_FCACHE_TYPE_UFS) {
51         osi_Panic("UFSOpen called for non-UFS cache\n");
52     }
53     if (!afs_osicred_initialized) {
54         /* valid for alpha_osf, SunOS, Ultrix */
55         memset((char *)&afs_osi_cred, 0, sizeof(struct AFS_UCRED));
56         crhold(&afs_osi_cred);  /* don't let it evaporate, since it is static */
57         afs_osicred_initialized = 1;
58     }
59     afile = (struct osi_file *)osi_AllocLargeSpace(sizeof(struct osi_file));
60     AFS_GUNLOCK();
61     if (!afile) {
62         osi_Panic("osi_UFSOpen: Failed to allocate %d bytes for osi_file.\n",
63                   sizeof(struct osi_file));
64     }
65     memset(afile, 0, sizeof(struct osi_file));
66 #if defined(HAVE_IGET)
67     tip = iget(afs_cacheSBp, (u_long) ainode);
68 #else
69     fid.i32.ino = ainode;
70     fid.i32.gen = 0;
71     dp = afs_cacheSBp->s_export_op->fh_to_dentry(afs_cacheSBp, &fid, sizeof(fid), FILEID_INO32_GEN);
72     if (!dp) 
73            osi_Panic("Can't get dentry for inode %d\n", ainode);          
74     tip = dp->d_inode;
75 #endif
76     tip->i_flags |= MS_NOATIME; /* Disable updating access times. */
77
78     filp = dentry_open(dp, mntget(afs_cacheMnt), O_RDWR);
79     if (IS_ERR(filp))
80         osi_Panic("Can't open inode %d\n", ainode);
81     afile->filp = filp;
82     afile->size = FILE_INODE(filp)->i_size;
83     AFS_GLOCK();
84     afile->offset = 0;
85     afile->proc = (int (*)())0;
86     afile->inum = ainode;       /* for hint validity checking */
87     return (void *)afile;
88 }
89 #else
90 void *
91 osi_UFSOpen(afs_int32 ainode)
92 {
93     register struct osi_file *afile = NULL;
94     extern int cacheDiskType;
95     afs_int32 code = 0;
96     struct inode *tip = NULL;
97     struct file *filp = NULL;
98     AFS_STATCNT(osi_UFSOpen);
99     if (cacheDiskType != AFS_FCACHE_TYPE_UFS) {
100         osi_Panic("UFSOpen called for non-UFS cache\n");
101     }
102     if (!afs_osicred_initialized) {
103         /* valid for alpha_osf, SunOS, Ultrix */
104         memset((char *)&afs_osi_cred, 0, sizeof(struct AFS_UCRED));
105         crhold(&afs_osi_cred);  /* don't let it evaporate, since it is static */
106         afs_osicred_initialized = 1;
107     }
108     afile = (struct osi_file *)osi_AllocLargeSpace(sizeof(struct osi_file));
109     AFS_GUNLOCK();
110     if (!afile) {
111         osi_Panic("osi_UFSOpen: Failed to allocate %d bytes for osi_file.\n",
112                   sizeof(struct osi_file));
113     }
114     memset(afile, 0, sizeof(struct osi_file));
115     filp = &afile->file;
116     filp->f_dentry = &afile->dentry;
117     tip = iget(afs_cacheSBp, (u_long) ainode);
118     if (!tip)
119         osi_Panic("Can't get inode %d\n", ainode);
120     FILE_INODE(filp) = tip;
121     tip->i_flags |= MS_NOATIME; /* Disable updating access times. */
122     filp->f_flags = O_RDWR;
123 #if defined(AFS_LINUX24_ENV)
124     filp->f_mode = FMODE_READ|FMODE_WRITE;
125     filp->f_op = fops_get(tip->i_fop);
126 #else
127     filp->f_op = tip->i_op->default_file_ops;
128 #endif
129     if (filp->f_op && filp->f_op->open)
130         code = filp->f_op->open(tip, filp);
131     if (code)
132         osi_Panic("Can't open inode %d\n", ainode);
133     afile->size = tip->i_size;
134     AFS_GLOCK();
135     afile->offset = 0;
136     afile->proc = (int (*)())0;
137     afile->inum = ainode;       /* for hint validity checking */
138     return (void *)afile;
139 }
140 #endif
141
142 int
143 afs_osi_Stat(register struct osi_file *afile, register struct osi_stat *astat)
144 {
145     register afs_int32 code;
146     AFS_STATCNT(osi_Stat);
147     MObtainWriteLock(&afs_xosi, 320);
148     astat->size = OSIFILE_INODE(afile)->i_size;
149 #if defined(AFS_LINUX26_ENV)
150     astat->mtime = OSIFILE_INODE(afile)->i_mtime.tv_sec;
151     astat->atime = OSIFILE_INODE(afile)->i_atime.tv_sec;
152 #else
153     astat->mtime = OSIFILE_INODE(afile)->i_mtime;
154     astat->atime = OSIFILE_INODE(afile)->i_atime;
155 #endif
156     code = 0;
157     MReleaseWriteLock(&afs_xosi);
158     return code;
159 }
160
161 #ifdef AFS_LINUX26_ENV
162 int
163 osi_UFSClose(register struct osi_file *afile)
164 {
165     AFS_STATCNT(osi_Close);
166     if (afile) {
167         if (OSIFILE_INODE(afile)) {
168             filp_close(afile->filp, NULL);
169         }
170     }
171
172     osi_FreeLargeSpace(afile);
173     return 0;
174 }
175 #else
176 int
177 osi_UFSClose(register struct osi_file *afile)
178 {
179     AFS_STATCNT(osi_Close);
180     if (afile) {
181         if (FILE_INODE(&afile->file)) {
182             struct file *filp = &afile->file;
183             if (filp->f_op && filp->f_op->release)
184                 filp->f_op->release(FILE_INODE(filp), filp);
185             iput(FILE_INODE(filp));
186         }
187     }
188
189     osi_FreeLargeSpace(afile);
190     return 0;
191 }
192 #endif
193
194 int
195 osi_UFSTruncate(register struct osi_file *afile, afs_int32 asize)
196 {
197     register afs_int32 code;
198     struct osi_stat tstat;
199     struct iattr newattrs;
200     struct inode *inode = OSIFILE_INODE(afile);
201     AFS_STATCNT(osi_Truncate);
202
203     /* This routine only shrinks files, and most systems
204      * have very slow truncates, even when the file is already
205      * small enough.  Check now and save some time.
206      */
207     code = afs_osi_Stat(afile, &tstat);
208     if (code || tstat.size <= asize)
209         return code;
210     MObtainWriteLock(&afs_xosi, 321);
211     AFS_GUNLOCK();
212 #ifdef STRUCT_INODE_HAS_I_ALLOC_SEM
213     down_write(&inode->i_alloc_sem);
214 #endif
215 #ifdef STRUCT_INODE_HAS_I_MUTEX
216     mutex_lock(&inode->i_mutex);
217 #else
218     down(&inode->i_sem);
219 #endif
220     newattrs.ia_size = asize;
221     newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
222 #if defined(AFS_LINUX24_ENV)
223     newattrs.ia_ctime = CURRENT_TIME;
224
225     /* avoid notify_change() since it wants to update dentry->d_parent */
226     lock_kernel();
227     code = inode_change_ok(inode, &newattrs);
228     if (!code)
229 #ifdef INODE_SETATTR_NOT_VOID
230         code = inode_setattr(inode, &newattrs);
231 #else
232         inode_setattr(inode, &newattrs);
233 #endif
234     unlock_kernel();
235     if (!code)
236         truncate_inode_pages(&inode->i_data, asize);
237 #else
238     inode->i_size = asize;
239     if (inode->i_sb->s_op && inode->i_sb->s_op->notify_change) {
240         code = inode->i_sb->s_op->notify_change(&afile->dentry, &newattrs);
241     }
242     if (!code) {
243         truncate_inode_pages(inode, asize);
244         if (inode->i_op && inode->i_op->truncate)
245             inode->i_op->truncate(inode);
246     }
247 #endif
248     code = -code;
249 #ifdef STRUCT_INODE_HAS_I_MUTEX
250     mutex_unlock(&inode->i_mutex);
251 #else
252     up(&inode->i_sem);
253 #endif
254 #ifdef STRUCT_INODE_HAS_I_ALLOC_SEM
255     up_write(&inode->i_alloc_sem);
256 #endif
257     AFS_GLOCK();
258     MReleaseWriteLock(&afs_xosi);
259     return code;
260 }
261
262
263 /* Generic read interface */
264 int
265 afs_osi_Read(register struct osi_file *afile, int offset, void *aptr,
266              afs_int32 asize)
267 {
268     struct uio auio;
269     struct iovec iov;
270     afs_int32 code;
271
272     AFS_STATCNT(osi_Read);
273
274     /*
275      * If the osi_file passed in is NULL, panic only if AFS is not shutting
276      * down. No point in crashing when we are already shutting down
277      */
278     if (!afile) {
279         if (!afs_shuttingdown)
280             osi_Panic("osi_Read called with null param");
281         else
282             return EIO;
283     }
284
285     if (offset != -1)
286         afile->offset = offset;
287     setup_uio(&auio, &iov, aptr, afile->offset, asize, UIO_READ, AFS_UIOSYS);
288     AFS_GUNLOCK();
289     code = osi_rdwr(afile, &auio, UIO_READ);
290     AFS_GLOCK();
291     if (code == 0) {
292         code = asize - auio.uio_resid;
293         afile->offset += code;
294     } else {
295         afs_Trace2(afs_iclSetp, CM_TRACE_READFAILED, ICL_TYPE_INT32, auio.uio_resid,
296                    ICL_TYPE_INT32, code);
297         code = -1;
298     }
299     return code;
300 }
301
302 /* Generic write interface */
303 int
304 afs_osi_Write(register struct osi_file *afile, afs_int32 offset, void *aptr,
305               afs_int32 asize)
306 {
307     struct uio auio;
308     struct iovec iov;
309     afs_int32 code;
310
311     AFS_STATCNT(osi_Write);
312
313     if (!afile) {
314         if (!afs_shuttingdown)
315             osi_Panic("afs_osi_Write called with null param");
316         else
317             return EIO;
318     }
319
320     if (offset != -1)
321         afile->offset = offset;
322     setup_uio(&auio, &iov, aptr, afile->offset, asize, UIO_WRITE, AFS_UIOSYS);
323     AFS_GUNLOCK();
324     code = osi_rdwr(afile, &auio, UIO_WRITE);
325     AFS_GLOCK();
326     if (code == 0) {
327         code = asize - auio.uio_resid;
328         afile->offset += code;
329     } else {
330         if (code == ENOSPC)
331             afs_warnuser
332                 ("\n\n\n*** Cache partition is FULL - Decrease cachesize!!! ***\n\n");
333         code = -1;
334     }
335
336     if (afile->proc)
337         (*afile->proc)(afile, code);
338
339     return code;
340 }
341
342
343 /*  This work should be handled by physstrat in ca/machdep.c.
344     This routine written from the RT NFS port strategy routine.
345     It has been generalized a bit, but should still be pretty clear. */
346 int
347 afs_osi_MapStrategy(int (*aproc) (struct buf * bp), register struct buf *bp)
348 {
349     afs_int32 returnCode;
350
351     AFS_STATCNT(osi_MapStrategy);
352     returnCode = (*aproc) (bp);
353
354     return returnCode;
355 }
356
357 void
358 shutdown_osifile(void)
359 {
360     AFS_STATCNT(shutdown_osifile);
361     if (afs_cold_shutdown) {
362         afs_osicred_initialized = 0;
363     }
364 }
365
366 /* Intialize cache device info and fragment size for disk cache partition. */
367 int
368 osi_InitCacheInfo(char *aname)
369 {
370     int code;
371     struct dentry *dp;
372     extern ino_t cacheInode;
373     extern struct osi_dev cacheDev;
374     extern afs_int32 afs_fsfragsize;
375     extern struct super_block *afs_cacheSBp;
376     extern struct vfsmount *afs_cacheMnt;
377     code = osi_lookupname_internal(aname, 1, &afs_cacheMnt, &dp);
378     if (code)
379         return ENOENT;
380
381     cacheInode = dp->d_inode->i_ino;
382     cacheDev.dev = dp->d_inode->i_sb->s_dev;
383     afs_fsfragsize = dp->d_inode->i_sb->s_blocksize - 1;
384     afs_cacheSBp = dp->d_inode->i_sb;
385
386     dput(dp);
387
388     return 0;
389 }
390
391
392 #define FOP_READ(F, B, C) (F)->f_op->read(F, B, (size_t)(C), &(F)->f_pos)
393 #define FOP_WRITE(F, B, C) (F)->f_op->write(F, B, (size_t)(C), &(F)->f_pos)
394
395 /* osi_rdwr
396  * seek, then read or write to an open inode. addrp points to data in
397  * kernel space.
398  */
399 int
400 osi_rdwr(struct osi_file *osifile, uio_t * uiop, int rw)
401 {
402 #ifdef AFS_LINUX26_ENV
403     struct file *filp = osifile->filp;
404 #else
405     struct file *filp = &osifile->file;
406 #endif
407     KERNEL_SPACE_DECL;
408     int code = 0;
409     struct iovec *iov;
410     afs_size_t count;
411     unsigned long savelim;
412
413     savelim = current->TASK_STRUCT_RLIM[RLIMIT_FSIZE].rlim_cur;
414     current->TASK_STRUCT_RLIM[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
415
416     if (uiop->uio_seg == AFS_UIOSYS)
417         TO_USER_SPACE();
418
419     /* seek to the desired position. Return -1 on error. */
420     if (filp->f_op->llseek) {
421         if (filp->f_op->llseek(filp, (loff_t) uiop->uio_offset, 0) != uiop->uio_offset)
422             return -1;
423     } else
424         filp->f_pos = uiop->uio_offset;
425
426     while (code == 0 && uiop->uio_resid > 0 && uiop->uio_iovcnt > 0) {
427         iov = uiop->uio_iov;
428         count = iov->iov_len;
429         if (count == 0) {
430             uiop->uio_iov++;
431             uiop->uio_iovcnt--;
432             continue;
433         }
434
435         if (rw == UIO_READ)
436             code = FOP_READ(filp, iov->iov_base, count);
437         else
438             code = FOP_WRITE(filp, iov->iov_base, count);
439
440         if (code < 0) {
441             code = -code;
442             break;
443         } else if (code == 0) {
444             /*
445              * This is bad -- we can't read any more data from the
446              * file, but we have no good way of signaling a partial
447              * read either.
448              */
449             code = EIO;
450             break;
451         }
452
453         iov->iov_base += code;
454         iov->iov_len -= code;
455         uiop->uio_resid -= code;
456         uiop->uio_offset += code;
457         code = 0;
458     }
459
460     if (uiop->uio_seg == AFS_UIOSYS)
461         TO_KERNEL_SPACE();
462
463     current->TASK_STRUCT_RLIM[RLIMIT_FSIZE].rlim_cur = savelim;
464
465     return code;
466 }
467
468 /* setup_uio 
469  * Setup a uio struct.
470  */
471 void
472 setup_uio(uio_t * uiop, struct iovec *iovecp, const char *buf, afs_offs_t pos,
473           int count, uio_flag_t flag, uio_seg_t seg)
474 {
475     iovecp->iov_base = (char *)buf;
476     iovecp->iov_len = count;
477     uiop->uio_iov = iovecp;
478     uiop->uio_iovcnt = 1;
479     uiop->uio_offset = pos;
480     uiop->uio_seg = seg;
481     uiop->uio_resid = count;
482     uiop->uio_flag = flag;
483 }
484
485
486 /* uiomove
487  * UIO_READ : dp -> uio
488  * UIO_WRITE : uio -> dp
489  */
490 int
491 uiomove(char *dp, int length, uio_flag_t rw, uio_t * uiop)
492 {
493     int count;
494     struct iovec *iov;
495     int code;
496
497     while (length > 0 && uiop->uio_resid > 0 && uiop->uio_iovcnt > 0) {
498         iov = uiop->uio_iov;
499         count = iov->iov_len;
500
501         if (!count) {
502             uiop->uio_iov++;
503             uiop->uio_iovcnt--;
504             continue;
505         }
506
507         if (count > length)
508             count = length;
509
510         switch (uiop->uio_seg) {
511         case AFS_UIOSYS:
512             switch (rw) {
513             case UIO_READ:
514                 memcpy(iov->iov_base, dp, count);
515                 break;
516             case UIO_WRITE:
517                 memcpy(dp, iov->iov_base, count);
518                 break;
519             default:
520                 printf("uiomove: Bad rw = %d\n", rw);
521                 return -EINVAL;
522             }
523             break;
524         case AFS_UIOUSER:
525             switch (rw) {
526             case UIO_READ:
527                 AFS_COPYOUT(dp, iov->iov_base, count, code);
528                 break;
529             case UIO_WRITE:
530                 AFS_COPYIN(iov->iov_base, dp, count, code);
531                 break;
532             default:
533                 printf("uiomove: Bad rw = %d\n", rw);
534                 return -EINVAL;
535             }
536             break;
537         default:
538             printf("uiomove: Bad seg = %d\n", uiop->uio_seg);
539             return -EINVAL;
540         }
541
542         dp += count;
543         length -= count;
544         iov->iov_base += count;
545         iov->iov_len -= count;
546         uiop->uio_offset += count;
547         uiop->uio_resid -= count;
548     }
549     return 0;
550 }
551