ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[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                        CF_LE_L(fsinfo->signature1), CF_LE_L(fsinfo->signature2),
73                        sbi->fsinfo_sector);
74         } else {
75                 if (sbi->free_clusters != -1)
76                         fsinfo->free_clusters = CF_LE_L(sbi->free_clusters);
77                 if (sbi->prev_free != -1)
78                         fsinfo->next_cluster = CF_LE_L(sbi->prev_free);
79                 mark_buffer_dirty(bh);
80         }
81         brelse(bh);
82 }
83
84 /*
85  * fat_add_cluster tries to allocate a new cluster and adds it to the
86  * file represented by inode.
87  */
88 int fat_add_cluster(struct inode *inode)
89 {
90         struct super_block *sb = inode->i_sb;
91         int ret, count, limit, new_dclus, new_fclus, last;
92         int cluster_bits = MSDOS_SB(sb)->cluster_bits;
93         
94         /* 
95          * We must locate the last cluster of the file to add this new
96          * one (new_dclus) to the end of the link list (the FAT).
97          *
98          * In order to confirm that the cluster chain is valid, we
99          * find out EOF first.
100          */
101         last = new_fclus = 0;
102         if (MSDOS_I(inode)->i_start) {
103                 int ret, fclus, dclus;
104
105                 ret = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus);
106                 if (ret < 0)
107                         return ret;
108                 new_fclus = fclus + 1;
109                 last = dclus;
110         }
111
112         /* find free FAT entry */
113         lock_fat(sb);
114         
115         if (MSDOS_SB(sb)->free_clusters == 0) {
116                 unlock_fat(sb);
117                 return -ENOSPC;
118         }
119
120         limit = MSDOS_SB(sb)->clusters + 2;
121         new_dclus = MSDOS_SB(sb)->prev_free + 1;
122         for (count = 0; count < MSDOS_SB(sb)->clusters; count++, new_dclus++) {
123                 new_dclus = new_dclus % limit;
124                 if (new_dclus < 2)
125                         new_dclus = 2;
126
127                 ret = fat_access(sb, new_dclus, -1);
128                 if (ret < 0) {
129                         unlock_fat(sb);
130                         return ret;
131                 } else if (ret == FAT_ENT_FREE)
132                         break;
133         }
134         if (count >= MSDOS_SB(sb)->clusters) {
135                 MSDOS_SB(sb)->free_clusters = 0;
136                 unlock_fat(sb);
137                 return -ENOSPC;
138         }
139
140         ret = fat_access(sb, new_dclus, FAT_ENT_EOF);
141         if (ret < 0) {
142                 unlock_fat(sb);
143                 return ret;
144         }
145
146         MSDOS_SB(sb)->prev_free = new_dclus;
147         if (MSDOS_SB(sb)->free_clusters != -1)
148                 MSDOS_SB(sb)->free_clusters--;
149         fat_clusters_flush(sb);
150         
151         unlock_fat(sb);
152
153         /* add new one to the last of the cluster chain */
154         if (last) {
155                 ret = fat_access(sb, last, new_dclus);
156                 if (ret < 0)
157                         return ret;
158                 fat_cache_add(inode, new_fclus, new_dclus);
159         } else {
160                 MSDOS_I(inode)->i_start = new_dclus;
161                 MSDOS_I(inode)->i_logstart = new_dclus;
162                 mark_inode_dirty(inode);
163         }
164         if (new_fclus != (inode->i_blocks >> (cluster_bits - 9))) {
165                 fat_fs_panic(sb, "clusters badly computed (%d != %lu)",
166                         new_fclus, inode->i_blocks >> (cluster_bits - 9));
167                 fat_cache_inval_inode(inode);
168         }
169         inode->i_blocks += MSDOS_SB(sb)->cluster_size >> 9;
170
171         return new_dclus;
172 }
173
174 struct buffer_head *fat_extend_dir(struct inode *inode)
175 {
176         struct super_block *sb = inode->i_sb;
177         struct buffer_head *bh, *res = NULL;
178         int nr, sec_per_clus = MSDOS_SB(sb)->sec_per_clus;
179         sector_t sector, last_sector;
180
181         if (MSDOS_SB(sb)->fat_bits != 32) {
182                 if (inode->i_ino == MSDOS_ROOT_INO)
183                         return ERR_PTR(-ENOSPC);
184         }
185
186         nr = fat_add_cluster(inode);
187         if (nr < 0)
188                 return ERR_PTR(nr);
189         
190         sector = ((sector_t)nr - 2) * sec_per_clus + MSDOS_SB(sb)->data_start;
191         last_sector = sector + sec_per_clus;
192         for ( ; sector < last_sector; sector++) {
193                 if ((bh = sb_getblk(sb, sector))) {
194                         memset(bh->b_data, 0, sb->s_blocksize);
195                         set_buffer_uptodate(bh);
196                         mark_buffer_dirty(bh);
197                         if (!res)
198                                 res = bh;
199                         else
200                                 brelse(bh);
201                 }
202         }
203         if (res == NULL)
204                 res = ERR_PTR(-EIO);
205         if (inode->i_size & (sb->s_blocksize - 1)) {
206                 fat_fs_panic(sb, "Odd directory size");
207                 inode->i_size = (inode->i_size + sb->s_blocksize)
208                         & ~((loff_t)sb->s_blocksize - 1);
209         }
210         inode->i_size += MSDOS_SB(sb)->cluster_size;
211         MSDOS_I(inode)->mmu_private += MSDOS_SB(sb)->cluster_size;
212
213         return res;
214 }
215
216 /* Linear day numbers of the respective 1sts in non-leap years. */
217
218 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
219                   /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
220
221
222 extern struct timezone sys_tz;
223
224
225 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
226
227 int date_dos2unix(unsigned short time,unsigned short date)
228 {
229         int month,year,secs;
230
231         /* first subtract and mask after that... Otherwise, if
232            date == 0, bad things happen */
233         month = ((date >> 5) - 1) & 15;
234         year = date >> 9;
235         secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
236             ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
237             month < 2 ? 1 : 0)+3653);
238                         /* days since 1.1.70 plus 80's leap day */
239         secs += sys_tz.tz_minuteswest*60;
240         return secs;
241 }
242
243
244 /* Convert linear UNIX date to a MS-DOS time/date pair. */
245
246 void fat_date_unix2dos(int unix_date,unsigned short *time,
247     unsigned short *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 = (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 = 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 }