This commit was generated by cvs2svn to compensate for changes in r517,
[linux-2.6.git] / fs / fat / misc.c
1 /*
2  *  linux/fs/fat/misc.c
3  *
4  *  Written 1992,1993 by Werner Almesberger
5  *  22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
6  *               and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
7  */
8
9 #include <linux/fs.h>
10 #include <linux/msdos_fs.h>
11 #include <linux/buffer_head.h>
12
13 /*
14  * fat_fs_panic reports a severe file system problem and sets the file system
15  * read-only. The file system can be made writable again by remounting it.
16  */
17
18 static char panic_msg[512];
19
20 void fat_fs_panic(struct super_block *s, const char *fmt, ...)
21 {
22         int not_ro;
23         va_list args;
24
25         va_start (args, fmt);
26         vsnprintf (panic_msg, sizeof(panic_msg), fmt, args);
27         va_end (args);
28
29         not_ro = !(s->s_flags & MS_RDONLY);
30         if (not_ro)
31                 s->s_flags |= MS_RDONLY;
32
33         printk(KERN_ERR "FAT: Filesystem panic (dev %s)\n"
34                "    %s\n", s->s_id, panic_msg);
35         if (not_ro)
36                 printk(KERN_ERR "    File system has been set read-only\n");
37 }
38
39 void lock_fat(struct super_block *sb)
40 {
41         down(&(MSDOS_SB(sb)->fat_lock));
42 }
43
44 void unlock_fat(struct super_block *sb)
45 {
46         up(&(MSDOS_SB(sb)->fat_lock));
47 }
48
49 /* Flushes the number of free clusters on FAT32 */
50 /* XXX: Need to write one per FSINFO block.  Currently only writes 1 */
51 void fat_clusters_flush(struct super_block *sb)
52 {
53         struct msdos_sb_info *sbi = MSDOS_SB(sb);
54         struct buffer_head *bh;
55         struct fat_boot_fsinfo *fsinfo;
56
57         if (sbi->fat_bits != 32)
58                 return;
59
60         bh = sb_bread(sb, sbi->fsinfo_sector);
61         if (bh == NULL) {
62                 printk(KERN_ERR "FAT bread failed in fat_clusters_flush\n");
63                 return;
64         }
65
66         fsinfo = (struct fat_boot_fsinfo *)bh->b_data;
67         /* Sanity check */
68         if (!IS_FSINFO(fsinfo)) {
69                 printk(KERN_ERR "FAT: Did not find valid FSINFO signature.\n"
70                        "     Found signature1 0x%08x signature2 0x%08x"
71                        " (sector = %lu)\n",
72                        le32_to_cpu(fsinfo->signature1),
73                        le32_to_cpu(fsinfo->signature2),
74                        sbi->fsinfo_sector);
75         } else {
76                 if (sbi->free_clusters != -1)
77                         fsinfo->free_clusters = cpu_to_le32(sbi->free_clusters);
78                 if (sbi->prev_free != -1)
79                         fsinfo->next_cluster = cpu_to_le32(sbi->prev_free);
80                 mark_buffer_dirty(bh);
81         }
82         brelse(bh);
83 }
84
85 /*
86  * fat_add_cluster tries to allocate a new cluster and adds it to the
87  * file represented by inode.
88  */
89 int fat_add_cluster(struct inode *inode)
90 {
91         struct super_block *sb = inode->i_sb;
92         int ret, count, limit, new_dclus, new_fclus, last;
93         int cluster_bits = MSDOS_SB(sb)->cluster_bits;
94         
95         /* 
96          * We must locate the last cluster of the file to add this new
97          * one (new_dclus) to the end of the link list (the FAT).
98          *
99          * In order to confirm that the cluster chain is valid, we
100          * find out EOF first.
101          */
102         last = new_fclus = 0;
103         if (MSDOS_I(inode)->i_start) {
104                 int ret, fclus, dclus;
105
106                 ret = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus);
107                 if (ret < 0)
108                         return ret;
109                 new_fclus = fclus + 1;
110                 last = dclus;
111         }
112
113         /* find free FAT entry */
114         lock_fat(sb);
115         
116         if (MSDOS_SB(sb)->free_clusters == 0) {
117                 unlock_fat(sb);
118                 return -ENOSPC;
119         }
120
121         limit = MSDOS_SB(sb)->clusters + 2;
122         new_dclus = MSDOS_SB(sb)->prev_free + 1;
123         for (count = 0; count < MSDOS_SB(sb)->clusters; count++, new_dclus++) {
124                 new_dclus = new_dclus % limit;
125                 if (new_dclus < 2)
126                         new_dclus = 2;
127
128                 ret = fat_access(sb, new_dclus, -1);
129                 if (ret < 0) {
130                         unlock_fat(sb);
131                         return ret;
132                 } else if (ret == FAT_ENT_FREE)
133                         break;
134         }
135         if (count >= MSDOS_SB(sb)->clusters) {
136                 MSDOS_SB(sb)->free_clusters = 0;
137                 unlock_fat(sb);
138                 return -ENOSPC;
139         }
140
141         ret = fat_access(sb, new_dclus, FAT_ENT_EOF);
142         if (ret < 0) {
143                 unlock_fat(sb);
144                 return ret;
145         }
146
147         MSDOS_SB(sb)->prev_free = new_dclus;
148         if (MSDOS_SB(sb)->free_clusters != -1)
149                 MSDOS_SB(sb)->free_clusters--;
150         fat_clusters_flush(sb);
151         
152         unlock_fat(sb);
153
154         /* add new one to the last of the cluster chain */
155         if (last) {
156                 ret = fat_access(sb, last, new_dclus);
157                 if (ret < 0)
158                         return ret;
159 //              fat_cache_add(inode, new_fclus, new_dclus);
160         } else {
161                 MSDOS_I(inode)->i_start = new_dclus;
162                 MSDOS_I(inode)->i_logstart = new_dclus;
163                 mark_inode_dirty(inode);
164         }
165         if (new_fclus != (inode->i_blocks >> (cluster_bits - 9))) {
166                 fat_fs_panic(sb, "clusters badly computed (%d != %lu)",
167                         new_fclus, inode->i_blocks >> (cluster_bits - 9));
168                 fat_cache_inval_inode(inode);
169         }
170         inode->i_blocks += MSDOS_SB(sb)->cluster_size >> 9;
171
172         return new_dclus;
173 }
174
175 struct buffer_head *fat_extend_dir(struct inode *inode)
176 {
177         struct super_block *sb = inode->i_sb;
178         struct buffer_head *bh, *res = NULL;
179         int nr, sec_per_clus = MSDOS_SB(sb)->sec_per_clus;
180         sector_t sector, last_sector;
181
182         if (MSDOS_SB(sb)->fat_bits != 32) {
183                 if (inode->i_ino == MSDOS_ROOT_INO)
184                         return ERR_PTR(-ENOSPC);
185         }
186
187         nr = fat_add_cluster(inode);
188         if (nr < 0)
189                 return ERR_PTR(nr);
190         
191         sector = ((sector_t)nr - 2) * sec_per_clus + MSDOS_SB(sb)->data_start;
192         last_sector = sector + sec_per_clus;
193         for ( ; sector < last_sector; sector++) {
194                 if ((bh = sb_getblk(sb, sector))) {
195                         memset(bh->b_data, 0, sb->s_blocksize);
196                         set_buffer_uptodate(bh);
197                         mark_buffer_dirty(bh);
198                         if (!res)
199                                 res = bh;
200                         else
201                                 brelse(bh);
202                 }
203         }
204         if (res == NULL)
205                 res = ERR_PTR(-EIO);
206         if (inode->i_size & (sb->s_blocksize - 1)) {
207                 fat_fs_panic(sb, "Odd directory size");
208                 inode->i_size = (inode->i_size + sb->s_blocksize)
209                         & ~((loff_t)sb->s_blocksize - 1);
210         }
211         inode->i_size += MSDOS_SB(sb)->cluster_size;
212         MSDOS_I(inode)->mmu_private += MSDOS_SB(sb)->cluster_size;
213
214         return res;
215 }
216
217 /* Linear day numbers of the respective 1sts in non-leap years. */
218
219 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
220                   /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
221
222
223 extern struct timezone sys_tz;
224
225
226 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
227
228 int date_dos2unix(unsigned short time,unsigned short date)
229 {
230         int month,year,secs;
231
232         /* first subtract and mask after that... Otherwise, if
233            date == 0, bad things happen */
234         month = ((date >> 5) - 1) & 15;
235         year = date >> 9;
236         secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
237             ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
238             month < 2 ? 1 : 0)+3653);
239                         /* days since 1.1.70 plus 80's leap day */
240         secs += sys_tz.tz_minuteswest*60;
241         return secs;
242 }
243
244
245 /* Convert linear UNIX date to a MS-DOS time/date pair. */
246
247 void fat_date_unix2dos(int unix_date,__le16 *time, __le16 *date)
248 {
249         int day,year,nl_day,month;
250
251         unix_date -= sys_tz.tz_minuteswest*60;
252
253         /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
254         if (unix_date < 315532800)
255                 unix_date = 315532800;
256
257         *time = cpu_to_le16((unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
258             (((unix_date/3600) % 24) << 11));
259         day = unix_date/86400-3652;
260         year = day/365;
261         if ((year+3)/4+365*year > day) year--;
262         day -= (year+3)/4+365*year;
263         if (day == 59 && !(year & 3)) {
264                 nl_day = day;
265                 month = 2;
266         }
267         else {
268                 nl_day = (year & 3) || day <= 59 ? day : day-1;
269                 for (month = 0; month < 12; month++)
270                         if (day_n[month] > nl_day) break;
271         }
272         *date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9));
273 }
274
275
276 /* Returns the inode number of the directory entry at offset pos. If bh is
277    non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
278    returned in bh.
279    AV. Most often we do it item-by-item. Makes sense to optimize.
280    AV. OK, there we go: if both bh and de are non-NULL we assume that we just
281    AV. want the next entry (took one explicit de=NULL in vfat/namei.c).
282    AV. It's done in fat_get_entry() (inlined), here the slow case lives.
283    AV. Additionally, when we return -1 (i.e. reached the end of directory)
284    AV. we make bh NULL. 
285  */
286
287 int fat__get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
288                    struct msdos_dir_entry **de, loff_t *i_pos)
289 {
290         struct super_block *sb = dir->i_sb;
291         struct msdos_sb_info *sbi = MSDOS_SB(sb);
292         sector_t phys, iblock;
293         loff_t offset;
294         int err;
295
296 next:
297         offset = *pos;
298         if (*bh)
299                 brelse(*bh);
300
301         *bh = NULL;
302         iblock = *pos >> sb->s_blocksize_bits;
303         err = fat_bmap(dir, iblock, &phys);
304         if (err || !phys)
305                 return -1;      /* beyond EOF or error */
306
307         *bh = sb_bread(sb, phys);
308         if (*bh == NULL) {
309                 printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n",
310                        (unsigned long long)phys);
311                 /* skip this block */
312                 *pos = (iblock + 1) << sb->s_blocksize_bits;
313                 goto next;
314         }
315
316         offset &= sb->s_blocksize - 1;
317         *pos += sizeof(struct msdos_dir_entry);
318         *de = (struct msdos_dir_entry *)((*bh)->b_data + offset);
319         *i_pos = ((loff_t)phys << sbi->dir_per_block_bits) + (offset >> MSDOS_DIR_BITS);
320
321         return 0;
322 }