added a lot of printk output to ease writing of emulator
[linux-2.4.21-pre4.git] / fs / read_write.c
1 /*
2  *  linux/fs/read_write.c
3  *
4  *  Copyright (C) 1991, 1992  Linus Torvalds
5  *  Minor pieces Copyright (C) 2002 Red Hat Inc, All Rights Reserved
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <linux/slab.h> 
23 #include <linux/stat.h>
24 #include <linux/fcntl.h>
25 #include <linux/file.h>
26 #include <linux/uio.h>
27 #include <linux/smp_lock.h>
28 #include <linux/dnotify.h>
29
30 #include <asm/uaccess.h>
31
32 struct file_operations generic_ro_fops = {
33         llseek:         generic_file_llseek,
34         read:           generic_file_read,
35         mmap:           generic_file_mmap,
36 };
37
38 ssize_t generic_read_dir(struct file *filp, char *buf, size_t siz, loff_t *ppos)
39 {
40         return -EISDIR;
41 }
42
43 loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
44 {
45         long long retval;
46
47         switch (origin) {
48                 case 2:
49                         offset += file->f_dentry->d_inode->i_size;
50                         break;
51                 case 1:
52                         offset += file->f_pos;
53         }
54         retval = -EINVAL;
55         if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) {
56                 if (offset != file->f_pos) {
57                         file->f_pos = offset;
58                         file->f_reada = 0;
59                         file->f_version = ++event;
60                 }
61                 retval = offset;
62         }
63         return retval;
64 }
65
66 loff_t no_llseek(struct file *file, loff_t offset, int origin)
67 {
68         return -ESPIPE;
69 }
70
71 loff_t default_llseek(struct file *file, loff_t offset, int origin)
72 {
73         long long retval;
74
75         switch (origin) {
76                 case 2:
77                         offset += file->f_dentry->d_inode->i_size;
78                         break;
79                 case 1:
80                         offset += file->f_pos;
81         }
82         retval = -EINVAL;
83         if (offset >= 0) {
84                 if (offset != file->f_pos) {
85                         file->f_pos = offset;
86                         file->f_reada = 0;
87                         file->f_version = ++event;
88                 }
89                 retval = offset;
90         }
91         return retval;
92 }
93
94 static inline loff_t llseek(struct file *file, loff_t offset, int origin)
95 {
96         loff_t (*fn)(struct file *, loff_t, int);
97         loff_t retval;
98
99         fn = default_llseek;
100         if (file->f_op && file->f_op->llseek)
101                 fn = file->f_op->llseek;
102         lock_kernel();
103         retval = fn(file, offset, origin);
104         unlock_kernel();
105         return retval;
106 }
107
108 asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
109 {
110         off_t retval;
111         struct file * file;
112
113         retval = -EBADF;
114         file = fget(fd);
115         if (!file)
116                 goto bad;
117         retval = -EINVAL;
118         if (origin <= 2) {
119                 loff_t res = llseek(file, offset, origin);
120                 retval = res;
121                 if (res != (loff_t)retval)
122                         retval = -EOVERFLOW;    /* LFS: should only happen on 32 bit platforms */
123         }
124         fput(file);
125 bad:
126         return retval;
127 }
128
129 #if !defined(__alpha__)
130 asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high,
131                            unsigned long offset_low, loff_t * result,
132                            unsigned int origin)
133 {
134         int retval;
135         struct file * file;
136         loff_t offset;
137
138         retval = -EBADF;
139         file = fget(fd);
140         if (!file)
141                 goto bad;
142         retval = -EINVAL;
143         if (origin > 2)
144                 goto out_putf;
145
146         offset = llseek(file, ((loff_t) offset_high << 32) | offset_low,
147                         origin);
148
149         retval = (int)offset;
150         if (offset >= 0) {
151                 retval = -EFAULT;
152                 if (!copy_to_user(result, &offset, sizeof(offset)))
153                         retval = 0;
154         }
155 out_putf:
156         fput(file);
157 bad:
158         return retval;
159 }
160 #endif
161
162 asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count)
163 {
164         ssize_t ret;
165         struct file * file;
166
167         ret = -EBADF;
168         file = fget(fd);
169         if (file) {
170                 if (file->f_mode & FMODE_READ) {
171                         ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode,
172                                                 file, file->f_pos, count);
173                         if (!ret) {
174                                 ssize_t (*read)(struct file *, char *, size_t, loff_t *);
175                                 ret = -EINVAL;
176                                 if (file->f_op && (read = file->f_op->read) != NULL)
177                                         ret = read(file, buf, count, &file->f_pos);
178                         }
179                 }
180                 if (ret > 0)
181                         dnotify_parent(file->f_dentry, DN_ACCESS);
182                 fput(file);
183         }
184         return ret;
185 }
186
187 asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
188 {
189         ssize_t ret;
190         struct file * file;
191
192         ret = -EBADF;
193         file = fget(fd);
194         if (file) {
195                 if (file->f_mode & FMODE_WRITE) {
196                         struct inode *inode = file->f_dentry->d_inode;
197                         ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file,
198                                 file->f_pos, count);
199                         if (!ret) {
200                                 ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
201                                 ret = -EINVAL;
202                                 if (file->f_op && (write = file->f_op->write) != NULL)
203                                         ret = write(file, buf, count, &file->f_pos);
204                         }
205                 }
206                 if (ret > 0)
207                         dnotify_parent(file->f_dentry, DN_MODIFY);
208                 fput(file);
209         }
210         return ret;
211 }
212
213
214 static ssize_t do_readv_writev(int type, struct file *file,
215                                const struct iovec * vector,
216                                unsigned long count)
217 {
218         typedef ssize_t (*io_fn_t)(struct file *, char *, size_t, loff_t *);
219         typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *);
220
221         size_t tot_len;
222         struct iovec iovstack[UIO_FASTIOV];
223         struct iovec *iov=iovstack;
224         ssize_t ret, i;
225         io_fn_t fn;
226         iov_fn_t fnv;
227         struct inode *inode;
228
229         /*
230          * First get the "struct iovec" from user memory and
231          * verify all the pointers
232          */
233         ret = 0;
234         if (!count)
235                 goto out_nofree;
236         ret = -EINVAL;
237         if (count > UIO_MAXIOV)
238                 goto out_nofree;
239         if (!file->f_op)
240                 goto out_nofree;
241         if (count > UIO_FASTIOV) {
242                 ret = -ENOMEM;
243                 iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL);
244                 if (!iov)
245                         goto out_nofree;
246         }
247         ret = -EFAULT;
248         if (copy_from_user(iov, vector, count*sizeof(*vector)))
249                 goto out;
250
251         /*
252          * Single unix specification:
253          * We should -EINVAL if an element length is not >= 0 and fitting an ssize_t
254          * The total length is fitting an ssize_t
255          *
256          * Be careful here because iov_len is a size_t not an ssize_t
257          */
258          
259         tot_len = 0;
260         ret = -EINVAL;
261         for (i = 0 ; i < count ; i++) {
262                 ssize_t len = (ssize_t) iov[i].iov_len;
263                 if (len < 0)    /* size_t not fitting an ssize_t .. */
264                         goto out;
265                 tot_len += len;
266                 /* We must do this work unsigned - signed overflow is
267                    undefined and gcc 3.2 now uses that fact sometimes... 
268                    
269                    FIXME: put in a proper limits.h for each platform */
270 #if BITS_PER_LONG==64
271                 if (tot_len > 0x7FFFFFFFFFFFFFFFUL)
272 #else
273                 if (tot_len > 0x7FFFFFFFUL)
274 #endif          
275                         goto out;
276         }
277
278         inode = file->f_dentry->d_inode;
279         /* VERIFY_WRITE actually means a read, as we write to user space */
280         ret = locks_verify_area((type == VERIFY_WRITE
281                                  ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE),
282                                 inode, file, file->f_pos, tot_len);
283         if (ret) goto out;
284
285         fnv = (type == VERIFY_WRITE ? file->f_op->readv : file->f_op->writev);
286         if (fnv) {
287                 ret = fnv(file, iov, count, &file->f_pos);
288                 goto out;
289         }
290
291         /* VERIFY_WRITE actually means a read, as we write to user space */
292         fn = (type == VERIFY_WRITE ? file->f_op->read :
293               (io_fn_t) file->f_op->write);
294
295         ret = 0;
296         vector = iov;
297         while (count > 0) {
298                 void * base;
299                 size_t len;
300                 ssize_t nr;
301
302                 base = vector->iov_base;
303                 len = vector->iov_len;
304                 vector++;
305                 count--;
306
307                 nr = fn(file, base, len, &file->f_pos);
308
309                 if (nr < 0) {
310                         if (!ret) ret = nr;
311                         break;
312                 }
313                 ret += nr;
314                 if (nr != len)
315                         break;
316         }
317
318 out:
319         if (iov != iovstack)
320                 kfree(iov);
321 out_nofree:
322         /* VERIFY_WRITE actually means a read, as we write to user space */
323         if ((ret + (type == VERIFY_WRITE)) > 0)
324                 dnotify_parent(file->f_dentry,
325                         (type == VERIFY_WRITE) ? DN_MODIFY : DN_ACCESS);
326         return ret;
327 }
328
329 asmlinkage ssize_t sys_readv(unsigned long fd, const struct iovec * vector,
330                              unsigned long count)
331 {
332         struct file * file;
333         ssize_t ret;
334
335
336         ret = -EBADF;
337         file = fget(fd);
338         if (!file)
339                 goto bad_file;
340         if (file->f_op && (file->f_mode & FMODE_READ) &&
341             (file->f_op->readv || file->f_op->read))
342                 ret = do_readv_writev(VERIFY_WRITE, file, vector, count);
343         fput(file);
344
345 bad_file:
346         return ret;
347 }
348
349 asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector,
350                               unsigned long count)
351 {
352         struct file * file;
353         ssize_t ret;
354
355
356         ret = -EBADF;
357         file = fget(fd);
358         if (!file)
359                 goto bad_file;
360         if (file->f_op && (file->f_mode & FMODE_WRITE) &&
361             (file->f_op->writev || file->f_op->write))
362                 ret = do_readv_writev(VERIFY_READ, file, vector, count);
363         fput(file);
364
365 bad_file:
366         return ret;
367 }
368
369 /* From the Single Unix Spec: pread & pwrite act like lseek to pos + op +
370    lseek back to original location.  They fail just like lseek does on
371    non-seekable files.  */
372
373 asmlinkage ssize_t sys_pread(unsigned int fd, char * buf,
374                              size_t count, loff_t pos)
375 {
376         ssize_t ret;
377         struct file * file;
378         ssize_t (*read)(struct file *, char *, size_t, loff_t *);
379
380         ret = -EBADF;
381         file = fget(fd);
382         if (!file)
383                 goto bad_file;
384         if (!(file->f_mode & FMODE_READ))
385                 goto out;
386         ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode,
387                                 file, pos, count);
388         if (ret)
389                 goto out;
390         ret = -EINVAL;
391         if (!file->f_op || !(read = file->f_op->read))
392                 goto out;
393         if (pos < 0)
394                 goto out;
395         ret = read(file, buf, count, &pos);
396         if (ret > 0)
397                 dnotify_parent(file->f_dentry, DN_ACCESS);
398 out:
399         fput(file);
400 bad_file:
401         return ret;
402 }
403
404 asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf,
405                               size_t count, loff_t pos)
406 {
407         ssize_t ret;
408         struct file * file;
409         ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
410
411         ret = -EBADF;
412         file = fget(fd);
413         if (!file)
414                 goto bad_file;
415         if (!(file->f_mode & FMODE_WRITE))
416                 goto out;
417         ret = locks_verify_area(FLOCK_VERIFY_WRITE, file->f_dentry->d_inode,
418                                 file, pos, count);
419         if (ret)
420                 goto out;
421         ret = -EINVAL;
422         if (!file->f_op || !(write = file->f_op->write))
423                 goto out;
424         if (pos < 0)
425                 goto out;
426
427         ret = write(file, buf, count, &pos);
428         if (ret > 0)
429                 dnotify_parent(file->f_dentry, DN_MODIFY);
430 out:
431         fput(file);
432 bad_file:
433         return ret;
434 }