1 /*
2 * linux/fs/msdos/file.c
3 *
4 * Written 1992,1993 by Werner Almesberger
5 *
6 * MS-DOS regular file handling primitives
7 */
8
9 #include <asm/segment.h>
10 #include <asm/system.h>
11
12 #include <linux/sched.h>
13 #include <linux/locks.h>
14 #include <linux/fs.h>
15 #include <linux/msdos_fs.h>
16 #include <linux/errno.h>
17 #include <linux/fcntl.h>
18 #include <linux/stat.h>
19 #include <linux/string.h>
20
21 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
22 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
23
24 #define PRINTK(x)
25
26 static struct file_operations msdos_file_operations = {
27 NULL, /* lseek - default */
28 msdos_file_read, /* read */
29 msdos_file_write, /* write */
30 NULL, /* readdir - bad */
31 NULL, /* select - default */
32 NULL, /* ioctl - default */
33 generic_mmap, /* mmap */
34 NULL, /* no special open is needed */
35 NULL, /* release */
36 file_fsync /* fsync */
37 };
38
39 struct inode_operations msdos_file_inode_operations = {
40 &msdos_file_operations, /* default file operations */
41 NULL, /* create */
42 NULL, /* lookup */
43 NULL, /* link */
44 NULL, /* unlink */
45 NULL, /* symlink */
46 NULL, /* mkdir */
47 NULL, /* rmdir */
48 NULL, /* mknod */
49 NULL, /* rename */
50 NULL, /* readlink */
51 NULL, /* follow_link */
52 msdos_bmap, /* bmap */
53 msdos_truncate, /* truncate */
54 NULL, /* permission */
55 NULL /* smap */
56 };
57
58 /*
59 Read a file into user space
60 */
61 int msdos_file_read(
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
62 struct inode *inode,
63 struct file *filp,
64 char *buf,
65 int count)
66 {
67 char *start;
68 int left,offset,size,cnt;
69 struct {
70 int to_reada; /* How many block to read all at once */
71 struct buffer_head *bhreq[64]; /* Buffers not already read */
72 int nbreq; /* Number of buffers to read */
73 struct buffer_head *bhlist[64]; /* All buffers needed */
74 int nblist; /* Number of buffers in bhlist */
75 int nolist;
76 }pre;
77 int i;
78
79
80 if (!inode) {
81 printk("msdos_file_read: inode = NULL\n");
82 return -EINVAL;
83 }
84 /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
85 if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
86 printk("msdos_file_read: mode = %07o\n",inode->i_mode);
87 return -EINVAL;
88 }
89 if (filp->f_pos >= inode->i_size || count <= 0) return 0;
90 /*
91 Tell the buffer cache which block we expect to read in advance
92 Since we are limited with the stack, we preread only 64
93 because we have to keep the result into the local
94 arrays pre.bhlist and pre.bhreq.
95 */
96 {
97 int file_sector = filp->f_pos >> SECTOR_BITS;
98 pre.to_reada = count / SECTOR_SIZE;
99 if (filp->f_reada){
100 int min_read = read_ahead[MAJOR(inode->i_dev)];
101 if (min_read > pre.to_reada) pre.to_reada = min_read;
102 }
103 if (pre.to_reada > 64) pre.to_reada = 64;
104 pre.nbreq = pre.nblist = 0;
105 for (i=0; i<pre.to_reada; i++){
106 int sector;
107 struct buffer_head *bh;
108 if (!(sector = msdos_smap(inode,file_sector++))) break;
109 bh = getblk(inode->i_dev,sector,SECTOR_SIZE);
110 if (bh == NULL) break;
111 pre.bhlist[pre.nblist++] = bh;
112 if (!bh->b_uptodate){
113 pre.bhreq[pre.nbreq++] = bh;
114 }
115 }
116 if (pre.nbreq > 0) ll_rw_block (READ,pre.nbreq,pre.bhreq);
117 }
118 start = buf;
119 pre.nolist = 0;
120 while ((left = MIN(inode->i_size-filp->f_pos,count-(buf-start))) > 0){
121 struct buffer_head *bh;
122 void *data;
123 PRINTK (("file_read pos %d\n",filp->f_pos));
124 if (pre.nolist >= pre.nblist){
125 /* This code is executed when more than 64 sectors */
126 /* are request at once */
127 int sector;
128 if (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS)))
129 break;
130 if (!(bh = msdos_sread(inode->i_dev,sector)))
131 break;
132 }else{
133 bh = pre.bhlist[pre.nolist];
134 pre.bhlist[pre.nolist++] = NULL;
135 wait_on_buffer(bh);
136 if (!bh->b_uptodate){
137 /* read error ? */
138 brelse (bh);
139 break;
140 }
141 }
142 data = bh->b_data;
143 offset = filp->f_pos & (SECTOR_SIZE-1);
144 filp->f_pos += (size = MIN(SECTOR_SIZE-offset,left));
145 if (MSDOS_I(inode)->i_binary) {
146 memcpy_tofs(buf,data+offset,size);
147 buf += size;
148 }
149 else for (cnt = size; cnt; cnt--) {
150 char ch;
151 if ((ch = *((char *) data+offset++)) == '\r')
152 size--;
153 else {
154 if (ch != 26) put_fs_byte(ch,buf++);
155 else {
156 filp->f_pos = inode->i_size;
157 brelse(bh);
158 break;
159 }
160 }
161 }
162 brelse(bh);
163 }
164 for (i=0; i<pre.nblist; i++) brelse (pre.bhlist[i]);
165 if (start == buf) return -EIO;
166 if (!IS_RDONLY(inode))
167 inode->i_atime = CURRENT_TIME;
168 PRINTK (("file_read ret %d\n",(buf-start)));
169 filp->f_reada = 1; /* Will be reset if a lseek is done */
170 return buf-start;
171 }
172
173 /*
174 Write to a file either from user space
175 */
176 int msdos_file_write(
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
177 struct inode *inode,
178 struct file *filp,
179 char *buf,
180 int count)
181 {
182 int sector,offset,size,left,written;
183 int error,carry;
184 char *start,*to,ch;
185 struct buffer_head *bh;
186 int binary_mode = MSDOS_I(inode)->i_binary;
187
188 if (!inode) {
189 printk("msdos_file_write: inode = NULL\n");
190 return -EINVAL;
191 }
192 /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
193 if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
194 printk("msdos_file_write: mode = %07o\n",inode->i_mode);
195 return -EINVAL;
196 }
197 /*
198 * ok, append may not work when many processes are writing at the same time
199 * but so what. That way leads to madness anyway.
200 */
201 if (filp->f_flags & O_APPEND) filp->f_pos = inode->i_size;
202 if (count <= 0) return 0;
203 error = carry = 0;
204 for (start = buf; count || carry; count -= size) {
205 while (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS)))
206 if ((error = msdos_add_cluster(inode)) < 0) break;
207 if (error) {
208 msdos_truncate(inode);
209 break;
210 }
211 offset = filp->f_pos & (SECTOR_SIZE-1);
212 size = MIN(SECTOR_SIZE-offset,MAX(carry,count));
213 if (binary_mode
214 && offset == 0
215 && (size == SECTOR_SIZE
216 || filp->f_pos + size >= inode->i_size)){
217 /* No need to read the block first since we will */
218 /* completely overwrite it */
219 /* or at least write past the end of file */
220 if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE))){
221 error = -EIO;
222 break;
223 }
224 }else if (!(bh = msdos_sread(inode->i_dev,sector))) {
225 error = -EIO;
226 break;
227 }
228 if (binary_mode) {
229 memcpy_fromfs(bh->b_data+offset,buf,written = size);
230 buf += size;
231 }
232 else {
233 written = left = SECTOR_SIZE-offset;
234 to = (char *) bh->b_data+(filp->f_pos & (SECTOR_SIZE-1));
235 if (carry) {
236 *to++ = '\n';
237 left--;
238 carry = 0;
239 }
240 for (size = 0; size < count && left; size++) {
241 if ((ch = get_fs_byte(buf++)) == '\n') {
242 *to++ = '\r';
243 left--;
244 }
245 if (!left) carry = 1;
246 else {
247 *to++ = ch;
248 left--;
249 }
250 }
251 written -= left;
252 }
253 filp->f_pos += written;
254 if (filp->f_pos > inode->i_size) {
255 inode->i_size = filp->f_pos;
256 inode->i_dirt = 1;
257 }
258 bh->b_uptodate = 1;
259 mark_buffer_dirty(bh, 0);
260 brelse(bh);
261 }
262 if (start == buf)
263 return error;
264 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
265 MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
266 inode->i_dirt = 1;
267 return buf-start;
268 }
269
270 void msdos_truncate(struct inode *inode)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
271 {
272 int cluster;
273
274 cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size;
275 (void) fat_free(inode,(inode->i_size+(cluster-1))/cluster);
276 MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
277 inode->i_dirt = 1;
278 }