--- linux/fs/msdos/misc.c 2018/04/24 18:12:17 1.1 +++ linux/fs/msdos/misc.c 2018/04/24 18:14:17 1.1.1.2 @@ -34,11 +34,16 @@ int is_binary(char conversion,char *exte if (!strncmp(extension,walk,3)) return 1; return 0; default: - panic("Invalid conversion mode"); + printk("Invalid conversion mode - defaulting to " + "binary.\n"); + return 1; } } +/* File creation lock. This is system-wide to avoid deadlocks in rename. */ +/* (rename might deadlock before detecting cross-FS moves.) */ + static struct wait_queue *creation_wait = NULL; static creation_lock = 0; @@ -57,6 +62,20 @@ void unlock_creation(void) } +void lock_fat(struct super_block *sb) +{ + while (MSDOS_SB(sb)->fat_lock) sleep_on(&MSDOS_SB(sb)->fat_wait); + MSDOS_SB(sb)->fat_lock = 1; +} + + +void unlock_fat(struct super_block *sb) +{ + MSDOS_SB(sb)->fat_lock = 0; + wake_up(&MSDOS_SB(sb)->fat_wait); +} + + int msdos_add_cluster(struct inode *inode) { static struct wait_queue *wait = NULL; @@ -67,8 +86,10 @@ int msdos_add_cluster(struct inode *inod struct buffer_head *bh; if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC; + if (!MSDOS_SB(inode->i_sb)->free_clusters) return -ENOSPC; while (lock) sleep_on(&wait); lock = 1; + lock_fat(inode->i_sb); limit = MSDOS_SB(inode->i_sb)->clusters; this = limit; /* to keep GCC happy */ for (count = 0; count < limit; count++) { @@ -76,57 +97,56 @@ int msdos_add_cluster(struct inode *inod if (fat_access(inode->i_sb,this,-1) == 0) break; } #ifdef DEBUG -printk("free cluster: %d\r\n",this); +printk("free cluster: %d\n",this); #endif previous = (count+previous+1) % limit; if (count >= limit) { + MSDOS_SB(inode->i_sb)->free_clusters = 0; + unlock_fat(inode->i_sb); lock = 0; wake_up(&wait); return -ENOSPC; } fat_access(inode->i_sb,this,MSDOS_SB(inode->i_sb)->fat_bits == 12 ? 0xff8 : 0xfff8); + if (MSDOS_SB(inode->i_sb)->free_clusters != -1) + MSDOS_SB(inode->i_sb)->free_clusters--; + unlock_fat(inode->i_sb); lock = 0; wake_up(&wait); #ifdef DEBUG -printk("set to %x\r\n",fat_access(inode->i_sb,this,-1)); +printk("set to %x\n",fat_access(inode->i_sb,this,-1)); #endif - if (!S_ISDIR(inode->i_mode)) { - last = inode->i_size ? get_cluster(inode,(inode->i_size-1)/ - SECTOR_SIZE/MSDOS_SB(inode->i_sb)->cluster_size) : 0; - } - else { - last = 0; - if (current = inode->i_data[D_START]) { - cache_lookup(inode,0x7fffffff,&last,¤t); - while (current && current != -1) - if (!(current = fat_access(inode->i_sb, - last = current,-1))) - panic("File without EOF"); - } + last = 0; + if (current = MSDOS_I(inode)->i_start) { + cache_lookup(inode,0x7fffffff,&last,¤t); + while (current && current != -1) + if (!(current = fat_access(inode->i_sb, + last = current,-1))) + panic("File without EOF"); } #ifdef DEBUG -printk("last = %d\r\n",last); +printk("last = %d\n",last); #endif if (last) fat_access(inode->i_sb,last,this); else { - inode->i_data[D_START] = this; + MSDOS_I(inode)->i_start = this; inode->i_dirt = 1; } #ifdef DEBUG -if (last) printk("next set to %d\r\n",fat_access(inode->i_sb,last,-1)); +if (last) printk("next set to %d\n",fat_access(inode->i_sb,last,-1)); #endif for (current = 0; current < MSDOS_SB(inode->i_sb)->cluster_size; current++) { sector = MSDOS_SB(inode->i_sb)->data_start+(this-2)* MSDOS_SB(inode->i_sb)->cluster_size+current; #ifdef DEBUG -printk("zeroing sector %d\r\n",sector); +printk("zeroing sector %d\n",sector); #endif if (current < MSDOS_SB(inode->i_sb)->cluster_size-1 && !(sector & 1)) { if (!(bh = getblk(inode->i_dev,sector >> 1, BLOCK_SIZE))) - printk("getblk failed\r\n"); + printk("getblk failed\n"); else { memset(bh->b_data,0,BLOCK_SIZE); bh->b_uptodate = 1; @@ -135,7 +155,7 @@ printk("zeroing sector %d\r\n",sector); } else { if (!(bh = msdos_sread(inode->i_dev,sector,&data))) - printk("msdos_sread failed\r\n"); + printk("msdos_sread failed\n"); else memset(data,0,SECTOR_SIZE); } if (bh) { @@ -143,13 +163,14 @@ printk("zeroing sector %d\r\n",sector); brelse(bh); } } + inode->i_blocks++; if (S_ISDIR(inode->i_mode)) { if (inode->i_size & (SECTOR_SIZE-1)) panic("Odd directory size"); inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->i_sb)-> cluster_size; #ifdef DEBUG -printk("size is %d now (%x)\r\n",inode->i_size,inode); +printk("size is %d now (%x)\n",inode->i_size,inode); #endif inode->i_dirt = 1; } @@ -163,18 +184,23 @@ static int day_n[] = { 0,31,59,90,120,15 /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ +extern struct timezone sys_tz; + + /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ int date_dos2unix(unsigned short time,unsigned short date) { - int month,year; + int month,year,secs; month = ((date >> 5) & 15)-1; year = date >> 9; - return (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400* + secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400* ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && month < 2 ? 1 : 0)+3653); /* days since 1.1.70 plus 80's leap day */ + secs += sys_tz.tz_minuteswest*60; + return secs; } @@ -185,6 +211,7 @@ void date_unix2dos(int unix_date,unsigne { int day,year,nl_day,month; + unix_date -= sys_tz.tz_minuteswest*60; *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ (((unix_date/3600) % 24) << 11); day = unix_date/86400-3652; @@ -216,15 +243,17 @@ int msdos_get_entry(struct inode *dir,in while (1) { offset = *pos; - if ((sector = msdos_smap(dir,*pos >> SECTOR_BITS)) == -1) + if ((sector = msdos_smap(dir,offset >> SECTOR_BITS)) == -1) return -1; if (!sector) return -1; /* FAT error ... */ *pos += sizeof(struct msdos_dir_entry); if (*bh) brelse(*bh); - if (!(*bh = msdos_sread(dir->i_dev,sector,&data))) + if (!(*bh = msdos_sread(dir->i_dev,sector,&data))) { + printk("Directory sread (sector %d) failed\n",sector); continue; + } *de = (struct msdos_dir_entry *) (data+(offset & (SECTOR_SIZE-1))); return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >> @@ -254,7 +283,7 @@ int msdos_scan(struct inode *dir,char *n else if (!de->name[0] || ((unsigned char *) (de->name))[0] == DELETED_FLAG) { if (!(inode = iget(dir->i_dev,*ino))) break; - if (!inode->i_data[D_BUSY]) { + if (!MSDOS_I(inode)->i_busy) { iput(inode); break; } @@ -274,32 +303,47 @@ int msdos_scan(struct inode *dir,char *n /* Now an ugly part: this set of directory scan routines works on clusters rather than on inodes and sectors. They are necessary to locate the '..' - directory "inode". */ + directory "inode". raw_found operates in three modes: if name is non-NULL, + the directory is scanned for an entry with that name. If ino is non-NULL, + the directory is scanned for an entry whose data starts at *number. If name + and ino are NULL, the directory entries are counted in *number. */ -static int raw_found(struct super_block *sb,int sector,char *name,int number, +static int raw_found(struct super_block *sb,int sector,char *name,int *number, int *ino) { struct buffer_head *bh; struct msdos_dir_entry *data; - int entry,start; + int entry,start,done; if (!(bh = msdos_sread(sb->s_dev,sector,(void **) &data))) return -EIO; - for (entry = 0; entry < MSDOS_DPS; entry++) - if (name ? !strncmp(data[entry].name,name,MSDOS_NAME) : - *(unsigned char *) data[entry].name != DELETED_FLAG && - data[entry].start == number) { + for (entry = 0; entry < MSDOS_DPS; entry++) { + if (name) done = !strncmp(data[entry].name,name,MSDOS_NAME); + else { + if (ino) + done = *(unsigned char *) data[entry].name != + DELETED_FLAG && data[entry].start == + *number; + else { + done = 0; + if (*data[entry].name && *(unsigned char *) + data[entry].name != DELETED_FLAG && + (data[entry].attr & ATTR_DIR)) (*number)++; + } + } + if (done) { if (ino) *ino = sector*MSDOS_DPS+entry; start = data[entry].start; brelse(bh); return start; } + } brelse(bh); return -1; } -static int raw_scan_root(struct super_block *sb,char *name,int number,int *ino) +static int raw_scan_root(struct super_block *sb,char *name,int *number,int *ino) { int count,cluster; @@ -312,10 +356,13 @@ static int raw_scan_root(struct super_bl static int raw_scan_nonroot(struct super_block *sb,int start,char *name, - int number,int *ino) + int *number,int *ino) { int count,cluster; +#ifdef DEBUG + printk("raw_scan_nonroot: start=%d\n",start); +#endif do { for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) { if ((cluster = raw_found(sb,(start-2)*MSDOS_SB(sb)-> @@ -323,6 +370,9 @@ static int raw_scan_nonroot(struct super number,ino)) >= 0) return cluster; } if (!(start = fat_access(sb,start,-1))) panic("FAT error"); +#ifdef DEBUG + printk("next start: %d\n",start); +#endif } while (start != -1); return -ENOENT; @@ -332,8 +382,8 @@ static int raw_scan_nonroot(struct super static int raw_scan(struct super_block *sb,int start,char *name,int number, int *ino) { - if (start) return raw_scan_nonroot(sb,start,name,number,ino); - else return raw_scan_root(sb,name,number,ino); + if (start) return raw_scan_nonroot(sb,start,name,&number,ino); + else return raw_scan_root(sb,name,&number,ino); } @@ -344,7 +394,7 @@ int msdos_parent_ino(struct inode *dir,i if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i"); if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino; if (!locked) lock_creation(); /* prevent renames */ - if ((current = raw_scan(dir->i_sb,dir->i_data[D_START],MSDOS_DOTDOT,0, + if ((current = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,0, NULL)) < 0) { if (!locked) unlock_creation(); return current; @@ -364,3 +414,19 @@ int msdos_parent_ino(struct inode *dir,i if (!locked) unlock_creation(); return this; } + + +int msdos_subdirs(struct inode *dir) +{ + int count; + + count = 0; + if (dir->i_ino == MSDOS_ROOT_INO) + (void) raw_scan_root(dir->i_sb,NULL,&count,NULL); + else { + if (!MSDOS_I(dir)->i_start) return 0; /* in mkdir */ + else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start, + NULL,&count,NULL); + } + return count; +}