* SA E200
* SA E200i
* SA E500
+ * SA P700m
* SA P212
* SA P410
* SA P410i
* SA P411
* SA P812
+ * SA P712m
+ * SA P711m
Detecting drive failures:
-------------------------
Works. Use "Insert file..." or external editor.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Gmail (Web GUI)
+
+If you just have to use Gmail to send patches, it CAN be made to work. It
+requires a bit of external help, though.
+
+The first problem is that Gmail converts tabs to spaces. This will
+totally break your patches. To prevent this, you have to use a different
+editor. There is a firefox extension called "ViewSourceWith"
+(https://addons.mozilla.org/en-US/firefox/addon/394) which allows you to
+edit any text box in the editor of your choice. Configure it to launch
+your favorite editor. When you want to send a patch, use this technique.
+Once you have crafted your messsage + patch, save and exit the editor,
+which should reload the Gmail edit box. GMAIL WILL PRESERVE THE TABS.
+Hoorah. Apparently you can cut-n-paste literal tabs, but Gmail will
+convert those to spaces upon sending!
+
+The second problem is that Gmail converts tabs to spaces on replies. If
+you reply to a patch, don't expect to be able to apply it as a patch.
+
+The last problem is that Gmail will base64-encode any message that has a
+non-ASCII character. That includes things like European names. Be aware.
+
+Gmail is not convenient for lkml patches, but CAN be made to work.
+
###
VFAT MOUNT OPTIONS
----------------------------------------------------------------------
+uid=### -- Set the owner of all files on this filesystem.
+ The default is the uid of current process.
+
+gid=### -- Set the group of all files on this filesystem.
+ The default is the gid of current process.
+
umask=### -- The permission mask (for files and directories, see umask(1)).
The default is the umask of current process.
characters on FAT filesystem.
By default, FAT_DEFAULT_CODEPAGE setting is used.
-iocharset=name -- Character set to use for converting between the
+iocharset=<name> -- Character set to use for converting between the
encoding is used for user visible filename and 16 bit
Unicode characters. Long filenames are stored on disk
in Unicode format, but Unix for the most part doesn't
r: relaxed, case insensitive
n: normal, default setting, currently case insensitive
+nocase -- This was deprecated for vfat. Use shortname=win95 instead.
+
shortname=lower|win95|winnt|mixed
-- Shortname display/create setting.
lower: convert to lowercase for display,
tz=UTC -- Interpret timestamps as UTC rather than local time.
This option disables the conversion of timestamps
between local time (as used by Windows on FAT) and UTC
- (which Linux uses internally). This is particuluarly
+ (which Linux uses internally). This is particularly
useful when mounting devices (like digital cameras)
that are set to UTC in order to avoid the pitfalls of
local time.
+showexec -- If set, the execute permission bits of the file will be
+ allowed only if the extension part of the name is .EXE,
+ .COM, or .BAT. Not set by default.
+
+debug -- Can be set, but unused by the current implementation.
+
+sys_immutable -- If set, ATTR_SYS attribute on FAT is handled as
+ IMMUTABLE flag on Linux. Not set by default.
+
+flush -- If set, the filesystem will try to flush to disk more
+ early than normal. Not set by default.
+
+rodir -- FAT has the ATTR_RO (read-only) attribute. But on Windows,
+ the ATTR_RO of the directory will be just ignored actually,
+ and is used by only applications as flag. E.g. it's setted
+ for the customized folder.
+
+ If you want to use ATTR_RO as read-only flag even for
+ the directory, set this option.
+
<bool>: 0,1,yes,no,true,false
TODO
Format:
<cpu number>,...,<cpu number>
or
- <cpu number>-<cpu number> (must be a positive range in ascending order)
+ <cpu number>-<cpu number>
+ (must be a positive range in ascending order)
or a mixture
<cpu number>,...,<cpu number>-<cpu number>
+
This option can be used to specify one or more CPUs
to isolate from the general SMP balancing and scheduling
- algorithms. The only way to move a process onto or off
- an "isolated" CPU is via the CPU affinity syscalls.
+ algorithms. You can move a process onto or off an
+ "isolated" CPU via the CPU affinity syscalls or cpuset.
<cpu number> begins at 0 and the maximum value is
"number of CPUs in system - 1".
W: http://xf.iksaif.net/acpi4asus
S: Maintained
-ASYNCHRONOUS TRANSFERS/TRANSFORMS API
+ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
P: Dan Williams
M: dan.j.williams@intel.com
P: Maciej Sosnowski
#endif
-/* sched_domains SD_NODE_INIT for NUMAQ machines */
+/* sched_domains SD_NODE_INIT for NUMA machines */
#define SD_NODE_INIT (struct sched_domain) { \
.min_interval = 8, \
.max_interval = 32, \
.flags = SD_LOAD_BALANCE \
| SD_BALANCE_EXEC \
| SD_BALANCE_FORK \
- | SD_SERIALIZE \
- | SD_WAKE_BALANCE, \
+ | SD_WAKE_AFFINE \
+ | SD_WAKE_BALANCE \
+ | SD_SERIALIZE, \
.last_balance = jiffies, \
.balance_interval = 1, \
}
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3245},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3247},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3249},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324A},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324B},
{PCI_VENDOR_ID_HP, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_RAID << 8, 0xffff << 8, 0},
{0,}
{0x3245103C, "Smart Array P410i", &SA5_access},
{0x3247103C, "Smart Array P411", &SA5_access},
{0x3249103C, "Smart Array P812", &SA5_access},
+ {0x324A103C, "Smart Array P712m", &SA5_access},
+ {0x324B103C, "Smart Array P711m", &SA5_access},
{0xFFFF103C, "Unknown Smart Array", &SA5_access},
};
disk->first_minor = drv_index << NWD_SHIFT;
disk->fops = &cciss_fops;
disk->private_data = &h->drv[drv_index];
+ disk->driverfs_dev = &h->pdev->dev;
/* Set up queue information */
blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask);
int i;
int j = 0;
int rc;
- int dac;
+ int dac, return_code;
+ InquiryData_struct *inq_buff = NULL;
i = alloc_cciss_hba();
if (i < 0)
/* Turn the interrupts on so we can service requests */
hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON);
+ /* Get the firmware version */
+ inq_buff = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL);
+ if (inq_buff == NULL) {
+ printk(KERN_ERR "cciss: out of memory\n");
+ goto clean4;
+ }
+
+ return_code = sendcmd_withirq(CISS_INQUIRY, i, inq_buff,
+ sizeof(InquiryData_struct), 0, 0 , 0, TYPE_CMD);
+ if (return_code == IO_OK) {
+ hba[i]->firm_ver[0] = inq_buff->data_byte[32];
+ hba[i]->firm_ver[1] = inq_buff->data_byte[33];
+ hba[i]->firm_ver[2] = inq_buff->data_byte[34];
+ hba[i]->firm_ver[3] = inq_buff->data_byte[35];
+ } else { /* send command failed */
+ printk(KERN_WARNING "cciss: unable to determine firmware"
+ " version of controller\n");
+ }
+
cciss_procinit(i);
hba[i]->cciss_max_sectors = 2048;
return 1;
clean4:
+ kfree(inq_buff);
#ifdef CONFIG_CISS_SCSI_TAPE
kfree(hba[i]->scsi_rejects.complete);
#endif
num_cntlrs_reg++;
}
- return(num_cntlrs_reg);
+ if (num_cntlrs_reg)
+ return 0;
+ else {
+ pci_unregister_driver(&cpqarray_pci_driver);
+ return -ENODEV;
+ }
}
/* Function to find the first free pointer into our hba[] array */
vc->vc_tab_stop[1] =
vc->vc_tab_stop[2] =
vc->vc_tab_stop[3] =
- vc->vc_tab_stop[4] = 0x01010101;
+ vc->vc_tab_stop[4] =
+ vc->vc_tab_stop[5] =
+ vc->vc_tab_stop[6] =
+ vc->vc_tab_stop[7] = 0x01010101;
vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
vc->vc_bell_duration = DEFAULT_BELL_DURATION;
vc->vc_tab_stop[1] =
vc->vc_tab_stop[2] =
vc->vc_tab_stop[3] =
- vc->vc_tab_stop[4] = 0;
+ vc->vc_tab_stop[4] =
+ vc->vc_tab_stop[5] =
+ vc->vc_tab_stop[6] =
+ vc->vc_tab_stop[7] = 0;
}
return;
case 'm':
/* Set 9: Macbook Pro 3,1 (Santa Rosa) */
{ "TALP", "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P",
"Th0H", "Th1H", "Th2H", "Tm0P", "Ts0P", NULL },
+/* Set 10: iMac 5,1 */
+ { "TA0P", "TC0D", "TC0P", "TG0D", "TH0P", "TO0P", "Tm0P", NULL },
+/* Set 11: Macbook 5,1 */
+ { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0P", "TN0D", "TN0P",
+ "TTF0", "Th0H", "Th1H", "ThFH", "Ts0P", "Ts0S", NULL },
+/* Set 12: Macbook Pro 5,1 */
+ { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D",
+ "TG0F", "TG0H", "TG0P", "TG0T", "TG1H", "TN0D", "TN0P", "TTF0",
+ "Th2H", "Tm0P", "Ts0P", "Ts0S", NULL },
+/* Set 13: iMac 8,1 */
+ { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P",
+ "TL0P", "TO0P", "TW0P", "Tm0P", "Tp0P", NULL },
};
/* List of keys used to read/write fan speeds */
{ .accelerometer = 1, .light = 1, .temperature_set = 8 },
/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */
{ .accelerometer = 1, .light = 1, .temperature_set = 9 },
+/* iMac 5: light sensor only, temperature set 10 */
+ { .accelerometer = 0, .light = 0, .temperature_set = 10 },
+/* MacBook 5: accelerometer, backlight and temperature set 11 */
+ { .accelerometer = 1, .light = 1, .temperature_set = 11 },
+/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */
+ { .accelerometer = 1, .light = 1, .temperature_set = 12 },
+/* iMac 8: light sensor only, temperature set 13 */
+ { .accelerometer = 0, .light = 0, .temperature_set = 13 },
};
/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
&applesmc_dmi_data[7]},
+ { applesmc_dmi_match, "Apple MacBook Pro 5", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
+ &applesmc_dmi_data[12]},
{ applesmc_dmi_match, "Apple MacBook Pro 4", {
DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") },
DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") },
&applesmc_dmi_data[6]},
+ { applesmc_dmi_match, "Apple MacBook 5", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") },
+ &applesmc_dmi_data[11]},
{ applesmc_dmi_match, "Apple MacBook", {
DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") },
&applesmc_dmi_data[4]},
+ { applesmc_dmi_match, "Apple iMac 8", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") },
+ &applesmc_dmi_data[13]},
+ { applesmc_dmi_match, "Apple iMac 5", {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") },
+ &applesmc_dmi_data[10]},
{ applesmc_dmi_match, "Apple iMac", {
DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
DMI_MATCH(DMI_PRODUCT_NAME,"iMac") },
/* Set the default CFI lock/unlock addresses */
cfi->addr_unlock1 = 0x555;
cfi->addr_unlock2 = 0x2aa;
- /* Modify the unlock address if we are in compatibility mode */
- if ( /* x16 in x8 mode */
- ((cfi->device_type == CFI_DEVICETYPE_X8) &&
- (cfi->cfiq->InterfaceDesc ==
- CFI_INTERFACE_X8_BY_X16_ASYNC)) ||
- /* x32 in x16 mode */
- ((cfi->device_type == CFI_DEVICETYPE_X16) &&
- (cfi->cfiq->InterfaceDesc ==
- CFI_INTERFACE_X16_BY_X32_ASYNC)))
- {
- cfi->addr_unlock1 = 0xaaa;
- cfi->addr_unlock2 = 0x555;
- }
} /* CFI mode */
else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
* several first banks can contain 0x7f instead of actual ID
*/
do {
- uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8),
- cfi_interleave(cfi),
- cfi->device_type);
+ uint32_t ofs = cfi_build_cmd_addr(0 + (bank << 8), map, cfi);
mask = (1 << (cfi->device_type * 8)) - 1;
result = map_read(map, base + ofs);
bank++;
{
map_word result;
unsigned long mask;
- u32 ofs = cfi_build_cmd_addr(1, cfi_interleave(cfi), cfi->device_type);
+ u32 ofs = cfi_build_cmd_addr(1, map, cfi);
mask = (1 << (cfi->device_type * 8)) -1;
result = map_read(map, base + ofs);
return result.x[0] & mask;
}
/* Ensure the unlock addresses we try stay inside the map */
- probe_offset1 = cfi_build_cmd_addr(cfi->addr_unlock1, cfi_interleave(cfi), cfi->device_type);
- probe_offset2 = cfi_build_cmd_addr(cfi->addr_unlock2, cfi_interleave(cfi), cfi->device_type);
+ probe_offset1 = cfi_build_cmd_addr(cfi->addr_unlock1, map, cfi);
+ probe_offset2 = cfi_build_cmd_addr(cfi->addr_unlock2, map, cfi);
if ( ((base + probe_offset1 + map_bankwidth(map)) >= map->size) ||
((base + probe_offset2 + map_bankwidth(map)) >= map->size))
goto retry;
dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year");
do {
alarm->time.tm_year++;
- } while (!rtc_valid_tm(&alarm->time));
+ } while (rtc_valid_tm(&alarm->time) != 0);
break;
default:
goto cleanup2;
}
- pr_info("%s: alarms up to one %s%s, %zd bytes nvram, %s irqs\n",
+ pr_info("%s: alarms up to one %s%s, %zd bytes nvram%s\n",
cmos_rtc.rtc->dev.bus_id,
is_valid_irq(rtc_irq)
? (cmos_rtc.mon_alrm
atmel_port->clk = clk_get(&pdev->dev, "usart");
clk_enable(atmel_port->clk);
port->uartclk = clk_get_rate(atmel_port->clk);
+ clk_disable(atmel_port->clk);
+ /* only enable clock when USART is in use */
}
atmel_port->use_dma_rx = data->use_dma_rx;
return -ENODEV;
}
+ clk_enable(atmel_ports[co->index].clk);
+
UART_PUT_IDR(port, -1);
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
.data = &atmel_uart,
};
-#define ATMEL_CONSOLE_DEVICE &atmel_console
+#define ATMEL_CONSOLE_DEVICE (&atmel_console)
/*
* Early console initialization (before VM subsystem initialized).
if (ret)
goto err_add_port;
+ if (atmel_is_console_port(&port->uart)
+ && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) {
+ /*
+ * The serial core enabled the clock for us, so undo
+ * the clk_enable() in atmel_console_setup()
+ */
+ clk_disable(port->clk);
+ }
+
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, port);
port->rx_ring.buf = NULL;
err_alloc_ring:
if (!atmel_is_console_port(&port->uart)) {
- clk_disable(port->clk);
clk_put(port->clk);
port->clk = NULL;
}
/* "port" is allocated statically, so we shouldn't free it */
- clk_disable(atmel_port->clk);
clk_put(atmel_port->clk);
return ret;
controller. The pre-release name for this device was 8track
and could also have been called by some vendors as PVI-nnnn.
+config FB_MB862XX
+ tristate "Fujitsu MB862xx GDC support"
+ depends on FB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Frame buffer driver for Fujitsu Carmine/Coral-P(A)/Lime controllers.
+
+config FB_MB862XX_PCI_GDC
+ bool "Carmine/Coral-P(A) GDC"
+ depends on PCI && FB_MB862XX
+ ---help---
+ This enables framebuffer support for Fujitsu Carmine/Coral-P(A)
+ PCI graphics controller devices.
+
+config FB_MB862XX_LIME
+ bool "Lime GDC"
+ depends on FB_MB862XX
+ depends on OF && !FB_MB862XX_PCI_GDC
+ select FB_FOREIGN_ENDIAN
+ select FB_LITTLE_ENDIAN
+ ---help---
+ Framebuffer support for Fujitsu Lime GDC on host CPU bus.
+
+config FB_PRE_INIT_FB
+ bool "Don't reinitialize, use bootloader's GDC/Display configuration"
+ depends on FB_MB862XX_LIME
+ ---help---
+ Select this option if display contents should be inherited as set by
+ the bootloader.
+
source "drivers/video/omap/Kconfig"
source "drivers/video/backlight/Kconfig"
obj-$(CONFIG_FB_OMAP) += omap/
obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
obj-$(CONFIG_FB_CARMINE) += carminefb.o
+obj-$(CONFIG_FB_MB862XX) += mb862xx/
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
return ret;
}
-static long
-fb_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
{
- struct inode *inode = file->f_path.dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info;
struct fb_ops *fb;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
void __user *argp = (void __user *)arg;
long ret = 0;
- info = registered_fb[fbidx];
- mutex_lock(&info->lock);
fb = info->fbops;
-
- if (!fb) {
- mutex_unlock(&info->lock);
+ if (!fb)
return -ENODEV;
- }
+
switch (cmd) {
case FBIOGET_VSCREENINFO:
ret = copy_to_user(argp, &info->var,
else
ret = fb->fb_ioctl(info, cmd, arg);
}
+ return ret;
+}
+
+static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+__acquires(&info->lock)
+__releases(&info->lock)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ int fbidx = iminor(inode);
+ struct fb_info *info;
+ long ret;
+
+ info = registered_fb[fbidx];
+ mutex_lock(&info->lock);
+ ret = do_fb_ioctl(info, cmd, arg);
mutex_unlock(&info->lock);
return ret;
}
compat_caddr_t transp;
};
-static int fb_getput_cmap(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
{
struct fb_cmap_user __user *cmap;
struct fb_cmap32 __user *cmap32;
put_user(compat_ptr(data), &cmap->transp))
return -EFAULT;
- err = fb_ioctl(file, cmd, (unsigned long) cmap);
+ err = do_fb_ioctl(info, cmd, (unsigned long) cmap);
if (!err) {
if (copy_in_user(&cmap32->start,
return err;
}
-static int fb_get_fscreeninfo(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
{
mm_segment_t old_fs;
struct fb_fix_screeninfo fix;
old_fs = get_fs();
set_fs(KERNEL_DS);
- err = fb_ioctl(file, cmd, (unsigned long) &fix);
+ err = do_fb_ioctl(info, cmd, (unsigned long) &fix);
set_fs(old_fs);
if (!err)
return err;
}
-static long
-fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long fb_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+__acquires(&info->lock)
+__releases(&info->lock)
{
struct inode *inode = file->f_path.dentry->d_inode;
int fbidx = iminor(inode);
case FBIOPUT_CON2FBMAP:
arg = (unsigned long) compat_ptr(arg);
case FBIOBLANK:
- mutex_unlock(&info->lock);
- return fb_ioctl(file, cmd, arg);
+ ret = do_fb_ioctl(info, cmd, arg);
+ break;
case FBIOGET_FSCREENINFO:
- ret = fb_get_fscreeninfo(inode, file, cmd, arg);
+ ret = fb_get_fscreeninfo(info, cmd, arg);
break;
case FBIOGETCMAP:
case FBIOPUTCMAP:
- ret = fb_getput_cmap(inode, file, cmd, arg);
+ ret = fb_getput_cmap(info, cmd, arg);
break;
default:
static int
fb_mmap(struct file *file, struct vm_area_struct * vma)
+__acquires(&info->lock)
+__releases(&info->lock)
{
int fbidx = iminor(file->f_path.dentry->d_inode);
struct fb_info *info = registered_fb[fbidx];
static int
fb_open(struct inode *inode, struct file *file)
+__acquires(&info->lock)
+__releases(&info->lock)
{
int fbidx = iminor(inode);
struct fb_info *info;
static int
fb_release(struct inode *inode, struct file *file)
+__acquires(&info->lock)
+__releases(&info->lock)
{
struct fb_info * const info = file->private_data;
--- /dev/null
+#
+# Makefile for the MB862xx framebuffer driver
+#
+
+obj-$(CONFIG_FB_MB862XX) := mb862xxfb.o
--- /dev/null
+/*
+ * Fujitsu MB862xx Graphics Controller Registers/Bits
+ */
+
+#ifndef _MB862XX_REG_H
+#define _MB862XX_REG_H
+
+#ifdef MB862XX_MMIO_BOTTOM
+#define MB862XX_MMIO_BASE 0x03fc0000
+#else
+#define MB862XX_MMIO_BASE 0x01fc0000
+#endif
+#define MB862XX_I2C_BASE 0x0000c000
+#define MB862XX_DISP_BASE 0x00010000
+#define MB862XX_CAP_BASE 0x00018000
+#define MB862XX_DRAW_BASE 0x00030000
+#define MB862XX_GEO_BASE 0x00038000
+#define MB862XX_PIO_BASE 0x00038000
+#define MB862XX_MMIO_SIZE 0x40000
+
+/* Host interface/pio registers */
+#define GC_IST 0x00000020
+#define GC_IMASK 0x00000024
+#define GC_SRST 0x0000002c
+#define GC_CCF 0x00000038
+#define GC_CID 0x000000f0
+#define GC_REVISION 0x00000084
+
+#define GC_CCF_CGE_100 0x00000000
+#define GC_CCF_CGE_133 0x00040000
+#define GC_CCF_CGE_166 0x00080000
+#define GC_CCF_COT_100 0x00000000
+#define GC_CCF_COT_133 0x00010000
+#define GC_CID_CNAME_MSK 0x0000ff00
+#define GC_CID_VERSION_MSK 0x000000ff
+
+/* define enabled interrupts hereby */
+#define GC_INT_EN 0x00000000
+
+/* Memory interface mode register */
+#define GC_MMR 0x0000fffc
+
+/* Display Controller registers */
+#define GC_DCM0 0x00000000
+#define GC_HTP 0x00000004
+#define GC_HDB_HDP 0x00000008
+#define GC_VSW_HSW_HSP 0x0000000c
+#define GC_VTR 0x00000010
+#define GC_VDP_VSP 0x00000014
+#define GC_WY_WX 0x00000018
+#define GC_WH_WW 0x0000001c
+#define GC_L0M 0x00000020
+#define GC_L0OA0 0x00000024
+#define GC_L0DA0 0x00000028
+#define GC_L0DY_L0DX 0x0000002c
+#define GC_DCM1 0x00000100
+#define GC_L0EM 0x00000110
+#define GC_L0WY_L0WX 0x00000114
+#define GC_L0WH_L0WW 0x00000118
+#define GC_DCM2 0x00000104
+#define GC_DCM3 0x00000108
+#define GC_CPM_CUTC 0x000000a0
+#define GC_CUOA0 0x000000a4
+#define GC_CUY0_CUX0 0x000000a8
+#define GC_CUOA1 0x000000ac
+#define GC_CUY1_CUX1 0x000000b0
+#define GC_L0PAL0 0x00000400
+
+#define GC_CPM_CEN0 0x00100000
+#define GC_CPM_CEN1 0x00200000
+
+#define GC_DCM01_ESY 0x00000004
+#define GC_DCM01_SC 0x00003f00
+#define GC_DCM01_RESV 0x00004000
+#define GC_DCM01_CKS 0x00008000
+#define GC_DCM01_L0E 0x00010000
+#define GC_DCM01_DEN 0x80000000
+#define GC_L0M_L0C_8 0x00000000
+#define GC_L0M_L0C_16 0x80000000
+#define GC_L0EM_L0EC_24 0x40000000
+#define GC_L0M_L0W_UNIT 64
+
+#define GC_DISP_REFCLK_400 400
+
+/* Carmine specific */
+#define MB86297_DRAW_BASE 0x00020000
+#define MB86297_DISP0_BASE 0x00100000
+#define MB86297_DISP1_BASE 0x00140000
+#define MB86297_WRBACK_BASE 0x00180000
+#define MB86297_CAP0_BASE 0x00200000
+#define MB86297_CAP1_BASE 0x00280000
+#define MB86297_DRAMCTRL_BASE 0x00300000
+#define MB86297_CTRL_BASE 0x00400000
+#define MB86297_I2C_BASE 0x00500000
+
+#define GC_CTRL_STATUS 0x00000000
+#define GC_CTRL_INT_MASK 0x00000004
+#define GC_CTRL_CLK_ENABLE 0x0000000c
+#define GC_CTRL_SOFT_RST 0x00000010
+
+#define GC_CTRL_CLK_EN_DRAM 0x00000001
+#define GC_CTRL_CLK_EN_2D3D 0x00000002
+#define GC_CTRL_CLK_EN_DISP0 0x00000020
+#define GC_CTRL_CLK_EN_DISP1 0x00000040
+
+#define GC_2D3D_REV 0x000004b4
+#define GC_RE_REVISION 0x24240200
+
+/* define enabled interrupts hereby */
+#define GC_CARMINE_INT_EN 0x00000004
+
+/* DRAM controller */
+#define GC_DCTL_MODE_ADD 0x00000000
+#define GC_DCTL_SETTIME1_EMODE 0x00000004
+#define GC_DCTL_REFRESH_SETTIME2 0x00000008
+#define GC_DCTL_RSV0_STATES 0x0000000C
+#define GC_DCTL_RSV2_RSV1 0x00000010
+#define GC_DCTL_DDRIF2_DDRIF1 0x00000014
+#define GC_DCTL_IOCONT1_IOCONT0 0x00000024
+
+#define GC_DCTL_STATES_MSK 0x0000000f
+#define GC_DCTL_INIT_WAIT_CNT 3000
+#define GC_DCTL_INIT_WAIT_INTERVAL 1
+
+/* DRAM ctrl values for Carmine PCI Eval. board */
+#define GC_EVB_DCTL_MODE_ADD 0x012105c3
+#define GC_EVB_DCTL_MODE_ADD_AFT_RST 0x002105c3
+#define GC_EVB_DCTL_SETTIME1_EMODE 0x47498000
+#define GC_EVB_DCTL_REFRESH_SETTIME2 0x00422a22
+#define GC_EVB_DCTL_RSV0_STATES 0x00200003
+#define GC_EVB_DCTL_RSV0_STATES_AFT_RST 0x00200002
+#define GC_EVB_DCTL_RSV2_RSV1 0x0000000f
+#define GC_EVB_DCTL_DDRIF2_DDRIF1 0x00556646
+#define GC_EVB_DCTL_IOCONT1_IOCONT0 0x05550555
+
+#define GC_DISP_REFCLK_533 533
+
+#endif
--- /dev/null
+/*
+ * drivers/mb862xx/mb862xxfb.c
+ *
+ * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver
+ *
+ * (C) 2008 Anatolij Gustschin <agust@denx.de>
+ * DENX Software Engineering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#if defined(CONFIG_PPC_OF)
+#include <linux/of_platform.h>
+#endif
+#include "mb862xxfb.h"
+#include "mb862xx_reg.h"
+
+#define NR_PALETTE 256
+#define MB862XX_MEM_SIZE 0x1000000
+#define CORALP_MEM_SIZE 0x4000000
+#define CARMINE_MEM_SIZE 0x8000000
+#define DRV_NAME "mb862xxfb"
+
+#if defined(CONFIG_LWMON5)
+static struct mb862xx_gc_mode lwmon5_gc_mode = {
+ /* Mode for Sharp LQ104V1DG61 TFT LCD Panel */
+ { "640x480", 60, 640, 480, 40000, 48, 16, 32, 11, 96, 2, 0, 0, 0 },
+ /* 16 bits/pixel, 32MB, 100MHz, SDRAM memory mode value */
+ 16, 0x2000000, GC_CCF_COT_100, 0x414fb7f2
+};
+#endif
+
+#if defined(CONFIG_SOCRATES)
+static struct mb862xx_gc_mode socrates_gc_mode = {
+ /* Mode for Prime View PM070WL4 TFT LCD Panel */
+ { "800x480", 45, 800, 480, 40000, 86, 42, 33, 10, 128, 2, 0, 0, 0 },
+ /* 16 bits/pixel, 16MB, 133MHz, SDRAM memory mode value */
+ 16, 0x1000000, GC_CCF_COT_133, 0x4157ba63
+};
+#endif
+
+/* Helpers */
+static inline int h_total(struct fb_var_screeninfo *var)
+{
+ return var->xres + var->left_margin +
+ var->right_margin + var->hsync_len;
+}
+
+static inline int v_total(struct fb_var_screeninfo *var)
+{
+ return var->yres + var->upper_margin +
+ var->lower_margin + var->vsync_len;
+}
+
+static inline int hsp(struct fb_var_screeninfo *var)
+{
+ return var->xres + var->right_margin - 1;
+}
+
+static inline int vsp(struct fb_var_screeninfo *var)
+{
+ return var->yres + var->lower_margin - 1;
+}
+
+static inline int d_pitch(struct fb_var_screeninfo *var)
+{
+ return var->xres * var->bits_per_pixel / 8;
+}
+
+static inline unsigned int chan_to_field(unsigned int chan,
+ struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int mb862xxfb_setcolreg(unsigned regno,
+ unsigned red, unsigned green, unsigned blue,
+ unsigned transp, struct fb_info *info)
+{
+ struct mb862xxfb_par *par = info->par;
+ unsigned int val;
+
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ if (regno < 16) {
+ val = chan_to_field(red, &info->var.red);
+ val |= chan_to_field(green, &info->var.green);
+ val |= chan_to_field(blue, &info->var.blue);
+ par->pseudo_palette[regno] = val;
+ }
+ break;
+ case FB_VISUAL_PSEUDOCOLOR:
+ if (regno < 256) {
+ val = (red >> 8) << 16;
+ val |= (green >> 8) << 8;
+ val |= blue >> 8;
+ outreg(disp, GC_L0PAL0 + (regno * 4), val);
+ }
+ break;
+ default:
+ return 1; /* unsupported type */
+ }
+ return 0;
+}
+
+static int mb862xxfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *fbi)
+{
+ unsigned long tmp;
+
+ if (fbi->dev)
+ dev_dbg(fbi->dev, "%s\n", __func__);
+
+ /* check if these values fit into the registers */
+ if (var->hsync_len > 255 || var->vsync_len > 255)
+ return -EINVAL;
+
+ if ((var->xres + var->right_margin) >= 4096)
+ return -EINVAL;
+
+ if ((var->yres + var->lower_margin) > 4096)
+ return -EINVAL;
+
+ if (h_total(var) > 4096 || v_total(var) > 4096)
+ return -EINVAL;
+
+ if (var->xres_virtual > 4096 || var->yres_virtual > 4096)
+ return -EINVAL;
+
+ if (var->bits_per_pixel <= 8)
+ var->bits_per_pixel = 8;
+ else if (var->bits_per_pixel <= 16)
+ var->bits_per_pixel = 16;
+ else if (var->bits_per_pixel <= 32)
+ var->bits_per_pixel = 32;
+
+ /*
+ * can cope with 8,16 or 24/32bpp if resulting
+ * pitch is divisible by 64 without remainder
+ */
+ if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) {
+ int r;
+
+ var->bits_per_pixel = 0;
+ do {
+ var->bits_per_pixel += 8;
+ r = d_pitch(&fbi->var) % GC_L0M_L0W_UNIT;
+ } while (r && var->bits_per_pixel <= 32);
+
+ if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT)
+ return -EINVAL;
+ }
+
+ /* line length is going to be 128 bit aligned */
+ tmp = (var->xres * var->bits_per_pixel) / 8;
+ if ((tmp & 15) != 0)
+ return -EINVAL;
+
+ /* set r/g/b positions and validate bpp */
+ switch (var->bits_per_pixel) {
+ case 8:
+ var->red.length = var->bits_per_pixel;
+ var->green.length = var->bits_per_pixel;
+ var->blue.length = var->bits_per_pixel;
+ var->red.offset = 0;
+ var->green.offset = 0;
+ var->blue.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 16:
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 24:
+ case 32:
+ var->transp.length = 8;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * set display parameters
+ */
+static int mb862xxfb_set_par(struct fb_info *fbi)
+{
+ struct mb862xxfb_par *par = fbi->par;
+ unsigned long reg, sc;
+
+ dev_dbg(par->dev, "%s\n", __func__);
+
+ if (par->pre_init)
+ return 0;
+
+ /* disp off */
+ reg = inreg(disp, GC_DCM1);
+ reg &= ~GC_DCM01_DEN;
+ outreg(disp, GC_DCM1, reg);
+
+ /* set display reference clock div. */
+ sc = par->refclk / (1000000 / fbi->var.pixclock) - 1;
+ reg = inreg(disp, GC_DCM1);
+ reg &= ~(GC_DCM01_CKS | GC_DCM01_RESV | GC_DCM01_SC);
+ reg |= sc << 8;
+ outreg(disp, GC_DCM1, reg);
+ dev_dbg(par->dev, "SC 0x%lx\n", sc);
+
+ /* disp dimension, format */
+ reg = pack(d_pitch(&fbi->var) / GC_L0M_L0W_UNIT,
+ (fbi->var.yres - 1));
+ if (fbi->var.bits_per_pixel == 16)
+ reg |= GC_L0M_L0C_16;
+ outreg(disp, GC_L0M, reg);
+
+ if (fbi->var.bits_per_pixel == 32) {
+ reg = inreg(disp, GC_L0EM);
+ outreg(disp, GC_L0EM, reg | GC_L0EM_L0EC_24);
+ }
+ outreg(disp, GC_WY_WX, 0);
+ reg = pack(fbi->var.yres - 1, fbi->var.xres);
+ outreg(disp, GC_WH_WW, reg);
+ outreg(disp, GC_L0OA0, 0);
+ outreg(disp, GC_L0DA0, 0);
+ outreg(disp, GC_L0DY_L0DX, 0);
+ outreg(disp, GC_L0WY_L0WX, 0);
+ outreg(disp, GC_L0WH_L0WW, reg);
+
+ /* both HW-cursors off */
+ reg = inreg(disp, GC_CPM_CUTC);
+ reg &= ~(GC_CPM_CEN0 | GC_CPM_CEN1);
+ outreg(disp, GC_CPM_CUTC, reg);
+
+ /* timings */
+ reg = pack(fbi->var.xres - 1, fbi->var.xres - 1);
+ outreg(disp, GC_HDB_HDP, reg);
+ reg = pack((fbi->var.yres - 1), vsp(&fbi->var));
+ outreg(disp, GC_VDP_VSP, reg);
+ reg = ((fbi->var.vsync_len - 1) << 24) |
+ pack((fbi->var.hsync_len - 1), hsp(&fbi->var));
+ outreg(disp, GC_VSW_HSW_HSP, reg);
+ outreg(disp, GC_HTP, pack(h_total(&fbi->var) - 1, 0));
+ outreg(disp, GC_VTR, pack(v_total(&fbi->var) - 1, 0));
+
+ /* display on */
+ reg = inreg(disp, GC_DCM1);
+ reg |= GC_DCM01_DEN | GC_DCM01_L0E;
+ reg &= ~GC_DCM01_ESY;
+ outreg(disp, GC_DCM1, reg);
+ return 0;
+}
+
+static int mb862xxfb_pan(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct mb862xxfb_par *par = info->par;
+ unsigned long reg;
+
+ reg = pack(var->yoffset, var->xoffset);
+ outreg(disp, GC_L0WY_L0WX, reg);
+
+ reg = pack(var->yres_virtual, var->xres_virtual);
+ outreg(disp, GC_L0WH_L0WW, reg);
+ return 0;
+}
+
+static int mb862xxfb_blank(int mode, struct fb_info *fbi)
+{
+ struct mb862xxfb_par *par = fbi->par;
+ unsigned long reg;
+
+ dev_dbg(fbi->dev, "blank mode=%d\n", mode);
+
+ switch (mode) {
+ case FB_BLANK_POWERDOWN:
+ reg = inreg(disp, GC_DCM1);
+ reg &= ~GC_DCM01_DEN;
+ outreg(disp, GC_DCM1, reg);
+ break;
+ case FB_BLANK_UNBLANK:
+ reg = inreg(disp, GC_DCM1);
+ reg |= GC_DCM01_DEN;
+ outreg(disp, GC_DCM1, reg);
+ break;
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+/* framebuffer ops */
+static struct fb_ops mb862xxfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = mb862xxfb_check_var,
+ .fb_set_par = mb862xxfb_set_par,
+ .fb_setcolreg = mb862xxfb_setcolreg,
+ .fb_blank = mb862xxfb_blank,
+ .fb_pan_display = mb862xxfb_pan,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+/* initialize fb_info data */
+static int mb862xxfb_init_fbinfo(struct fb_info *fbi)
+{
+ struct mb862xxfb_par *par = fbi->par;
+ struct mb862xx_gc_mode *mode = par->gc_mode;
+ unsigned long reg;
+
+ fbi->fbops = &mb862xxfb_ops;
+ fbi->pseudo_palette = par->pseudo_palette;
+ fbi->screen_base = par->fb_base;
+ fbi->screen_size = par->mapped_vram;
+
+ strcpy(fbi->fix.id, DRV_NAME);
+ fbi->fix.smem_start = (unsigned long)par->fb_base_phys;
+ fbi->fix.smem_len = par->mapped_vram;
+ fbi->fix.mmio_start = (unsigned long)par->mmio_base_phys;
+ fbi->fix.mmio_len = par->mmio_len;
+ fbi->fix.accel = FB_ACCEL_NONE;
+ fbi->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbi->fix.type_aux = 0;
+ fbi->fix.xpanstep = 1;
+ fbi->fix.ypanstep = 1;
+ fbi->fix.ywrapstep = 0;
+
+ reg = inreg(disp, GC_DCM1);
+ if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) {
+ /* get the disp mode from active display cfg */
+ unsigned long sc = ((reg & GC_DCM01_SC) >> 8) + 1;
+ unsigned long hsp, vsp, ht, vt;
+
+ dev_dbg(par->dev, "using bootloader's disp. mode\n");
+ fbi->var.pixclock = (sc * 1000000) / par->refclk;
+ fbi->var.xres = (inreg(disp, GC_HDB_HDP) & 0x0fff) + 1;
+ reg = inreg(disp, GC_VDP_VSP);
+ fbi->var.yres = ((reg >> 16) & 0x0fff) + 1;
+ vsp = (reg & 0x0fff) + 1;
+ fbi->var.xres_virtual = fbi->var.xres;
+ fbi->var.yres_virtual = fbi->var.yres;
+ reg = inreg(disp, GC_L0EM);
+ if (reg & GC_L0EM_L0EC_24) {
+ fbi->var.bits_per_pixel = 32;
+ } else {
+ reg = inreg(disp, GC_L0M);
+ if (reg & GC_L0M_L0C_16)
+ fbi->var.bits_per_pixel = 16;
+ else
+ fbi->var.bits_per_pixel = 8;
+ }
+ reg = inreg(disp, GC_VSW_HSW_HSP);
+ fbi->var.hsync_len = ((reg & 0xff0000) >> 16) + 1;
+ fbi->var.vsync_len = ((reg & 0x3f000000) >> 24) + 1;
+ hsp = (reg & 0xffff) + 1;
+ ht = ((inreg(disp, GC_HTP) & 0xfff0000) >> 16) + 1;
+ fbi->var.right_margin = hsp - fbi->var.xres;
+ fbi->var.left_margin = ht - hsp - fbi->var.hsync_len;
+ vt = ((inreg(disp, GC_VTR) & 0xfff0000) >> 16) + 1;
+ fbi->var.lower_margin = vsp - fbi->var.yres;
+ fbi->var.upper_margin = vt - vsp - fbi->var.vsync_len;
+ } else if (mode) {
+ dev_dbg(par->dev, "using supplied mode\n");
+ fb_videomode_to_var(&fbi->var, (struct fb_videomode *)mode);
+ fbi->var.bits_per_pixel = mode->def_bpp ? mode->def_bpp : 8;
+ } else {
+ int ret;
+
+ ret = fb_find_mode(&fbi->var, fbi, "640x480-16@60",
+ NULL, 0, NULL, 16);
+ if (ret == 0 || ret == 4) {
+ dev_err(par->dev,
+ "failed to get initial mode\n");
+ return -EINVAL;
+ }
+ }
+
+ fbi->var.xoffset = 0;
+ fbi->var.yoffset = 0;
+ fbi->var.grayscale = 0;
+ fbi->var.nonstd = 0;
+ fbi->var.height = -1;
+ fbi->var.width = -1;
+ fbi->var.accel_flags = 0;
+ fbi->var.vmode = FB_VMODE_NONINTERLACED;
+ fbi->var.activate = FB_ACTIVATE_NOW;
+ fbi->flags = FBINFO_DEFAULT |
+#ifdef __BIG_ENDIAN
+ FBINFO_FOREIGN_ENDIAN |
+#endif
+ FBINFO_HWACCEL_XPAN |
+ FBINFO_HWACCEL_YPAN;
+
+ /* check and possibly fix bpp */
+ if ((fbi->fbops->fb_check_var)(&fbi->var, fbi))
+ dev_err(par->dev, "check_var() failed on initial setup?\n");
+
+ fbi->fix.visual = fbi->var.bits_per_pixel == 8 ?
+ FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
+ fbi->fix.line_length = (fbi->var.xres_virtual *
+ fbi->var.bits_per_pixel) / 8;
+ return 0;
+}
+
+/*
+ * show some display controller and cursor registers
+ */
+static ssize_t mb862xxfb_show_dispregs(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct mb862xxfb_par *par = fbi->par;
+ char *ptr = buf;
+ unsigned int reg;
+
+ for (reg = GC_DCM0; reg <= GC_L0DY_L0DX; reg += 4)
+ ptr += sprintf(ptr, "%08x = %08x\n",
+ reg, inreg(disp, reg));
+
+ for (reg = GC_CPM_CUTC; reg <= GC_CUY1_CUX1; reg += 4)
+ ptr += sprintf(ptr, "%08x = %08x\n",
+ reg, inreg(disp, reg));
+
+ for (reg = GC_DCM1; reg <= GC_L0WH_L0WW; reg += 4)
+ ptr += sprintf(ptr, "%08x = %08x\n",
+ reg, inreg(disp, reg));
+
+ return ptr - buf;
+}
+
+static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL);
+
+irqreturn_t mb862xx_intr(int irq, void *dev_id)
+{
+ struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id;
+ unsigned long reg_ist, mask;
+
+ if (!par)
+ return IRQ_NONE;
+
+ if (par->type == BT_CARMINE) {
+ /* Get Interrupt Status */
+ reg_ist = inreg(ctrl, GC_CTRL_STATUS);
+ mask = inreg(ctrl, GC_CTRL_INT_MASK);
+ if (reg_ist == 0)
+ return IRQ_HANDLED;
+
+ reg_ist &= mask;
+ if (reg_ist == 0)
+ return IRQ_HANDLED;
+
+ /* Clear interrupt status */
+ outreg(ctrl, 0x0, reg_ist);
+ } else {
+ /* Get status */
+ reg_ist = inreg(host, GC_IST);
+ mask = inreg(host, GC_IMASK);
+
+ reg_ist &= mask;
+ if (reg_ist == 0)
+ return IRQ_HANDLED;
+
+ /* Clear status */
+ outreg(host, GC_IST, ~reg_ist);
+ }
+ return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_FB_MB862XX_LIME)
+/*
+ * GDC (Lime, Coral(B/Q), Mint, ...) on host bus
+ */
+static int mb862xx_gdc_init(struct mb862xxfb_par *par)
+{
+ unsigned long ccf, mmr;
+ unsigned long ver, rev;
+
+ if (!par)
+ return -ENODEV;
+
+#if defined(CONFIG_FB_PRE_INIT_FB)
+ par->pre_init = 1;
+#endif
+ par->host = par->mmio_base;
+ par->i2c = par->mmio_base + MB862XX_I2C_BASE;
+ par->disp = par->mmio_base + MB862XX_DISP_BASE;
+ par->cap = par->mmio_base + MB862XX_CAP_BASE;
+ par->draw = par->mmio_base + MB862XX_DRAW_BASE;
+ par->geo = par->mmio_base + MB862XX_GEO_BASE;
+ par->pio = par->mmio_base + MB862XX_PIO_BASE;
+
+ par->refclk = GC_DISP_REFCLK_400;
+
+ ver = inreg(host, GC_CID);
+ rev = inreg(pio, GC_REVISION);
+ if ((ver == 0x303) && (rev & 0xffffff00) == 0x20050100) {
+ dev_info(par->dev, "Fujitsu Lime v1.%d found\n",
+ (int)rev & 0xff);
+ par->type = BT_LIME;
+ ccf = par->gc_mode ? par->gc_mode->ccf : GC_CCF_COT_100;
+ mmr = par->gc_mode ? par->gc_mode->mmr : 0x414fb7f2;
+ } else {
+ dev_info(par->dev, "? GDC, CID/Rev.: 0x%lx/0x%lx \n", ver, rev);
+ return -ENODEV;
+ }
+
+ if (!par->pre_init) {
+ outreg(host, GC_CCF, ccf);
+ udelay(200);
+ outreg(host, GC_MMR, mmr);
+ udelay(10);
+ }
+
+ /* interrupt status */
+ outreg(host, GC_IST, 0);
+ outreg(host, GC_IMASK, GC_INT_EN);
+ return 0;
+}
+
+static int __devinit of_platform_mb862xx_probe(struct of_device *ofdev,
+ const struct of_device_id *id)
+{
+ struct device_node *np = ofdev->node;
+ struct device *dev = &ofdev->dev;
+ struct mb862xxfb_par *par;
+ struct fb_info *info;
+ struct resource res;
+ resource_size_t res_size;
+ unsigned long ret = -ENODEV;
+
+ if (of_address_to_resource(np, 0, &res)) {
+ dev_err(dev, "Invalid address\n");
+ return -ENXIO;
+ }
+
+ info = framebuffer_alloc(sizeof(struct mb862xxfb_par), dev);
+ if (info == NULL) {
+ dev_err(dev, "cannot allocate framebuffer\n");
+ return -ENOMEM;
+ }
+
+ par = info->par;
+ par->info = info;
+ par->dev = dev;
+
+ par->irq = irq_of_parse_and_map(np, 0);
+ if (par->irq == NO_IRQ) {
+ dev_err(dev, "failed to map irq\n");
+ ret = -ENODEV;
+ goto fbrel;
+ }
+
+ res_size = 1 + res.end - res.start;
+ par->res = request_mem_region(res.start, res_size, DRV_NAME);
+ if (par->res == NULL) {
+ dev_err(dev, "Cannot claim framebuffer/mmio\n");
+ ret = -ENXIO;
+ goto irqdisp;
+ }
+
+#if defined(CONFIG_LWMON5)
+ par->gc_mode = &lwmon5_gc_mode;
+#endif
+
+#if defined(CONFIG_SOCRATES)
+ par->gc_mode = &socrates_gc_mode;
+#endif
+
+ par->fb_base_phys = res.start;
+ par->mmio_base_phys = res.start + MB862XX_MMIO_BASE;
+ par->mmio_len = MB862XX_MMIO_SIZE;
+ if (par->gc_mode)
+ par->mapped_vram = par->gc_mode->max_vram;
+ else
+ par->mapped_vram = MB862XX_MEM_SIZE;
+
+ par->fb_base = ioremap(par->fb_base_phys, par->mapped_vram);
+ if (par->fb_base == NULL) {
+ dev_err(dev, "Cannot map framebuffer\n");
+ goto rel_reg;
+ }
+
+ par->mmio_base = ioremap(par->mmio_base_phys, par->mmio_len);
+ if (par->mmio_base == NULL) {
+ dev_err(dev, "Cannot map registers\n");
+ goto fb_unmap;
+ }
+
+ dev_dbg(dev, "fb phys 0x%llx 0x%lx\n",
+ (u64)par->fb_base_phys, (ulong)par->mapped_vram);
+ dev_dbg(dev, "mmio phys 0x%llx 0x%lx, (irq = %d)\n",
+ (u64)par->mmio_base_phys, (ulong)par->mmio_len, par->irq);
+
+ if (mb862xx_gdc_init(par))
+ goto io_unmap;
+
+ if (request_irq(par->irq, mb862xx_intr, IRQF_DISABLED,
+ DRV_NAME, (void *)par)) {
+ dev_err(dev, "Cannot request irq\n");
+ goto io_unmap;
+ }
+
+ mb862xxfb_init_fbinfo(info);
+
+ if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0) < 0) {
+ dev_err(dev, "Could not allocate cmap for fb_info.\n");
+ goto free_irq;
+ }
+
+ if ((info->fbops->fb_set_par)(info))
+ dev_err(dev, "set_var() failed on initial setup?\n");
+
+ if (register_framebuffer(info)) {
+ dev_err(dev, "failed to register framebuffer\n");
+ goto rel_cmap;
+ }
+
+ dev_set_drvdata(dev, info);
+
+ if (device_create_file(dev, &dev_attr_dispregs))
+ dev_err(dev, "Can't create sysfs regdump file\n");
+ return 0;
+
+rel_cmap:
+ fb_dealloc_cmap(&info->cmap);
+free_irq:
+ outreg(host, GC_IMASK, 0);
+ free_irq(par->irq, (void *)par);
+io_unmap:
+ iounmap(par->mmio_base);
+fb_unmap:
+ iounmap(par->fb_base);
+rel_reg:
+ release_mem_region(res.start, res_size);
+irqdisp:
+ irq_dispose_mapping(par->irq);
+fbrel:
+ dev_set_drvdata(dev, NULL);
+ framebuffer_release(info);
+ return ret;
+}
+
+static int __devexit of_platform_mb862xx_remove(struct of_device *ofdev)
+{
+ struct fb_info *fbi = dev_get_drvdata(&ofdev->dev);
+ struct mb862xxfb_par *par = fbi->par;
+ resource_size_t res_size = 1 + par->res->end - par->res->start;
+ unsigned long reg;
+
+ dev_dbg(fbi->dev, "%s release\n", fbi->fix.id);
+
+ /* display off */
+ reg = inreg(disp, GC_DCM1);
+ reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E);
+ outreg(disp, GC_DCM1, reg);
+
+ /* disable interrupts */
+ outreg(host, GC_IMASK, 0);
+
+ free_irq(par->irq, (void *)par);
+ irq_dispose_mapping(par->irq);
+
+ device_remove_file(&ofdev->dev, &dev_attr_dispregs);
+
+ unregister_framebuffer(fbi);
+ fb_dealloc_cmap(&fbi->cmap);
+
+ iounmap(par->mmio_base);
+ iounmap(par->fb_base);
+
+ dev_set_drvdata(&ofdev->dev, NULL);
+ release_mem_region(par->res->start, res_size);
+ framebuffer_release(fbi);
+ return 0;
+}
+
+/*
+ * common types
+ */
+static struct of_device_id __devinitdata of_platform_mb862xx_tbl[] = {
+ { .compatible = "fujitsu,MB86276", },
+ { .compatible = "fujitsu,lime", },
+ { .compatible = "fujitsu,MB86277", },
+ { .compatible = "fujitsu,mint", },
+ { .compatible = "fujitsu,MB86293", },
+ { .compatible = "fujitsu,MB86294", },
+ { .compatible = "fujitsu,coral", },
+ { /* end */ }
+};
+
+static struct of_platform_driver of_platform_mb862xxfb_driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ .match_table = of_platform_mb862xx_tbl,
+ .probe = of_platform_mb862xx_probe,
+ .remove = __devexit_p(of_platform_mb862xx_remove),
+};
+#endif
+
+#if defined(CONFIG_FB_MB862XX_PCI_GDC)
+static int coralp_init(struct mb862xxfb_par *par)
+{
+ int cn, ver;
+
+ par->host = par->mmio_base;
+ par->i2c = par->mmio_base + MB862XX_I2C_BASE;
+ par->disp = par->mmio_base + MB862XX_DISP_BASE;
+ par->cap = par->mmio_base + MB862XX_CAP_BASE;
+ par->draw = par->mmio_base + MB862XX_DRAW_BASE;
+ par->geo = par->mmio_base + MB862XX_GEO_BASE;
+ par->pio = par->mmio_base + MB862XX_PIO_BASE;
+
+ par->refclk = GC_DISP_REFCLK_400;
+
+ ver = inreg(host, GC_CID);
+ cn = (ver & GC_CID_CNAME_MSK) >> 8;
+ ver = ver & GC_CID_VERSION_MSK;
+ if (cn == 3) {
+ dev_info(par->dev, "Fujitsu Coral-%s GDC Rev.%d found\n",\
+ (ver == 6) ? "P" : (ver == 8) ? "PA" : "?",
+ par->pdev->revision);
+ outreg(host, GC_CCF, GC_CCF_CGE_166 | GC_CCF_COT_133);
+ udelay(200);
+ outreg(host, GC_MMR, GC_MMR_CORALP_EVB_VAL);
+ udelay(10);
+ /* Clear interrupt status */
+ outreg(host, GC_IST, 0);
+ } else {
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int init_dram_ctrl(struct mb862xxfb_par *par)
+{
+ unsigned long i = 0;
+
+ /*
+ * Set io mode first! Spec. says IC may be destroyed
+ * if not set to SSTL2/LVCMOS before init.
+ */
+ outreg(dram_ctrl, GC_DCTL_IOCONT1_IOCONT0, GC_EVB_DCTL_IOCONT1_IOCONT0);
+
+ /* DRAM init */
+ outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD);
+ outreg(dram_ctrl, GC_DCTL_SETTIME1_EMODE, GC_EVB_DCTL_SETTIME1_EMODE);
+ outreg(dram_ctrl, GC_DCTL_REFRESH_SETTIME2,
+ GC_EVB_DCTL_REFRESH_SETTIME2);
+ outreg(dram_ctrl, GC_DCTL_RSV2_RSV1, GC_EVB_DCTL_RSV2_RSV1);
+ outreg(dram_ctrl, GC_DCTL_DDRIF2_DDRIF1, GC_EVB_DCTL_DDRIF2_DDRIF1);
+ outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES);
+
+ /* DLL reset done? */
+ while ((inreg(dram_ctrl, GC_DCTL_RSV0_STATES) & GC_DCTL_STATES_MSK)) {
+ udelay(GC_DCTL_INIT_WAIT_INTERVAL);
+ if (i++ > GC_DCTL_INIT_WAIT_CNT) {
+ dev_err(par->dev, "VRAM init failed.\n");
+ return -EINVAL;
+ }
+ }
+ outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD_AFT_RST);
+ outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES_AFT_RST);
+ return 0;
+}
+
+static int carmine_init(struct mb862xxfb_par *par)
+{
+ unsigned long reg;
+
+ par->ctrl = par->mmio_base + MB86297_CTRL_BASE;
+ par->i2c = par->mmio_base + MB86297_I2C_BASE;
+ par->disp = par->mmio_base + MB86297_DISP0_BASE;
+ par->disp1 = par->mmio_base + MB86297_DISP1_BASE;
+ par->cap = par->mmio_base + MB86297_CAP0_BASE;
+ par->cap1 = par->mmio_base + MB86297_CAP1_BASE;
+ par->draw = par->mmio_base + MB86297_DRAW_BASE;
+ par->dram_ctrl = par->mmio_base + MB86297_DRAMCTRL_BASE;
+ par->wrback = par->mmio_base + MB86297_WRBACK_BASE;
+
+ par->refclk = GC_DISP_REFCLK_533;
+
+ /* warm up */
+ reg = GC_CTRL_CLK_EN_DRAM | GC_CTRL_CLK_EN_2D3D | GC_CTRL_CLK_EN_DISP0;
+ outreg(ctrl, GC_CTRL_CLK_ENABLE, reg);
+
+ /* check for engine module revision */
+ if (inreg(draw, GC_2D3D_REV) == GC_RE_REVISION)
+ dev_info(par->dev, "Fujitsu Carmine GDC Rev.%d found\n",
+ par->pdev->revision);
+ else
+ goto err_init;
+
+ reg &= ~GC_CTRL_CLK_EN_2D3D;
+ outreg(ctrl, GC_CTRL_CLK_ENABLE, reg);
+
+ /* set up vram */
+ if (init_dram_ctrl(par) < 0)
+ goto err_init;
+
+ outreg(ctrl, GC_CTRL_INT_MASK, 0);
+ return 0;
+
+err_init:
+ outreg(ctrl, GC_CTRL_CLK_ENABLE, 0);
+ return -EINVAL;
+}
+
+static inline int mb862xx_pci_gdc_init(struct mb862xxfb_par *par)
+{
+ switch (par->type) {
+ case BT_CORALP:
+ return coralp_init(par);
+ case BT_CARMINE:
+ return carmine_init(par);
+ default:
+ return -ENODEV;
+ }
+}
+
+#define CHIP_ID(id) \
+ { PCI_DEVICE(PCI_VENDOR_ID_FUJITSU_LIMITED, id) }
+
+static struct pci_device_id mb862xx_pci_tbl[] __devinitdata = {
+ /* MB86295/MB86296 */
+ CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALP),
+ CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALPA),
+ /* MB86297 */
+ CHIP_ID(PCI_DEVICE_ID_FUJITSU_CARMINE),
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, mb862xx_pci_tbl);
+
+static int __devinit mb862xx_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct mb862xxfb_par *par;
+ struct fb_info *info;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ret = pci_enable_device(pdev);
+ if (ret < 0) {
+ dev_err(dev, "Cannot enable PCI device\n");
+ goto out;
+ }
+
+ info = framebuffer_alloc(sizeof(struct mb862xxfb_par), dev);
+ if (!info) {
+ dev_err(dev, "framebuffer alloc failed\n");
+ ret = -ENOMEM;
+ goto dis_dev;
+ }
+
+ par = info->par;
+ par->info = info;
+ par->dev = dev;
+ par->pdev = pdev;
+ par->irq = pdev->irq;
+
+ ret = pci_request_regions(pdev, DRV_NAME);
+ if (ret < 0) {
+ dev_err(dev, "Cannot reserve region(s) for PCI device\n");
+ goto rel_fb;
+ }
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_FUJITSU_CORALP:
+ case PCI_DEVICE_ID_FUJITSU_CORALPA:
+ par->fb_base_phys = pci_resource_start(par->pdev, 0);
+ par->mapped_vram = CORALP_MEM_SIZE;
+ par->mmio_base_phys = par->fb_base_phys + MB862XX_MMIO_BASE;
+ par->mmio_len = MB862XX_MMIO_SIZE;
+ par->type = BT_CORALP;
+ break;
+ case PCI_DEVICE_ID_FUJITSU_CARMINE:
+ par->fb_base_phys = pci_resource_start(par->pdev, 2);
+ par->mmio_base_phys = pci_resource_start(par->pdev, 3);
+ par->mmio_len = pci_resource_len(par->pdev, 3);
+ par->mapped_vram = CARMINE_MEM_SIZE;
+ par->type = BT_CARMINE;
+ break;
+ default:
+ /* should never occur */
+ goto rel_reg;
+ }
+
+ par->fb_base = ioremap(par->fb_base_phys, par->mapped_vram);
+ if (par->fb_base == NULL) {
+ dev_err(dev, "Cannot map framebuffer\n");
+ goto rel_reg;
+ }
+
+ par->mmio_base = ioremap(par->mmio_base_phys, par->mmio_len);
+ if (par->mmio_base == NULL) {
+ dev_err(dev, "Cannot map registers\n");
+ ret = -EIO;
+ goto fb_unmap;
+ }
+
+ dev_dbg(dev, "fb phys 0x%llx 0x%lx\n",
+ (u64)par->fb_base_phys, (ulong)par->mapped_vram);
+ dev_dbg(dev, "mmio phys 0x%llx 0x%lx\n",
+ (u64)par->mmio_base_phys, (ulong)par->mmio_len);
+
+ if (mb862xx_pci_gdc_init(par))
+ goto io_unmap;
+
+ if (request_irq(par->irq, mb862xx_intr, IRQF_DISABLED | IRQF_SHARED,
+ DRV_NAME, (void *)par)) {
+ dev_err(dev, "Cannot request irq\n");
+ goto io_unmap;
+ }
+
+ mb862xxfb_init_fbinfo(info);
+
+ if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0) < 0) {
+ dev_err(dev, "Could not allocate cmap for fb_info.\n");
+ ret = -ENOMEM;
+ goto free_irq;
+ }
+
+ if ((info->fbops->fb_set_par)(info))
+ dev_err(dev, "set_var() failed on initial setup?\n");
+
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_err(dev, "failed to register framebuffer\n");
+ goto rel_cmap;
+ }
+
+ pci_set_drvdata(pdev, info);
+
+ if (device_create_file(dev, &dev_attr_dispregs))
+ dev_err(dev, "Can't create sysfs regdump file\n");
+
+ if (par->type == BT_CARMINE)
+ outreg(ctrl, GC_CTRL_INT_MASK, GC_CARMINE_INT_EN);
+ else
+ outreg(host, GC_IMASK, GC_INT_EN);
+
+ return 0;
+
+rel_cmap:
+ fb_dealloc_cmap(&info->cmap);
+free_irq:
+ free_irq(par->irq, (void *)par);
+io_unmap:
+ iounmap(par->mmio_base);
+fb_unmap:
+ iounmap(par->fb_base);
+rel_reg:
+ pci_release_regions(pdev);
+rel_fb:
+ framebuffer_release(info);
+dis_dev:
+ pci_disable_device(pdev);
+out:
+ return ret;
+}
+
+static void __devexit mb862xx_pci_remove(struct pci_dev *pdev)
+{
+ struct fb_info *fbi = pci_get_drvdata(pdev);
+ struct mb862xxfb_par *par = fbi->par;
+ unsigned long reg;
+
+ dev_dbg(fbi->dev, "%s release\n", fbi->fix.id);
+
+ /* display off */
+ reg = inreg(disp, GC_DCM1);
+ reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E);
+ outreg(disp, GC_DCM1, reg);
+
+ if (par->type == BT_CARMINE) {
+ outreg(ctrl, GC_CTRL_INT_MASK, 0);
+ outreg(ctrl, GC_CTRL_CLK_ENABLE, 0);
+ } else {
+ outreg(host, GC_IMASK, 0);
+ }
+
+ device_remove_file(&pdev->dev, &dev_attr_dispregs);
+
+ pci_set_drvdata(pdev, NULL);
+ unregister_framebuffer(fbi);
+ fb_dealloc_cmap(&fbi->cmap);
+
+ free_irq(par->irq, (void *)par);
+ iounmap(par->mmio_base);
+ iounmap(par->fb_base);
+
+ pci_release_regions(pdev);
+ framebuffer_release(fbi);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver mb862xxfb_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = mb862xx_pci_tbl,
+ .probe = mb862xx_pci_probe,
+ .remove = __devexit_p(mb862xx_pci_remove),
+};
+#endif
+
+static int __devinit mb862xxfb_init(void)
+{
+ int ret = -ENODEV;
+
+#if defined(CONFIG_FB_MB862XX_LIME)
+ ret = of_register_platform_driver(&of_platform_mb862xxfb_driver);
+#endif
+#if defined(CONFIG_FB_MB862XX_PCI_GDC)
+ ret = pci_register_driver(&mb862xxfb_pci_driver);
+#endif
+ return ret;
+}
+
+static void __exit mb862xxfb_exit(void)
+{
+#if defined(CONFIG_FB_MB862XX_LIME)
+ of_unregister_platform_driver(&of_platform_mb862xxfb_driver);
+#endif
+#if defined(CONFIG_FB_MB862XX_PCI_GDC)
+ pci_unregister_driver(&mb862xxfb_pci_driver);
+#endif
+}
+
+module_init(mb862xxfb_init);
+module_exit(mb862xxfb_exit);
+
+MODULE_DESCRIPTION("Fujitsu MB862xx Framebuffer driver");
+MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+#ifndef __MB862XX_H__
+#define __MB862XX_H__
+
+#define PCI_VENDOR_ID_FUJITSU_LIMITED 0x10cf
+#define PCI_DEVICE_ID_FUJITSU_CORALP 0x2019
+#define PCI_DEVICE_ID_FUJITSU_CORALPA 0x201e
+#define PCI_DEVICE_ID_FUJITSU_CARMINE 0x202b
+
+#define GC_MMR_CORALP_EVB_VAL 0x11d7fa13
+
+enum gdctype {
+ BT_NONE,
+ BT_LIME,
+ BT_MINT,
+ BT_CORAL,
+ BT_CORALP,
+ BT_CARMINE,
+};
+
+struct mb862xx_gc_mode {
+ struct fb_videomode def_mode; /* mode of connected display */
+ unsigned int def_bpp; /* default depth */
+ unsigned long max_vram; /* connected SDRAM size */
+ unsigned long ccf; /* gdc clk */
+ unsigned long mmr; /* memory mode for SDRAM */
+};
+
+/* private data */
+struct mb862xxfb_par {
+ struct fb_info *info; /* fb info head */
+ struct device *dev;
+ struct pci_dev *pdev;
+ struct resource *res; /* framebuffer/mmio resource */
+
+ resource_size_t fb_base_phys; /* fb base, 36-bit PPC440EPx */
+ resource_size_t mmio_base_phys; /* io base addr */
+ void __iomem *fb_base; /* remapped framebuffer */
+ void __iomem *mmio_base; /* remapped registers */
+ size_t mapped_vram; /* length of remapped vram */
+ size_t mmio_len; /* length of register region */
+
+ void __iomem *host; /* relocatable reg. bases */
+ void __iomem *i2c;
+ void __iomem *disp;
+ void __iomem *disp1;
+ void __iomem *cap;
+ void __iomem *cap1;
+ void __iomem *draw;
+ void __iomem *geo;
+ void __iomem *pio;
+ void __iomem *ctrl;
+ void __iomem *dram_ctrl;
+ void __iomem *wrback;
+
+ unsigned int irq;
+ unsigned int type; /* GDC type */
+ unsigned int refclk; /* disp. reference clock */
+ struct mb862xx_gc_mode *gc_mode; /* GDC mode init data */
+ int pre_init; /* don't init display if 1 */
+
+ u32 pseudo_palette[16];
+};
+
+#if defined(CONFIG_FB_MB862XX_LIME) && defined(CONFIG_FB_MB862XX_PCI_GDC)
+#error "Select Lime GDC or CoralP/Carmine support, but not both together"
+#endif
+#if defined(CONFIG_FB_MB862XX_LIME)
+#define gdc_read __raw_readl
+#define gdc_write __raw_writel
+#else
+#define gdc_read readl
+#define gdc_write writel
+#endif
+
+#define inreg(type, off) \
+ gdc_read((par->type + (off)))
+
+#define outreg(type, off, val) \
+ gdc_write((val), (par->type + (off)))
+
+#define pack(a, b) (((a) << 16) | (b))
+
+#endif
#include <linux/bitops.h>
#include <linux/uaccess.h>
-#include <asm/arch/at91_wdt.h>
+#include <mach/at91_wdt.h>
#define DRV_NAME "AT91SAM9 Watchdog"
obj-$(CONFIG_CODA_FS) += coda/
obj-$(CONFIG_MINIX_FS) += minix/
obj-$(CONFIG_FAT_FS) += fat/
-obj-$(CONFIG_MSDOS_FS) += msdos/
-obj-$(CONFIG_VFAT_FS) += vfat/
obj-$(CONFIG_BFS_FS) += bfs/
obj-$(CONFIG_ISO9660_FS) += isofs/
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+
*/
static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param)
{
- int err = -EINVAL;
+ int err;
- if (check_dev_ioctl_version(cmd, param)) {
+ err = check_dev_ioctl_version(cmd, param);
+ if (err) {
AUTOFS_WARN("invalid device control module version "
"supplied for cmd(0x%08x)", cmd);
goto out;
mntget(mnt);
dget(dentry);
- if (!autofs4_follow_mount(&mnt, &dentry))
+ if (!follow_down(&mnt, &dentry))
goto done;
- /* This is an autofs submount, we can't expire it */
- if (is_autofs4_dentry(dentry))
- goto done;
+ if (is_autofs4_dentry(dentry)) {
+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+
+ /* This is an autofs submount, we can't expire it */
+ if (sbi->type == AUTOFS_TYPE_INDIRECT)
+ goto done;
+
+ /*
+ * Otherwise it's an offset mount and we need to check
+ * if we can umount its mount, if there is one.
+ */
+ if (!d_mountpoint(dentry))
+ goto done;
+ }
/* Update the expiry counter if fs is busy */
if (!may_umount_tree(mnt)) {
static int ext3_sync_fs(struct super_block *sb, int wait)
{
- tid_t target;
-
sb->s_dirt = 0;
- if (journal_start_commit(EXT3_SB(sb)->s_journal, &target)) {
- if (wait)
- log_wait_commit(EXT3_SB(sb)->s_journal, target);
- }
+ if (wait)
+ ext3_force_commit(sb);
+ else
+ journal_start_commit(EXT3_SB(sb)->s_journal, NULL);
+
return 0;
}
#
obj-$(CONFIG_FAT_FS) += fat.o
+obj-$(CONFIG_VFAT_FS) += vfat.o
+obj-$(CONFIG_MSDOS_FS) += msdos.o
-fat-objs := cache.o dir.o fatent.o file.o inode.o misc.o
+fat-y := cache.o dir.o fatent.o file.o inode.o misc.o
+vfat-y := namei_vfat.o
+msdos-y := namei_msdos.o
*/
#include <linux/fs.h>
-#include <linux/msdos_fs.h>
#include <linux/buffer_head.h>
+#include "fat.h"
/* this must be > 0. */
#define FAT_MAX_CACHE 8
}
int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
- unsigned long *mapped_blocks)
+ unsigned long *mapped_blocks, int create)
{
struct super_block *sb = inode->i_sb;
struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ const unsigned long blocksize = sb->s_blocksize;
+ const unsigned char blocksize_bits = sb->s_blocksize_bits;
sector_t last_block;
int cluster, offset;
}
return 0;
}
- last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
- >> sb->s_blocksize_bits;
- if (sector >= last_block)
- return 0;
+
+ last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits;
+ if (sector >= last_block) {
+ if (!create)
+ return 0;
+
+ /*
+ * ->mmu_private can access on only allocation path.
+ * (caller must hold ->i_mutex)
+ */
+ last_block = (MSDOS_I(inode)->mmu_private + (blocksize - 1))
+ >> blocksize_bits;
+ if (sector >= last_block)
+ return 0;
+ }
cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits);
offset = sector & (sbi->sec_per_clus - 1);
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/time.h>
-#include <linux/msdos_fs.h>
#include <linux/smp_lock.h>
#include <linux/buffer_head.h>
#include <linux/compat.h>
#include <asm/uaccess.h>
+#include "fat.h"
static inline loff_t fat_make_i_pos(struct super_block *sb,
struct buffer_head *bh,
*bh = NULL;
iblock = *pos >> sb->s_blocksize_bits;
- err = fat_bmap(dir, iblock, &phys, &mapped_blocks);
+ err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0);
if (err || !phys)
return -1; /* beyond EOF or error */
*bh = sb_bread(sb, phys);
if (*bh == NULL) {
printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n",
- (unsigned long long)phys);
+ (llu)phys);
/* skip this block */
*pos = (iblock + 1) << sb->s_blocksize_bits;
goto next;
if (de->attr == ATTR_EXT) {
int status = fat_parse_long(inode, &cpos, &bh, &de,
&unicode, &nr_slots);
- if (status < 0)
- return status;
- else if (status == PARSE_INVALID)
+ if (status < 0) {
+ err = status;
+ goto end_of_dir;
+ } else if (status == PARSE_INVALID)
continue;
else if (status == PARSE_NOT_LONGNAME)
goto parse_record;
#endif /* CONFIG_COMPAT */
const struct file_operations fat_dir_operations = {
+ .llseek = generic_file_llseek,
.read = generic_read_dir,
.readdir = fat_readdir,
.ioctl = fat_dir_ioctl,
struct msdos_dir_entry *de;
sector_t blknr;
__le16 date, time;
+ u8 time_cs;
int err, cluster;
err = fat_alloc_clusters(dir, &cluster, 1);
goto error_free;
}
- fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc);
+ fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);
de = (struct msdos_dir_entry *)bhs[0]->b_data;
/* filling the new directory slots ("." and ".." entries) */
de[0].lcase = de[1].lcase = 0;
de[0].time = de[1].time = time;
de[0].date = de[1].date = date;
- de[0].ctime_cs = de[1].ctime_cs = 0;
if (sbi->options.isvfat) {
/* extra timestamps */
de[0].ctime = de[1].ctime = time;
+ de[0].ctime_cs = de[1].ctime_cs = time_cs;
de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date;
} else {
de[0].ctime = de[1].ctime = 0;
+ de[0].ctime_cs = de[1].ctime_cs = 0;
de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0;
}
de[0].start = cpu_to_le16(cluster);
--- /dev/null
+#ifndef _FAT_H
+#define _FAT_H
+
+#include <linux/buffer_head.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/msdos_fs.h>
+
+/*
+ * vfat shortname flags
+ */
+#define VFAT_SFN_DISPLAY_LOWER 0x0001 /* convert to lowercase for display */
+#define VFAT_SFN_DISPLAY_WIN95 0x0002 /* emulate win95 rule for display */
+#define VFAT_SFN_DISPLAY_WINNT 0x0004 /* emulate winnt rule for display */
+#define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */
+#define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */
+
+struct fat_mount_options {
+ uid_t fs_uid;
+ gid_t fs_gid;
+ unsigned short fs_fmask;
+ unsigned short fs_dmask;
+ unsigned short codepage; /* Codepage for shortname conversions */
+ char *iocharset; /* Charset used for filename input/display */
+ unsigned short shortname; /* flags for shortname display/create rule */
+ unsigned char name_check; /* r = relaxed, n = normal, s = strict */
+ unsigned short allow_utime;/* permission for setting the [am]time */
+ unsigned quiet:1, /* set = fake successful chmods and chowns */
+ showexec:1, /* set = only set x bit for com/exe/bat */
+ sys_immutable:1, /* set = system files are immutable */
+ dotsOK:1, /* set = hidden and system files are named '.filename' */
+ isvfat:1, /* 0=no vfat long filename support, 1=vfat support */
+ utf8:1, /* Use of UTF-8 character set (Default) */
+ unicode_xlate:1, /* create escape sequences for unhandled Unicode */
+ numtail:1, /* Does first alias have a numeric '~1' type tail? */
+ flush:1, /* write things quickly */
+ nocase:1, /* Does this need case conversion? 0=need case conversion*/
+ usefree:1, /* Use free_clusters for FAT32 */
+ tz_utc:1, /* Filesystem timestamps are in UTC */
+ rodir:1; /* allow ATTR_RO for directory */
+};
+
+#define FAT_HASH_BITS 8
+#define FAT_HASH_SIZE (1UL << FAT_HASH_BITS)
+
+/*
+ * MS-DOS file system in-core superblock data
+ */
+struct msdos_sb_info {
+ unsigned short sec_per_clus; /* sectors/cluster */
+ unsigned short cluster_bits; /* log2(cluster_size) */
+ unsigned int cluster_size; /* cluster size */
+ unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */
+ unsigned short fat_start;
+ unsigned long fat_length; /* FAT start & length (sec.) */
+ unsigned long dir_start;
+ unsigned short dir_entries; /* root dir start & entries */
+ unsigned long data_start; /* first data sector */
+ unsigned long max_cluster; /* maximum cluster number */
+ unsigned long root_cluster; /* first cluster of the root directory */
+ unsigned long fsinfo_sector; /* sector number of FAT32 fsinfo */
+ struct mutex fat_lock;
+ unsigned int prev_free; /* previously allocated cluster number */
+ unsigned int free_clusters; /* -1 if undefined */
+ unsigned int free_clus_valid; /* is free_clusters valid? */
+ struct fat_mount_options options;
+ struct nls_table *nls_disk; /* Codepage used on disk */
+ struct nls_table *nls_io; /* Charset used for input and display */
+ const void *dir_ops; /* Opaque; default directory operations */
+ int dir_per_block; /* dir entries per block */
+ int dir_per_block_bits; /* log2(dir_per_block) */
+
+ int fatent_shift;
+ struct fatent_operations *fatent_ops;
+
+ spinlock_t inode_hash_lock;
+ struct hlist_head inode_hashtable[FAT_HASH_SIZE];
+};
+
+#define FAT_CACHE_VALID 0 /* special case for valid cache */
+
+/*
+ * MS-DOS file system inode data in memory
+ */
+struct msdos_inode_info {
+ spinlock_t cache_lru_lock;
+ struct list_head cache_lru;
+ int nr_caches;
+ /* for avoiding the race between fat_free() and fat_get_cluster() */
+ unsigned int cache_valid_id;
+
+ /* NOTE: mmu_private is 64bits, so must hold ->i_mutex to access */
+ loff_t mmu_private; /* physically allocated size */
+
+ int i_start; /* first cluster or 0 */
+ int i_logstart; /* logical first cluster */
+ int i_attrs; /* unused attribute bits */
+ loff_t i_pos; /* on-disk position of directory entry or 0 */
+ struct hlist_node i_fat_hash; /* hash by i_location */
+ struct inode vfs_inode;
+};
+
+struct fat_slot_info {
+ loff_t i_pos; /* on-disk position of directory entry */
+ loff_t slot_off; /* offset for slot or de start */
+ int nr_slots; /* number of slots + 1(de) in filename */
+ struct msdos_dir_entry *de;
+ struct buffer_head *bh;
+};
+
+static inline struct msdos_sb_info *MSDOS_SB(struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+
+static inline struct msdos_inode_info *MSDOS_I(struct inode *inode)
+{
+ return container_of(inode, struct msdos_inode_info, vfs_inode);
+}
+
+/*
+ * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to
+ * save ATTR_RO instead of ->i_mode.
+ *
+ * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only
+ * bit, it's just used as flag for app.
+ */
+static inline int fat_mode_can_hold_ro(struct inode *inode)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
+ mode_t mask;
+
+ if (S_ISDIR(inode->i_mode)) {
+ if (!sbi->options.rodir)
+ return 0;
+ mask = ~sbi->options.fs_dmask;
+ } else
+ mask = ~sbi->options.fs_fmask;
+
+ if (!(mask & S_IWUGO))
+ return 0;
+ return 1;
+}
+
+/* Convert attribute bits and a mask to the UNIX mode. */
+static inline mode_t fat_make_mode(struct msdos_sb_info *sbi,
+ u8 attrs, mode_t mode)
+{
+ if (attrs & ATTR_RO && !((attrs & ATTR_DIR) && !sbi->options.rodir))
+ mode &= ~S_IWUGO;
+
+ if (attrs & ATTR_DIR)
+ return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
+ else
+ return (mode & ~sbi->options.fs_fmask) | S_IFREG;
+}
+
+/* Return the FAT attribute byte for this inode */
+static inline u8 fat_make_attrs(struct inode *inode)
+{
+ u8 attrs = MSDOS_I(inode)->i_attrs;
+ if (S_ISDIR(inode->i_mode))
+ attrs |= ATTR_DIR;
+ if (fat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO))
+ attrs |= ATTR_RO;
+ return attrs;
+}
+
+static inline void fat_save_attrs(struct inode *inode, u8 attrs)
+{
+ if (fat_mode_can_hold_ro(inode))
+ MSDOS_I(inode)->i_attrs = attrs & ATTR_UNUSED;
+ else
+ MSDOS_I(inode)->i_attrs = attrs & (ATTR_UNUSED | ATTR_RO);
+}
+
+static inline unsigned char fat_checksum(const __u8 *name)
+{
+ unsigned char s = name[0];
+ s = (s<<7) + (s>>1) + name[1]; s = (s<<7) + (s>>1) + name[2];
+ s = (s<<7) + (s>>1) + name[3]; s = (s<<7) + (s>>1) + name[4];
+ s = (s<<7) + (s>>1) + name[5]; s = (s<<7) + (s>>1) + name[6];
+ s = (s<<7) + (s>>1) + name[7]; s = (s<<7) + (s>>1) + name[8];
+ s = (s<<7) + (s>>1) + name[9]; s = (s<<7) + (s>>1) + name[10];
+ return s;
+}
+
+static inline sector_t fat_clus_to_blknr(struct msdos_sb_info *sbi, int clus)
+{
+ return ((sector_t)clus - FAT_START_ENT) * sbi->sec_per_clus
+ + sbi->data_start;
+}
+
+static inline void fat16_towchar(wchar_t *dst, const __u8 *src, size_t len)
+{
+#ifdef __BIG_ENDIAN
+ while (len--) {
+ *dst++ = src[0] | (src[1] << 8);
+ src += 2;
+ }
+#else
+ memcpy(dst, src, len * 2);
+#endif
+}
+
+static inline void fatwchar_to16(__u8 *dst, const wchar_t *src, size_t len)
+{
+#ifdef __BIG_ENDIAN
+ while (len--) {
+ dst[0] = *src & 0x00FF;
+ dst[1] = (*src & 0xFF00) >> 8;
+ dst += 2;
+ src++;
+ }
+#else
+ memcpy(dst, src, len * 2);
+#endif
+}
+
+/* fat/cache.c */
+extern void fat_cache_inval_inode(struct inode *inode);
+extern int fat_get_cluster(struct inode *inode, int cluster,
+ int *fclus, int *dclus);
+extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
+ unsigned long *mapped_blocks, int create);
+
+/* fat/dir.c */
+extern const struct file_operations fat_dir_operations;
+extern int fat_search_long(struct inode *inode, const unsigned char *name,
+ int name_len, struct fat_slot_info *sinfo);
+extern int fat_dir_empty(struct inode *dir);
+extern int fat_subdirs(struct inode *dir);
+extern int fat_scan(struct inode *dir, const unsigned char *name,
+ struct fat_slot_info *sinfo);
+extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
+ struct msdos_dir_entry **de, loff_t *i_pos);
+extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts);
+extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
+ struct fat_slot_info *sinfo);
+extern int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo);
+
+/* fat/fatent.c */
+struct fat_entry {
+ int entry;
+ union {
+ u8 *ent12_p[2];
+ __le16 *ent16_p;
+ __le32 *ent32_p;
+ } u;
+ int nr_bhs;
+ struct buffer_head *bhs[2];
+};
+
+static inline void fatent_init(struct fat_entry *fatent)
+{
+ fatent->nr_bhs = 0;
+ fatent->entry = 0;
+ fatent->u.ent32_p = NULL;
+ fatent->bhs[0] = fatent->bhs[1] = NULL;
+}
+
+static inline void fatent_set_entry(struct fat_entry *fatent, int entry)
+{
+ fatent->entry = entry;
+ fatent->u.ent32_p = NULL;
+}
+
+static inline void fatent_brelse(struct fat_entry *fatent)
+{
+ int i;
+ fatent->u.ent32_p = NULL;
+ for (i = 0; i < fatent->nr_bhs; i++)
+ brelse(fatent->bhs[i]);
+ fatent->nr_bhs = 0;
+ fatent->bhs[0] = fatent->bhs[1] = NULL;
+}
+
+extern void fat_ent_access_init(struct super_block *sb);
+extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent,
+ int entry);
+extern int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
+ int new, int wait);
+extern int fat_alloc_clusters(struct inode *inode, int *cluster,
+ int nr_cluster);
+extern int fat_free_clusters(struct inode *inode, int cluster);
+extern int fat_count_free_clusters(struct super_block *sb);
+
+/* fat/file.c */
+extern int fat_generic_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+extern const struct file_operations fat_file_operations;
+extern const struct inode_operations fat_file_inode_operations;
+extern int fat_setattr(struct dentry * dentry, struct iattr * attr);
+extern void fat_truncate(struct inode *inode);
+extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *stat);
+
+/* fat/inode.c */
+extern void fat_attach(struct inode *inode, loff_t i_pos);
+extern void fat_detach(struct inode *inode);
+extern struct inode *fat_iget(struct super_block *sb, loff_t i_pos);
+extern struct inode *fat_build_inode(struct super_block *sb,
+ struct msdos_dir_entry *de, loff_t i_pos);
+extern int fat_sync_inode(struct inode *inode);
+extern int fat_fill_super(struct super_block *sb, void *data, int silent,
+ const struct inode_operations *fs_dir_inode_ops, int isvfat);
+
+extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
+ struct inode *i2);
+/* fat/misc.c */
+extern void fat_fs_panic(struct super_block *s, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3))) __cold;
+extern void fat_clusters_flush(struct super_block *sb);
+extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster);
+extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
+ __le16 __time, __le16 __date, u8 time_cs);
+extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts,
+ __le16 *time, __le16 *date, u8 *time_cs);
+extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs);
+
+int fat_cache_init(void);
+void fat_cache_destroy(void);
+
+/* helper for printk */
+typedef unsigned long long llu;
+
+#endif /* !_FAT_H */
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/blkdev.h>
+#include "fat.h"
struct fatent_operations {
void (*ent_blocknr)(struct super_block *, int, int *, sector_t *);
err_brelse:
brelse(bhs[0]);
err:
- printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
- (unsigned long long)blocknr);
+ printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n", (llu)blocknr);
return -EIO;
}
fatent->bhs[0] = sb_bread(sb, blocknr);
if (!fatent->bhs[0]) {
printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
- (unsigned long long)blocknr);
+ (llu)blocknr);
return -EIO;
}
fatent->nr_bhs = 1;
/* Is this fatent's blocks including this entry? */
if (!fatent->nr_bhs || bhs[0]->b_blocknr != blocknr)
return 0;
- /* Does this entry need the next block? */
- if (sbi->fat_bits == 12 && (offset + 1) >= sb->s_blocksize) {
- if (fatent->nr_bhs != 2 || bhs[1]->b_blocknr != (blocknr + 1))
- return 0;
+ if (sbi->fat_bits == 12) {
+ if ((offset + 1) < sb->s_blocksize) {
+ /* This entry is on bhs[0]. */
+ if (fatent->nr_bhs == 2) {
+ brelse(bhs[1]);
+ fatent->nr_bhs = 1;
+ }
+ } else {
+ /* This entry needs the next block. */
+ if (fatent->nr_bhs != 2)
+ return 0;
+ if (bhs[1]->b_blocknr != (blocknr + 1))
+ return 0;
+ }
}
ops->ent_set_ptr(fatent, offset);
return 1;
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/time.h>
-#include <linux/msdos_fs.h>
#include <linux/buffer_head.h>
#include <linux/writeback.h>
#include <linux/backing-dev.h>
#include <linux/blkdev.h>
#include <linux/fsnotify.h>
#include <linux/security.h>
+#include "fat.h"
int fat_generic_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
u32 attr;
- if (inode->i_ino == MSDOS_ROOT_INO)
- attr = ATTR_DIR;
- else
- attr = fat_attr(inode);
+ mutex_lock(&inode->i_mutex);
+ attr = fat_make_attrs(inode);
+ mutex_unlock(&inode->i_mutex);
return put_user(attr, user_attr);
}
/* Merge in ATTR_VOLUME and ATTR_DIR */
attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
(is_dir ? ATTR_DIR : 0);
- oldattr = fat_attr(inode);
+ oldattr = fat_make_attrs(inode);
/* Equivalent to a chmod() */
ia.ia_valid = ATTR_MODE | ATTR_CTIME;
ia.ia_ctime = current_fs_time(inode->i_sb);
- if (is_dir) {
- ia.ia_mode = MSDOS_MKMODE(attr,
- S_IRWXUGO & ~sbi->options.fs_dmask)
- | S_IFDIR;
- } else {
- ia.ia_mode = MSDOS_MKMODE(attr,
- (S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO))
- & ~sbi->options.fs_fmask)
- | S_IFREG;
+ if (is_dir)
+ ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO);
+ else {
+ ia.ia_mode = fat_make_mode(sbi, attr,
+ S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO));
}
/* The root directory has no attributes */
inode->i_flags &= S_IMMUTABLE;
}
- MSDOS_I(inode)->i_attrs = attr & ATTR_UNUSED;
+ fat_save_attrs(inode, attr);
mark_inode_dirty(inode);
up:
mnt_drop_write(filp->f_path.mnt);
/*
* Note, the basic check is already done by a caller of
- * (attr->ia_mode & ~MSDOS_VALID_MODE)
+ * (attr->ia_mode & ~FAT_VALID_MODE)
*/
if (S_ISREG(inode->i_mode))
/*
* Of the r and x bits, all (subject to umask) must be present. Of the
* w bits, either all (subject to umask) or none must be present.
+ *
+ * If fat_mode_can_hold_ro(inode) is false, can't change w bits.
*/
if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
return -EPERM;
- if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
- return -EPERM;
+ if (fat_mode_can_hold_ro(inode)) {
+ if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
+ return -EPERM;
+ } else {
+ if ((perm & S_IWUGO) != (S_IWUGO & ~mask))
+ return -EPERM;
+ }
*mode_ptr &= S_IFMT | perm;
}
#define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
+/* valid file mode bits */
+#define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO)
int fat_setattr(struct dentry *dentry, struct iattr *attr)
{
struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
struct inode *inode = dentry->d_inode;
- int error = 0;
unsigned int ia_valid;
+ int error;
/*
* Expand the file. Since inode_setattr() updates ->i_size
((attr->ia_valid & ATTR_GID) &&
(attr->ia_gid != sbi->options.fs_gid)) ||
((attr->ia_valid & ATTR_MODE) &&
- (attr->ia_mode & ~MSDOS_VALID_MODE)))
+ (attr->ia_mode & ~FAT_VALID_MODE)))
error = -EPERM;
if (error) {
attr->ia_valid &= ~ATTR_MODE;
}
- error = inode_setattr(inode, attr);
+ if (attr->ia_valid)
+ error = inode_setattr(inode, attr);
out:
return error;
}
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/seq_file.h>
-#include <linux/msdos_fs.h>
#include <linux/pagemap.h>
#include <linux/mpage.h>
#include <linux/buffer_head.h>
#include <linux/uio.h>
#include <linux/writeback.h>
#include <linux/log2.h>
+#include <linux/hash.h>
#include <asm/unaligned.h>
+#include "fat.h"
#ifndef CONFIG_FAT_DEFAULT_IOCHARSET
/* if user don't select VFAT, this is undefined. */
sector_t phys;
int err, offset;
- err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
+ err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create);
if (err)
return err;
if (phys) {
*max_blocks = min(mapped_blocks, *max_blocks);
MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits;
- err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
+ err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create);
if (err)
return err;
static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
{
- return generic_block_bmap(mapping, block, fat_get_block);
+ sector_t blocknr;
+
+ /* fat_get_cluster() assumes the requested blocknr isn't truncated. */
+ mutex_lock(&mapping->host->i_mutex);
+ blocknr = generic_block_bmap(mapping, block, fat_get_block);
+ mutex_unlock(&mapping->host->i_mutex);
+
+ return blocknr;
}
static const struct address_space_operations fat_aops = {
INIT_HLIST_HEAD(&sbi->inode_hashtable[i]);
}
-static inline unsigned long fat_hash(struct super_block *sb, loff_t i_pos)
+static inline unsigned long fat_hash(loff_t i_pos)
{
- unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb;
- tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS * 2);
- return tmp & FAT_HASH_MASK;
+ return hash_32(i_pos, FAT_HASH_BITS);
}
void fat_attach(struct inode *inode, loff_t i_pos)
{
- struct super_block *sb = inode->i_sb;
- struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
+ struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
spin_lock(&sbi->inode_hash_lock);
MSDOS_I(inode)->i_pos = i_pos;
- hlist_add_head(&MSDOS_I(inode)->i_fat_hash,
- sbi->inode_hashtable + fat_hash(sb, i_pos));
+ hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head);
spin_unlock(&sbi->inode_hash_lock);
}
-
EXPORT_SYMBOL_GPL(fat_attach);
void fat_detach(struct inode *inode)
hlist_del_init(&MSDOS_I(inode)->i_fat_hash);
spin_unlock(&sbi->inode_hash_lock);
}
-
EXPORT_SYMBOL_GPL(fat_detach);
struct inode *fat_iget(struct super_block *sb, loff_t i_pos)
{
struct msdos_sb_info *sbi = MSDOS_SB(sb);
- struct hlist_head *head = sbi->inode_hashtable + fat_hash(sb, i_pos);
+ struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
struct hlist_node *_p;
struct msdos_inode_info *i;
struct inode *inode = NULL;
if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
inode->i_generation &= ~1;
- inode->i_mode = MSDOS_MKMODE(de->attr,
- S_IRWXUGO & ~sbi->options.fs_dmask) | S_IFDIR;
+ inode->i_mode = fat_make_mode(sbi, de->attr, S_IRWXUGO);
inode->i_op = sbi->dir_ops;
inode->i_fop = &fat_dir_operations;
inode->i_nlink = fat_subdirs(inode);
} else { /* not a directory */
inode->i_generation |= 1;
- inode->i_mode = MSDOS_MKMODE(de->attr,
- ((sbi->options.showexec && !is_exec(de->name + 8))
- ? S_IRUGO|S_IWUGO : S_IRWXUGO)
- & ~sbi->options.fs_fmask) | S_IFREG;
+ inode->i_mode = fat_make_mode(sbi, de->attr,
+ ((sbi->options.showexec && !is_exec(de->name + 8))
+ ? S_IRUGO|S_IWUGO : S_IRWXUGO));
MSDOS_I(inode)->i_start = le16_to_cpu(de->start);
if (sbi->fat_bits == 32)
MSDOS_I(inode)->i_start |= (le16_to_cpu(de->starthi) << 16);
if (sbi->options.sys_immutable)
inode->i_flags |= S_IMMUTABLE;
}
- MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED;
+ fat_save_attrs(inode, de->attr);
+
inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1))
& ~((loff_t)sbi->cluster_size - 1)) >> 9;
- inode->i_mtime.tv_sec =
- date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date),
- sbi->options.tz_utc);
- inode->i_mtime.tv_nsec = 0;
+
+ fat_time_fat2unix(sbi, &inode->i_mtime, de->time, de->date, 0);
if (sbi->options.isvfat) {
- int secs = de->ctime_cs / 100;
- int csecs = de->ctime_cs % 100;
- inode->i_ctime.tv_sec =
- date_dos2unix(le16_to_cpu(de->ctime),
- le16_to_cpu(de->cdate),
- sbi->options.tz_utc) + secs;
- inode->i_ctime.tv_nsec = csecs * 10000000;
- inode->i_atime.tv_sec =
- date_dos2unix(0, le16_to_cpu(de->adate),
- sbi->options.tz_utc);
- inode->i_atime.tv_nsec = 0;
+ fat_time_fat2unix(sbi, &inode->i_ctime, de->ctime,
+ de->cdate, de->ctime_cs);
+ fat_time_fat2unix(sbi, &inode->i_atime, 0, de->adate, 0);
} else
inode->i_ctime = inode->i_atime = inode->i_mtime;
static void fat_clear_inode(struct inode *inode)
{
- struct super_block *sb = inode->i_sb;
- struct msdos_sb_info *sbi = MSDOS_SB(sb);
-
- spin_lock(&sbi->inode_hash_lock);
fat_cache_inval_inode(inode);
- hlist_del_init(&MSDOS_I(inode)->i_fat_hash);
- spin_unlock(&sbi->inode_hash_lock);
+ fat_detach(inode);
}
static void fat_write_super(struct super_block *sb)
return 0;
}
+static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
+ struct inode *inode)
+{
+ loff_t i_pos;
+#if BITS_PER_LONG == 32
+ spin_lock(&sbi->inode_hash_lock);
+#endif
+ i_pos = MSDOS_I(inode)->i_pos;
+#if BITS_PER_LONG == 32
+ spin_unlock(&sbi->inode_hash_lock);
+#endif
+ return i_pos;
+}
+
static int fat_write_inode(struct inode *inode, int wait)
{
struct super_block *sb = inode->i_sb;
loff_t i_pos;
int err;
+ if (inode->i_ino == MSDOS_ROOT_INO)
+ return 0;
+
retry:
- i_pos = MSDOS_I(inode)->i_pos;
- if (inode->i_ino == MSDOS_ROOT_INO || !i_pos)
+ i_pos = fat_i_pos_read(sbi, inode);
+ if (!i_pos)
return 0;
bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits);
raw_entry->size = 0;
else
raw_entry->size = cpu_to_le32(inode->i_size);
- raw_entry->attr = fat_attr(inode);
+ raw_entry->attr = fat_make_attrs(inode);
raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart);
raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16);
- fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time,
- &raw_entry->date, sbi->options.tz_utc);
+ fat_time_unix2fat(sbi, &inode->i_mtime, &raw_entry->time,
+ &raw_entry->date, NULL);
if (sbi->options.isvfat) {
__le16 atime;
- fat_date_unix2dos(inode->i_ctime.tv_sec, &raw_entry->ctime,
- &raw_entry->cdate, sbi->options.tz_utc);
- fat_date_unix2dos(inode->i_atime.tv_sec, &atime,
- &raw_entry->adate, sbi->options.tz_utc);
- raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 +
- inode->i_ctime.tv_nsec / 10000000;
+ fat_time_unix2fat(sbi, &inode->i_ctime, &raw_entry->ctime,
+ &raw_entry->cdate, &raw_entry->ctime_cs);
+ fat_time_unix2fat(sbi, &inode->i_atime, &atime,
+ &raw_entry->adate, NULL);
}
spin_unlock(&sbi->inode_hash_lock);
mark_buffer_dirty(bh);
seq_puts(m, ",uni_xlate");
if (!opts->numtail)
seq_puts(m, ",nonumtail");
+ if (opts->rodir)
+ seq_puts(m, ",rodir");
}
- if (sbi->options.flush)
+ if (opts->flush)
seq_puts(m, ",flush");
if (opts->tz_utc)
seq_puts(m, ",tz=UTC");
Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
- Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err,
+ Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err,
};
static const match_table_t fat_tokens = {
{Opt_nonumtail_yes, "nonumtail=yes"},
{Opt_nonumtail_yes, "nonumtail=true"},
{Opt_nonumtail_yes, "nonumtail"},
+ {Opt_rodir, "rodir"},
{Opt_err, NULL}
};
opts->allow_utime = -1;
opts->codepage = fat_default_codepage;
opts->iocharset = fat_default_iocharset;
- if (is_vfat)
+ if (is_vfat) {
opts->shortname = VFAT_SFN_DISPLAY_LOWER|VFAT_SFN_CREATE_WIN95;
- else
+ opts->rodir = 0;
+ } else {
opts->shortname = 0;
+ opts->rodir = 1;
+ }
opts->name_check = 'n';
opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK = 0;
opts->utf8 = opts->unicode_xlate = 0;
case Opt_nonumtail_yes: /* empty or 1 or yes or true */
opts->numtail = 0; /* negated option */
break;
+ case Opt_rodir:
+ opts->rodir = 1;
+ break;
/* obsolete mount options */
case Opt_obsolate:
inode->i_gid = sbi->options.fs_gid;
inode->i_version++;
inode->i_generation = 0;
- inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_dmask) | S_IFDIR;
+ inode->i_mode = fat_make_mode(sbi, ATTR_DIR, S_IRWXUGO);
inode->i_op = sbi->dir_ops;
inode->i_fop = &fat_dir_operations;
if (sbi->fat_bits == 32) {
MSDOS_I(inode)->i_logstart = 0;
MSDOS_I(inode)->mmu_private = inode->i_size;
- MSDOS_I(inode)->i_attrs = ATTR_NONE;
+ fat_save_attrs(inode, ATTR_DIR);
inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = 0;
inode->i_mtime.tv_nsec = inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = 0;
inode->i_nlink = fat_subdirs(inode)+2;
#include <linux/module.h>
#include <linux/fs.h>
-#include <linux/msdos_fs.h>
#include <linux/buffer_head.h>
+#include "fat.h"
/*
* fat_fs_panic reports a severe file system problem and sets the file system
mark_inode_dirty(inode);
}
if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) {
- fat_fs_panic(sb, "clusters badly computed (%d != %lu)",
- new_fclus, inode->i_blocks >> (sbi->cluster_bits - 9));
+ fat_fs_panic(sb, "clusters badly computed (%d != %llu)",
+ new_fclus,
+ (llu)(inode->i_blocks >> (sbi->cluster_bits - 9)));
fat_cache_inval_inode(inode);
}
inode->i_blocks += nr_cluster << (sbi->cluster_bits - 9);
extern struct timezone sys_tz;
+/*
+ * The epoch of FAT timestamp is 1980.
+ * : bits : value
+ * date: 0 - 4: day (1 - 31)
+ * date: 5 - 8: month (1 - 12)
+ * date: 9 - 15: year (0 - 127) from 1980
+ * time: 0 - 4: sec (0 - 29) 2sec counts
+ * time: 5 - 10: min (0 - 59)
+ * time: 11 - 15: hour (0 - 23)
+ */
+#define SECS_PER_MIN 60
+#define SECS_PER_HOUR (60 * 60)
+#define SECS_PER_DAY (SECS_PER_HOUR * 24)
+#define UNIX_SECS_1980 315532800L
+#if BITS_PER_LONG == 64
+#define UNIX_SECS_2108 4354819200L
+#endif
+/* days between 1.1.70 and 1.1.80 (2 leap days) */
+#define DAYS_DELTA (365 * 10 + 2)
+/* 120 (2100 - 1980) isn't leap year */
+#define YEAR_2100 120
+#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != YEAR_2100)
+
/* Linear day numbers of the respective 1sts in non-leap years. */
-static int day_n[] = {
- /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
- 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0
+static time_t days_in_year[] = {
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
+ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
};
-/* 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 tz_utc)
+/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
+void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
+ __le16 __time, __le16 __date, u8 time_cs)
{
- int month, year, secs;
+ u16 time = le16_to_cpu(__time), date = le16_to_cpu(__date);
+ time_t second, day, leap_day, month, year;
- /*
- * first subtract and mask after that... Otherwise, if
- * date == 0, bad things happen
- */
- month = ((date >> 5) - 1) & 15;
- year = date >> 9;
- 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 */
- if (!tz_utc)
- secs += sys_tz.tz_minuteswest*60;
- return secs;
+ year = date >> 9;
+ month = max(1, (date >> 5) & 0xf);
+ day = max(1, date & 0x1f) - 1;
+
+ leap_day = (year + 3) / 4;
+ if (year > YEAR_2100) /* 2100 isn't leap year */
+ leap_day--;
+ if (IS_LEAP_YEAR(year) && month > 2)
+ leap_day++;
+
+ second = (time & 0x1f) << 1;
+ second += ((time >> 5) & 0x3f) * SECS_PER_MIN;
+ second += (time >> 11) * SECS_PER_HOUR;
+ second += (year * 365 + leap_day
+ + days_in_year[month] + day
+ + DAYS_DELTA) * SECS_PER_DAY;
+
+ if (!sbi->options.tz_utc)
+ second += sys_tz.tz_minuteswest * SECS_PER_MIN;
+
+ if (time_cs) {
+ ts->tv_sec = second + (time_cs / 100);
+ ts->tv_nsec = (time_cs % 100) * 10000000;
+ } else {
+ ts->tv_sec = second;
+ ts->tv_nsec = 0;
+ }
}
-/* Convert linear UNIX date to a MS-DOS time/date pair. */
-void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, int tz_utc)
+/* Convert linear UNIX date to a FAT time/date pair. */
+void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts,
+ __le16 *time, __le16 *date, u8 *time_cs)
{
- int day, year, nl_day, month;
+ time_t second = ts->tv_sec;
+ time_t day, leap_day, month, year;
- if (!tz_utc)
- unix_date -= sys_tz.tz_minuteswest*60;
+ if (!sbi->options.tz_utc)
+ second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
- if (unix_date < 315532800)
- unix_date = 315532800;
-
- *time = cpu_to_le16((unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
- (((unix_date/3600) % 24) << 11));
- day = unix_date/86400-3652;
- year = day/365;
- if ((year+3)/4+365*year > day)
+ if (second < UNIX_SECS_1980) {
+ *time = 0;
+ *date = cpu_to_le16((0 << 9) | (1 << 5) | 1);
+ if (time_cs)
+ *time_cs = 0;
+ return;
+ }
+#if BITS_PER_LONG == 64
+ if (second >= UNIX_SECS_2108) {
+ *time = cpu_to_le16((23 << 11) | (59 << 5) | 29);
+ *date = cpu_to_le16((127 << 9) | (12 << 5) | 31);
+ if (time_cs)
+ *time_cs = 199;
+ return;
+ }
+#endif
+
+ day = second / SECS_PER_DAY - DAYS_DELTA;
+ year = day / 365;
+ leap_day = (year + 3) / 4;
+ if (year > YEAR_2100) /* 2100 isn't leap year */
+ leap_day--;
+ if (year * 365 + leap_day > day)
year--;
- day -= (year+3)/4+365*year;
- if (day == 59 && !(year & 3)) {
- nl_day = day;
+ leap_day = (year + 3) / 4;
+ if (year > YEAR_2100) /* 2100 isn't leap year */
+ leap_day--;
+ day -= year * 365 + leap_day;
+
+ if (IS_LEAP_YEAR(year) && day == days_in_year[3]) {
month = 2;
} else {
- nl_day = (year & 3) || day <= 59 ? day : day-1;
- for (month = 0; month < 12; month++) {
- if (day_n[month] > nl_day)
+ if (IS_LEAP_YEAR(year) && day > days_in_year[3])
+ day--;
+ for (month = 1; month < 12; month++) {
+ if (days_in_year[month + 1] > day)
break;
}
}
- *date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9));
-}
+ day -= days_in_year[month];
-EXPORT_SYMBOL_GPL(fat_date_unix2dos);
+ *time = cpu_to_le16(((second / SECS_PER_HOUR) % 24) << 11
+ | ((second / SECS_PER_MIN) % 60) << 5
+ | (second % SECS_PER_MIN) >> 1);
+ *date = cpu_to_le16((year << 9) | (month << 5) | (day + 1));
+ if (time_cs)
+ *time_cs = (ts->tv_sec & 1) * 100 + ts->tv_nsec / 10000000;
+}
+EXPORT_SYMBOL_GPL(fat_time_unix2fat);
int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
{
--- /dev/null
+/*
+ * linux/fs/msdos/namei.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
+ * Rewritten for constant inumbers 1999 by Al Viro
+ */
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/buffer_head.h>
+#include <linux/smp_lock.h>
+#include "fat.h"
+
+/* Characters that are undesirable in an MS-DOS file name */
+static unsigned char bad_chars[] = "*?<>|\"";
+static unsigned char bad_if_strict[] = "+=,; ";
+
+/***** Formats an MS-DOS file name. Rejects invalid names. */
+static int msdos_format_name(const unsigned char *name, int len,
+ unsigned char *res, struct fat_mount_options *opts)
+ /*
+ * name is the proposed name, len is its length, res is
+ * the resulting name, opts->name_check is either (r)elaxed,
+ * (n)ormal or (s)trict, opts->dotsOK allows dots at the
+ * beginning of name (for hidden files)
+ */
+{
+ unsigned char *walk;
+ unsigned char c;
+ int space;
+
+ if (name[0] == '.') { /* dotfile because . and .. already done */
+ if (opts->dotsOK) {
+ /* Get rid of dot - test for it elsewhere */
+ name++;
+ len--;
+ } else
+ return -EINVAL;
+ }
+ /*
+ * disallow names that _really_ start with a dot
+ */
+ space = 1;
+ c = 0;
+ for (walk = res; len && walk - res < 8; walk++) {
+ c = *name++;
+ len--;
+ if (opts->name_check != 'r' && strchr(bad_chars, c))
+ return -EINVAL;
+ if (opts->name_check == 's' && strchr(bad_if_strict, c))
+ return -EINVAL;
+ if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
+ return -EINVAL;
+ if (c < ' ' || c == ':' || c == '\\')
+ return -EINVAL;
+ /*
+ * 0xE5 is legal as a first character, but we must substitute
+ * 0x05 because 0xE5 marks deleted files. Yes, DOS really
+ * does this.
+ * It seems that Microsoft hacked DOS to support non-US
+ * characters after the 0xE5 character was already in use to
+ * mark deleted files.
+ */
+ if ((res == walk) && (c == 0xE5))
+ c = 0x05;
+ if (c == '.')
+ break;
+ space = (c == ' ');
+ *walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c - 32 : c;
+ }
+ if (space)
+ return -EINVAL;
+ if (opts->name_check == 's' && len && c != '.') {
+ c = *name++;
+ len--;
+ if (c != '.')
+ return -EINVAL;
+ }
+ while (c != '.' && len--)
+ c = *name++;
+ if (c == '.') {
+ while (walk - res < 8)
+ *walk++ = ' ';
+ while (len > 0 && walk - res < MSDOS_NAME) {
+ c = *name++;
+ len--;
+ if (opts->name_check != 'r' && strchr(bad_chars, c))
+ return -EINVAL;
+ if (opts->name_check == 's' &&
+ strchr(bad_if_strict, c))
+ return -EINVAL;
+ if (c < ' ' || c == ':' || c == '\\')
+ return -EINVAL;
+ if (c == '.') {
+ if (opts->name_check == 's')
+ return -EINVAL;
+ break;
+ }
+ if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
+ return -EINVAL;
+ space = c == ' ';
+ if (!opts->nocase && c >= 'a' && c <= 'z')
+ *walk++ = c - 32;
+ else
+ *walk++ = c;
+ }
+ if (space)
+ return -EINVAL;
+ if (opts->name_check == 's' && len)
+ return -EINVAL;
+ }
+ while (walk - res < MSDOS_NAME)
+ *walk++ = ' ';
+
+ return 0;
+}
+
+/***** Locates a directory entry. Uses unformatted name. */
+static int msdos_find(struct inode *dir, const unsigned char *name, int len,
+ struct fat_slot_info *sinfo)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
+ unsigned char msdos_name[MSDOS_NAME];
+ int err;
+
+ err = msdos_format_name(name, len, msdos_name, &sbi->options);
+ if (err)
+ return -ENOENT;
+
+ err = fat_scan(dir, msdos_name, sinfo);
+ if (!err && sbi->options.dotsOK) {
+ if (name[0] == '.') {
+ if (!(sinfo->de->attr & ATTR_HIDDEN))
+ err = -ENOENT;
+ } else {
+ if (sinfo->de->attr & ATTR_HIDDEN)
+ err = -ENOENT;
+ }
+ if (err)
+ brelse(sinfo->bh);
+ }
+ return err;
+}
+
+/*
+ * Compute the hash for the msdos name corresponding to the dentry.
+ * Note: if the name is invalid, we leave the hash code unchanged so
+ * that the existing dentry can be used. The msdos fs routines will
+ * return ENOENT or EINVAL as appropriate.
+ */
+static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
+ unsigned char msdos_name[MSDOS_NAME];
+ int error;
+
+ error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
+ if (!error)
+ qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
+ return 0;
+}
+
+/*
+ * Compare two msdos names. If either of the names are invalid,
+ * we fall back to doing the standard name comparison.
+ */
+static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
+ unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
+ int error;
+
+ error = msdos_format_name(a->name, a->len, a_msdos_name, options);
+ if (error)
+ goto old_compare;
+ error = msdos_format_name(b->name, b->len, b_msdos_name, options);
+ if (error)
+ goto old_compare;
+ error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
+out:
+ return error;
+
+old_compare:
+ error = 1;
+ if (a->len == b->len)
+ error = memcmp(a->name, b->name, a->len);
+ goto out;
+}
+
+static struct dentry_operations msdos_dentry_operations = {
+ .d_hash = msdos_hash,
+ .d_compare = msdos_cmp,
+};
+
+/*
+ * AV. Wrappers for FAT sb operations. Is it wise?
+ */
+
+/***** Get inode using directory and name */
+static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct super_block *sb = dir->i_sb;
+ struct fat_slot_info sinfo;
+ struct inode *inode;
+ int err;
+
+ lock_super(sb);
+
+ err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
+ if (err) {
+ if (err == -ENOENT) {
+ inode = NULL;
+ goto out;
+ }
+ goto error;
+ }
+
+ inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+ brelse(sinfo.bh);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto error;
+ }
+out:
+ unlock_super(sb);
+ dentry->d_op = &msdos_dentry_operations;
+ dentry = d_splice_alias(inode, dentry);
+ if (dentry)
+ dentry->d_op = &msdos_dentry_operations;
+ return dentry;
+
+error:
+ unlock_super(sb);
+ return ERR_PTR(err);
+}
+
+/***** Creates a directory entry (name is already formatted). */
+static int msdos_add_entry(struct inode *dir, const unsigned char *name,
+ int is_dir, int is_hid, int cluster,
+ struct timespec *ts, struct fat_slot_info *sinfo)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
+ struct msdos_dir_entry de;
+ __le16 time, date;
+ int err;
+
+ memcpy(de.name, name, MSDOS_NAME);
+ de.attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+ if (is_hid)
+ de.attr |= ATTR_HIDDEN;
+ de.lcase = 0;
+ fat_time_unix2fat(sbi, ts, &time, &date, NULL);
+ de.cdate = de.adate = 0;
+ de.ctime = 0;
+ de.ctime_cs = 0;
+ de.time = time;
+ de.date = date;
+ de.start = cpu_to_le16(cluster);
+ de.starthi = cpu_to_le16(cluster >> 16);
+ de.size = 0;
+
+ err = fat_add_entries(dir, &de, 1, sinfo);
+ if (err)
+ return err;
+
+ dir->i_ctime = dir->i_mtime = *ts;
+ if (IS_DIRSYNC(dir))
+ (void)fat_sync_inode(dir);
+ else
+ mark_inode_dirty(dir);
+
+ return 0;
+}
+
+/***** Create a file */
+static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = NULL;
+ struct fat_slot_info sinfo;
+ struct timespec ts;
+ unsigned char msdos_name[MSDOS_NAME];
+ int err, is_hid;
+
+ lock_super(sb);
+
+ err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
+ msdos_name, &MSDOS_SB(sb)->options);
+ if (err)
+ goto out;
+ is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
+ /* Have to do it due to foo vs. .foo conflicts */
+ if (!fat_scan(dir, msdos_name, &sinfo)) {
+ brelse(sinfo.bh);
+ err = -EINVAL;
+ goto out;
+ }
+
+ ts = CURRENT_TIME_SEC;
+ err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
+ if (err)
+ goto out;
+ inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+ brelse(sinfo.bh);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out;
+ }
+ inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+ d_instantiate(dentry, inode);
+out:
+ unlock_super(sb);
+ if (!err)
+ err = fat_flush_inodes(sb, dir, inode);
+ return err;
+}
+
+/***** Remove a directory */
+static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = dentry->d_inode;
+ struct fat_slot_info sinfo;
+ int err;
+
+ lock_super(sb);
+ /*
+ * Check whether the directory is not in use, then check
+ * whether it is empty.
+ */
+ err = fat_dir_empty(inode);
+ if (err)
+ goto out;
+ err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
+ if (err)
+ goto out;
+
+ err = fat_remove_entries(dir, &sinfo); /* and releases bh */
+ if (err)
+ goto out;
+ drop_nlink(dir);
+
+ clear_nlink(inode);
+ inode->i_ctime = CURRENT_TIME_SEC;
+ fat_detach(inode);
+out:
+ unlock_super(sb);
+ if (!err)
+ err = fat_flush_inodes(sb, dir, inode);
+
+ return err;
+}
+
+/***** Make a directory */
+static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct fat_slot_info sinfo;
+ struct inode *inode;
+ unsigned char msdos_name[MSDOS_NAME];
+ struct timespec ts;
+ int err, is_hid, cluster;
+
+ lock_super(sb);
+
+ err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
+ msdos_name, &MSDOS_SB(sb)->options);
+ if (err)
+ goto out;
+ is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
+ /* foo vs .foo situation */
+ if (!fat_scan(dir, msdos_name, &sinfo)) {
+ brelse(sinfo.bh);
+ err = -EINVAL;
+ goto out;
+ }
+
+ ts = CURRENT_TIME_SEC;
+ cluster = fat_alloc_new_dir(dir, &ts);
+ if (cluster < 0) {
+ err = cluster;
+ goto out;
+ }
+ err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
+ if (err)
+ goto out_free;
+ inc_nlink(dir);
+
+ inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+ brelse(sinfo.bh);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ /* the directory was completed, just return a error */
+ goto out;
+ }
+ inode->i_nlink = 2;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+ d_instantiate(dentry, inode);
+
+ unlock_super(sb);
+ fat_flush_inodes(sb, dir, inode);
+ return 0;
+
+out_free:
+ fat_free_clusters(dir, cluster);
+out:
+ unlock_super(sb);
+ return err;
+}
+
+/***** Unlink a file */
+static int msdos_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct super_block *sb= inode->i_sb;
+ struct fat_slot_info sinfo;
+ int err;
+
+ lock_super(sb);
+ err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
+ if (err)
+ goto out;
+
+ err = fat_remove_entries(dir, &sinfo); /* and releases bh */
+ if (err)
+ goto out;
+ clear_nlink(inode);
+ inode->i_ctime = CURRENT_TIME_SEC;
+ fat_detach(inode);
+out:
+ unlock_super(sb);
+ if (!err)
+ err = fat_flush_inodes(sb, dir, inode);
+
+ return err;
+}
+
+static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
+ struct dentry *old_dentry,
+ struct inode *new_dir, unsigned char *new_name,
+ struct dentry *new_dentry, int is_hid)
+{
+ struct buffer_head *dotdot_bh;
+ struct msdos_dir_entry *dotdot_de;
+ struct inode *old_inode, *new_inode;
+ struct fat_slot_info old_sinfo, sinfo;
+ struct timespec ts;
+ loff_t dotdot_i_pos, new_i_pos;
+ int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
+
+ old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
+
+ err = fat_scan(old_dir, old_name, &old_sinfo);
+ if (err) {
+ err = -EIO;
+ goto out;
+ }
+
+ is_dir = S_ISDIR(old_inode->i_mode);
+ update_dotdot = (is_dir && old_dir != new_dir);
+ if (update_dotdot) {
+ if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
+ &dotdot_i_pos) < 0) {
+ err = -EIO;
+ goto out;
+ }
+ }
+
+ old_attrs = MSDOS_I(old_inode)->i_attrs;
+ err = fat_scan(new_dir, new_name, &sinfo);
+ if (!err) {
+ if (!new_inode) {
+ /* "foo" -> ".foo" case. just change the ATTR_HIDDEN */
+ if (sinfo.de != old_sinfo.de) {
+ err = -EINVAL;
+ goto out;
+ }
+ if (is_hid)
+ MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+ else
+ MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+ if (IS_DIRSYNC(old_dir)) {
+ err = fat_sync_inode(old_inode);
+ if (err) {
+ MSDOS_I(old_inode)->i_attrs = old_attrs;
+ goto out;
+ }
+ } else
+ mark_inode_dirty(old_inode);
+
+ old_dir->i_version++;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
+ if (IS_DIRSYNC(old_dir))
+ (void)fat_sync_inode(old_dir);
+ else
+ mark_inode_dirty(old_dir);
+ goto out;
+ }
+ }
+
+ ts = CURRENT_TIME_SEC;
+ if (new_inode) {
+ if (err)
+ goto out;
+ if (is_dir) {
+ err = fat_dir_empty(new_inode);
+ if (err)
+ goto out;
+ }
+ new_i_pos = MSDOS_I(new_inode)->i_pos;
+ fat_detach(new_inode);
+ } else {
+ err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
+ &ts, &sinfo);
+ if (err)
+ goto out;
+ new_i_pos = sinfo.i_pos;
+ }
+ new_dir->i_version++;
+
+ fat_detach(old_inode);
+ fat_attach(old_inode, new_i_pos);
+ if (is_hid)
+ MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+ else
+ MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+ if (IS_DIRSYNC(new_dir)) {
+ err = fat_sync_inode(old_inode);
+ if (err)
+ goto error_inode;
+ } else
+ mark_inode_dirty(old_inode);
+
+ if (update_dotdot) {
+ int start = MSDOS_I(new_dir)->i_logstart;
+ dotdot_de->start = cpu_to_le16(start);
+ dotdot_de->starthi = cpu_to_le16(start >> 16);
+ mark_buffer_dirty(dotdot_bh);
+ if (IS_DIRSYNC(new_dir)) {
+ err = sync_dirty_buffer(dotdot_bh);
+ if (err)
+ goto error_dotdot;
+ }
+ drop_nlink(old_dir);
+ if (!new_inode)
+ inc_nlink(new_dir);
+ }
+
+ err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */
+ old_sinfo.bh = NULL;
+ if (err)
+ goto error_dotdot;
+ old_dir->i_version++;
+ old_dir->i_ctime = old_dir->i_mtime = ts;
+ if (IS_DIRSYNC(old_dir))
+ (void)fat_sync_inode(old_dir);
+ else
+ mark_inode_dirty(old_dir);
+
+ if (new_inode) {
+ drop_nlink(new_inode);
+ if (is_dir)
+ drop_nlink(new_inode);
+ new_inode->i_ctime = ts;
+ }
+out:
+ brelse(sinfo.bh);
+ brelse(dotdot_bh);
+ brelse(old_sinfo.bh);
+ return err;
+
+error_dotdot:
+ /* data cluster is shared, serious corruption */
+ corrupt = 1;
+
+ if (update_dotdot) {
+ int start = MSDOS_I(old_dir)->i_logstart;
+ dotdot_de->start = cpu_to_le16(start);
+ dotdot_de->starthi = cpu_to_le16(start >> 16);
+ mark_buffer_dirty(dotdot_bh);
+ corrupt |= sync_dirty_buffer(dotdot_bh);
+ }
+error_inode:
+ fat_detach(old_inode);
+ fat_attach(old_inode, old_sinfo.i_pos);
+ MSDOS_I(old_inode)->i_attrs = old_attrs;
+ if (new_inode) {
+ fat_attach(new_inode, new_i_pos);
+ if (corrupt)
+ corrupt |= fat_sync_inode(new_inode);
+ } else {
+ /*
+ * If new entry was not sharing the data cluster, it
+ * shouldn't be serious corruption.
+ */
+ int err2 = fat_remove_entries(new_dir, &sinfo);
+ if (corrupt)
+ corrupt |= err2;
+ sinfo.bh = NULL;
+ }
+ if (corrupt < 0) {
+ fat_fs_panic(new_dir->i_sb,
+ "%s: Filesystem corrupted (i_pos %lld)",
+ __func__, sinfo.i_pos);
+ }
+ goto out;
+}
+
+/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
+static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct super_block *sb = old_dir->i_sb;
+ unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
+ int err, is_hid;
+
+ lock_super(sb);
+
+ err = msdos_format_name(old_dentry->d_name.name,
+ old_dentry->d_name.len, old_msdos_name,
+ &MSDOS_SB(old_dir->i_sb)->options);
+ if (err)
+ goto out;
+ err = msdos_format_name(new_dentry->d_name.name,
+ new_dentry->d_name.len, new_msdos_name,
+ &MSDOS_SB(new_dir->i_sb)->options);
+ if (err)
+ goto out;
+
+ is_hid =
+ (new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.');
+
+ err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
+ new_dir, new_msdos_name, new_dentry, is_hid);
+out:
+ unlock_super(sb);
+ if (!err)
+ err = fat_flush_inodes(sb, old_dir, new_dir);
+ return err;
+}
+
+static const struct inode_operations msdos_dir_inode_operations = {
+ .create = msdos_create,
+ .lookup = msdos_lookup,
+ .unlink = msdos_unlink,
+ .mkdir = msdos_mkdir,
+ .rmdir = msdos_rmdir,
+ .rename = msdos_rename,
+ .setattr = fat_setattr,
+ .getattr = fat_getattr,
+};
+
+static int msdos_fill_super(struct super_block *sb, void *data, int silent)
+{
+ int res;
+
+ res = fat_fill_super(sb, data, silent, &msdos_dir_inode_operations, 0);
+ if (res)
+ return res;
+
+ sb->s_flags |= MS_NOATIME;
+ sb->s_root->d_op = &msdos_dentry_operations;
+ return 0;
+}
+
+static int msdos_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data, struct vfsmount *mnt)
+{
+ return get_sb_bdev(fs_type, flags, dev_name, data, msdos_fill_super,
+ mnt);
+}
+
+static struct file_system_type msdos_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "msdos",
+ .get_sb = msdos_get_sb,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+static int __init init_msdos_fs(void)
+{
+ return register_filesystem(&msdos_fs_type);
+}
+
+static void __exit exit_msdos_fs(void)
+{
+ unregister_filesystem(&msdos_fs_type);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Werner Almesberger");
+MODULE_DESCRIPTION("MS-DOS filesystem support");
+
+module_init(init_msdos_fs)
+module_exit(exit_msdos_fs)
--- /dev/null
+/*
+ * linux/fs/vfat/namei.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * Windows95/Windows NT compatible extended MSDOS filesystem
+ * by Gordon Chaffee Copyright (C) 1995. Send bug reports for the
+ * VFAT filesystem to <chaffee@cs.berkeley.edu>. Specify
+ * what file operation caused you trouble and if you can duplicate
+ * the problem, send a script that demonstrates it.
+ *
+ * Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de>
+ *
+ * Support Multibyte characters and cleanup by
+ * OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/buffer_head.h>
+#include <linux/namei.h>
+#include "fat.h"
+
+/*
+ * If new entry was created in the parent, it could create the 8.3
+ * alias (the shortname of logname). So, the parent may have the
+ * negative-dentry which matches the created 8.3 alias.
+ *
+ * If it happened, the negative dentry isn't actually negative
+ * anymore. So, drop it.
+ */
+static int vfat_revalidate_shortname(struct dentry *dentry)
+{
+ int ret = 1;
+ spin_lock(&dentry->d_lock);
+ if (dentry->d_time != dentry->d_parent->d_inode->i_version)
+ ret = 0;
+ spin_unlock(&dentry->d_lock);
+ return ret;
+}
+
+static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+ /* This is not negative dentry. Always valid. */
+ if (dentry->d_inode)
+ return 1;
+ return vfat_revalidate_shortname(dentry);
+}
+
+static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
+{
+ /*
+ * This is not negative dentry. Always valid.
+ *
+ * Note, rename() to existing directory entry will have ->d_inode,
+ * and will use existing name which isn't specified name by user.
+ *
+ * We may be able to drop this positive dentry here. But dropping
+ * positive dentry isn't good idea. So it's unsupported like
+ * rename("filename", "FILENAME") for now.
+ */
+ if (dentry->d_inode)
+ return 1;
+
+ /*
+ * This may be nfsd (or something), anyway, we can't see the
+ * intent of this. So, since this can be for creation, drop it.
+ */
+ if (!nd)
+ return 0;
+
+ /*
+ * Drop the negative dentry, in order to make sure to use the
+ * case sensitive name which is specified by user if this is
+ * for creation.
+ */
+ if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) {
+ if (nd->flags & LOOKUP_CREATE)
+ return 0;
+ }
+
+ return vfat_revalidate_shortname(dentry);
+}
+
+/* returns the length of a struct qstr, ignoring trailing dots */
+static unsigned int vfat_striptail_len(struct qstr *qstr)
+{
+ unsigned int len = qstr->len;
+
+ while (len && qstr->name[len - 1] == '.')
+ len--;
+ return len;
+}
+
+/*
+ * Compute the hash for the vfat name corresponding to the dentry.
+ * Note: if the name is invalid, we leave the hash code unchanged so
+ * that the existing dentry can be used. The vfat fs routines will
+ * return ENOENT or EINVAL as appropriate.
+ */
+static int vfat_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr));
+ return 0;
+}
+
+/*
+ * Compute the hash for the vfat name corresponding to the dentry.
+ * Note: if the name is invalid, we leave the hash code unchanged so
+ * that the existing dentry can be used. The vfat fs routines will
+ * return ENOENT or EINVAL as appropriate.
+ */
+static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
+{
+ struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
+ const unsigned char *name;
+ unsigned int len;
+ unsigned long hash;
+
+ name = qstr->name;
+ len = vfat_striptail_len(qstr);
+
+ hash = init_name_hash();
+ while (len--)
+ hash = partial_name_hash(nls_tolower(t, *name++), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+/*
+ * Case insensitive compare of two vfat names.
+ */
+static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
+ unsigned int alen, blen;
+
+ /* A filename cannot end in '.' or we treat it like it has none */
+ alen = vfat_striptail_len(a);
+ blen = vfat_striptail_len(b);
+ if (alen == blen) {
+ if (nls_strnicmp(t, a->name, b->name, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Case sensitive compare of two vfat names.
+ */
+static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ unsigned int alen, blen;
+
+ /* A filename cannot end in '.' or we treat it like it has none */
+ alen = vfat_striptail_len(a);
+ blen = vfat_striptail_len(b);
+ if (alen == blen) {
+ if (strncmp(a->name, b->name, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static struct dentry_operations vfat_ci_dentry_ops = {
+ .d_revalidate = vfat_revalidate_ci,
+ .d_hash = vfat_hashi,
+ .d_compare = vfat_cmpi,
+};
+
+static struct dentry_operations vfat_dentry_ops = {
+ .d_revalidate = vfat_revalidate,
+ .d_hash = vfat_hash,
+ .d_compare = vfat_cmp,
+};
+
+/* Characters that are undesirable in an MS-DOS file name */
+
+static inline wchar_t vfat_bad_char(wchar_t w)
+{
+ return (w < 0x0020)
+ || (w == '*') || (w == '?') || (w == '<') || (w == '>')
+ || (w == '|') || (w == '"') || (w == ':') || (w == '/')
+ || (w == '\\');
+}
+
+static inline wchar_t vfat_replace_char(wchar_t w)
+{
+ return (w == '[') || (w == ']') || (w == ';') || (w == ',')
+ || (w == '+') || (w == '=');
+}
+
+static wchar_t vfat_skip_char(wchar_t w)
+{
+ return (w == '.') || (w == ' ');
+}
+
+static inline int vfat_is_used_badchars(const wchar_t *s, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (vfat_bad_char(s[i]))
+ return -EINVAL;
+
+ if (s[i - 1] == ' ') /* last character cannot be space */
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vfat_find_form(struct inode *dir, unsigned char *name)
+{
+ struct fat_slot_info sinfo;
+ int err = fat_scan(dir, name, &sinfo);
+ if (err)
+ return -ENOENT;
+ brelse(sinfo.bh);
+ return 0;
+}
+
+/*
+ * 1) Valid characters for the 8.3 format alias are any combination of
+ * letters, uppercase alphabets, digits, any of the
+ * following special characters:
+ * $ % ' ` - @ { } ~ ! # ( ) & _ ^
+ * In this case Longfilename is not stored in disk.
+ *
+ * WinNT's Extension:
+ * File name and extension name is contain uppercase/lowercase
+ * only. And it is expressed by CASE_LOWER_BASE and CASE_LOWER_EXT.
+ *
+ * 2) File name is 8.3 format, but it contain the uppercase and
+ * lowercase char, muliti bytes char, etc. In this case numtail is not
+ * added, but Longfilename is stored.
+ *
+ * 3) When the one except for the above, or the following special
+ * character are contained:
+ * . [ ] ; , + =
+ * numtail is added, and Longfilename must be stored in disk .
+ */
+struct shortname_info {
+ unsigned char lower:1,
+ upper:1,
+ valid:1;
+};
+#define INIT_SHORTNAME_INFO(x) do { \
+ (x)->lower = 1; \
+ (x)->upper = 1; \
+ (x)->valid = 1; \
+} while (0)
+
+static inline int to_shortname_char(struct nls_table *nls,
+ unsigned char *buf, int buf_size,
+ wchar_t *src, struct shortname_info *info)
+{
+ int len;
+
+ if (vfat_skip_char(*src)) {
+ info->valid = 0;
+ return 0;
+ }
+ if (vfat_replace_char(*src)) {
+ info->valid = 0;
+ buf[0] = '_';
+ return 1;
+ }
+
+ len = nls->uni2char(*src, buf, buf_size);
+ if (len <= 0) {
+ info->valid = 0;
+ buf[0] = '_';
+ len = 1;
+ } else if (len == 1) {
+ unsigned char prev = buf[0];
+
+ if (buf[0] >= 0x7F) {
+ info->lower = 0;
+ info->upper = 0;
+ }
+
+ buf[0] = nls_toupper(nls, buf[0]);
+ if (isalpha(buf[0])) {
+ if (buf[0] == prev)
+ info->lower = 0;
+ else
+ info->upper = 0;
+ }
+ } else {
+ info->lower = 0;
+ info->upper = 0;
+ }
+
+ return len;
+}
+
+/*
+ * Given a valid longname, create a unique shortname. Make sure the
+ * shortname does not exist
+ * Returns negative number on error, 0 for a normal
+ * return, and 1 for valid shortname
+ */
+static int vfat_create_shortname(struct inode *dir, struct nls_table *nls,
+ wchar_t *uname, int ulen,
+ unsigned char *name_res, unsigned char *lcase)
+{
+ struct fat_mount_options *opts = &MSDOS_SB(dir->i_sb)->options;
+ wchar_t *ip, *ext_start, *end, *name_start;
+ unsigned char base[9], ext[4], buf[8], *p;
+ unsigned char charbuf[NLS_MAX_CHARSET_SIZE];
+ int chl, chi;
+ int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen;
+ int is_shortname;
+ struct shortname_info base_info, ext_info;
+
+ is_shortname = 1;
+ INIT_SHORTNAME_INFO(&base_info);
+ INIT_SHORTNAME_INFO(&ext_info);
+
+ /* Now, we need to create a shortname from the long name */
+ ext_start = end = &uname[ulen];
+ while (--ext_start >= uname) {
+ if (*ext_start == 0x002E) { /* is `.' */
+ if (ext_start == end - 1) {
+ sz = ulen;
+ ext_start = NULL;
+ }
+ break;
+ }
+ }
+
+ if (ext_start == uname - 1) {
+ sz = ulen;
+ ext_start = NULL;
+ } else if (ext_start) {
+ /*
+ * Names which start with a dot could be just
+ * an extension eg. "...test". In this case Win95
+ * uses the extension as the name and sets no extension.
+ */
+ name_start = &uname[0];
+ while (name_start < ext_start) {
+ if (!vfat_skip_char(*name_start))
+ break;
+ name_start++;
+ }
+ if (name_start != ext_start) {
+ sz = ext_start - uname;
+ ext_start++;
+ } else {
+ sz = ulen;
+ ext_start = NULL;
+ }
+ }
+
+ numtail_baselen = 6;
+ numtail2_baselen = 2;
+ for (baselen = i = 0, p = base, ip = uname; i < sz; i++, ip++) {
+ chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
+ ip, &base_info);
+ if (chl == 0)
+ continue;
+
+ if (baselen < 2 && (baselen + chl) > 2)
+ numtail2_baselen = baselen;
+ if (baselen < 6 && (baselen + chl) > 6)
+ numtail_baselen = baselen;
+ for (chi = 0; chi < chl; chi++) {
+ *p++ = charbuf[chi];
+ baselen++;
+ if (baselen >= 8)
+ break;
+ }
+ if (baselen >= 8) {
+ if ((chi < chl - 1) || (ip + 1) - uname < sz)
+ is_shortname = 0;
+ break;
+ }
+ }
+ if (baselen == 0) {
+ return -EINVAL;
+ }
+
+ extlen = 0;
+ if (ext_start) {
+ for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
+ chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
+ ip, &ext_info);
+ if (chl == 0)
+ continue;
+
+ if ((extlen + chl) > 3) {
+ is_shortname = 0;
+ break;
+ }
+ for (chi = 0; chi < chl; chi++) {
+ *p++ = charbuf[chi];
+ extlen++;
+ }
+ if (extlen >= 3) {
+ if (ip + 1 != end)
+ is_shortname = 0;
+ break;
+ }
+ }
+ }
+ ext[extlen] = '\0';
+ base[baselen] = '\0';
+
+ /* Yes, it can happen. ".\xe5" would do it. */
+ if (base[0] == DELETED_FLAG)
+ base[0] = 0x05;
+
+ /* OK, at this point we know that base is not longer than 8 symbols,
+ * ext is not longer than 3, base is nonempty, both don't contain
+ * any bad symbols (lowercase transformed to uppercase).
+ */
+
+ memset(name_res, ' ', MSDOS_NAME);
+ memcpy(name_res, base, baselen);
+ memcpy(name_res + 8, ext, extlen);
+ *lcase = 0;
+ if (is_shortname && base_info.valid && ext_info.valid) {
+ if (vfat_find_form(dir, name_res) == 0)
+ return -EEXIST;
+
+ if (opts->shortname & VFAT_SFN_CREATE_WIN95) {
+ return (base_info.upper && ext_info.upper);
+ } else if (opts->shortname & VFAT_SFN_CREATE_WINNT) {
+ if ((base_info.upper || base_info.lower) &&
+ (ext_info.upper || ext_info.lower)) {
+ if (!base_info.upper && base_info.lower)
+ *lcase |= CASE_LOWER_BASE;
+ if (!ext_info.upper && ext_info.lower)
+ *lcase |= CASE_LOWER_EXT;
+ return 1;
+ }
+ return 0;
+ } else {
+ BUG();
+ }
+ }
+
+ if (opts->numtail == 0)
+ if (vfat_find_form(dir, name_res) < 0)
+ return 0;
+
+ /*
+ * Try to find a unique extension. This used to
+ * iterate through all possibilities sequentially,
+ * but that gave extremely bad performance. Windows
+ * only tries a few cases before using random
+ * values for part of the base.
+ */
+
+ if (baselen > 6) {
+ baselen = numtail_baselen;
+ name_res[7] = ' ';
+ }
+ name_res[baselen] = '~';
+ for (i = 1; i < 10; i++) {
+ name_res[baselen + 1] = i + '0';
+ if (vfat_find_form(dir, name_res) < 0)
+ return 0;
+ }
+
+ i = jiffies & 0xffff;
+ sz = (jiffies >> 16) & 0x7;
+ if (baselen > 2) {
+ baselen = numtail2_baselen;
+ name_res[7] = ' ';
+ }
+ name_res[baselen + 4] = '~';
+ name_res[baselen + 5] = '1' + sz;
+ while (1) {
+ sprintf(buf, "%04X", i);
+ memcpy(&name_res[baselen], buf, 4);
+ if (vfat_find_form(dir, name_res) < 0)
+ break;
+ i -= 11;
+ }
+ return 0;
+}
+
+/* Translate a string, including coded sequences into Unicode */
+static int
+xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,
+ int *longlen, int *outlen, int escape, int utf8,
+ struct nls_table *nls)
+{
+ const unsigned char *ip;
+ unsigned char nc;
+ unsigned char *op;
+ unsigned int ec;
+ int i, k, fill;
+ int charlen;
+
+ if (utf8) {
+ int name_len = strlen(name);
+
+ *outlen = utf8_mbstowcs((wchar_t *)outname, name, PATH_MAX);
+
+ /*
+ * We stripped '.'s before and set len appropriately,
+ * but utf8_mbstowcs doesn't care about len
+ */
+ *outlen -= (name_len - len);
+
+ if (*outlen > 255)
+ return -ENAMETOOLONG;
+
+ op = &outname[*outlen * sizeof(wchar_t)];
+ } else {
+ if (nls) {
+ for (i = 0, ip = name, op = outname, *outlen = 0;
+ i < len && *outlen <= 255;
+ *outlen += 1)
+ {
+ if (escape && (*ip == ':')) {
+ if (i > len - 5)
+ return -EINVAL;
+ ec = 0;
+ for (k = 1; k < 5; k++) {
+ nc = ip[k];
+ ec <<= 4;
+ if (nc >= '0' && nc <= '9') {
+ ec |= nc - '0';
+ continue;
+ }
+ if (nc >= 'a' && nc <= 'f') {
+ ec |= nc - ('a' - 10);
+ continue;
+ }
+ if (nc >= 'A' && nc <= 'F') {
+ ec |= nc - ('A' - 10);
+ continue;
+ }
+ return -EINVAL;
+ }
+ *op++ = ec & 0xFF;
+ *op++ = ec >> 8;
+ ip += 5;
+ i += 5;
+ } else {
+ if ((charlen = nls->char2uni(ip, len - i, (wchar_t *)op)) < 0)
+ return -EINVAL;
+ ip += charlen;
+ i += charlen;
+ op += 2;
+ }
+ }
+ if (i < len)
+ return -ENAMETOOLONG;
+ } else {
+ for (i = 0, ip = name, op = outname, *outlen = 0;
+ i < len && *outlen <= 255;
+ i++, *outlen += 1)
+ {
+ *op++ = *ip++;
+ *op++ = 0;
+ }
+ if (i < len)
+ return -ENAMETOOLONG;
+ }
+ }
+
+ *longlen = *outlen;
+ if (*outlen % 13) {
+ *op++ = 0;
+ *op++ = 0;
+ *outlen += 1;
+ if (*outlen % 13) {
+ fill = 13 - (*outlen % 13);
+ for (i = 0; i < fill; i++) {
+ *op++ = 0xff;
+ *op++ = 0xff;
+ }
+ *outlen += fill;
+ }
+ }
+
+ return 0;
+}
+
+static int vfat_build_slots(struct inode *dir, const unsigned char *name,
+ int len, int is_dir, int cluster,
+ struct timespec *ts,
+ struct msdos_dir_slot *slots, int *nr_slots)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
+ struct fat_mount_options *opts = &sbi->options;
+ struct msdos_dir_slot *ps;
+ struct msdos_dir_entry *de;
+ unsigned char cksum, lcase;
+ unsigned char msdos_name[MSDOS_NAME];
+ wchar_t *uname;
+ __le16 time, date;
+ u8 time_cs;
+ int err, ulen, usize, i;
+ loff_t offset;
+
+ *nr_slots = 0;
+
+ uname = __getname();
+ if (!uname)
+ return -ENOMEM;
+
+ err = xlate_to_uni(name, len, (unsigned char *)uname, &ulen, &usize,
+ opts->unicode_xlate, opts->utf8, sbi->nls_io);
+ if (err)
+ goto out_free;
+
+ err = vfat_is_used_badchars(uname, ulen);
+ if (err)
+ goto out_free;
+
+ err = vfat_create_shortname(dir, sbi->nls_disk, uname, ulen,
+ msdos_name, &lcase);
+ if (err < 0)
+ goto out_free;
+ else if (err == 1) {
+ de = (struct msdos_dir_entry *)slots;
+ err = 0;
+ goto shortname;
+ }
+
+ /* build the entry of long file name */
+ cksum = fat_checksum(msdos_name);
+
+ *nr_slots = usize / 13;
+ for (ps = slots, i = *nr_slots; i > 0; i--, ps++) {
+ ps->id = i;
+ ps->attr = ATTR_EXT;
+ ps->reserved = 0;
+ ps->alias_checksum = cksum;
+ ps->start = 0;
+ offset = (i - 1) * 13;
+ fatwchar_to16(ps->name0_4, uname + offset, 5);
+ fatwchar_to16(ps->name5_10, uname + offset + 5, 6);
+ fatwchar_to16(ps->name11_12, uname + offset + 11, 2);
+ }
+ slots[0].id |= 0x40;
+ de = (struct msdos_dir_entry *)ps;
+
+shortname:
+ /* build the entry of 8.3 alias name */
+ (*nr_slots)++;
+ memcpy(de->name, msdos_name, MSDOS_NAME);
+ de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+ de->lcase = lcase;
+ fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);
+ de->time = de->ctime = time;
+ de->date = de->cdate = de->adate = date;
+ de->ctime_cs = time_cs;
+ de->start = cpu_to_le16(cluster);
+ de->starthi = cpu_to_le16(cluster >> 16);
+ de->size = 0;
+out_free:
+ __putname(uname);
+ return err;
+}
+
+static int vfat_add_entry(struct inode *dir, struct qstr *qname, int is_dir,
+ int cluster, struct timespec *ts,
+ struct fat_slot_info *sinfo)
+{
+ struct msdos_dir_slot *slots;
+ unsigned int len;
+ int err, nr_slots;
+
+ len = vfat_striptail_len(qname);
+ if (len == 0)
+ return -ENOENT;
+
+ slots = kmalloc(sizeof(*slots) * MSDOS_SLOTS, GFP_NOFS);
+ if (slots == NULL)
+ return -ENOMEM;
+
+ err = vfat_build_slots(dir, qname->name, len, is_dir, cluster, ts,
+ slots, &nr_slots);
+ if (err)
+ goto cleanup;
+
+ err = fat_add_entries(dir, slots, nr_slots, sinfo);
+ if (err)
+ goto cleanup;
+
+ /* update timestamp */
+ dir->i_ctime = dir->i_mtime = dir->i_atime = *ts;
+ if (IS_DIRSYNC(dir))
+ (void)fat_sync_inode(dir);
+ else
+ mark_inode_dirty(dir);
+cleanup:
+ kfree(slots);
+ return err;
+}
+
+static int vfat_find(struct inode *dir, struct qstr *qname,
+ struct fat_slot_info *sinfo)
+{
+ unsigned int len = vfat_striptail_len(qname);
+ if (len == 0)
+ return -ENOENT;
+ return fat_search_long(dir, qname->name, len, sinfo);
+}
+
+static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct super_block *sb = dir->i_sb;
+ struct fat_slot_info sinfo;
+ struct inode *inode;
+ struct dentry *alias;
+ int err;
+
+ lock_super(sb);
+
+ err = vfat_find(dir, &dentry->d_name, &sinfo);
+ if (err) {
+ if (err == -ENOENT) {
+ inode = NULL;
+ goto out;
+ }
+ goto error;
+ }
+
+ inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+ brelse(sinfo.bh);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto error;
+ }
+
+ alias = d_find_alias(inode);
+ if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) {
+ /*
+ * This inode has non DCACHE_DISCONNECTED dentry. This
+ * means, the user did ->lookup() by an another name
+ * (longname vs 8.3 alias of it) in past.
+ *
+ * Switch to new one for reason of locality if possible.
+ */
+ BUG_ON(d_unhashed(alias));
+ if (!S_ISDIR(inode->i_mode))
+ d_move(alias, dentry);
+ iput(inode);
+ unlock_super(sb);
+ return alias;
+ }
+out:
+ unlock_super(sb);
+ dentry->d_op = sb->s_root->d_op;
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ dentry = d_splice_alias(inode, dentry);
+ if (dentry) {
+ dentry->d_op = sb->s_root->d_op;
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ }
+ return dentry;
+
+error:
+ unlock_super(sb);
+ return ERR_PTR(err);
+}
+
+static int vfat_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ struct fat_slot_info sinfo;
+ struct timespec ts;
+ int err;
+
+ lock_super(sb);
+
+ ts = CURRENT_TIME_SEC;
+ err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo);
+ if (err)
+ goto out;
+ dir->i_version++;
+
+ inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+ brelse(sinfo.bh);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto out;
+ }
+ inode->i_version++;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ d_instantiate(dentry, inode);
+out:
+ unlock_super(sb);
+ return err;
+}
+
+static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct super_block *sb = dir->i_sb;
+ struct fat_slot_info sinfo;
+ int err;
+
+ lock_super(sb);
+
+ err = fat_dir_empty(inode);
+ if (err)
+ goto out;
+ err = vfat_find(dir, &dentry->d_name, &sinfo);
+ if (err)
+ goto out;
+
+ err = fat_remove_entries(dir, &sinfo); /* and releases bh */
+ if (err)
+ goto out;
+ drop_nlink(dir);
+
+ clear_nlink(inode);
+ inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
+ fat_detach(inode);
+out:
+ unlock_super(sb);
+
+ return err;
+}
+
+static int vfat_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct super_block *sb = dir->i_sb;
+ struct fat_slot_info sinfo;
+ int err;
+
+ lock_super(sb);
+
+ err = vfat_find(dir, &dentry->d_name, &sinfo);
+ if (err)
+ goto out;
+
+ err = fat_remove_entries(dir, &sinfo); /* and releases bh */
+ if (err)
+ goto out;
+ clear_nlink(inode);
+ inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
+ fat_detach(inode);
+out:
+ unlock_super(sb);
+
+ return err;
+}
+
+static int vfat_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ struct fat_slot_info sinfo;
+ struct timespec ts;
+ int err, cluster;
+
+ lock_super(sb);
+
+ ts = CURRENT_TIME_SEC;
+ cluster = fat_alloc_new_dir(dir, &ts);
+ if (cluster < 0) {
+ err = cluster;
+ goto out;
+ }
+ err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &ts, &sinfo);
+ if (err)
+ goto out_free;
+ dir->i_version++;
+ inc_nlink(dir);
+
+ inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+ brelse(sinfo.bh);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ /* the directory was completed, just return a error */
+ goto out;
+ }
+ inode->i_version++;
+ inode->i_nlink = 2;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
+ /* timestamp is already written, so mark_inode_dirty() is unneeded. */
+
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ d_instantiate(dentry, inode);
+
+ unlock_super(sb);
+ return 0;
+
+out_free:
+ fat_free_clusters(dir, cluster);
+out:
+ unlock_super(sb);
+ return err;
+}
+
+static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct buffer_head *dotdot_bh;
+ struct msdos_dir_entry *dotdot_de;
+ struct inode *old_inode, *new_inode;
+ struct fat_slot_info old_sinfo, sinfo;
+ struct timespec ts;
+ loff_t dotdot_i_pos, new_i_pos;
+ int err, is_dir, update_dotdot, corrupt = 0;
+ struct super_block *sb = old_dir->i_sb;
+
+ old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
+ lock_super(sb);
+ err = vfat_find(old_dir, &old_dentry->d_name, &old_sinfo);
+ if (err)
+ goto out;
+
+ is_dir = S_ISDIR(old_inode->i_mode);
+ update_dotdot = (is_dir && old_dir != new_dir);
+ if (update_dotdot) {
+ if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
+ &dotdot_i_pos) < 0) {
+ err = -EIO;
+ goto out;
+ }
+ }
+
+ ts = CURRENT_TIME_SEC;
+ if (new_inode) {
+ if (is_dir) {
+ err = fat_dir_empty(new_inode);
+ if (err)
+ goto out;
+ }
+ new_i_pos = MSDOS_I(new_inode)->i_pos;
+ fat_detach(new_inode);
+ } else {
+ err = vfat_add_entry(new_dir, &new_dentry->d_name, is_dir, 0,
+ &ts, &sinfo);
+ if (err)
+ goto out;
+ new_i_pos = sinfo.i_pos;
+ }
+ new_dir->i_version++;
+
+ fat_detach(old_inode);
+ fat_attach(old_inode, new_i_pos);
+ if (IS_DIRSYNC(new_dir)) {
+ err = fat_sync_inode(old_inode);
+ if (err)
+ goto error_inode;
+ } else
+ mark_inode_dirty(old_inode);
+
+ if (update_dotdot) {
+ int start = MSDOS_I(new_dir)->i_logstart;
+ dotdot_de->start = cpu_to_le16(start);
+ dotdot_de->starthi = cpu_to_le16(start >> 16);
+ mark_buffer_dirty(dotdot_bh);
+ if (IS_DIRSYNC(new_dir)) {
+ err = sync_dirty_buffer(dotdot_bh);
+ if (err)
+ goto error_dotdot;
+ }
+ drop_nlink(old_dir);
+ if (!new_inode)
+ inc_nlink(new_dir);
+ }
+
+ err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */
+ old_sinfo.bh = NULL;
+ if (err)
+ goto error_dotdot;
+ old_dir->i_version++;
+ old_dir->i_ctime = old_dir->i_mtime = ts;
+ if (IS_DIRSYNC(old_dir))
+ (void)fat_sync_inode(old_dir);
+ else
+ mark_inode_dirty(old_dir);
+
+ if (new_inode) {
+ drop_nlink(new_inode);
+ if (is_dir)
+ drop_nlink(new_inode);
+ new_inode->i_ctime = ts;
+ }
+out:
+ brelse(sinfo.bh);
+ brelse(dotdot_bh);
+ brelse(old_sinfo.bh);
+ unlock_super(sb);
+
+ return err;
+
+error_dotdot:
+ /* data cluster is shared, serious corruption */
+ corrupt = 1;
+
+ if (update_dotdot) {
+ int start = MSDOS_I(old_dir)->i_logstart;
+ dotdot_de->start = cpu_to_le16(start);
+ dotdot_de->starthi = cpu_to_le16(start >> 16);
+ mark_buffer_dirty(dotdot_bh);
+ corrupt |= sync_dirty_buffer(dotdot_bh);
+ }
+error_inode:
+ fat_detach(old_inode);
+ fat_attach(old_inode, old_sinfo.i_pos);
+ if (new_inode) {
+ fat_attach(new_inode, new_i_pos);
+ if (corrupt)
+ corrupt |= fat_sync_inode(new_inode);
+ } else {
+ /*
+ * If new entry was not sharing the data cluster, it
+ * shouldn't be serious corruption.
+ */
+ int err2 = fat_remove_entries(new_dir, &sinfo);
+ if (corrupt)
+ corrupt |= err2;
+ sinfo.bh = NULL;
+ }
+ if (corrupt < 0) {
+ fat_fs_panic(new_dir->i_sb,
+ "%s: Filesystem corrupted (i_pos %lld)",
+ __func__, sinfo.i_pos);
+ }
+ goto out;
+}
+
+static const struct inode_operations vfat_dir_inode_operations = {
+ .create = vfat_create,
+ .lookup = vfat_lookup,
+ .unlink = vfat_unlink,
+ .mkdir = vfat_mkdir,
+ .rmdir = vfat_rmdir,
+ .rename = vfat_rename,
+ .setattr = fat_setattr,
+ .getattr = fat_getattr,
+};
+
+static int vfat_fill_super(struct super_block *sb, void *data, int silent)
+{
+ int res;
+
+ res = fat_fill_super(sb, data, silent, &vfat_dir_inode_operations, 1);
+ if (res)
+ return res;
+
+ if (MSDOS_SB(sb)->options.name_check != 's')
+ sb->s_root->d_op = &vfat_ci_dentry_ops;
+ else
+ sb->s_root->d_op = &vfat_dentry_ops;
+
+ return 0;
+}
+
+static int vfat_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data, struct vfsmount *mnt)
+{
+ return get_sb_bdev(fs_type, flags, dev_name, data, vfat_fill_super,
+ mnt);
+}
+
+static struct file_system_type vfat_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "vfat",
+ .get_sb = vfat_get_sb,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+static int __init init_vfat_fs(void)
+{
+ return register_filesystem(&vfat_fs_type);
+}
+
+static void __exit exit_vfat_fs(void)
+{
+ unregister_filesystem(&vfat_fs_type);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VFAT filesystem support");
+MODULE_AUTHOR("Gordon Chaffee");
+
+module_init(init_vfat_fs)
+module_exit(exit_vfat_fs)
for (;;) {
allow_signal(SIGHUP);
again:
+ spin_lock(&c->erase_completion_lock);
if (!jffs2_thread_should_wake(c)) {
set_current_state (TASK_INTERRUPTIBLE);
+ spin_unlock(&c->erase_completion_lock);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
- /* Yes, there's a race here; we checked jffs2_thread_should_wake()
- before setting current->state to TASK_INTERRUPTIBLE. But it doesn't
- matter - We don't care if we miss a wakeup, because the GC thread
- is only an optimisation anyway. */
schedule();
- }
+ } else
+ spin_unlock(&c->erase_completion_lock);
+
/* This thread is purely an optimisation. But if it runs when
other things could be running, it actually makes things a
static void *lzo_mem;
static void *lzo_compress_buf;
-static DEFINE_MUTEX(deflate_mutex);
+static DEFINE_MUTEX(deflate_mutex); /* for lzo_mem and lzo_compress_buf */
static void free_workspace(void)
{
mutex_lock(&deflate_mutex);
ret = lzo1x_1_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem);
- mutex_unlock(&deflate_mutex);
-
if (ret != LZO_E_OK)
- return -1;
+ goto fail;
if (compress_size > *dstlen)
- return -1;
+ goto fail;
memcpy(cpage_out, lzo_compress_buf, compress_size);
- *dstlen = compress_size;
+ mutex_unlock(&deflate_mutex);
+ *dstlen = compress_size;
return 0;
+
+ fail:
+ mutex_unlock(&deflate_mutex);
+ return -1;
}
static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
jffs2_sum_reset_collected(c->summary); /* reset collected summary */
+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
/* adjust write buffer offset, else we get a non contiguous write bug */
if (!(c->wbuf_ofs % c->sector_size) && !c->wbuf_len)
c->wbuf_ofs = 0xffffffff;
+#endif
D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset));
+++ /dev/null
-#
-# Makefile for the Linux msdos filesystem routines.
-#
-
-obj-$(CONFIG_MSDOS_FS) += msdos.o
-
-msdos-y := namei.o
+++ /dev/null
-/*
- * linux/fs/msdos/namei.c
- *
- * Written 1992,1993 by Werner Almesberger
- * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
- * Rewritten for constant inumbers 1999 by Al Viro
- */
-
-#include <linux/module.h>
-#include <linux/time.h>
-#include <linux/buffer_head.h>
-#include <linux/msdos_fs.h>
-#include <linux/smp_lock.h>
-
-/* Characters that are undesirable in an MS-DOS file name */
-static unsigned char bad_chars[] = "*?<>|\"";
-static unsigned char bad_if_strict[] = "+=,; ";
-
-/***** Formats an MS-DOS file name. Rejects invalid names. */
-static int msdos_format_name(const unsigned char *name, int len,
- unsigned char *res, struct fat_mount_options *opts)
- /*
- * name is the proposed name, len is its length, res is
- * the resulting name, opts->name_check is either (r)elaxed,
- * (n)ormal or (s)trict, opts->dotsOK allows dots at the
- * beginning of name (for hidden files)
- */
-{
- unsigned char *walk;
- unsigned char c;
- int space;
-
- if (name[0] == '.') { /* dotfile because . and .. already done */
- if (opts->dotsOK) {
- /* Get rid of dot - test for it elsewhere */
- name++;
- len--;
- } else
- return -EINVAL;
- }
- /*
- * disallow names that _really_ start with a dot
- */
- space = 1;
- c = 0;
- for (walk = res; len && walk - res < 8; walk++) {
- c = *name++;
- len--;
- if (opts->name_check != 'r' && strchr(bad_chars, c))
- return -EINVAL;
- if (opts->name_check == 's' && strchr(bad_if_strict, c))
- return -EINVAL;
- if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
- return -EINVAL;
- if (c < ' ' || c == ':' || c == '\\')
- return -EINVAL;
- /*
- * 0xE5 is legal as a first character, but we must substitute
- * 0x05 because 0xE5 marks deleted files. Yes, DOS really
- * does this.
- * It seems that Microsoft hacked DOS to support non-US
- * characters after the 0xE5 character was already in use to
- * mark deleted files.
- */
- if ((res == walk) && (c == 0xE5))
- c = 0x05;
- if (c == '.')
- break;
- space = (c == ' ');
- *walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c - 32 : c;
- }
- if (space)
- return -EINVAL;
- if (opts->name_check == 's' && len && c != '.') {
- c = *name++;
- len--;
- if (c != '.')
- return -EINVAL;
- }
- while (c != '.' && len--)
- c = *name++;
- if (c == '.') {
- while (walk - res < 8)
- *walk++ = ' ';
- while (len > 0 && walk - res < MSDOS_NAME) {
- c = *name++;
- len--;
- if (opts->name_check != 'r' && strchr(bad_chars, c))
- return -EINVAL;
- if (opts->name_check == 's' &&
- strchr(bad_if_strict, c))
- return -EINVAL;
- if (c < ' ' || c == ':' || c == '\\')
- return -EINVAL;
- if (c == '.') {
- if (opts->name_check == 's')
- return -EINVAL;
- break;
- }
- if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
- return -EINVAL;
- space = c == ' ';
- if (!opts->nocase && c >= 'a' && c <= 'z')
- *walk++ = c - 32;
- else
- *walk++ = c;
- }
- if (space)
- return -EINVAL;
- if (opts->name_check == 's' && len)
- return -EINVAL;
- }
- while (walk - res < MSDOS_NAME)
- *walk++ = ' ';
-
- return 0;
-}
-
-/***** Locates a directory entry. Uses unformatted name. */
-static int msdos_find(struct inode *dir, const unsigned char *name, int len,
- struct fat_slot_info *sinfo)
-{
- struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
- unsigned char msdos_name[MSDOS_NAME];
- int err;
-
- err = msdos_format_name(name, len, msdos_name, &sbi->options);
- if (err)
- return -ENOENT;
-
- err = fat_scan(dir, msdos_name, sinfo);
- if (!err && sbi->options.dotsOK) {
- if (name[0] == '.') {
- if (!(sinfo->de->attr & ATTR_HIDDEN))
- err = -ENOENT;
- } else {
- if (sinfo->de->attr & ATTR_HIDDEN)
- err = -ENOENT;
- }
- if (err)
- brelse(sinfo->bh);
- }
- return err;
-}
-
-/*
- * Compute the hash for the msdos name corresponding to the dentry.
- * Note: if the name is invalid, we leave the hash code unchanged so
- * that the existing dentry can be used. The msdos fs routines will
- * return ENOENT or EINVAL as appropriate.
- */
-static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
-{
- struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
- unsigned char msdos_name[MSDOS_NAME];
- int error;
-
- error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
- if (!error)
- qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
- return 0;
-}
-
-/*
- * Compare two msdos names. If either of the names are invalid,
- * we fall back to doing the standard name comparison.
- */
-static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
-{
- struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
- unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
- int error;
-
- error = msdos_format_name(a->name, a->len, a_msdos_name, options);
- if (error)
- goto old_compare;
- error = msdos_format_name(b->name, b->len, b_msdos_name, options);
- if (error)
- goto old_compare;
- error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
-out:
- return error;
-
-old_compare:
- error = 1;
- if (a->len == b->len)
- error = memcmp(a->name, b->name, a->len);
- goto out;
-}
-
-static struct dentry_operations msdos_dentry_operations = {
- .d_hash = msdos_hash,
- .d_compare = msdos_cmp,
-};
-
-/*
- * AV. Wrappers for FAT sb operations. Is it wise?
- */
-
-/***** Get inode using directory and name */
-static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
- struct nameidata *nd)
-{
- struct super_block *sb = dir->i_sb;
- struct fat_slot_info sinfo;
- struct inode *inode = NULL;
- int res;
-
- dentry->d_op = &msdos_dentry_operations;
-
- lock_super(sb);
- res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
- if (res == -ENOENT)
- goto add;
- if (res < 0)
- goto out;
- inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
- brelse(sinfo.bh);
- if (IS_ERR(inode)) {
- res = PTR_ERR(inode);
- goto out;
- }
-add:
- res = 0;
- dentry = d_splice_alias(inode, dentry);
- if (dentry)
- dentry->d_op = &msdos_dentry_operations;
-out:
- unlock_super(sb);
- if (!res)
- return dentry;
- return ERR_PTR(res);
-}
-
-/***** Creates a directory entry (name is already formatted). */
-static int msdos_add_entry(struct inode *dir, const unsigned char *name,
- int is_dir, int is_hid, int cluster,
- struct timespec *ts, struct fat_slot_info *sinfo)
-{
- struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
- struct msdos_dir_entry de;
- __le16 time, date;
- int err;
-
- memcpy(de.name, name, MSDOS_NAME);
- de.attr = is_dir ? ATTR_DIR : ATTR_ARCH;
- if (is_hid)
- de.attr |= ATTR_HIDDEN;
- de.lcase = 0;
- fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc);
- de.cdate = de.adate = 0;
- de.ctime = 0;
- de.ctime_cs = 0;
- de.time = time;
- de.date = date;
- de.start = cpu_to_le16(cluster);
- de.starthi = cpu_to_le16(cluster >> 16);
- de.size = 0;
-
- err = fat_add_entries(dir, &de, 1, sinfo);
- if (err)
- return err;
-
- dir->i_ctime = dir->i_mtime = *ts;
- if (IS_DIRSYNC(dir))
- (void)fat_sync_inode(dir);
- else
- mark_inode_dirty(dir);
-
- return 0;
-}
-
-/***** Create a file */
-static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
- struct nameidata *nd)
-{
- struct super_block *sb = dir->i_sb;
- struct inode *inode = NULL;
- struct fat_slot_info sinfo;
- struct timespec ts;
- unsigned char msdos_name[MSDOS_NAME];
- int err, is_hid;
-
- lock_super(sb);
-
- err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
- msdos_name, &MSDOS_SB(sb)->options);
- if (err)
- goto out;
- is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
- /* Have to do it due to foo vs. .foo conflicts */
- if (!fat_scan(dir, msdos_name, &sinfo)) {
- brelse(sinfo.bh);
- err = -EINVAL;
- goto out;
- }
-
- ts = CURRENT_TIME_SEC;
- err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
- if (err)
- goto out;
- inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
- brelse(sinfo.bh);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out;
- }
- inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
- /* timestamp is already written, so mark_inode_dirty() is unneeded. */
-
- d_instantiate(dentry, inode);
-out:
- unlock_super(sb);
- if (!err)
- err = fat_flush_inodes(sb, dir, inode);
- return err;
-}
-
-/***** Remove a directory */
-static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
-{
- struct super_block *sb = dir->i_sb;
- struct inode *inode = dentry->d_inode;
- struct fat_slot_info sinfo;
- int err;
-
- lock_super(sb);
- /*
- * Check whether the directory is not in use, then check
- * whether it is empty.
- */
- err = fat_dir_empty(inode);
- if (err)
- goto out;
- err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
- if (err)
- goto out;
-
- err = fat_remove_entries(dir, &sinfo); /* and releases bh */
- if (err)
- goto out;
- drop_nlink(dir);
-
- clear_nlink(inode);
- inode->i_ctime = CURRENT_TIME_SEC;
- fat_detach(inode);
-out:
- unlock_super(sb);
- if (!err)
- err = fat_flush_inodes(sb, dir, inode);
-
- return err;
-}
-
-/***** Make a directory */
-static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)
-{
- struct super_block *sb = dir->i_sb;
- struct fat_slot_info sinfo;
- struct inode *inode;
- unsigned char msdos_name[MSDOS_NAME];
- struct timespec ts;
- int err, is_hid, cluster;
-
- lock_super(sb);
-
- err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
- msdos_name, &MSDOS_SB(sb)->options);
- if (err)
- goto out;
- is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
- /* foo vs .foo situation */
- if (!fat_scan(dir, msdos_name, &sinfo)) {
- brelse(sinfo.bh);
- err = -EINVAL;
- goto out;
- }
-
- ts = CURRENT_TIME_SEC;
- cluster = fat_alloc_new_dir(dir, &ts);
- if (cluster < 0) {
- err = cluster;
- goto out;
- }
- err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
- if (err)
- goto out_free;
- inc_nlink(dir);
-
- inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
- brelse(sinfo.bh);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- /* the directory was completed, just return a error */
- goto out;
- }
- inode->i_nlink = 2;
- inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
- /* timestamp is already written, so mark_inode_dirty() is unneeded. */
-
- d_instantiate(dentry, inode);
-
- unlock_super(sb);
- fat_flush_inodes(sb, dir, inode);
- return 0;
-
-out_free:
- fat_free_clusters(dir, cluster);
-out:
- unlock_super(sb);
- return err;
-}
-
-/***** Unlink a file */
-static int msdos_unlink(struct inode *dir, struct dentry *dentry)
-{
- struct inode *inode = dentry->d_inode;
- struct super_block *sb= inode->i_sb;
- struct fat_slot_info sinfo;
- int err;
-
- lock_super(sb);
- err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
- if (err)
- goto out;
-
- err = fat_remove_entries(dir, &sinfo); /* and releases bh */
- if (err)
- goto out;
- clear_nlink(inode);
- inode->i_ctime = CURRENT_TIME_SEC;
- fat_detach(inode);
-out:
- unlock_super(sb);
- if (!err)
- err = fat_flush_inodes(sb, dir, inode);
-
- return err;
-}
-
-static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
- struct dentry *old_dentry,
- struct inode *new_dir, unsigned char *new_name,
- struct dentry *new_dentry, int is_hid)
-{
- struct buffer_head *dotdot_bh;
- struct msdos_dir_entry *dotdot_de;
- struct inode *old_inode, *new_inode;
- struct fat_slot_info old_sinfo, sinfo;
- struct timespec ts;
- loff_t dotdot_i_pos, new_i_pos;
- int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
-
- old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
- old_inode = old_dentry->d_inode;
- new_inode = new_dentry->d_inode;
-
- err = fat_scan(old_dir, old_name, &old_sinfo);
- if (err) {
- err = -EIO;
- goto out;
- }
-
- is_dir = S_ISDIR(old_inode->i_mode);
- update_dotdot = (is_dir && old_dir != new_dir);
- if (update_dotdot) {
- if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
- &dotdot_i_pos) < 0) {
- err = -EIO;
- goto out;
- }
- }
-
- old_attrs = MSDOS_I(old_inode)->i_attrs;
- err = fat_scan(new_dir, new_name, &sinfo);
- if (!err) {
- if (!new_inode) {
- /* "foo" -> ".foo" case. just change the ATTR_HIDDEN */
- if (sinfo.de != old_sinfo.de) {
- err = -EINVAL;
- goto out;
- }
- if (is_hid)
- MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
- else
- MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
- if (IS_DIRSYNC(old_dir)) {
- err = fat_sync_inode(old_inode);
- if (err) {
- MSDOS_I(old_inode)->i_attrs = old_attrs;
- goto out;
- }
- } else
- mark_inode_dirty(old_inode);
-
- old_dir->i_version++;
- old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
- if (IS_DIRSYNC(old_dir))
- (void)fat_sync_inode(old_dir);
- else
- mark_inode_dirty(old_dir);
- goto out;
- }
- }
-
- ts = CURRENT_TIME_SEC;
- if (new_inode) {
- if (err)
- goto out;
- if (is_dir) {
- err = fat_dir_empty(new_inode);
- if (err)
- goto out;
- }
- new_i_pos = MSDOS_I(new_inode)->i_pos;
- fat_detach(new_inode);
- } else {
- err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
- &ts, &sinfo);
- if (err)
- goto out;
- new_i_pos = sinfo.i_pos;
- }
- new_dir->i_version++;
-
- fat_detach(old_inode);
- fat_attach(old_inode, new_i_pos);
- if (is_hid)
- MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
- else
- MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
- if (IS_DIRSYNC(new_dir)) {
- err = fat_sync_inode(old_inode);
- if (err)
- goto error_inode;
- } else
- mark_inode_dirty(old_inode);
-
- if (update_dotdot) {
- int start = MSDOS_I(new_dir)->i_logstart;
- dotdot_de->start = cpu_to_le16(start);
- dotdot_de->starthi = cpu_to_le16(start >> 16);
- mark_buffer_dirty(dotdot_bh);
- if (IS_DIRSYNC(new_dir)) {
- err = sync_dirty_buffer(dotdot_bh);
- if (err)
- goto error_dotdot;
- }
- drop_nlink(old_dir);
- if (!new_inode)
- inc_nlink(new_dir);
- }
-
- err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */
- old_sinfo.bh = NULL;
- if (err)
- goto error_dotdot;
- old_dir->i_version++;
- old_dir->i_ctime = old_dir->i_mtime = ts;
- if (IS_DIRSYNC(old_dir))
- (void)fat_sync_inode(old_dir);
- else
- mark_inode_dirty(old_dir);
-
- if (new_inode) {
- drop_nlink(new_inode);
- if (is_dir)
- drop_nlink(new_inode);
- new_inode->i_ctime = ts;
- }
-out:
- brelse(sinfo.bh);
- brelse(dotdot_bh);
- brelse(old_sinfo.bh);
- return err;
-
-error_dotdot:
- /* data cluster is shared, serious corruption */
- corrupt = 1;
-
- if (update_dotdot) {
- int start = MSDOS_I(old_dir)->i_logstart;
- dotdot_de->start = cpu_to_le16(start);
- dotdot_de->starthi = cpu_to_le16(start >> 16);
- mark_buffer_dirty(dotdot_bh);
- corrupt |= sync_dirty_buffer(dotdot_bh);
- }
-error_inode:
- fat_detach(old_inode);
- fat_attach(old_inode, old_sinfo.i_pos);
- MSDOS_I(old_inode)->i_attrs = old_attrs;
- if (new_inode) {
- fat_attach(new_inode, new_i_pos);
- if (corrupt)
- corrupt |= fat_sync_inode(new_inode);
- } else {
- /*
- * If new entry was not sharing the data cluster, it
- * shouldn't be serious corruption.
- */
- int err2 = fat_remove_entries(new_dir, &sinfo);
- if (corrupt)
- corrupt |= err2;
- sinfo.bh = NULL;
- }
- if (corrupt < 0) {
- fat_fs_panic(new_dir->i_sb,
- "%s: Filesystem corrupted (i_pos %lld)",
- __func__, sinfo.i_pos);
- }
- goto out;
-}
-
-/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
-static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
-{
- struct super_block *sb = old_dir->i_sb;
- unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
- int err, is_hid;
-
- lock_super(sb);
-
- err = msdos_format_name(old_dentry->d_name.name,
- old_dentry->d_name.len, old_msdos_name,
- &MSDOS_SB(old_dir->i_sb)->options);
- if (err)
- goto out;
- err = msdos_format_name(new_dentry->d_name.name,
- new_dentry->d_name.len, new_msdos_name,
- &MSDOS_SB(new_dir->i_sb)->options);
- if (err)
- goto out;
-
- is_hid =
- (new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.');
-
- err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
- new_dir, new_msdos_name, new_dentry, is_hid);
-out:
- unlock_super(sb);
- if (!err)
- err = fat_flush_inodes(sb, old_dir, new_dir);
- return err;
-}
-
-static const struct inode_operations msdos_dir_inode_operations = {
- .create = msdos_create,
- .lookup = msdos_lookup,
- .unlink = msdos_unlink,
- .mkdir = msdos_mkdir,
- .rmdir = msdos_rmdir,
- .rename = msdos_rename,
- .setattr = fat_setattr,
- .getattr = fat_getattr,
-};
-
-static int msdos_fill_super(struct super_block *sb, void *data, int silent)
-{
- int res;
-
- res = fat_fill_super(sb, data, silent, &msdos_dir_inode_operations, 0);
- if (res)
- return res;
-
- sb->s_flags |= MS_NOATIME;
- sb->s_root->d_op = &msdos_dentry_operations;
- return 0;
-}
-
-static int msdos_get_sb(struct file_system_type *fs_type,
- int flags, const char *dev_name,
- void *data, struct vfsmount *mnt)
-{
- return get_sb_bdev(fs_type, flags, dev_name, data, msdos_fill_super,
- mnt);
-}
-
-static struct file_system_type msdos_fs_type = {
- .owner = THIS_MODULE,
- .name = "msdos",
- .get_sb = msdos_get_sb,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
-};
-
-static int __init init_msdos_fs(void)
-{
- return register_filesystem(&msdos_fs_type);
-}
-
-static void __exit exit_msdos_fs(void)
-{
- unregister_filesystem(&msdos_fs_type);
-}
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Werner Almesberger");
-MODULE_DESCRIPTION("MS-DOS filesystem support");
-
-module_init(init_msdos_fs)
-module_exit(exit_msdos_fs)
+++ /dev/null
-#
-# Makefile for the linux vfat-filesystem routines.
-#
-
-obj-$(CONFIG_VFAT_FS) += vfat.o
-
-vfat-y := namei.o
+++ /dev/null
-/*
- * linux/fs/vfat/namei.c
- *
- * Written 1992,1993 by Werner Almesberger
- *
- * Windows95/Windows NT compatible extended MSDOS filesystem
- * by Gordon Chaffee Copyright (C) 1995. Send bug reports for the
- * VFAT filesystem to <chaffee@cs.berkeley.edu>. Specify
- * what file operation caused you trouble and if you can duplicate
- * the problem, send a script that demonstrates it.
- *
- * Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de>
- *
- * Support Multibyte characters and cleanup by
- * OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
- */
-
-#include <linux/module.h>
-
-#include <linux/jiffies.h>
-#include <linux/msdos_fs.h>
-#include <linux/ctype.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/buffer_head.h>
-#include <linux/namei.h>
-
-static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
-{
- int ret = 1;
-
- if (!dentry->d_inode &&
- nd && !(nd->flags & LOOKUP_CONTINUE) && (nd->flags & LOOKUP_CREATE))
- /*
- * negative dentry is dropped, in order to make sure
- * to use the name which a user desires if this is
- * create path.
- */
- ret = 0;
- else {
- spin_lock(&dentry->d_lock);
- if (dentry->d_time != dentry->d_parent->d_inode->i_version)
- ret = 0;
- spin_unlock(&dentry->d_lock);
- }
- return ret;
-}
-
-/* returns the length of a struct qstr, ignoring trailing dots */
-static unsigned int vfat_striptail_len(struct qstr *qstr)
-{
- unsigned int len = qstr->len;
-
- while (len && qstr->name[len - 1] == '.')
- len--;
- return len;
-}
-
-/*
- * Compute the hash for the vfat name corresponding to the dentry.
- * Note: if the name is invalid, we leave the hash code unchanged so
- * that the existing dentry can be used. The vfat fs routines will
- * return ENOENT or EINVAL as appropriate.
- */
-static int vfat_hash(struct dentry *dentry, struct qstr *qstr)
-{
- qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr));
- return 0;
-}
-
-/*
- * Compute the hash for the vfat name corresponding to the dentry.
- * Note: if the name is invalid, we leave the hash code unchanged so
- * that the existing dentry can be used. The vfat fs routines will
- * return ENOENT or EINVAL as appropriate.
- */
-static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
-{
- struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
- const unsigned char *name;
- unsigned int len;
- unsigned long hash;
-
- name = qstr->name;
- len = vfat_striptail_len(qstr);
-
- hash = init_name_hash();
- while (len--)
- hash = partial_name_hash(nls_tolower(t, *name++), hash);
- qstr->hash = end_name_hash(hash);
-
- return 0;
-}
-
-/*
- * Case insensitive compare of two vfat names.
- */
-static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
-{
- struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
- unsigned int alen, blen;
-
- /* A filename cannot end in '.' or we treat it like it has none */
- alen = vfat_striptail_len(a);
- blen = vfat_striptail_len(b);
- if (alen == blen) {
- if (nls_strnicmp(t, a->name, b->name, alen) == 0)
- return 0;
- }
- return 1;
-}
-
-/*
- * Case sensitive compare of two vfat names.
- */
-static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
-{
- unsigned int alen, blen;
-
- /* A filename cannot end in '.' or we treat it like it has none */
- alen = vfat_striptail_len(a);
- blen = vfat_striptail_len(b);
- if (alen == blen) {
- if (strncmp(a->name, b->name, alen) == 0)
- return 0;
- }
- return 1;
-}
-
-static struct dentry_operations vfat_dentry_ops[4] = {
- {
- .d_hash = vfat_hashi,
- .d_compare = vfat_cmpi,
- },
- {
- .d_revalidate = vfat_revalidate,
- .d_hash = vfat_hashi,
- .d_compare = vfat_cmpi,
- },
- {
- .d_hash = vfat_hash,
- .d_compare = vfat_cmp,
- },
- {
- .d_revalidate = vfat_revalidate,
- .d_hash = vfat_hash,
- .d_compare = vfat_cmp,
- }
-};
-
-/* Characters that are undesirable in an MS-DOS file name */
-
-static inline wchar_t vfat_bad_char(wchar_t w)
-{
- return (w < 0x0020)
- || (w == '*') || (w == '?') || (w == '<') || (w == '>')
- || (w == '|') || (w == '"') || (w == ':') || (w == '/')
- || (w == '\\');
-}
-
-static inline wchar_t vfat_replace_char(wchar_t w)
-{
- return (w == '[') || (w == ']') || (w == ';') || (w == ',')
- || (w == '+') || (w == '=');
-}
-
-static wchar_t vfat_skip_char(wchar_t w)
-{
- return (w == '.') || (w == ' ');
-}
-
-static inline int vfat_is_used_badchars(const wchar_t *s, int len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- if (vfat_bad_char(s[i]))
- return -EINVAL;
-
- if (s[i - 1] == ' ') /* last character cannot be space */
- return -EINVAL;
-
- return 0;
-}
-
-static int vfat_find_form(struct inode *dir, unsigned char *name)
-{
- struct fat_slot_info sinfo;
- int err = fat_scan(dir, name, &sinfo);
- if (err)
- return -ENOENT;
- brelse(sinfo.bh);
- return 0;
-}
-
-/*
- * 1) Valid characters for the 8.3 format alias are any combination of
- * letters, uppercase alphabets, digits, any of the
- * following special characters:
- * $ % ' ` - @ { } ~ ! # ( ) & _ ^
- * In this case Longfilename is not stored in disk.
- *
- * WinNT's Extension:
- * File name and extension name is contain uppercase/lowercase
- * only. And it is expressed by CASE_LOWER_BASE and CASE_LOWER_EXT.
- *
- * 2) File name is 8.3 format, but it contain the uppercase and
- * lowercase char, muliti bytes char, etc. In this case numtail is not
- * added, but Longfilename is stored.
- *
- * 3) When the one except for the above, or the following special
- * character are contained:
- * . [ ] ; , + =
- * numtail is added, and Longfilename must be stored in disk .
- */
-struct shortname_info {
- unsigned char lower:1,
- upper:1,
- valid:1;
-};
-#define INIT_SHORTNAME_INFO(x) do { \
- (x)->lower = 1; \
- (x)->upper = 1; \
- (x)->valid = 1; \
-} while (0)
-
-static inline int to_shortname_char(struct nls_table *nls,
- unsigned char *buf, int buf_size,
- wchar_t *src, struct shortname_info *info)
-{
- int len;
-
- if (vfat_skip_char(*src)) {
- info->valid = 0;
- return 0;
- }
- if (vfat_replace_char(*src)) {
- info->valid = 0;
- buf[0] = '_';
- return 1;
- }
-
- len = nls->uni2char(*src, buf, buf_size);
- if (len <= 0) {
- info->valid = 0;
- buf[0] = '_';
- len = 1;
- } else if (len == 1) {
- unsigned char prev = buf[0];
-
- if (buf[0] >= 0x7F) {
- info->lower = 0;
- info->upper = 0;
- }
-
- buf[0] = nls_toupper(nls, buf[0]);
- if (isalpha(buf[0])) {
- if (buf[0] == prev)
- info->lower = 0;
- else
- info->upper = 0;
- }
- } else {
- info->lower = 0;
- info->upper = 0;
- }
-
- return len;
-}
-
-/*
- * Given a valid longname, create a unique shortname. Make sure the
- * shortname does not exist
- * Returns negative number on error, 0 for a normal
- * return, and 1 for valid shortname
- */
-static int vfat_create_shortname(struct inode *dir, struct nls_table *nls,
- wchar_t *uname, int ulen,
- unsigned char *name_res, unsigned char *lcase)
-{
- struct fat_mount_options *opts = &MSDOS_SB(dir->i_sb)->options;
- wchar_t *ip, *ext_start, *end, *name_start;
- unsigned char base[9], ext[4], buf[8], *p;
- unsigned char charbuf[NLS_MAX_CHARSET_SIZE];
- int chl, chi;
- int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen;
- int is_shortname;
- struct shortname_info base_info, ext_info;
-
- is_shortname = 1;
- INIT_SHORTNAME_INFO(&base_info);
- INIT_SHORTNAME_INFO(&ext_info);
-
- /* Now, we need to create a shortname from the long name */
- ext_start = end = &uname[ulen];
- while (--ext_start >= uname) {
- if (*ext_start == 0x002E) { /* is `.' */
- if (ext_start == end - 1) {
- sz = ulen;
- ext_start = NULL;
- }
- break;
- }
- }
-
- if (ext_start == uname - 1) {
- sz = ulen;
- ext_start = NULL;
- } else if (ext_start) {
- /*
- * Names which start with a dot could be just
- * an extension eg. "...test". In this case Win95
- * uses the extension as the name and sets no extension.
- */
- name_start = &uname[0];
- while (name_start < ext_start) {
- if (!vfat_skip_char(*name_start))
- break;
- name_start++;
- }
- if (name_start != ext_start) {
- sz = ext_start - uname;
- ext_start++;
- } else {
- sz = ulen;
- ext_start = NULL;
- }
- }
-
- numtail_baselen = 6;
- numtail2_baselen = 2;
- for (baselen = i = 0, p = base, ip = uname; i < sz; i++, ip++) {
- chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
- ip, &base_info);
- if (chl == 0)
- continue;
-
- if (baselen < 2 && (baselen + chl) > 2)
- numtail2_baselen = baselen;
- if (baselen < 6 && (baselen + chl) > 6)
- numtail_baselen = baselen;
- for (chi = 0; chi < chl; chi++) {
- *p++ = charbuf[chi];
- baselen++;
- if (baselen >= 8)
- break;
- }
- if (baselen >= 8) {
- if ((chi < chl - 1) || (ip + 1) - uname < sz)
- is_shortname = 0;
- break;
- }
- }
- if (baselen == 0) {
- return -EINVAL;
- }
-
- extlen = 0;
- if (ext_start) {
- for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
- chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
- ip, &ext_info);
- if (chl == 0)
- continue;
-
- if ((extlen + chl) > 3) {
- is_shortname = 0;
- break;
- }
- for (chi = 0; chi < chl; chi++) {
- *p++ = charbuf[chi];
- extlen++;
- }
- if (extlen >= 3) {
- if (ip + 1 != end)
- is_shortname = 0;
- break;
- }
- }
- }
- ext[extlen] = '\0';
- base[baselen] = '\0';
-
- /* Yes, it can happen. ".\xe5" would do it. */
- if (base[0] == DELETED_FLAG)
- base[0] = 0x05;
-
- /* OK, at this point we know that base is not longer than 8 symbols,
- * ext is not longer than 3, base is nonempty, both don't contain
- * any bad symbols (lowercase transformed to uppercase).
- */
-
- memset(name_res, ' ', MSDOS_NAME);
- memcpy(name_res, base, baselen);
- memcpy(name_res + 8, ext, extlen);
- *lcase = 0;
- if (is_shortname && base_info.valid && ext_info.valid) {
- if (vfat_find_form(dir, name_res) == 0)
- return -EEXIST;
-
- if (opts->shortname & VFAT_SFN_CREATE_WIN95) {
- return (base_info.upper && ext_info.upper);
- } else if (opts->shortname & VFAT_SFN_CREATE_WINNT) {
- if ((base_info.upper || base_info.lower) &&
- (ext_info.upper || ext_info.lower)) {
- if (!base_info.upper && base_info.lower)
- *lcase |= CASE_LOWER_BASE;
- if (!ext_info.upper && ext_info.lower)
- *lcase |= CASE_LOWER_EXT;
- return 1;
- }
- return 0;
- } else {
- BUG();
- }
- }
-
- if (opts->numtail == 0)
- if (vfat_find_form(dir, name_res) < 0)
- return 0;
-
- /*
- * Try to find a unique extension. This used to
- * iterate through all possibilities sequentially,
- * but that gave extremely bad performance. Windows
- * only tries a few cases before using random
- * values for part of the base.
- */
-
- if (baselen > 6) {
- baselen = numtail_baselen;
- name_res[7] = ' ';
- }
- name_res[baselen] = '~';
- for (i = 1; i < 10; i++) {
- name_res[baselen + 1] = i + '0';
- if (vfat_find_form(dir, name_res) < 0)
- return 0;
- }
-
- i = jiffies & 0xffff;
- sz = (jiffies >> 16) & 0x7;
- if (baselen > 2) {
- baselen = numtail2_baselen;
- name_res[7] = ' ';
- }
- name_res[baselen + 4] = '~';
- name_res[baselen + 5] = '1' + sz;
- while (1) {
- sprintf(buf, "%04X", i);
- memcpy(&name_res[baselen], buf, 4);
- if (vfat_find_form(dir, name_res) < 0)
- break;
- i -= 11;
- }
- return 0;
-}
-
-/* Translate a string, including coded sequences into Unicode */
-static int
-xlate_to_uni(const unsigned char *name, int len, unsigned char *outname,
- int *longlen, int *outlen, int escape, int utf8,
- struct nls_table *nls)
-{
- const unsigned char *ip;
- unsigned char nc;
- unsigned char *op;
- unsigned int ec;
- int i, k, fill;
- int charlen;
-
- if (utf8) {
- int name_len = strlen(name);
-
- *outlen = utf8_mbstowcs((wchar_t *)outname, name, PATH_MAX);
-
- /*
- * We stripped '.'s before and set len appropriately,
- * but utf8_mbstowcs doesn't care about len
- */
- *outlen -= (name_len - len);
-
- if (*outlen > 255)
- return -ENAMETOOLONG;
-
- op = &outname[*outlen * sizeof(wchar_t)];
- } else {
- if (nls) {
- for (i = 0, ip = name, op = outname, *outlen = 0;
- i < len && *outlen <= 255;
- *outlen += 1)
- {
- if (escape && (*ip == ':')) {
- if (i > len - 5)
- return -EINVAL;
- ec = 0;
- for (k = 1; k < 5; k++) {
- nc = ip[k];
- ec <<= 4;
- if (nc >= '0' && nc <= '9') {
- ec |= nc - '0';
- continue;
- }
- if (nc >= 'a' && nc <= 'f') {
- ec |= nc - ('a' - 10);
- continue;
- }
- if (nc >= 'A' && nc <= 'F') {
- ec |= nc - ('A' - 10);
- continue;
- }
- return -EINVAL;
- }
- *op++ = ec & 0xFF;
- *op++ = ec >> 8;
- ip += 5;
- i += 5;
- } else {
- if ((charlen = nls->char2uni(ip, len - i, (wchar_t *)op)) < 0)
- return -EINVAL;
- ip += charlen;
- i += charlen;
- op += 2;
- }
- }
- if (i < len)
- return -ENAMETOOLONG;
- } else {
- for (i = 0, ip = name, op = outname, *outlen = 0;
- i < len && *outlen <= 255;
- i++, *outlen += 1)
- {
- *op++ = *ip++;
- *op++ = 0;
- }
- if (i < len)
- return -ENAMETOOLONG;
- }
- }
-
- *longlen = *outlen;
- if (*outlen % 13) {
- *op++ = 0;
- *op++ = 0;
- *outlen += 1;
- if (*outlen % 13) {
- fill = 13 - (*outlen % 13);
- for (i = 0; i < fill; i++) {
- *op++ = 0xff;
- *op++ = 0xff;
- }
- *outlen += fill;
- }
- }
-
- return 0;
-}
-
-static int vfat_build_slots(struct inode *dir, const unsigned char *name,
- int len, int is_dir, int cluster,
- struct timespec *ts,
- struct msdos_dir_slot *slots, int *nr_slots)
-{
- struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
- struct fat_mount_options *opts = &sbi->options;
- struct msdos_dir_slot *ps;
- struct msdos_dir_entry *de;
- unsigned char cksum, lcase;
- unsigned char msdos_name[MSDOS_NAME];
- wchar_t *uname;
- __le16 time, date;
- int err, ulen, usize, i;
- loff_t offset;
-
- *nr_slots = 0;
-
- uname = __getname();
- if (!uname)
- return -ENOMEM;
-
- err = xlate_to_uni(name, len, (unsigned char *)uname, &ulen, &usize,
- opts->unicode_xlate, opts->utf8, sbi->nls_io);
- if (err)
- goto out_free;
-
- err = vfat_is_used_badchars(uname, ulen);
- if (err)
- goto out_free;
-
- err = vfat_create_shortname(dir, sbi->nls_disk, uname, ulen,
- msdos_name, &lcase);
- if (err < 0)
- goto out_free;
- else if (err == 1) {
- de = (struct msdos_dir_entry *)slots;
- err = 0;
- goto shortname;
- }
-
- /* build the entry of long file name */
- cksum = fat_checksum(msdos_name);
-
- *nr_slots = usize / 13;
- for (ps = slots, i = *nr_slots; i > 0; i--, ps++) {
- ps->id = i;
- ps->attr = ATTR_EXT;
- ps->reserved = 0;
- ps->alias_checksum = cksum;
- ps->start = 0;
- offset = (i - 1) * 13;
- fatwchar_to16(ps->name0_4, uname + offset, 5);
- fatwchar_to16(ps->name5_10, uname + offset + 5, 6);
- fatwchar_to16(ps->name11_12, uname + offset + 11, 2);
- }
- slots[0].id |= 0x40;
- de = (struct msdos_dir_entry *)ps;
-
-shortname:
- /* build the entry of 8.3 alias name */
- (*nr_slots)++;
- memcpy(de->name, msdos_name, MSDOS_NAME);
- de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
- de->lcase = lcase;
- fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc);
- de->time = de->ctime = time;
- de->date = de->cdate = de->adate = date;
- de->ctime_cs = 0;
- de->start = cpu_to_le16(cluster);
- de->starthi = cpu_to_le16(cluster >> 16);
- de->size = 0;
-out_free:
- __putname(uname);
- return err;
-}
-
-static int vfat_add_entry(struct inode *dir, struct qstr *qname, int is_dir,
- int cluster, struct timespec *ts,
- struct fat_slot_info *sinfo)
-{
- struct msdos_dir_slot *slots;
- unsigned int len;
- int err, nr_slots;
-
- len = vfat_striptail_len(qname);
- if (len == 0)
- return -ENOENT;
-
- slots = kmalloc(sizeof(*slots) * MSDOS_SLOTS, GFP_NOFS);
- if (slots == NULL)
- return -ENOMEM;
-
- err = vfat_build_slots(dir, qname->name, len, is_dir, cluster, ts,
- slots, &nr_slots);
- if (err)
- goto cleanup;
-
- err = fat_add_entries(dir, slots, nr_slots, sinfo);
- if (err)
- goto cleanup;
-
- /* update timestamp */
- dir->i_ctime = dir->i_mtime = dir->i_atime = *ts;
- if (IS_DIRSYNC(dir))
- (void)fat_sync_inode(dir);
- else
- mark_inode_dirty(dir);
-cleanup:
- kfree(slots);
- return err;
-}
-
-static int vfat_find(struct inode *dir, struct qstr *qname,
- struct fat_slot_info *sinfo)
-{
- unsigned int len = vfat_striptail_len(qname);
- if (len == 0)
- return -ENOENT;
- return fat_search_long(dir, qname->name, len, sinfo);
-}
-
-static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
- struct nameidata *nd)
-{
- struct super_block *sb = dir->i_sb;
- struct fat_slot_info sinfo;
- struct inode *inode = NULL;
- struct dentry *alias;
- int err, table;
-
- lock_super(sb);
- table = (MSDOS_SB(sb)->options.name_check == 's') ? 2 : 0;
- dentry->d_op = &vfat_dentry_ops[table];
-
- err = vfat_find(dir, &dentry->d_name, &sinfo);
- if (err) {
- table++;
- goto error;
- }
- inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
- brelse(sinfo.bh);
- if (IS_ERR(inode)) {
- unlock_super(sb);
- return ERR_CAST(inode);
- }
- alias = d_find_alias(inode);
- if (alias) {
- if (d_invalidate(alias) == 0)
- dput(alias);
- else {
- iput(inode);
- unlock_super(sb);
- return alias;
- }
-
- }
-error:
- unlock_super(sb);
- dentry->d_op = &vfat_dentry_ops[table];
- dentry->d_time = dentry->d_parent->d_inode->i_version;
- dentry = d_splice_alias(inode, dentry);
- if (dentry) {
- dentry->d_op = &vfat_dentry_ops[table];
- dentry->d_time = dentry->d_parent->d_inode->i_version;
- }
- return dentry;
-}
-
-static int vfat_create(struct inode *dir, struct dentry *dentry, int mode,
- struct nameidata *nd)
-{
- struct super_block *sb = dir->i_sb;
- struct inode *inode;
- struct fat_slot_info sinfo;
- struct timespec ts;
- int err;
-
- lock_super(sb);
-
- ts = CURRENT_TIME_SEC;
- err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo);
- if (err)
- goto out;
- dir->i_version++;
-
- inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
- brelse(sinfo.bh);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- goto out;
- }
- inode->i_version++;
- inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
- /* timestamp is already written, so mark_inode_dirty() is unneeded. */
-
- dentry->d_time = dentry->d_parent->d_inode->i_version;
- d_instantiate(dentry, inode);
-out:
- unlock_super(sb);
- return err;
-}
-
-static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
-{
- struct inode *inode = dentry->d_inode;
- struct super_block *sb = dir->i_sb;
- struct fat_slot_info sinfo;
- int err;
-
- lock_super(sb);
-
- err = fat_dir_empty(inode);
- if (err)
- goto out;
- err = vfat_find(dir, &dentry->d_name, &sinfo);
- if (err)
- goto out;
-
- err = fat_remove_entries(dir, &sinfo); /* and releases bh */
- if (err)
- goto out;
- drop_nlink(dir);
-
- clear_nlink(inode);
- inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
- fat_detach(inode);
-out:
- unlock_super(sb);
-
- return err;
-}
-
-static int vfat_unlink(struct inode *dir, struct dentry *dentry)
-{
- struct inode *inode = dentry->d_inode;
- struct super_block *sb = dir->i_sb;
- struct fat_slot_info sinfo;
- int err;
-
- lock_super(sb);
-
- err = vfat_find(dir, &dentry->d_name, &sinfo);
- if (err)
- goto out;
-
- err = fat_remove_entries(dir, &sinfo); /* and releases bh */
- if (err)
- goto out;
- clear_nlink(inode);
- inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
- fat_detach(inode);
-out:
- unlock_super(sb);
-
- return err;
-}
-
-static int vfat_mkdir(struct inode *dir, struct dentry *dentry, int mode)
-{
- struct super_block *sb = dir->i_sb;
- struct inode *inode;
- struct fat_slot_info sinfo;
- struct timespec ts;
- int err, cluster;
-
- lock_super(sb);
-
- ts = CURRENT_TIME_SEC;
- cluster = fat_alloc_new_dir(dir, &ts);
- if (cluster < 0) {
- err = cluster;
- goto out;
- }
- err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &ts, &sinfo);
- if (err)
- goto out_free;
- dir->i_version++;
- inc_nlink(dir);
-
- inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
- brelse(sinfo.bh);
- if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
- /* the directory was completed, just return a error */
- goto out;
- }
- inode->i_version++;
- inode->i_nlink = 2;
- inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
- /* timestamp is already written, so mark_inode_dirty() is unneeded. */
-
- dentry->d_time = dentry->d_parent->d_inode->i_version;
- d_instantiate(dentry, inode);
-
- unlock_super(sb);
- return 0;
-
-out_free:
- fat_free_clusters(dir, cluster);
-out:
- unlock_super(sb);
- return err;
-}
-
-static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
-{
- struct buffer_head *dotdot_bh;
- struct msdos_dir_entry *dotdot_de;
- struct inode *old_inode, *new_inode;
- struct fat_slot_info old_sinfo, sinfo;
- struct timespec ts;
- loff_t dotdot_i_pos, new_i_pos;
- int err, is_dir, update_dotdot, corrupt = 0;
- struct super_block *sb = old_dir->i_sb;
-
- old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
- old_inode = old_dentry->d_inode;
- new_inode = new_dentry->d_inode;
- lock_super(sb);
- err = vfat_find(old_dir, &old_dentry->d_name, &old_sinfo);
- if (err)
- goto out;
-
- is_dir = S_ISDIR(old_inode->i_mode);
- update_dotdot = (is_dir && old_dir != new_dir);
- if (update_dotdot) {
- if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
- &dotdot_i_pos) < 0) {
- err = -EIO;
- goto out;
- }
- }
-
- ts = CURRENT_TIME_SEC;
- if (new_inode) {
- if (is_dir) {
- err = fat_dir_empty(new_inode);
- if (err)
- goto out;
- }
- new_i_pos = MSDOS_I(new_inode)->i_pos;
- fat_detach(new_inode);
- } else {
- err = vfat_add_entry(new_dir, &new_dentry->d_name, is_dir, 0,
- &ts, &sinfo);
- if (err)
- goto out;
- new_i_pos = sinfo.i_pos;
- }
- new_dir->i_version++;
-
- fat_detach(old_inode);
- fat_attach(old_inode, new_i_pos);
- if (IS_DIRSYNC(new_dir)) {
- err = fat_sync_inode(old_inode);
- if (err)
- goto error_inode;
- } else
- mark_inode_dirty(old_inode);
-
- if (update_dotdot) {
- int start = MSDOS_I(new_dir)->i_logstart;
- dotdot_de->start = cpu_to_le16(start);
- dotdot_de->starthi = cpu_to_le16(start >> 16);
- mark_buffer_dirty(dotdot_bh);
- if (IS_DIRSYNC(new_dir)) {
- err = sync_dirty_buffer(dotdot_bh);
- if (err)
- goto error_dotdot;
- }
- drop_nlink(old_dir);
- if (!new_inode)
- inc_nlink(new_dir);
- }
-
- err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */
- old_sinfo.bh = NULL;
- if (err)
- goto error_dotdot;
- old_dir->i_version++;
- old_dir->i_ctime = old_dir->i_mtime = ts;
- if (IS_DIRSYNC(old_dir))
- (void)fat_sync_inode(old_dir);
- else
- mark_inode_dirty(old_dir);
-
- if (new_inode) {
- drop_nlink(new_inode);
- if (is_dir)
- drop_nlink(new_inode);
- new_inode->i_ctime = ts;
- }
-out:
- brelse(sinfo.bh);
- brelse(dotdot_bh);
- brelse(old_sinfo.bh);
- unlock_super(sb);
-
- return err;
-
-error_dotdot:
- /* data cluster is shared, serious corruption */
- corrupt = 1;
-
- if (update_dotdot) {
- int start = MSDOS_I(old_dir)->i_logstart;
- dotdot_de->start = cpu_to_le16(start);
- dotdot_de->starthi = cpu_to_le16(start >> 16);
- mark_buffer_dirty(dotdot_bh);
- corrupt |= sync_dirty_buffer(dotdot_bh);
- }
-error_inode:
- fat_detach(old_inode);
- fat_attach(old_inode, old_sinfo.i_pos);
- if (new_inode) {
- fat_attach(new_inode, new_i_pos);
- if (corrupt)
- corrupt |= fat_sync_inode(new_inode);
- } else {
- /*
- * If new entry was not sharing the data cluster, it
- * shouldn't be serious corruption.
- */
- int err2 = fat_remove_entries(new_dir, &sinfo);
- if (corrupt)
- corrupt |= err2;
- sinfo.bh = NULL;
- }
- if (corrupt < 0) {
- fat_fs_panic(new_dir->i_sb,
- "%s: Filesystem corrupted (i_pos %lld)",
- __func__, sinfo.i_pos);
- }
- goto out;
-}
-
-static const struct inode_operations vfat_dir_inode_operations = {
- .create = vfat_create,
- .lookup = vfat_lookup,
- .unlink = vfat_unlink,
- .mkdir = vfat_mkdir,
- .rmdir = vfat_rmdir,
- .rename = vfat_rename,
- .setattr = fat_setattr,
- .getattr = fat_getattr,
-};
-
-static int vfat_fill_super(struct super_block *sb, void *data, int silent)
-{
- int res;
-
- res = fat_fill_super(sb, data, silent, &vfat_dir_inode_operations, 1);
- if (res)
- return res;
-
- if (MSDOS_SB(sb)->options.name_check != 's')
- sb->s_root->d_op = &vfat_dentry_ops[0];
- else
- sb->s_root->d_op = &vfat_dentry_ops[2];
-
- return 0;
-}
-
-static int vfat_get_sb(struct file_system_type *fs_type,
- int flags, const char *dev_name,
- void *data, struct vfsmount *mnt)
-{
- return get_sb_bdev(fs_type, flags, dev_name, data, vfat_fill_super,
- mnt);
-}
-
-static struct file_system_type vfat_fs_type = {
- .owner = THIS_MODULE,
- .name = "vfat",
- .get_sb = vfat_get_sb,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
-};
-
-static int __init init_vfat_fs(void)
-{
- return register_filesystem(&vfat_fs_type);
-}
-
-static void __exit exit_vfat_fs(void)
-{
- unregister_filesystem(&vfat_fs_type);
-}
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("VFAT filesystem support");
-MODULE_AUTHOR("Gordon Chaffee");
-
-module_init(init_vfat_fs)
-module_exit(exit_vfat_fs)
timer->_softexpires = ktime_add_safe(timer->_softexpires, time);
}
-static inline void hrtimer_add_expires_ns(struct hrtimer *timer, unsigned long ns)
+static inline void hrtimer_add_expires_ns(struct hrtimer *timer, u64 ns)
{
timer->_expires = ktime_add_ns(timer->_expires, ns);
timer->_softexpires = ktime_add_ns(timer->_softexpires, ns);
#define DELETED_FLAG 0xe5 /* marks file as deleted when in name[0] */
#define IS_FREE(n) (!*(n) || *(n) == DELETED_FLAG)
-/* valid file mode bits */
-#define MSDOS_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)
-/* Convert attribute bits and a mask to the UNIX mode. */
-#define MSDOS_MKMODE(a, m) (m & (a & ATTR_RO ? S_IRUGO|S_IXUGO : S_IRWXUGO))
-
#define MSDOS_NAME 11 /* maximum name length */
#define MSDOS_LONGNAME 256 /* maximum name length */
#define MSDOS_SLOTS 21 /* max # of slots for short and long names */
};
#ifdef __KERNEL__
-
-#include <linux/buffer_head.h>
-#include <linux/string.h>
-#include <linux/nls.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-
-/*
- * vfat shortname flags
- */
-#define VFAT_SFN_DISPLAY_LOWER 0x0001 /* convert to lowercase for display */
-#define VFAT_SFN_DISPLAY_WIN95 0x0002 /* emulate win95 rule for display */
-#define VFAT_SFN_DISPLAY_WINNT 0x0004 /* emulate winnt rule for display */
-#define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */
-#define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */
-
-struct fat_mount_options {
- uid_t fs_uid;
- gid_t fs_gid;
- unsigned short fs_fmask;
- unsigned short fs_dmask;
- unsigned short codepage; /* Codepage for shortname conversions */
- char *iocharset; /* Charset used for filename input/display */
- unsigned short shortname; /* flags for shortname display/create rule */
- unsigned char name_check; /* r = relaxed, n = normal, s = strict */
- unsigned short allow_utime;/* permission for setting the [am]time */
- unsigned quiet:1, /* set = fake successful chmods and chowns */
- showexec:1, /* set = only set x bit for com/exe/bat */
- sys_immutable:1, /* set = system files are immutable */
- dotsOK:1, /* set = hidden and system files are named '.filename' */
- isvfat:1, /* 0=no vfat long filename support, 1=vfat support */
- utf8:1, /* Use of UTF-8 character set (Default) */
- unicode_xlate:1, /* create escape sequences for unhandled Unicode */
- numtail:1, /* Does first alias have a numeric '~1' type tail? */
- flush:1, /* write things quickly */
- nocase:1, /* Does this need case conversion? 0=need case conversion*/
- usefree:1, /* Use free_clusters for FAT32 */
- tz_utc:1; /* Filesystem timestamps are in UTC */
-};
-
-#define FAT_HASH_BITS 8
-#define FAT_HASH_SIZE (1UL << FAT_HASH_BITS)
-#define FAT_HASH_MASK (FAT_HASH_SIZE-1)
-
-/*
- * MS-DOS file system in-core superblock data
- */
-struct msdos_sb_info {
- unsigned short sec_per_clus; /* sectors/cluster */
- unsigned short cluster_bits; /* log2(cluster_size) */
- unsigned int cluster_size; /* cluster size */
- unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */
- unsigned short fat_start;
- unsigned long fat_length; /* FAT start & length (sec.) */
- unsigned long dir_start;
- unsigned short dir_entries; /* root dir start & entries */
- unsigned long data_start; /* first data sector */
- unsigned long max_cluster; /* maximum cluster number */
- unsigned long root_cluster; /* first cluster of the root directory */
- unsigned long fsinfo_sector; /* sector number of FAT32 fsinfo */
- struct mutex fat_lock;
- unsigned int prev_free; /* previously allocated cluster number */
- unsigned int free_clusters; /* -1 if undefined */
- unsigned int free_clus_valid; /* is free_clusters valid? */
- struct fat_mount_options options;
- struct nls_table *nls_disk; /* Codepage used on disk */
- struct nls_table *nls_io; /* Charset used for input and display */
- const void *dir_ops; /* Opaque; default directory operations */
- int dir_per_block; /* dir entries per block */
- int dir_per_block_bits; /* log2(dir_per_block) */
-
- int fatent_shift;
- struct fatent_operations *fatent_ops;
-
- spinlock_t inode_hash_lock;
- struct hlist_head inode_hashtable[FAT_HASH_SIZE];
-};
-
-#define FAT_CACHE_VALID 0 /* special case for valid cache */
-
-/*
- * MS-DOS file system inode data in memory
- */
-struct msdos_inode_info {
- spinlock_t cache_lru_lock;
- struct list_head cache_lru;
- int nr_caches;
- /* for avoiding the race between fat_free() and fat_get_cluster() */
- unsigned int cache_valid_id;
-
- loff_t mmu_private;
- int i_start; /* first cluster or 0 */
- int i_logstart; /* logical first cluster */
- int i_attrs; /* unused attribute bits */
- loff_t i_pos; /* on-disk position of directory entry or 0 */
- struct hlist_node i_fat_hash; /* hash by i_location */
- struct inode vfs_inode;
-};
-
-struct fat_slot_info {
- loff_t i_pos; /* on-disk position of directory entry */
- loff_t slot_off; /* offset for slot or de start */
- int nr_slots; /* number of slots + 1(de) in filename */
- struct msdos_dir_entry *de;
- struct buffer_head *bh;
-};
-
-static inline struct msdos_sb_info *MSDOS_SB(struct super_block *sb)
-{
- return sb->s_fs_info;
-}
-
-static inline struct msdos_inode_info *MSDOS_I(struct inode *inode)
-{
- return container_of(inode, struct msdos_inode_info, vfs_inode);
-}
-
-/* Return the FAT attribute byte for this inode */
-static inline u8 fat_attr(struct inode *inode)
-{
- return ((inode->i_mode & S_IWUGO) ? ATTR_NONE : ATTR_RO) |
- (S_ISDIR(inode->i_mode) ? ATTR_DIR : ATTR_NONE) |
- MSDOS_I(inode)->i_attrs;
-}
-
-static inline unsigned char fat_checksum(const __u8 *name)
-{
- unsigned char s = name[0];
- s = (s<<7) + (s>>1) + name[1]; s = (s<<7) + (s>>1) + name[2];
- s = (s<<7) + (s>>1) + name[3]; s = (s<<7) + (s>>1) + name[4];
- s = (s<<7) + (s>>1) + name[5]; s = (s<<7) + (s>>1) + name[6];
- s = (s<<7) + (s>>1) + name[7]; s = (s<<7) + (s>>1) + name[8];
- s = (s<<7) + (s>>1) + name[9]; s = (s<<7) + (s>>1) + name[10];
- return s;
-}
-
-static inline sector_t fat_clus_to_blknr(struct msdos_sb_info *sbi, int clus)
-{
- return ((sector_t)clus - FAT_START_ENT) * sbi->sec_per_clus
- + sbi->data_start;
-}
-
-static inline void fat16_towchar(wchar_t *dst, const __u8 *src, size_t len)
-{
-#ifdef __BIG_ENDIAN
- while (len--) {
- *dst++ = src[0] | (src[1] << 8);
- src += 2;
- }
-#else
- memcpy(dst, src, len * 2);
-#endif
-}
-
-static inline void fatwchar_to16(__u8 *dst, const wchar_t *src, size_t len)
-{
-#ifdef __BIG_ENDIAN
- while (len--) {
- dst[0] = *src & 0x00FF;
- dst[1] = (*src & 0xFF00) >> 8;
- dst += 2;
- src++;
- }
-#else
- memcpy(dst, src, len * 2);
-#endif
-}
-
/* media of boot sector */
static inline int fat_valid_media(u8 media)
{
return 0xf8 <= media || media == 0xf0;
}
-
-/* fat/cache.c */
-extern void fat_cache_inval_inode(struct inode *inode);
-extern int fat_get_cluster(struct inode *inode, int cluster,
- int *fclus, int *dclus);
-extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
- unsigned long *mapped_blocks);
-
-/* fat/dir.c */
-extern const struct file_operations fat_dir_operations;
-extern int fat_search_long(struct inode *inode, const unsigned char *name,
- int name_len, struct fat_slot_info *sinfo);
-extern int fat_dir_empty(struct inode *dir);
-extern int fat_subdirs(struct inode *dir);
-extern int fat_scan(struct inode *dir, const unsigned char *name,
- struct fat_slot_info *sinfo);
-extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
- struct msdos_dir_entry **de, loff_t *i_pos);
-extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts);
-extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
- struct fat_slot_info *sinfo);
-extern int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo);
-
-/* fat/fatent.c */
-struct fat_entry {
- int entry;
- union {
- u8 *ent12_p[2];
- __le16 *ent16_p;
- __le32 *ent32_p;
- } u;
- int nr_bhs;
- struct buffer_head *bhs[2];
-};
-
-static inline void fatent_init(struct fat_entry *fatent)
-{
- fatent->nr_bhs = 0;
- fatent->entry = 0;
- fatent->u.ent32_p = NULL;
- fatent->bhs[0] = fatent->bhs[1] = NULL;
-}
-
-static inline void fatent_set_entry(struct fat_entry *fatent, int entry)
-{
- fatent->entry = entry;
- fatent->u.ent32_p = NULL;
-}
-
-static inline void fatent_brelse(struct fat_entry *fatent)
-{
- int i;
- fatent->u.ent32_p = NULL;
- for (i = 0; i < fatent->nr_bhs; i++)
- brelse(fatent->bhs[i]);
- fatent->nr_bhs = 0;
- fatent->bhs[0] = fatent->bhs[1] = NULL;
-}
-
-extern void fat_ent_access_init(struct super_block *sb);
-extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent,
- int entry);
-extern int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
- int new, int wait);
-extern int fat_alloc_clusters(struct inode *inode, int *cluster,
- int nr_cluster);
-extern int fat_free_clusters(struct inode *inode, int cluster);
-extern int fat_count_free_clusters(struct super_block *sb);
-
-/* fat/file.c */
-extern int fat_generic_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg);
-extern const struct file_operations fat_file_operations;
-extern const struct inode_operations fat_file_inode_operations;
-extern int fat_setattr(struct dentry * dentry, struct iattr * attr);
-extern void fat_truncate(struct inode *inode);
-extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry,
- struct kstat *stat);
-
-/* fat/inode.c */
-extern void fat_attach(struct inode *inode, loff_t i_pos);
-extern void fat_detach(struct inode *inode);
-extern struct inode *fat_iget(struct super_block *sb, loff_t i_pos);
-extern struct inode *fat_build_inode(struct super_block *sb,
- struct msdos_dir_entry *de, loff_t i_pos);
-extern int fat_sync_inode(struct inode *inode);
-extern int fat_fill_super(struct super_block *sb, void *data, int silent,
- const struct inode_operations *fs_dir_inode_ops, int isvfat);
-
-extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
- struct inode *i2);
-/* fat/misc.c */
-extern void fat_fs_panic(struct super_block *s, const char *fmt, ...);
-extern void fat_clusters_flush(struct super_block *sb);
-extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster);
-extern int date_dos2unix(unsigned short time, unsigned short date, int tz_utc);
-extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date,
- int tz_utc);
-extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs);
-
-int fat_cache_init(void);
-void fat_cache_destroy(void);
-
-#endif /* __KERNEL__ */
-
-#endif
+#endif /* !__KERNEL__ */
+#endif /* !_LINUX_MSDOS_FS_H */
/*
* Returns the command address according to the given geometry.
*/
-static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
+static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
+ struct map_info *map, struct cfi_private *cfi)
{
- return (cmd_ofs * type) * interleave;
+ unsigned bankwidth = map_bankwidth(map);
+ unsigned interleave = cfi_interleave(cfi);
+ unsigned type = cfi->device_type;
+ uint32_t addr;
+
+ addr = (cmd_ofs * type) * interleave;
+
+ /* Modify the unlock address if we are in compatiblity mode.
+ * For 16bit devices on 8 bit busses
+ * and 32bit devices on 16 bit busses
+ * set the low bit of the alternating bit sequence of the address.
+ */
+ if (((type * interleave) > bankwidth) && ((uint8_t)cmd_ofs == 0xaa))
+ addr |= (type >> 1)*interleave;
+
+ return addr;
}
/*
int type, map_word *prev_val)
{
map_word val;
- uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);
+ uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, map, cfi);
val = cfi_build_cmd(cmd, map, cfi);
if (prev_val)
*/
unsigned long timer_slack_ns;
unsigned long default_timer_slack_ns;
+
+ struct list_head *scm_work_list;
};
/*
.wake_idx = 1, \
.forkexec_idx = 1, \
.flags = SD_LOAD_BALANCE \
- | SD_BALANCE_NEWIDLE \
- | SD_BALANCE_FORK \
| SD_BALANCE_EXEC \
+ | SD_BALANCE_FORK \
| SD_WAKE_AFFINE \
+ | SD_WAKE_BALANCE \
| BALANCE_FOR_PKG_POWER,\
.last_balance = jiffies, \
.balance_interval = 1, \
struct scm_fp_list
{
- int count;
- struct file *fp[SCM_MAX_FD];
+ struct list_head list;
+ int count;
+ struct file *fp[SCM_MAX_FD];
};
struct scm_cookie
list_del(&cgrp->sibling);
spin_lock(&cgrp->dentry->d_lock);
d = dget(cgrp->dentry);
- cgrp->dentry = NULL;
spin_unlock(&d->d_lock);
cgroup_d_remove_dir(d);
* 'curr' points to currently running entity on this cfs_rq.
* It is set to NULL otherwise (i.e when none are currently running).
*/
- struct sched_entity *curr, *next;
+ struct sched_entity *curr, *next, *last;
unsigned long nr_spread_over;
/*
* Buddy candidates are cache hot:
*/
- if (sched_feat(CACHE_HOT_BUDDY) && (&p->se == cfs_rq_of(&p->se)->next))
+ if (sched_feat(CACHE_HOT_BUDDY) &&
+ (&p->se == cfs_rq_of(&p->se)->next ||
+ &p->se == cfs_rq_of(&p->se)->last))
return 1;
if (p->sched_class != &fair_sched_class)
cfs_rq->rb_leftmost = next_node;
}
- if (cfs_rq->next == se)
- cfs_rq->next = NULL;
-
rb_erase(&se->run_node, &cfs_rq->tasks_timeline);
}
-static inline struct rb_node *first_fair(struct cfs_rq *cfs_rq)
-{
- return cfs_rq->rb_leftmost;
-}
-
static struct sched_entity *__pick_next_entity(struct cfs_rq *cfs_rq)
{
- return rb_entry(first_fair(cfs_rq), struct sched_entity, run_node);
+ struct rb_node *left = cfs_rq->rb_leftmost;
+
+ if (!left)
+ return NULL;
+
+ return rb_entry(left, struct sched_entity, run_node);
}
-static inline struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq)
+static struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq)
{
struct rb_node *last = rb_last(&cfs_rq->tasks_timeline);
#endif
}
+ if (cfs_rq->last == se)
+ cfs_rq->last = NULL;
+
+ if (cfs_rq->next == se)
+ cfs_rq->next = NULL;
+
if (se != cfs_rq->curr)
__dequeue_entity(cfs_rq, se);
account_entity_dequeue(cfs_rq, se);
static int
wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se);
-static struct sched_entity *
-pick_next(struct cfs_rq *cfs_rq, struct sched_entity *se)
-{
- if (!cfs_rq->next || wakeup_preempt_entity(cfs_rq->next, se) == 1)
- return se;
-
- return cfs_rq->next;
-}
-
static struct sched_entity *pick_next_entity(struct cfs_rq *cfs_rq)
{
- struct sched_entity *se = NULL;
+ struct sched_entity *se = __pick_next_entity(cfs_rq);
- if (first_fair(cfs_rq)) {
- se = __pick_next_entity(cfs_rq);
- se = pick_next(cfs_rq, se);
- set_next_entity(cfs_rq, se);
- }
+ if (cfs_rq->next && wakeup_preempt_entity(cfs_rq->next, se) < 1)
+ return cfs_rq->next;
+
+ if (cfs_rq->last && wakeup_preempt_entity(cfs_rq->last, se) < 1)
+ return cfs_rq->last;
return se;
}
return 0;
}
+static void set_last_buddy(struct sched_entity *se)
+{
+ for_each_sched_entity(se)
+ cfs_rq_of(se)->last = se;
+}
+
+static void set_next_buddy(struct sched_entity *se)
+{
+ for_each_sched_entity(se)
+ cfs_rq_of(se)->next = se;
+}
+
/*
* Preempt the current task with a newly woken task if needed:
*/
static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int sync)
{
struct task_struct *curr = rq->curr;
- struct cfs_rq *cfs_rq = task_cfs_rq(curr);
struct sched_entity *se = &curr->se, *pse = &p->se;
if (unlikely(rt_prio(p->prio))) {
+ struct cfs_rq *cfs_rq = task_cfs_rq(curr);
+
update_rq_clock(rq);
update_curr(cfs_rq);
resched_task(curr);
return;
}
+ if (unlikely(p->sched_class != &fair_sched_class))
+ return;
+
if (unlikely(se == pse))
return;
- cfs_rq_of(pse)->next = pse;
+ /*
+ * Only set the backward buddy when the current task is still on the
+ * rq. This can happen when a wakeup gets interleaved with schedule on
+ * the ->pre_schedule() or idle_balance() point, either of which can
+ * drop the rq lock.
+ *
+ * Also, during early boot the idle thread is in the fair class, for
+ * obvious reasons its a bad idea to schedule back to the idle thread.
+ */
+ if (sched_feat(LAST_BUDDY) && likely(se->on_rq && curr != rq->idle))
+ set_last_buddy(se);
+ set_next_buddy(pse);
/*
* We can come here with TIF_NEED_RESCHED already set from new task
do {
se = pick_next_entity(cfs_rq);
+ set_next_entity(cfs_rq, se);
cfs_rq = group_cfs_rq(se);
} while (cfs_rq);
SCHED_FEAT(LB_WAKEUP_UPDATE, 1)
SCHED_FEAT(ASYM_EFF_LOAD, 1)
SCHED_FEAT(WAKEUP_OVERLAP, 0)
+SCHED_FEAT(LAST_BUDDY, 1)
return 0;
}
+static void clear_gigantic_page(struct page *page,
+ unsigned long addr, unsigned long sz)
+{
+ int i;
+ struct page *p = page;
+
+ might_sleep();
+ for (i = 0; i < sz/PAGE_SIZE; i++, p = mem_map_next(p, page, i)) {
+ cond_resched();
+ clear_user_highpage(p, addr + i * PAGE_SIZE);
+ }
+}
static void clear_huge_page(struct page *page,
unsigned long addr, unsigned long sz)
{
int i;
+ if (unlikely(sz > MAX_ORDER_NR_PAGES))
+ return clear_gigantic_page(page, addr, sz);
+
might_sleep();
for (i = 0; i < sz/PAGE_SIZE; i++) {
cond_resched();
}
}
+static void copy_gigantic_page(struct page *dst, struct page *src,
+ unsigned long addr, struct vm_area_struct *vma)
+{
+ int i;
+ struct hstate *h = hstate_vma(vma);
+ struct page *dst_base = dst;
+ struct page *src_base = src;
+ might_sleep();
+ for (i = 0; i < pages_per_huge_page(h); ) {
+ cond_resched();
+ copy_user_highpage(dst, src, addr + i*PAGE_SIZE, vma);
+
+ i++;
+ dst = mem_map_next(dst, dst_base, i);
+ src = mem_map_next(src, src_base, i);
+ }
+}
static void copy_huge_page(struct page *dst, struct page *src,
unsigned long addr, struct vm_area_struct *vma)
{
int i;
struct hstate *h = hstate_vma(vma);
+ if (unlikely(pages_per_huge_page(h) > MAX_ORDER_NR_PAGES))
+ return copy_gigantic_page(dst, src, addr, vma);
+
might_sleep();
for (i = 0; i < pages_per_huge_page(h); i++) {
cond_resched();
{
int i;
+ VM_BUG_ON(h->order >= MAX_ORDER);
+
h->nr_huge_pages--;
h->nr_huge_pages_node[page_to_nid(page)]--;
for (i = 0; i < pages_per_huge_page(h); i++) {
return 1;
}
+static void prep_compound_huge_page(struct page *page, int order)
+{
+ if (unlikely(order > (MAX_ORDER - 1)))
+ prep_compound_gigantic_page(page, order);
+ else
+ prep_compound_page(page, order);
+}
+
/* Put bootmem huge pages into the standard lists after mem_map is up */
static void __init gather_bootmem_prealloc(void)
{
struct hstate *h = m->hstate;
__ClearPageReserved(page);
WARN_ON(page_count(page) != 1);
- prep_compound_page(page, h->order);
+ prep_compound_huge_page(page, h->order);
prep_new_huge_page(h, page, page_to_nid(page));
}
}
if (zeropage_ok)
pages[i] = ZERO_PAGE(0);
else
- pages[i] = page + pfn_offset;
+ pages[i] = mem_map_offset(page, pfn_offset);
get_page(pages[i]);
}
unsigned long floor, unsigned long ceiling);
extern void prep_compound_page(struct page *page, unsigned long order);
+extern void prep_compound_gigantic_page(struct page *page, unsigned long order);
static inline void set_page_count(struct page *page, int v)
{
#endif /* CONFIG_UNEVICTABLE_LRU */
+/*
+ * Return the mem_map entry representing the 'offset' subpage within
+ * the maximally aligned gigantic page 'base'. Handle any discontiguity
+ * in the mem_map at MAX_ORDER_NR_PAGES boundaries.
+ */
+static inline struct page *mem_map_offset(struct page *base, int offset)
+{
+ if (unlikely(offset >= MAX_ORDER_NR_PAGES))
+ return pfn_to_page(page_to_pfn(base) + offset);
+ return base + offset;
+}
+
+/*
+ * Iterator over all subpages withing the maximally aligned gigantic
+ * page 'base'. Handle any discontiguity in the mem_map.
+ */
+static inline struct page *mem_map_next(struct page *iter,
+ struct page *base, int offset)
+{
+ if (unlikely((offset & (MAX_ORDER_NR_PAGES - 1)) == 0)) {
+ unsigned long pfn = page_to_pfn(base) + offset;
+ if (!pfn_valid(pfn))
+ return NULL;
+ return pfn_to_page(pfn);
+ }
+ return iter + 1;
+}
+
/*
* FLATMEM and DISCONTIGMEM configurations use alloc_bootmem_node,
* so all functions starting at paging_init should be marked __init
int err;
struct vm_area_struct *first, *vma, *prev;
- if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
-
- err = migrate_prep();
- if (err)
- return ERR_PTR(err);
- }
first = find_vma(mm, start);
if (!first)
const nodemask_t *from_nodes, const nodemask_t *to_nodes, int flags)
{
int busy = 0;
- int err = 0;
+ int err;
nodemask_t tmp;
+ err = migrate_prep();
+ if (err)
+ return err;
+
down_read(&mm->mmap_sem);
err = migrate_vmas(mm, from_nodes, to_nodes, flags);
start, start + len, mode, mode_flags,
nmask ? nodes_addr(*nmask)[0] : -1);
+ if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) {
+
+ err = migrate_prep();
+ if (err)
+ return err;
+ }
down_write(&mm->mmap_sem);
vma = check_range(mm, start, end, nmask,
flags | MPOL_MF_INVERT, &pagelist);
struct page_to_node *pp;
LIST_HEAD(pagelist);
+ migrate_prep();
down_read(&mm->mmap_sem);
/*
* Build a list of pages to migrate
*/
- migrate_prep();
for (pp = pm; pp->node != MAX_NUMNODES; pp++) {
struct vm_area_struct *vma;
struct page *page;
* badness - calculate a numeric value for how bad this task has been
* @p: task struct of which task we should calculate
* @uptime: current uptime in seconds
- * @mem: target memory controller
*
* The formula used is relatively simple and documented inline in the
* function. The main rationale is that we want to select a good task
continue;
if (mem && !task_in_mem_cgroup(p, mem))
continue;
+ if (!thread_group_leader(p))
+ continue;
task_lock(p);
printk(KERN_INFO "[%5d] %5d %5d %8lu %8lu %3d %3d %s\n",
}
void prep_compound_page(struct page *page, unsigned long order)
+{
+ int i;
+ int nr_pages = 1 << order;
+
+ set_compound_page_dtor(page, free_compound_page);
+ set_compound_order(page, order);
+ __SetPageHead(page);
+ for (i = 1; i < nr_pages; i++) {
+ struct page *p = page + i;
+
+ __SetPageTail(p);
+ p->first_page = page;
+ }
+}
+
+#ifdef CONFIG_HUGETLBFS
+void prep_compound_gigantic_page(struct page *page, unsigned long order)
{
int i;
int nr_pages = 1 << order;
set_compound_page_dtor(page, free_compound_page);
set_compound_order(page, order);
__SetPageHead(page);
- for (i = 1; i < nr_pages; i++, p++) {
- if (unlikely((i & (MAX_ORDER_NR_PAGES - 1)) == 0))
- p = pfn_to_page(page_to_pfn(page) + i);
+ for (i = 1; i < nr_pages; i++, p = mem_map_next(p, page, i)) {
__SetPageTail(p);
p->first_page = page;
}
}
+#endif
static void destroy_compound_page(struct page *page, unsigned long order)
{
int i;
int nr_pages = 1 << order;
- struct page *p = page + 1;
if (unlikely(compound_order(page) != order))
bad_page(page);
if (unlikely(!PageHead(page)))
bad_page(page);
__ClearPageHead(page);
- for (i = 1; i < nr_pages; i++, p++) {
- if (unlikely((i & (MAX_ORDER_NR_PAGES - 1)) == 0))
- p = pfn_to_page(page_to_pfn(page) + i);
+ for (i = 1; i < nr_pages; i++) {
+ struct page *p = page + i;
if (unlikely(!PageTail(p) |
(p->first_page != page)))
if (page && get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
break;
}
- if (pfn < end_pfn)
+ page = __first_valid_page(start_pfn, end_pfn - start_pfn);
+ if ((pfn < end_pfn) || !page)
return -EBUSY;
/* Check all pages are free or Marked as ISOLATED */
- zone = page_zone(pfn_to_page(pfn));
+ zone = page_zone(page);
spin_lock_irqsave(&zone->lock, flags);
ret = __test_page_isolated_in_pageblock(start_pfn, end_pfn);
spin_unlock_irqrestore(&zone->lock, flags);
unsigned long pfn = pte_pfn(*pte);
int actual_node = early_pfn_to_nid(pfn);
- if (actual_node != node)
+ if (node_distance(actual_node, node) > LOCAL_DISTANCE)
printk(KERN_WARNING "[%lx-%lx] potential offnode "
"page_structs\n", start, end - 1);
}
if (!fpl)
return -ENOMEM;
*fplp = fpl;
+ INIT_LIST_HEAD(&fpl->list);
fpl->count = 0;
}
fpp = &fpl->fp[fpl->count];
if (fpl) {
scm->fp = NULL;
- for (i=fpl->count-1; i>=0; i--)
- fput(fpl->fp[i]);
- kfree(fpl);
+ if (current->scm_work_list) {
+ list_add_tail(&fpl->list, current->scm_work_list);
+ } else {
+ LIST_HEAD(work_list);
+
+ current->scm_work_list = &work_list;
+
+ list_add(&fpl->list, &work_list);
+ while (!list_empty(&work_list)) {
+ fpl = list_first_entry(&work_list, struct scm_fp_list, list);
+
+ list_del(&fpl->list);
+ for (i=fpl->count-1; i>=0; i--)
+ fput(fpl->fp[i]);
+ kfree(fpl);
+ }
+
+ current->scm_work_list = NULL;
+ }
}
}
new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL);
if (new_fpl) {
+ INIT_LIST_HEAD(&new_fpl->list);
for (i=fpl->count-1; i>=0; i--)
get_file(fpl->fp[i]);
memcpy(new_fpl, fpl, sizeof(*fpl));