diff options
| author | Matt Fleming <matt.fleming@intel.com> | 2012-12-07 11:33:45 +0000 |
|---|---|---|
| committer | Matt Fleming <matt.fleming@intel.com> | 2012-12-07 11:33:45 +0000 |
| commit | 10f6cf6eef0a7da7dad1933efdbfb101155792d0 (patch) | |
| tree | d8ee3bfc6e55e739e0f135cd6d945955c670dd46 /core | |
| parent | 35928ee37da523e5f992cc462a4a4193d0bfaa4c (diff) | |
| parent | ddb10ce99c327888ade4d2ba3e4c50ad12aaa059 (diff) | |
| download | syslinux-6.00-pre3.tar.gz | |
Merge tag 'syslinux-5.00' into firmwaresyslinux-6.00-pre3
Conflicts:
Makefile
com32/elflink/ldlinux/Makefile
com32/lib/sys/module/elf_module.c
core/cleanup.c
core/comboot.inc
core/conio.c
core/fs/fs.c
core/init.c
core/mem/free.c
core/mem/malloc.c
core/timer.inc
diag/geodsp/Makefile
extlinux/main.c
mk/embedded.mk
modules/Makefile
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'core')
54 files changed, 3924 insertions, 1092 deletions
diff --git a/core/bios.c b/core/bios.c index 5b4a8cef..7b41f398 100644 --- a/core/bios.c +++ b/core/bios.c @@ -462,6 +462,9 @@ struct vesa_ops bios_vesa_ops = { static uint32_t min_lowmem_heap = 65536; extern char __lowmem_heap[]; uint8_t KbdFlags; /* Check for keyboard escapes */ +__export uint8_t KbdMap[256]; /* Keyboard map */ + +__export uint16_t PXERetry; static inline void check_escapes(void) { diff --git a/core/bios.inc b/core/bios.inc index 33a3cd4c..2e150594 100644 --- a/core/bios.inc +++ b/core/bios.inc @@ -18,7 +18,6 @@ %ifndef _BIOS_INC %define _BIOS_INC - global BIOS_fbm, BIOS_timer ; Interrupt vectors absolute 4*1Ch diff --git a/core/call16.c b/core/call16.c index 0943a721..471aef96 100644 --- a/core/call16.c +++ b/core/call16.c @@ -20,7 +20,7 @@ #include <stdio.h> #include "core.h" -const com32sys_t zero_regs; /* Common all-zero register set */ +__export const com32sys_t zero_regs; /* Common all-zero register set */ static inline uint32_t eflags(void) { @@ -38,7 +38,8 @@ static inline uint32_t eflags(void) return v; } -void call16(void (*func)(void), const com32sys_t *ireg, com32sys_t *oreg) +__export void call16(void (*func)(void), const com32sys_t *ireg, + com32sys_t *oreg) { com32sys_t xreg = *ireg; diff --git a/core/callback.inc b/core/callback.inc index d98d8008..454b4522 100644 --- a/core/callback.inc +++ b/core/callback.inc @@ -37,12 +37,12 @@ ; - Return segment (== real mode cs == 0) ; - Return flags ; - global core_farcall + global core_farcall:function hidden core_farcall: mov eax,[esp+1*4] ; CS:IP jmp core_syscall - global core_intcall + global core_intcall:function hidden core_intcall: movzx eax,byte [esp+1*4] ; INT number mov eax,[eax*4] ; Get CS:IP from low memory @@ -142,7 +142,7 @@ core_syscall: ; followed by the return CS:IP and the CS:IP of the target function. ; The value of IF is copied from the calling routine. ; - global core_cfarcall + global core_cfarcall:function hidden core_cfarcall: pushfd ; Save IF among other things... push ebx diff --git a/core/cleanup.c b/core/cleanup.c index 4abefcd0..de318d98 100644 --- a/core/cleanup.c +++ b/core/cleanup.c @@ -47,7 +47,7 @@ void bios_cleanup_hardware(void) * * Shut down anything transient. */ -void cleanup_hardware(void) +__export void cleanup_hardware(void) { firmware->cleanup(); } diff --git a/core/com32.inc b/core/com32.inc index 929f50ec..9c565f1d 100644 --- a/core/com32.inc +++ b/core/com32.inc @@ -40,7 +40,7 @@ com32_entry equ free_high_memory ; ; Danger, Will Robinson: it's not clear the use of ; core_xfer_buf is safe here. - global __entry_esp, __com32 + global __com32:data hidden alignz 4 __entry_esp: dd 0 ; Dummy to avoid _exit issues diff --git a/core/comboot.inc b/core/comboot.inc index fcd6c756..3197c8a7 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -95,7 +95,7 @@ comboot_setup_api: ; Restore the original state of the COMBOOT API vectors, and free ; any low memory allocated by the comboot module. ; - global comboot_cleanup_api + global comboot_cleanup_api:function hidden comboot_cleanup_api: pusha mov si,DOSSaveVectors @@ -114,7 +114,7 @@ DOSSaveVectors resd 32 comboot_vectors: dw comboot_return ; INT 20 = exit - dw comboot_int21 ; INT 21 = DOS-compatible system calls + dw comboot_err(21h) ; INT 21 = DOS-compatible system calls dw comboot_int22 ; INT 22 = native system calls dw comboot_err(23h) ; INT 23 = DOS Ctrl-C handler dw comboot_err(24h) ; INT 24 = DOS critical error handler @@ -148,31 +148,6 @@ comboot_vectors: section .text16 -; INT 21h: generic DOS system call -comboot_int21: sti - push ds - push es - push fs - push gs - pushad - cld - mov bp,cs - mov ds,bp - mov es,bp - mov bp,sp ; Set up stack frame - - pm_call pm_adjust_screen ; The COMBOOT program might hav changed the screen - - mov cx,int21_count - mov si,int21_table -.again: lodsb - cmp al,P_AH - lodsw - loopne .again - ; The last function in the list is the - ; "no such function" function - clc - call ax ; Call the invoked function comboot_resume: mov bp,sp ; In case the function clobbers BP setc P_FLAGSL ; Propagate CF->error @@ -212,7 +187,7 @@ comboot_bogus_tail: jmp kaboom ; Proper return vector -; Note: this gets invoked both via INT 21h and directly via INT 20h. +; Note: this gets invoked directly via INT 20h. ; We don't need to cld explicitly here, because comboot_exit does that ; when invoking RESET_STACK_AND_SEGS. comboot_return: @@ -367,348 +342,36 @@ comapi_err: ret ; -; INT 22h AX=0001h Get SYSLINUX version +; INT 22h AX=001Ch Get pointer to auxillary data vector ; -comapi_get_version: - ; Number of API functions supported - mov P_AX,int22_count - ; SYSLINUX version - mov P_CX,(VERSION_MAJOR << 8)+VERSION_MINOR - ; SYSLINUX derivative ID byte - mov P_DX,my_id - ; For future use - mov P_BX,cs ; cs == 0 - +comapi_getadv: mov P_ES,ds - ; ES:SI -> version banner - mov P_SI,syslinux_banner + 2 ; Skip leading CR LF - ; ES:DI -> copyright string - mov P_DI,copyright_str + 1 ; Skip leading space - -comapi_nop: - clc - ret - -; -; INT 22h AX=0002h Write string -; -; Write null-terminated string in ES:BX -; -comapi_writestr: - mov ds,P_ES - mov si,P_BX - pm_call pm_writestr - clc - ret - -; -; INT 22h AX=0003h Run command -; -; Terminates the COMBOOT program and executes the command line in -; ES:BX as if it had been entered by the user. -; -comapi_run: - mov es,P_ES - mov bx,P_BX - pm_call pm_env32_run - ret - -; -; INT 22h AX=0004h Run default command -; -; Terminates the COMBOOT program and executes the default command line -; as if a timeout had happened or the user pressed <Enter>. -; -comapi_run_default: - push auto_boot - jmp comboot_exit - -; -; INT 22h AX=0005h Force text mode -; -; Puts the video in standard text mode -; -comapi_textmode: - pm_call syslinux_force_text_mode - clc - ret - -; -; INT 22h AX=0006h Open file -; -comapi_open: - mov es,P_ES - mov si,P_SI - pm_call pm_open_file - mov P_EAX,eax - mov P_CX,cx - mov P_SI,si - ret - -; -; INT 22h AX=0007h Read file -; -comapi_read: - mov es,P_ES - mov bx,P_BX - mov si,P_SI - mov cx,P_CX - pm_call getfssec - jnc .noteof - xor si,si ; SI <- 0 on EOF, CF <- 0 -.noteof: mov P_SI,si - mov P_ECX,ecx - ret - -; -; INT 22h AX=0008h Close file -; -comapi_close: - mov si,P_SI - pm_call pm_close_file - clc - ret - -; -; INT 22h AX=0009h Call PXE stack -; -%if IS_PXELINUX -comapi_pxecall: - mov bx,P_BX - mov es,P_ES - mov di,P_DI - call pxenv - mov ax,[PXEStatus] - mov P_AX,ax + mov P_BX,adv0.data + mov P_CX,ADV_LEN ret -%else -comapi_pxecall equ comapi_err ; Not available -%endif ; -; INT 22h AX=000Bh Get Serial Console Configuration +; INT 22h AX=001Dh Write auxillary data vector ; -comapi_serialcfg: - pm_call pm_serialcfg - mov P_DX,ax - mov P_CX,cx - mov P_BX,bx - clc - ret +comapi_writeadv equ adv_write ; -; INT 22h AX=000Ch Perform final cleanup +; INT 22h AX=0024h Cleanup, shuffle and boot raw ; -comapi_cleanup: +comapi_shufraw: %if IS_PXELINUX ; Unload PXE if requested test dl,3 setnz [KeepPXE] - sub bp,sp ; unload_pxe may move the stack around + sub bp,sp ; unload_pxe may move the stack around pm_call unload_pxe - add bp,sp ; restore frame pointer... + add bp,sp ; restore frame pointer... %elif IS_SYSLINUX || IS_EXTLINUX ; Restore original FDC table mov eax,[OrigFDCTabPtr] mov [fdctab],eax %endif pm_call cleanup_hardware - clc - ret - -; -; INT 22h AX=000Dh Obsolete -; - -; -; INT 22h AX=000Eh Get configuration file name -; -comapi_configfile: - mov P_ES,cs - mov P_BX,ConfigName - clc - ret - -; -; INT 22h AX=000Fh Get IPAPPEND strings -; -comapi_ipappend: - mov P_ES,cs - mov P_CX,numIPAppends - mov P_BX,IPAppends - clc - ret - -; -; INT 22h AX=0010h Resolve hostname -; -%if IS_PXELINUX - extern pm_pxe_dns_resolv -comapi_dnsresolv: - mov ds,P_ES - mov si,P_BX - pm_call pm_pxe_dns_resolv - mov P_EAX,eax - clc - ret -%else -comapi_dnsresolv equ comapi_err -%endif - - section .text16 - -; -; INT 22h AX=0011h Obsolete -; - -; -; INT 22h AX=0012h Obsolete -; - -; -; INT 22h AX=0013h Idle call -; -comapi_idle: - call do_idle - clc - ret - -; -; INT 22h AX=0014h Local boot -; -comapi_localboot: - mov ax,P_DX - pm_call pm_local_boot - ret - -; -; INT 22h AX=0015h Feature flags -; -comapi_features: - mov P_ES,cs - mov P_BX,feature_flags - mov P_CX,feature_flags_len - clc - ret - -; -; INT 22h AX=0016h Run kernel image -; -comapi_runkernel: - ret - -; -; INT 22h AX=0017h Report video mode change -; -comapi_usingvga: - mov ax,P_BX - cmp ax,0Fh ; Unknown flags = failure - ja .error - mov cx,P_CX - mov dx,P_DX - pm_call pm_using_vga - clc - ret -.error: - stc - ret - -; -; INT 22h AX=0018h Query custom font -; -comapi_userfont: - mov al,[UserFont] - and al,al - jz .done - mov al,[VGAFontSize] - pm_call pm_userfont - mov P_ES,es - mov P_BX,bx - -.done: ; CF=0 here - mov P_AL,al - ret - -; -; INT 22h AX=0019h Read disk -; -%if IS_SYSLINUX || IS_ISOLINUX || IS_EXTLINUX -comapi_readdisk: - cmp P_EDI,0 ; Reserved for future expansion - jnz .err - mov eax,P_EDX - mov edx,P_ESI - mov es,P_ES - mov bx,P_BX - mov bp,P_CX ; WE CANNOT use P_* after touching bp! - call getlinsec - clc - ret -.err: - stc - ret -%else -comapi_readdisk equ comapi_err -%endif - -; -; INT 22h AX=001Ah Obsolete -; - -; -; INT 22h AX=001Bh Obsolete -; - -; -; INT 22h AX=001Ch Get pointer to auxillary data vector -; -comapi_getadv: - mov P_ES,ds - mov P_BX,adv0.data - mov P_CX,ADV_LEN - ret - -; -; INT 22h AX=001Dh Write auxillary data vector -; -comapi_writeadv equ adv_write - -; -; INT 22h AX=001Eh Keyboard remapping table -comapi_kbdtable: - cmp P_DX,0 - jne .err - mov P_AX,1 ; Version - mov P_CX,256 ; Length - mov P_ES,cs - mov P_BX,KbdMap - ret -.err: - stc - ret - -; -; INT 22h AX=001Fh Get current working directory -; -comapi_getcwd: - mov P_ES,cs - mov P_BX,CurrentDirName - clc - ret - -; -; INT 22h AX=0023h Query shuffler size -; -comapi_shufsize: - ; +15 is padding to guarantee alignment - mov P_CX,__bcopyxx_len + 15 - ret - -; -; INT 22h AX=0024h Cleanup, shuffle and boot raw -; -comapi_shufraw: - call comapi_cleanup mov edi,P_EDI mov esi,P_ESI mov ecx,P_ECX @@ -723,62 +386,44 @@ comapi_initadv: section .data16 -%macro int21 2 - db %1 - dw %2 -%endmacro - -int21_table: - int21 00h, comboot_return - int21 01h, comboot_getkey - int21 02h, comboot_writechr - int21 04h, comboot_writeserial - int21 08h, comboot_getkeynoecho - int21 09h, comboot_writestr - int21 0Bh, comboot_checkkey - int21 30h, comboot_checkver - int21 4Ch, comboot_return - int21 -1, comboot_bad_int21 -int21_count equ ($-int21_table)/3 - alignz 2 int22_table: dw comapi_err ; 0000 unimplemented syscall - dw comapi_get_version ; 0001 get SYSLINUX version - dw comapi_writestr ; 0002 write string - dw comapi_run ; 0003 run specified command - dw comapi_run_default ; 0004 run default command - dw comapi_textmode ; 0005 force text mode - dw comapi_open ; 0006 open file - dw comapi_read ; 0007 read file - dw comapi_close ; 0008 close file - dw comapi_pxecall ; 0009 call PXE stack + dw comapi_err ; 0001 get SYSLINUX version + dw comapi_err ; 0002 write string + dw comapi_err ; 0003 run specified command + dw comapi_err ; 0004 run default command + dw comapi_err ; 0005 force text mode + dw comapi_err ; 0006 open file + dw comapi_err ; 0007 read file + dw comapi_err ; 0008 close file + dw comapi_err ; 0009 call PXE stack dw comapi_err ; 000A derivative-specific info - dw comapi_serialcfg ; 000B get serial port config - dw comapi_cleanup ; 000C perform final cleanup + dw comapi_err ; 000B get serial port config + dw comapi_err ; 000C perform final cleanup dw comapi_err ; 000D clean up then bootstrap - dw comapi_configfile ; 000E get name of config file - dw comapi_ipappend ; 000F get ipappend strings - dw comapi_dnsresolv ; 0010 resolve hostname + dw comapi_err ; 000E get name of config file + dw comapi_err ; 000F get ipappend strings + dw comapi_err ; 0010 resolve hostname dw comapi_err ; 0011 maximum shuffle descriptors dw comapi_err ; 0012 cleanup, shuffle and boot - dw comapi_idle ; 0013 idle call - dw comapi_localboot ; 0014 local boot - dw comapi_features ; 0015 feature flags - dw comapi_runkernel ; 0016 run kernel image - dw comapi_usingvga ; 0017 report video mode change - dw comapi_userfont ; 0018 query custom font - dw comapi_readdisk ; 0019 read disk + dw comapi_err ; 0013 idle call + dw comapi_err ; 0014 local boot + dw comapi_err ; 0015 feature flags + dw comapi_err ; 0016 run kernel image + dw comapi_err ; 0017 report video mode change + dw comapi_err ; 0018 query custom font + dw comapi_err ; 0019 read disk dw comapi_err ; 001A cleanup, shuffle and boot to pm dw comapi_err ; 001B cleanup, shuffle and boot to rm dw comapi_getadv ; 001C get pointer to ADV dw comapi_writeadv ; 001D write ADV to disk - dw comapi_kbdtable ; 001E keyboard remapping table - dw comapi_getcwd ; 001F get current working directory + dw comapi_err ; 001E keyboard remapping table + dw comapi_err ; 001F get current working directory dw comapi_err ; 0020 open directory dw comapi_err ; 0021 read directory dw comapi_err ; 0022 close directory - dw comapi_shufsize ; 0023 query shuffler size + dw comapi_err ; 0023 query shuffler size dw comapi_shufraw ; 0024 cleanup, shuffle and boot raw dw comapi_initadv ; 0025 initialize adv structure int22_count equ ($-int22_table)/2 @@ -788,32 +433,14 @@ APIKeyFlag db 0 zero_string db 0 ; Empty, null-terminated string -; -; This is the feature flag array for INT 22h AX=0015h -; -; Note: PXELINUX clears the idle is noop flag if appropriate -; in pxe_detect_nic_type -; - global feature_flags, feature_flags_len -feature_flags: - db 1 ; Have local boot, idle is not noop -feature_flags_len equ ($-feature_flags) - err_notdos db ': attempted DOS system call INT ',0 err_comlarge db 'COMBOOT image too large.', CR, LF, 0 - global VGAFontSize, UserFont - alignz 2 -VGAFontSize dw 16 ; Defaults to 16 byte font -UserFont db 0 ; Using a user-specified font - section .bss16 alignb 4 DOSErrTramp resd 33 ; Error trampolines - global ConfigName -ConfigName resb FILENAME_MAX %ifndef HAVE_CURRENTDIRNAME - global CurrentDirName + global CurrentDirName:data hidden CurrentDirName resb FILENAME_MAX %endif diff --git a/core/conio.c b/core/conio.c index d34c9cf2..a351fd14 100644 --- a/core/conio.c +++ b/core/conio.c @@ -38,30 +38,21 @@ union screen _screensize; /* * Serial console stuff. */ -uint16_t SerialPort = 0; /* Serial port base (or 0 for no serial port) */ -uint16_t BaudDivisor = 115200/9600; /* Baud rate divisor */ -uint8_t FlowOutput = 0; /* Output to assert for serial flow */ -uint8_t FlowInput = 0; /* Input bits for serial flow */ -uint8_t FlowIgnore = 0; /* Ignore input unless these bits set */ +__export uint16_t SerialPort = 0; /* Serial port base (or 0 for no serial port) */ +__export uint8_t FlowInput = 0; /* Input bits for serial flow */ +__export uint16_t BaudDivisor = 115200/9600; /* Baud rate divisor */ +__export uint8_t FlowIgnore = 0; /* Ignore input unless these bits set */ +__export uint16_t DisplayCon = 0x01; /* Display console enabled */ +__export uint8_t FlowOutput = 0; /* Output to assert for serial flow */ uint8_t ScrollAttribute = 0x07; /* Grey on white (normal text color) */ -uint16_t DisplayCon = 0x01; /* Display console enabled */ -static uint8_t TextAttribute; /* Text attribute for message file */ -static uint8_t DisplayMask; /* Display modes mask */ - -/* Routine to interpret next print char */ -static void (*NextCharJump)(char); - -void msg_initvars(void); -static void msg_setfg(char data); -static void msg_putchar(char ch); /* * loadkeys: Load a LILO-style keymap * * Returns 0 on success, or -1 on error. */ -int loadkeys(char *filename) +__export int loadkeys(char *filename) { FILE *f; @@ -76,53 +67,10 @@ int loadkeys(char *filename) } /* - * - * get_msg_file: Load a text file and write its contents to the screen, - * interpreting color codes. - * - * Returns 0 on success, -1 on failure. - */ -int get_msg_file(char *filename) -{ - FILE *f; - char ch; - - f = fopen(filename, "r"); - if (!f) - return -1; - - TextAttribute = 0x7; /* Default grey on white */ - DisplayMask = 0x7; /* Display text in all modes */ - msg_initvars(); - - /* - * Read the text file a byte at a time and interpret that - * byte. - */ - while ((ch = getc(f)) != EOF) { - /* DOS EOF? */ - if (ch == 0x1A) - break; - - /* - * 01h = text mode - * 02h = graphics mode - */ - UsingVGA &= 0x1; - UsingVGA += 1; - - NextCharJump(ch); /* Do what shall be done */ - } - - fclose(f); - return 0; -} - -/* * write_serial: If serial output is enabled, write character on * serial port. */ -void write_serial(char data) +__export void write_serial(char data) { if (!SerialPort) return; @@ -179,16 +127,10 @@ void pm_serialcfg(com32sys_t *regs) serialcfg(®s->eax.w[0], ®s->ecx.w[0], ®s->ebx.w[0]); } -static void write_serial_displaymask(char data) -{ - if (DisplayMask & 0x4) - write_serial(data); -} - /* * write_serial_str: write_serial for strings */ -void write_serial_str(char *data) +__export void write_serial_str(char *data) { char ch; @@ -197,15 +139,6 @@ void write_serial_str(char *data) } /* - * write_serial_str_displaymask: d:o, but ignore if DisplayMask & 04h == 0 - */ -static void write_serial_str_displaymask(char *data) -{ - if (DisplayMask & 0x4) - write_serial_str(data); -} - -/* * pollchar: check if we have an input character pending * * Returns 1 if character pending. @@ -250,7 +183,7 @@ int bios_pollchar(void) return data; } -int pollchar(void) +__export int pollchar(void) { return firmware->i_ops->pollchar(); } @@ -334,7 +267,7 @@ char bios_getchar(char *hi) /* * getchar: Read a character from keyboard or serial port */ -char getchar(char *hi) +__export char getchar(char *hi) { return firmware->i_ops->getchar(hi); } @@ -343,223 +276,3 @@ void pm_getchar(com32sys_t *regs) { regs->eax.b[0] = getchar((char *)®s->eax.b[1]); } - -static void msg_setbg(char data) -{ - if (unhexchar(&data) == 0) { - data <<= 4; - if (DisplayMask & UsingVGA) - TextAttribute = data; - - NextCharJump = msg_setfg; - } else { - TextAttribute = 0x7; /* Default attribute */ - NextCharJump = msg_putchar; - } -} - -static void msg_setfg(char data) -{ - if (unhexchar(&data) == 0) { - if (DisplayMask & UsingVGA) { - /* setbg set foreground to 0 */ - TextAttribute |= data; - } - } else - TextAttribute = 0x7; /* Default attribute */ - - NextCharJump = msg_putchar; -} - -static inline void msg_ctrl_o(void) -{ - NextCharJump = msg_setbg; -} - -static void msg_gotoxy(void) -{ - firmware->o_ops->set_cursor(CursorCol, CursorRow, true); -} - -static void msg_newline(void) -{ - com32sys_t ireg, oreg; - char crlf_msg[] = { '\r', '\n', '\0' }; - - write_serial_str_displaymask(crlf_msg); - - if (!(DisplayMask & UsingVGA)) - return; - - CursorCol = 0; - if ((CursorRow + 1) <= VidRows) - CursorRow++; - else { - CursorRow = VidRows; /* New cursor at the bottom */ - firmware->o_ops->scroll_up(VidRows, VidCols, ScrollAttribute); - } - - firmware->o_ops->set_cursor(CursorCol, CursorRow, true); -} - -static void msg_formfeed(void) -{ - char crff_msg[] = { '\r', '\f', '\0' }; - - write_serial_str_displaymask(crff_msg); - - if (DisplayMask & UsingVGA) { - CursorDX = 0x0; /* Upper left hand corner */ - - firmware->o_ops->erase(0, 0, VidCols, VidRows, TextAttribute); - firmware->o_ops->set_cursor(CursorCol, CursorRow, true); - } -} - -static void msg_novga(void) -{ - syslinux_force_text_mode(); - msg_initvars(); -} - -static void msg_viewimage(void) -{ - FILE *f; - - *VGAFilePtr = '\0'; /* Zero-terminate filename */ - - mangle_name(VGAFileMBuf, VGAFileBuf); - f = fopen(VGAFileMBuf, "r"); - if (!f) { - /* Not there */ - NextCharJump = msg_putchar; - return; - } - - vgadisplayfile(f); - fclose(f); - msg_initvars(); -} - -/* - * Getting VGA filename - */ -static void msg_filename(char data) -{ - /* <LF> = end of filename */ - if (data == 0x0A) { - msg_viewimage(); - return; - } - - /* Ignore space/control char */ - if (data > ' ') { - if ((char *)VGAFilePtr < (VGAFileBuf + sizeof(VGAFileBuf))) - *VGAFilePtr++ = data; - } -} - -static void msg_vga(void) -{ - NextCharJump = msg_filename; - VGAFilePtr = (uint16_t *)VGAFileBuf; -} - -static void msg_line_wrap(void) -{ - if (!(DisplayMask & UsingVGA)) - return; - - CursorCol = 0; - if ((CursorRow + 1) <= VidRows) - CursorRow++; - else { - /* Scroll up one line */ - firmware->o_ops->scroll_up(VidRows, VidCols, ScrollAttribute); - } - - firmware->o_ops->set_cursor(CursorCol, CursorRow, true); -} - -static void msg_normal(char data) -{ - com32sys_t ireg, oreg; - - /* Write to serial port */ - write_serial_displaymask(data); - - if (!(DisplayMask & UsingVGA)) - return; /* Not screen */ - - if (!(DisplayCon & 0x01)) - return; - - /* Write to screen */ - firmware->o_ops->write_char(data, TextAttribute); - - if ((CursorCol + 1) <= VidCols) { - CursorCol++; - firmware->o_ops->set_cursor(CursorCol, CursorRow, true); - } else - msg_line_wrap(); /* Screen wraparound */ -} - -static void msg_modectl(char data) -{ - data &= 0x07; - DisplayMask = data; - NextCharJump = msg_putchar; -} - -static void msg_putchar(char ch) -{ - /* 10h to 17h are mode controls */ - if (ch >= 0x10 && ch < 0x18) { - msg_modectl(ch); - return; - } - - switch (ch) { - case 0x0F: /* ^O = color code follows */ - msg_ctrl_o(); - break; - case 0x0D: /* Ignore <CR> */ - break; - case 0x0A: /* <LF> = newline */ - msg_newline(); - break; - case 0x0C: /* <FF> = clear screen */ - msg_formfeed(); - break; - case 0x07: /* <BEL> = beep */ - if (firmware->o_ops->beep) - firmware->o_ops->beep(); - break; - case 0x19: /* <EM> = return to text mode */ - msg_novga(); - break; - case 0x18: /* <CAN> = VGA filename follows */ - msg_vga(); - break; - default: - msg_normal(ch); - break; - } -} - -/* - * Subroutine to initialize variables, also needed after loading - * graphics file. - */ -void msg_initvars(void) -{ - com32sys_t ireg, oreg; - int x, y; - - firmware->o_ops->get_cursor(&x, &y); - CursorCol = x; - CursorRow = y; - - /* Initialize state machine */ - NextCharJump = msg_putchar; -} diff --git a/core/diskboot.inc b/core/diskboot.inc index be816263..ce75b8c9 100644 --- a/core/diskboot.inc +++ b/core/diskboot.inc @@ -103,7 +103,6 @@ superblock_len_fat32 equ $-superblock+54 zb 54 ; Maximum needed size superblock_max equ $-superblock - global SecPerClust SecPerClust equ bxSecPerClust ; @@ -385,7 +384,11 @@ getonesec_cbios: ; ; kaboom: write a message and bail out. ; +%ifdef BINFMT global kaboom +%else + global kaboom:function hidden +%endif disk_error: kaboom: xor si,si diff --git a/core/diskfs.inc b/core/diskfs.inc index dcbc924a..827f5003 100644 --- a/core/diskfs.inc +++ b/core/diskfs.inc @@ -60,7 +60,7 @@ vk_end: equ $ ; Should be <= vk_size ; Memory below this point is reserved for the BIOS and the MBR ; section .earlybss - global trackbuf + global trackbuf:data hidden trackbufsize equ 8192 trackbuf resb trackbufsize ; Track buffer goes here ; ends at 2800h @@ -91,24 +91,15 @@ auto_boot: jmp kaboom section .bss16 - global CmdOptPtr, KbdMap alignb 4 ThisKbdTo resd 1 ; Temporary holder for KbdTimeout ThisTotalTo resd 1 ; Temporary holder for TotalTimeout KernelExtPtr resw 1 ; During search, final null pointer -CmdOptPtr resw 1 ; Pointer to first option on cmd line -KbdFlags resb 1 ; Check for keyboard escapes FuncFlag resb 1 ; Escape sequences received from keyboard KernelType resb 1 ; Kernel type, from vkernel, if known -KbdMap resb 256 ; Keyboard map global KernelName KernelName resb FILENAME_MAX ; Mangled name for kernel - section .config - global PXERetry -PXERetry dw 0 ; Extra PXE retries section .data16 - global SerialNotice -SerialNotice db 1 ; Only print this once global IPAppends, numIPAppends %if IS_PXELINUX extern IPOption diff --git a/core/diskstart.inc b/core/diskstart.inc index 603a6db2..875b4093 100644 --- a/core/diskstart.inc +++ b/core/diskstart.inc @@ -92,7 +92,7 @@ BannerPtr dw syslinux_banner - LDLINUX_SYS ; Base directory name and subvolume, if applicable. ; %define HAVE_CURRENTDIRNAME - global CurrentDirName, SubvolName + global CurrentDirName:data hidden, SubvolName:data hidden CurrentDirName times CURRENTDIR_MAX db 0 SubvolName times SUBVOL_MAX db 0 @@ -232,7 +232,7 @@ verify_checksum: ; ; This routine assumes CS == DS. ; - global getlinsec + global getlinsec:function hidden getlinsec: pushad add eax,[Hidden] ; Add partition offset diff --git a/core/elflink/load_env32.c b/core/elflink/load_env32.c index f3dd1e77..1fa43bd6 100644 --- a/core/elflink/load_env32.c +++ b/core/elflink/load_env32.c @@ -57,7 +57,7 @@ void init_module_subsystem(struct elf_module *module) list_add(&module->list, &modules_head); } -int start_ldlinux(char **argv) +__export int start_ldlinux(char **argv) { int rv; @@ -69,9 +69,19 @@ again: * unload all the modules loaded since ldlinux.c32, * and restart initialisation. This is especially * important for config files. + * + * But before we do that, try our best to make sure + * that spawn_load() is gonna succeed, e.g. that we + * can find LDLINUX it in PATH. */ struct elf_module *ldlinux; + FILE *f; + + f = findpath(LDLINUX); + if (!f) + return ENOENT; + fclose(f); ldlinux = unload_modules_since(LDLINUX); /* @@ -113,14 +123,13 @@ void load_env32(com32sys_t * regs __unused) dprintf("Starting %s elf module subsystem...\n", ELF_MOD_SYS); - PATH = malloc(strlen(PATH_DEFAULT) + 1); + PATH = malloc(strlen(CurrentDirName) + 1); if (!PATH) { printf("Couldn't allocate memory for PATH\n"); return; } - strcpy(PATH, PATH_DEFAULT); - PATH[strlen(PATH_DEFAULT)] = '\0'; + strcpy(PATH, CurrentDirName); size = (size_t)__dynstr_end - (size_t)__dynstr_start; core_module.strtable_size = size; @@ -150,7 +159,7 @@ void load_env32(com32sys_t * regs __unused) writestr("\nFailed to load ldlinux.c32"); } -int create_args_and_load(char *cmdline) +__export int create_args_and_load(char *cmdline) { char *p, **argv; int argc; @@ -175,9 +184,10 @@ int create_args_and_load(char *cmdline) * Generate a copy of argv on the stack as this is * traditionally where process arguments go. * - * argv[0] must be the command name. + * argv[0] must be the command name. Remember to allocate + * space for the sentinel NULL. */ - argv = alloca(argc * sizeof(char *)); + argv = alloca((argc + 1) * sizeof(char *)); for (i = 0, p = cmdline; i < argc; i++) { char *start; diff --git a/core/font.c b/core/font.c index 1e4e606d..edc9de8e 100644 --- a/core/font.c +++ b/core/font.c @@ -27,7 +27,9 @@ #include "graphics.h" #include "core.h" -__lowmem char fontbuf[8192]; +__export uint8_t UserFont = 0; /* Using a user-specified font */ + +__export __lowmem char fontbuf[8192]; uint16_t GXPixCols = 1; /* Graphics mode pixel columns */ uint16_t GXPixRows = 1; /* Graphics mode pixel rows */ @@ -36,7 +38,7 @@ uint16_t GXPixRows = 1; /* Graphics mode pixel rows */ * loadfont: Load a .psf font file and install it onto the VGA console * (if we're not on a VGA screen then ignore.) */ -void loadfont(const char *filename) +__export void loadfont(const char *filename) { struct psfheader { uint16_t magic; diff --git a/core/fs/chdir.c b/core/fs/chdir.c index 903cabce..5d3a545f 100644 --- a/core/fs/chdir.c +++ b/core/fs/chdir.c @@ -54,7 +54,7 @@ static size_t generic_inode_to_path(struct inode *inode, char *dst, size_t bufsi return s; } -size_t realpath(char *dst, const char *src, size_t bufsize) +__export size_t realpath(char *dst, const char *src, size_t bufsize) { int rv; struct file *file; @@ -83,7 +83,7 @@ size_t realpath(char *dst, const char *src, size_t bufsize) return s; } -int chdir(const char *src) +__export int chdir(const char *src) { int rv; struct file *file; diff --git a/core/fs/fs.c b/core/fs/fs.c index 21e7684a..40f97d5e 100644 --- a/core/fs/fs.c +++ b/core/fs/fs.c @@ -9,13 +9,13 @@ #include "fs.h" #include "cache.h" -char *PATH; +__export char *PATH; /* The currently mounted filesystem */ -struct fs_info *this_fs = NULL; /* Root filesystem */ +__export struct fs_info *this_fs = NULL; /* Root filesystem */ /* Actual file structures (we don't have malloc yet...) */ -struct file files[MAX_OPEN]; +__export struct file files[MAX_OPEN]; /* Symlink hard limits */ #define MAX_SYMLINK_CNT 20 @@ -74,7 +74,7 @@ static inline void free_file(struct file *file) memset(file, 0, sizeof *file); } -void _close_file(struct file *file) +__export void _close_file(struct file *file) { if (file->fs) file->fs->fs_ops->close_file(file); @@ -84,7 +84,7 @@ void _close_file(struct file *file) /* * Find and open the configuration file */ -int open_config(void) +__export int open_config(void) { int fd, handle; struct file_info *fp; @@ -116,7 +116,7 @@ void pm_mangle_name(com32sys_t *regs) mangle_name(dst, src); } -void mangle_name(char *dst, const char *src) +__export void mangle_name(char *dst, const char *src) { this_fs->fs_ops->mangle_name(dst, src); } @@ -219,11 +219,10 @@ void pm_searchdir(com32sys_t *regs) int searchdir(const char *name) { - struct inode *inode = NULL; - struct inode *parent = NULL; + static char root_name[] = "/"; struct file *file; - char *pathbuf = NULL; - char *part, *p, echar; + char *path, *inode_name, *next_inode_name; + struct inode *tmp, *inode = NULL; int symlink_count = MAX_SYMLINK_CNT; dprintf("searchdir: %s root: %p cwd: %p\n", @@ -245,113 +244,165 @@ int searchdir(const char *name) /* else, try the generic-path-lookup method */ - parent = get_inode(this_fs->cwd); - p = pathbuf = strdup(name); - if (!pathbuf) - goto err; + /* Copy the path */ + path = strdup(name); + if (!path) { + dprintf("searchdir: Couldn't copy path\n"); + goto err_path; + } + + /* Work with the current directory, by default */ + inode = get_inode(this_fs->cwd); + if (!inode) { + dprintf("searchdir: Couldn't use current directory\n"); + goto err_curdir; + } - do { - got_link: - if (*p == '/') { - put_inode(parent); - parent = get_inode(this_fs->root); + for (inode_name = path; inode_name; inode_name = next_inode_name) { + /* Root directory? */ + if (inode_name[0] == '/') { + next_inode_name = inode_name + 1; + inode_name = root_name; + } else { + /* Find the next inode name */ + next_inode_name = strchr(inode_name + 1, '/'); + if (next_inode_name) { + /* Terminate the current inode name and point to next */ + *next_inode_name++ = '\0'; + } + } + if (next_inode_name) { + /* Advance beyond redundant slashes */ + while (*next_inode_name == '/') + next_inode_name++; + + /* Check if we're at the end */ + if (*next_inode_name == '\0') + next_inode_name = NULL; + } + dprintf("searchdir: inode_name: %s\n", inode_name); + if (next_inode_name) + dprintf("searchdir: Remaining: %s\n", next_inode_name); + + /* Root directory? */ + if (inode_name[0] == '/') { + /* Release any chain that's already been established */ + put_inode(inode); + inode = get_inode(this_fs->root); + continue; } - do { - inode = get_inode(parent); - - while (*p == '/') - p++; - - if (!*p) - break; - - part = p; - while ((echar = *p) && echar != '/') - p++; - *p++ = '\0'; - - if (part[0] == '.' && part[1] == '.' && part[2] == '\0') { - if (inode->parent) { - put_inode(parent); - parent = get_inode(inode->parent); - put_inode(inode); - inode = NULL; - if (!echar) { - /* Terminal double dots */ - inode = parent; - parent = inode->parent ? - get_inode(inode->parent) : NULL; - } - } - } else if (part[0] != '.' || part[1] != '\0') { - inode = this_fs->fs_ops->iget(part, parent); - if (!inode) - goto err; - if (inode->mode == DT_LNK) { - char *linkbuf, *q; - int name_len = echar ? strlen(p) : 0; - int total_len = inode->size + name_len + 2; - int link_len; - - if (!this_fs->fs_ops->readlink || - --symlink_count == 0 || /* limit check */ - total_len > MAX_SYMLINK_BUF) - goto err; - - linkbuf = malloc(total_len); - if (!linkbuf) - goto err; - - link_len = this_fs->fs_ops->readlink(inode, linkbuf); - if (link_len <= 0) { - free(linkbuf); - goto err; - } - - q = linkbuf + link_len; - - if (echar) { - if (link_len > 0 && q[-1] != '/') - *q++ = '/'; - - memcpy(q, p, name_len+1); - } else { - *q = '\0'; - } - - free(pathbuf); - p = pathbuf = linkbuf; - put_inode(inode); - inode = NULL; - goto got_link; - } - - inode->name = strdup(part); - dprintf("path component: %s\n", inode->name); - - inode->parent = parent; - parent = NULL; - - if (!echar) - break; - - if (inode->mode != DT_DIR) - goto err; - - parent = inode; - inode = NULL; + /* Current directory? */ + if (!strncmp(inode_name, ".", sizeof ".")) + continue; + + /* Parent directory? */ + if (!strncmp(inode_name, "..", sizeof "..")) { + /* If there is no parent, just ignore it */ + if (!inode->parent) + continue; + + /* Add a reference to the parent so we can release the child */ + tmp = get_inode(inode->parent); + + /* Releasing the child will drop the parent back down to 1 */ + put_inode(inode); + + inode = tmp; + continue; + } + + /* Anything else */ + tmp = inode; + inode = this_fs->fs_ops->iget(inode_name, inode); + if (!inode) { + /* Failure. Release the chain */ + put_inode(tmp); + break; + } + + /* Sanity-check */ + if (inode->parent && inode->parent != tmp) { + dprintf("searchdir: iget returned a different parent\n"); + put_inode(inode); + inode = NULL; + put_inode(tmp); + break; + } + inode->parent = tmp; + inode->name = strdup(inode_name); + dprintf("searchdir: path component: %s\n", inode->name); + + /* Symlink handling */ + if (inode->mode == DT_LNK) { + char *new_path; + int new_len, copied; + + /* target path + NUL */ + new_len = inode->size + 1; + + if (next_inode_name) { + /* target path + slash + remaining + NUL */ + new_len += strlen(next_inode_name) + 1; + } + + if (!this_fs->fs_ops->readlink || + /* limit checks */ + --symlink_count == 0 || + new_len > MAX_SYMLINK_BUF) + goto err_new_len; + + new_path = malloc(new_len); + if (!new_path) + goto err_new_path; + + copied = this_fs->fs_ops->readlink(inode, new_path); + if (copied <= 0) + goto err_copied; + new_path[copied] = '\0'; + dprintf("searchdir: Symlink: %s\n", new_path); + + if (next_inode_name) { + new_path[copied] = '/'; + strcpy(new_path + copied + 1, next_inode_name); + dprintf("searchdir: New path: %s\n", new_path); } - } while (echar); - } while (0); - free(pathbuf); - pathbuf = NULL; - put_inode(parent); - parent = NULL; + free(path); + path = next_inode_name = new_path; - if (!inode) + /* Add a reference to the parent so we can release the child */ + tmp = get_inode(inode->parent); + + /* Releasing the child will drop the parent back down to 1 */ + put_inode(inode); + + inode = tmp; + continue; +err_copied: + free(new_path); +err_new_path: +err_new_len: + put_inode(inode); + inode = NULL; + break; + } + + /* If there's more to process, this should be a directory */ + if (next_inode_name && inode->mode != DT_DIR) { + dprintf("searchdir: Expected a directory\n"); + put_inode(inode); + inode = NULL; + break; + } + } +err_curdir: + free(path); +err_path: + if (!inode) { + dprintf("searchdir: Not found\n"); goto err; + } file->inode = inode; file->offset = 0; @@ -360,16 +411,12 @@ int searchdir(const char *name) err: dprintf("serachdir: error seraching file %s\n", name); - put_inode(inode); - put_inode(parent); - if (pathbuf) - free(pathbuf); _close_file(file); err_no_close: return -1; } -int open_file(const char *name, struct com32_filedata *filedata) +__export int open_file(const char *name, struct com32_filedata *filedata) { int rv; struct file *file; @@ -419,7 +466,7 @@ void pm_open_file(com32sys_t *regs) } } -void close_file(uint16_t handle) +__export void close_file(uint16_t handle) { struct file *file; diff --git a/core/fs/getcwd.c b/core/fs/getcwd.c index ee62411f..70b93152 100644 --- a/core/fs/getcwd.c +++ b/core/fs/getcwd.c @@ -1,7 +1,7 @@ #include <string.h> #include "fs.h" -char *core_getcwd(char *buf, size_t size) +__export char *core_getcwd(char *buf, size_t size) { char *ret = NULL; diff --git a/core/fs/lib/searchconfig.c b/core/fs/lib/searchconfig.c index f2d740b3..b2a964e8 100644 --- a/core/fs/lib/searchconfig.c +++ b/core/fs/lib/searchconfig.c @@ -4,8 +4,8 @@ #include <core.h> #include <fs.h> -char ConfigName[FILENAME_MAX]; -char config_cwd[FILENAME_MAX]; +__export char ConfigName[FILENAME_MAX]; +__export char config_cwd[FILENAME_MAX]; /* * This searches for a specified set of filenames in a specified set diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index 6f490ce8..11270044 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -2,6 +2,7 @@ #include <stdio.h> #include <string.h> #include <core.h> +#include <bios.h> #include <fs.h> #include <minmax.h> #include <sys/cpu.h> @@ -796,8 +797,10 @@ static void __pxe_searchdir(const char *filename, struct file *file) sendreq: timeout = *timeout_ptr++; - if (!timeout) + if (!timeout) { + free_socket(inode); return; /* No file available... */ + } oldtime = jiffies(); socket->tftp_remoteip = ip; @@ -1268,9 +1271,9 @@ static const void *memory_scan(uintptr_t start, int (*func)(const void *)) static const struct pxe_t *memory_scan_for_pxe_struct(void) { - extern uint16_t BIOS_fbm; /* Starting segment */ + uint16_t start = bios_fbm(); /* Starting segment */ - return memory_scan(BIOS_fbm << 10, is_pxe); + return memory_scan(start << 10, is_pxe); } static const struct pxenv_t *memory_scan_for_pxenv_struct(void) @@ -1675,11 +1678,11 @@ void unload_pxe(uint16_t flags) uint16_t Status; /* All calls have this as the first member */ } unload_call; - dprintf("FBM before unload = %d\n", BIOS_fbm); + dprintf("FBM before unload = %d\n", bios_fbm()); err = reset_pxe(); - dprintf("FBM after reset_pxe = %d, err = %d\n", BIOS_fbm, err); + dprintf("FBM after reset_pxe = %d, err = %d\n", bios_fbm(), err); /* If we want to keep PXE around, we still need to reset it */ if (flags || err) @@ -1699,8 +1702,8 @@ void unload_pxe(uint16_t flags) } api = 0xff00; - if (real_base_mem <= BIOS_fbm) { /* Sanity check */ - dprintf("FBM %d < real_base_mem %d\n", BIOS_fbm, real_base_mem); + if (real_base_mem <= bios_fbm()) { /* Sanity check */ + dprintf("FBM %d < real_base_mem %d\n", bios_fbm(), real_base_mem); goto cant_free; } api++; @@ -1708,20 +1711,20 @@ void unload_pxe(uint16_t flags) /* Check that PXE actually unhooked the INT 0x1A chain */ int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a)); int_addr >>= 10; - if (int_addr >= real_base_mem || int_addr < BIOS_fbm) { - BIOS_fbm = real_base_mem; - dprintf("FBM after unload_pxe = %d\n", BIOS_fbm); + if (int_addr >= real_base_mem || int_addr < bios_fbm()) { + set_bios_fbm(real_base_mem); + dprintf("FBM after unload_pxe = %d\n", bios_fbm()); return; } dprintf("Can't free FBM, real_base_mem = %d, " "FBM = %d, INT 1A = %08x (%d)\n", - real_base_mem, BIOS_fbm, + real_base_mem, bios_fbm(), *(uint32_t *)(4 * 0x1a), int_addr); cant_free: printf("Failed to free base memory error %04x-%08x (%d/%dK)\n", - api, *(uint32_t *)(4 * 0x1a), BIOS_fbm, real_base_mem); + api, *(uint32_t *)(4 * 0x1a), bios_fbm(), real_base_mem); return; } diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index 99a2cf25..3439dd6a 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -201,7 +201,6 @@ extern bool have_uuid; extern uint8_t uuid_type; extern uint8_t uuid[]; -extern uint16_t BIOS_fbm; extern const uint8_t TimeoutTable[]; /* diff --git a/core/fs/readdir.c b/core/fs/readdir.c index d071affd..e2d593fc 100644 --- a/core/fs/readdir.c +++ b/core/fs/readdir.c @@ -7,7 +7,7 @@ /* * Open a directory */ -DIR *opendir(const char *path) +__export DIR *opendir(const char *path) { int rv; struct file *file; @@ -29,7 +29,7 @@ DIR *opendir(const char *path) /* * Read one directory entry at one time. */ -struct dirent *readdir(DIR *dir) +__export struct dirent *readdir(DIR *dir) { static struct dirent buf; struct file *dd_dir = (struct file *)dir; @@ -47,7 +47,7 @@ struct dirent *readdir(DIR *dir) /* * Close a directory */ -int closedir(DIR *dir) +__export int closedir(DIR *dir) { struct file *dd_dir = (struct file *)dir; _close_file(dd_dir); diff --git a/core/fs/xfs/misc.h b/core/fs/xfs/misc.h new file mode 100644 index 00000000..7f2f1b33 --- /dev/null +++ b/core/fs/xfs/misc.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MISC_H_ +#define MISC_H_ + +/* Return a 64-bit litte-endian value from a given 64-bit big-endian one */ +static inline uint64_t be64_to_cpu(uint64_t val) +{ + return (uint64_t)((((uint64_t)val & (uint64_t)0x00000000000000ffULL) << 56) | + (((uint64_t)val & (uint64_t)0x000000000000ff00ULL) << 40) | + (((uint64_t)val & (uint64_t)0x0000000000ff0000ULL) << 24) | + (((uint64_t)val & (uint64_t)0x00000000ff000000ULL) << 8) | + (((uint64_t)val & (uint64_t)0x000000ff00000000ULL) >> 8) | + (((uint64_t)val & (uint64_t)0x0000ff0000000000ULL) >> 24) | + (((uint64_t)val & (uint64_t)0x00ff000000000000ULL) >> 40) | + (((uint64_t)val & (uint64_t)0xff00000000000000ULL) >> 56)); +} + +/* Return a 32-bit litte-endian value from a given 32-bit big-endian one */ +static inline uint32_t be32_to_cpu(uint32_t val) +{ + return (uint32_t)((((uint32_t)val & (uint32_t)0x000000ffUL) << 24) | + (((uint32_t)val & (uint32_t)0x0000ff00UL) << 8) | + (((uint32_t)val & (uint32_t)0x00ff0000UL) >> 8) | + (((uint32_t)val & (uint32_t)0xff000000UL) >> 24)); +} + +/* Return a 16-bit litte-endian value from a given 16-bit big-endian one */ +static inline uint16_t be16_to_cpu(uint16_t val) +{ + return (uint16_t)((((uint16_t)val & (uint16_t)0x00ffU) << 8) | + (((uint16_t)val & (uint16_t)0xff00U) >> 8)); +} + +#endif /* MISC_H_ */ diff --git a/core/fs/xfs/xfs.c b/core/fs/xfs/xfs.c new file mode 100644 index 00000000..89a9aef2 --- /dev/null +++ b/core/fs/xfs/xfs.c @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <dprintf.h> +#include <stdio.h> +#include <string.h> +#include <sys/dirent.h> +#include <cache.h> +#include <core.h> +#include <disk.h> +#include <fs.h> +#include <ilog2.h> +#include <klibc/compiler.h> +#include <ctype.h> + +#include "codepage.h" +#include "xfs_types.h" +#include "xfs_sb.h" +#include "xfs_ag.h" +#include "misc.h" +#include "xfs.h" +#include "xfs_dinode.h" +#include "xfs_dir2.h" +#include "xfs_readdir.h" + +static inline int xfs_fmt_local_readdir(struct file *file, + struct dirent *dirent, xfs_dinode_t *core) +{ + return xfs_readdir_dir2_block(file, dirent, core); +} + +static inline int xfs_fmt_extents_readdir(struct file *file, + struct dirent *dirent, + xfs_dinode_t *core) +{ + int retval; + + if (be32_to_cpu(core->di_nextents) <= 1) { + /* Single-block Directories */ + retval = xfs_readdir_dir2_block(file, dirent, core); + } else if (xfs_dir2_isleaf(file->fs, core)) { + /* Leaf Directory */ + retval = xfs_readdir_dir2_leaf(file, dirent, core); + } else { + /* Node Directory */ + retval = xfs_readdir_dir2_node(file, dirent, core); + } + + return retval; +} + +static int xfs_readdir(struct file *file, struct dirent *dirent) +{ + struct fs_info *fs = file->fs; + xfs_dinode_t *core; + struct inode *inode = file->inode; + int retval = -1; + + core = xfs_dinode_get_core(fs, inode->ino); + if (!core) { + xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino); + return -1; + } + + if (core->di_format == XFS_DINODE_FMT_LOCAL) + retval = xfs_fmt_local_readdir(file, dirent, core); + else if (core->di_format == XFS_DINODE_FMT_EXTENTS) + retval = xfs_fmt_extents_readdir(file, dirent, core); + + return retval; +} + +static uint32_t xfs_getfssec(struct file *file, char *buf, int sectors, + bool *have_more) +{ + return generic_getfssec(file, buf, sectors, have_more); +} + +static int xfs_next_extent(struct inode *inode, uint32_t lstart) +{ + struct fs_info *fs = inode->fs; + xfs_dinode_t *core = NULL; + xfs_bmbt_irec_t rec; + block_t bno; + xfs_bmdr_block_t *rblock; + int fsize; + xfs_bmbt_ptr_t *pp; + xfs_btree_block_t *blk; + uint16_t nextents; + block_t nextbno; + uint32_t index; + + (void)lstart; + + core = xfs_dinode_get_core(fs, inode->ino); + if (!core) { + xfs_error("Failed to get dinode from disk (ino %llx)", inode->ino); + goto out; + } + + /* The data fork contains the file's data extents */ + if (XFS_PVT(inode)->i_cur_extent == be32_to_cpu(core->di_nextents)) + goto out; + + if (core->di_format == XFS_DINODE_FMT_EXTENTS) { + bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] + + XFS_PVT(inode)->i_cur_extent++); + + bno = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs); + + XFS_PVT(inode)->i_offset = rec.br_startoff; + + inode->next_extent.pstart = bno << BLOCK_SHIFT(fs) >> SECTOR_SHIFT(fs); + inode->next_extent.len = ((rec.br_blockcount << BLOCK_SHIFT(fs)) + + SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs); + } else if (core->di_format == XFS_DINODE_FMT_BTREE) { + xfs_debug("XFS_DINODE_FMT_BTREE"); + index = XFS_PVT(inode)->i_cur_extent++; + rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0]; + fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK); + pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0)); + bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs); + + /* Find the leaf */ + for (;;) { + blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); + if (be16_to_cpu(blk->bb_level) == 0) + break; + + pp = XFS_BMBT_PTR_ADDR(fs, blk, 1, + xfs_bmdr_maxrecs(XFS_INFO(fs)->blocksize, 0)); + bno = fsblock_to_bytes(fs, be64_to_cpu(pp[0])) >> BLOCK_SHIFT(fs); + } + + /* Find the right extent among threaded leaves */ + for (;;) { + nextbno = be64_to_cpu(blk->bb_u.l.bb_rightsib); + nextents = be16_to_cpu(blk->bb_numrecs); + if (nextents - index > 0) { + bmbt_irec_get(&rec, XFS_BMDR_REC_ADDR(blk, index + 1)); + + bno = fsblock_to_bytes(fs, rec.br_startblock) + >> BLOCK_SHIFT(fs); + + XFS_PVT(inode)->i_offset = rec.br_startoff; + + inode->next_extent.pstart = bno << BLOCK_SHIFT(fs) + >> SECTOR_SHIFT(fs); + inode->next_extent.len = ((rec.br_blockcount + << BLOCK_SHIFT(fs)) + + SECTOR_SIZE(fs) - 1) + >> SECTOR_SHIFT(fs); + break; + } + + index -= nextents; + bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs); + blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); + } + } + + return 0; + +out: + return -1; +} + +static inline struct inode *xfs_fmt_local_find_entry(const char *dname, + struct inode *parent, + xfs_dinode_t *core) +{ + return xfs_dir2_local_find_entry(dname, parent, core); +} + +static inline struct inode *xfs_fmt_extents_find_entry(const char *dname, + struct inode *parent, + xfs_dinode_t *core) +{ + struct inode *inode; + + if (be32_to_cpu(core->di_nextents) <= 1) { + /* Single-block Directories */ + inode = xfs_dir2_block_find_entry(dname, parent, core); + } else if (xfs_dir2_isleaf(parent->fs, core)) { + /* Leaf Directory */ + inode = xfs_dir2_leaf_find_entry(dname, parent, core); + } else { + /* Node Directory */ + inode = xfs_dir2_node_find_entry(dname, parent, core); + } + + return inode; +} + +static inline struct inode *xfs_fmt_btree_find_entry(const char *dname, + struct inode *parent, + xfs_dinode_t *core) +{ + return xfs_dir2_node_find_entry(dname, parent, core); +} + +static struct inode *xfs_iget(const char *dname, struct inode *parent) +{ + struct fs_info *fs = parent->fs; + xfs_dinode_t *core = NULL; + struct inode *inode = NULL; + + xfs_debug("dname %s parent %p parent ino %lu", dname, parent, parent->ino); + + core = xfs_dinode_get_core(fs, parent->ino); + if (!core) { + xfs_error("Failed to get dinode from disk (ino 0x%llx)", parent->ino); + goto out; + } + + if (core->di_format == XFS_DINODE_FMT_LOCAL) { + inode = xfs_fmt_local_find_entry(dname, parent, core); + } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) { + inode = xfs_fmt_extents_find_entry(dname, parent, core); + } else if (core->di_format == XFS_DINODE_FMT_BTREE) { + inode = xfs_fmt_btree_find_entry(dname, parent, core); + } else { + xfs_debug("format %hhu", core->di_format); + xfs_debug("TODO: format \"local\" and \"extents\" are the only " + "supported ATM"); + goto out; + } + + if (!inode) { + xfs_debug("Entry not found!"); + goto out; + } + + if (inode->mode == DT_REG) { + XFS_PVT(inode)->i_offset = 0; + XFS_PVT(inode)->i_cur_extent = 0; + } else if (inode->mode == DT_DIR) { + XFS_PVT(inode)->i_btree_offset = 0; + XFS_PVT(inode)->i_leaf_ent_offset = 0; + } + + return inode; + +out: + return NULL; +} + +static int xfs_readlink(struct inode *inode, char *buf) +{ + struct fs_info *fs = inode->fs; + xfs_dinode_t *core; + int pathlen = -1; + xfs_bmbt_irec_t rec; + block_t db; + char *dir_buf; + + core = xfs_dinode_get_core(fs, inode->ino); + if (!core) { + xfs_error("Failed to get dinode from disk (ino 0x%llx)", inode->ino); + goto out; + } + + pathlen = be64_to_cpu(core->di_size); + if (!pathlen) + goto out; + + if (pathlen < 0 || pathlen > MAXPATHLEN) { + xfs_error("inode (%llu) bad symlink length (%d)", + inode->ino, pathlen); + goto out; + } + + if (core->di_format == XFS_DINODE_FMT_LOCAL) { + memcpy(buf, (char *)&core->di_literal_area[0], pathlen); + } else if (core->di_format == XFS_DINODE_FMT_EXTENTS) { + bmbt_irec_get(&rec, (xfs_bmbt_rec_t *)&core->di_literal_area[0]); + db = fsblock_to_bytes(fs, rec.br_startblock) >> BLOCK_SHIFT(fs); + dir_buf = xfs_dir2_get_dirblks(fs, db, rec.br_blockcount); + + /* + * Syslinux only supports filesystem block size larger than or equal to + * 4 KiB. Thus, one directory block is far enough to hold the maximum + * symbolic link file content, which is only 1024 bytes long. + */ + memcpy(buf, dir_buf, pathlen); + free(dir_buf); + } + +out: + return pathlen; +} + +static struct inode *xfs_iget_root(struct fs_info *fs) +{ + xfs_dinode_t *core = NULL; + struct inode *inode = xfs_new_inode(fs); + + xfs_debug("Looking for the root inode..."); + + core = xfs_dinode_get_core(fs, XFS_INFO(fs)->rootino); + if (!core) { + xfs_error("Inode core's magic number does not match!"); + xfs_debug("magic number 0x%04x", be16_to_cpu(core->di_magic)); + goto out; + } + + fill_xfs_inode_pvt(fs, inode, XFS_INFO(fs)->rootino); + + xfs_debug("Root inode has been found!"); + + if ((be16_to_cpu(core->di_mode) & S_IFMT) != S_IFDIR) { + xfs_error("root inode is not a directory ?! No makes sense..."); + goto out; + } + + inode->ino = XFS_INFO(fs)->rootino; + inode->mode = DT_DIR; + inode->size = be64_to_cpu(core->di_size); + + return inode; + +out: + free(inode); + + return NULL; +} + +static inline int xfs_read_superblock(struct fs_info *fs, xfs_sb_t *sb) +{ + struct disk *disk = fs->fs_dev->disk; + + if (!disk->rdwr_sectors(disk, sb, XFS_SB_DADDR, 1, false)) + return -1; + + return 0; +} + +static struct xfs_fs_info *xfs_new_sb_info(xfs_sb_t *sb) +{ + struct xfs_fs_info *info; + + info = malloc(sizeof *info); + if (!info) + malloc_error("xfs_fs_info structure"); + + info->blocksize = be32_to_cpu(sb->sb_blocksize); + info->block_shift = sb->sb_blocklog; + info->dirblksize = 1 << (sb->sb_blocklog + sb->sb_dirblklog); + info->dirblklog = sb->sb_dirblklog; + info->inopb_shift = sb->sb_inopblog; + info->agblk_shift = sb->sb_agblklog; + info->rootino = be64_to_cpu(sb->sb_rootino); + info->agblocks = be32_to_cpu(sb->sb_agblocks); + info->agblocks_shift = sb->sb_agblklog; + info->agcount = be32_to_cpu(sb->sb_agcount); + info->inodesize = be16_to_cpu(sb->sb_inodesize); + info->inode_shift = sb->sb_inodelog; + + return info; +} + +static int xfs_fs_init(struct fs_info *fs) +{ + struct disk *disk = fs->fs_dev->disk; + xfs_sb_t sb; + struct xfs_fs_info *info; + + xfs_debug("fs %p", fs); + + SECTOR_SHIFT(fs) = disk->sector_shift; + SECTOR_SIZE(fs) = 1 << SECTOR_SHIFT(fs); + + if (xfs_read_superblock(fs, &sb)) { + xfs_error("Superblock read failed"); + goto out; + } + + if (!xfs_is_valid_magicnum(&sb)) { + xfs_error("Invalid superblock"); + goto out; + } + + xfs_debug("magicnum 0x%lX", be32_to_cpu(sb.sb_magicnum)); + + info = xfs_new_sb_info(&sb); + if (!info) { + xfs_error("Failed to fill in filesystem-specific info structure"); + goto out; + } + + fs->fs_info = info; + + xfs_debug("block_shift %u blocksize 0x%lX (%lu)", info->block_shift, + info->blocksize, info->blocksize); + + xfs_debug("rootino 0x%llX (%llu)", info->rootino, info->rootino); + + BLOCK_SHIFT(fs) = info->block_shift; + BLOCK_SIZE(fs) = info->blocksize; + + cache_init(fs->fs_dev, BLOCK_SHIFT(fs)); + + XFS_INFO(fs)->dirleafblk = xfs_dir2_db_to_da(fs, XFS_DIR2_LEAF_FIRSTDB(fs)); + + return BLOCK_SHIFT(fs); + +out: + return -1; +} + +const struct fs_ops xfs_fs_ops = { + .fs_name = "xfs", + .fs_flags = FS_USEMEM | FS_THISIND, + .fs_init = xfs_fs_init, + .iget_root = xfs_iget_root, + .searchdir = NULL, + .getfssec = xfs_getfssec, + .open_config = generic_open_config, + .close_file = generic_close_file, + .mangle_name = generic_mangle_name, + .readdir = xfs_readdir, + .iget = xfs_iget, + .next_extent = xfs_next_extent, + .readlink = xfs_readlink, +}; diff --git a/core/fs/xfs/xfs.h b/core/fs/xfs/xfs.h new file mode 100644 index 00000000..da57221a --- /dev/null +++ b/core/fs/xfs/xfs.h @@ -0,0 +1,752 @@ +/* + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * Some parts borrowed from Linux kernel tree (linux/fs/xfs): + * + * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XFS_H_ +#define XFS_H_ + +#include <disk.h> +#include <fs.h> +#include <dprintf.h> + +#include "xfs_types.h" +#include "xfs_ag.h" + +#define xfs_error(fmt, args...) \ + printf("xfs: " fmt "\n", ## args); + +#define xfs_debug(fmt, args...) \ + dprintf("%s: " fmt "\n", __func__, ## args); + +struct xfs_fs_info; + +#define XFS_INFO(fs) ((struct xfs_fs_info *)((fs)->fs_info)) +#define XFS_PVT(ino) ((struct xfs_inode *)((ino)->pvt)) + +#define XFS_INO_MASK(k) (uint32_t)((1ULL << (k)) - 1) +#define XFS_INO_OFFSET_BITS(fs) (fs)->inopb_shift +#define XFS_INO_AGINO_BITS(fs) \ + (XFS_INFO((fs))->inopb_shift + XFS_INFO((fs))->agblk_shift) + +#define XFS_INO_TO_AGINO(fs, i) \ + ((xfs_agino_t)(i) & XFS_INO_MASK(XFS_INO_AGINO_BITS(fs))) + +#define XFS_INO_TO_AGNO(fs, ino) \ + ((xfs_agnumber_t)((ino) >> (XFS_INFO((fs))->inopb_shift + \ + XFS_INFO((fs))->agblk_shift))) + +#define XFS_INO_TO_OFFSET(fs, i) \ + ((int)(i) & XFS_INO_MASK(XFS_INO_OFFSET_BITS(fs))) + +#define XFS_AGNO_TO_FSB(fs, agno) \ + ((block_t)((agno) << XFS_INFO((fs))->agblocks_shift)) + +#define XFS_AGI_OFFS(fs, mp) \ + ((xfs_agi_t *)((uint8_t *)(mp) + 2 * SECTOR_SIZE((fs)))) + +#define XFS_GET_DIR_INO4(di) \ + (((uint32_t)(di).i[0] << 24) | ((di).i[1] << 16) | ((di).i[2] << 8) | \ + ((di).i[3])) + +#define XFS_DI_HI(di) \ + (((uint32_t)(di).i[1] << 16) | ((di).i[2] << 8) | ((di).i[3])) + +#define XFS_DI_LO(di) \ + (((uint32_t)(di).i[4] << 24) | ((di).i[5] << 16) | ((di).i[6] << 8) | \ + ((di).i[7])) + +#define XFS_GET_DIR_INO8(di) \ + (((xfs_ino_t)XFS_DI_LO(di) & 0xffffffffULL) | \ + ((xfs_ino_t)XFS_DI_HI(di) << 32)) + +#define XFS_FSB_TO_AGNO(fs, fsbno) \ + ((xfs_agnumber_t)((fsbno) >> XFS_INFO((fs))->agblk_shift)) +#define XFS_FSB_TO_AGBNO(fs, fsbno) \ + ((xfs_agblock_t)((fsbno) & (uint32_t)((1ULL << \ + XFS_INFO((fs))->agblk_shift) - 1))) + +#define agblock_to_bytes(fs, x) \ + ((uint64_t)(x) << BLOCK_SHIFT((fs))) +#define agino_to_bytes(fs, x) \ + ((uint64_t)(x) << XFS_INFO((fs))->inode_shift) +#define agnumber_to_bytes(fs, x) \ + agblock_to_bytes(fs, (uint64_t)(x) * XFS_INFO((fs))->agblocks) +#define fsblock_to_bytes(fs,x) \ + (agnumber_to_bytes(fs, XFS_FSB_TO_AGNO(fs, (x))) + \ + agblock_to_bytes(fs, XFS_FSB_TO_AGBNO(fs, (x)))) +#define ino_to_bytes(fs, x) \ + (agnumber_to_bytes(fs, XFS_INO_TO_AGNO(fs, (x))) + \ + agino_to_bytes(fs, XFS_INO_TO_AGINO(fs, (x)))) + +/* Superblock's LBA */ +#define XFS_SB_DADDR ((xfs_daddr_t)0) /* daddr in filesystem/ag */ + +/* Magic numbers */ +#define XFS_AGI_MAGIC "XAGI" +#define XFS_IBT_MAGIC "IABT" +#define XFS_DINODE_MAGIC "IN" + +#define XFS_DIR2_BLOCK_MAGIC 0x58443242U /* XD2B: single block dirs */ +#define XFS_DIR2_DATA_MAGIC 0x58443244U /* XD2D: multiblock dirs */ +#define XFS_DIR2_FREE_MAGIC 0x58443246U /* XD2F: free index blocks */ + +#define XFS_DIR2_NULL_DATAPTR ((uint32_t)0) + +/* File types and modes */ +#define S_IFMT 00170000 +#define S_IFSOCK 0140000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFBLK 0060000 +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFIFO 0010000 +#define S_ISUID 0004000 +#define S_ISGID 0002000 +#define S_ISVTX 0001000 + +#define MAXPATHLEN 1024 +/* + * NOTE: The fields in the superblock are stored in big-endian format on disk. + */ +typedef struct xfs_sb { + uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ + uint32_t sb_blocksize; /* logical block size, bytes */ + xfs_drfsbno_t sb_dblocks; /* number of data blocks */ + xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */ + xfs_drtbno_t sb_rextents; /* number of realtime extents */ + uuid_t sb_uuid; /* file system unique id */ + xfs_dfsbno_t sb_logstart; /* starting block of log if internal */ + xfs_ino_t sb_rootino; /* root inode number */ + xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */ + xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */ + xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */ + xfs_agblock_t sb_agblocks; /* size of an allocation group */ + xfs_agnumber_t sb_agcount; /* number of allocation groups */ + xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */ + xfs_extlen_t sb_logblocks; /* number of log blocks */ + uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ + uint16_t sb_sectsize; /* volume sector size, bytes */ + uint16_t sb_inodesize; /* inode size, bytes */ + uint16_t sb_inopblock; /* inodes per block */ + char sb_fname[12]; /* file system name */ + uint8_t sb_blocklog; /* log2 of sb_blocksize */ + uint8_t sb_sectlog; /* log2 of sb_sectsize */ + uint8_t sb_inodelog; /* log2 of sb_inodesize */ + uint8_t sb_inopblog; /* log2 of sb_inopblock */ + uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ + uint8_t sb_rextslog; /* log2 of sb_rextents */ + uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ + uint8_t sb_imax_pct; /* max % of fs for inode space */ + /* statistics */ + /* + * These fields must remain contiguous. If you really + * want to change their layout, make sure you fix the + * code in xfs_trans_apply_sb_deltas(). + */ + uint64_t sb_icount; /* allocated inodes */ + uint64_t sb_ifree; /* free inodes */ + uint64_t sb_fdblocks; /* free data blocks */ + uint64_t sb_frextents; /* free realtime extents */ + /* + * End contiguous fields. + */ + xfs_ino_t sb_uquotino; /* user quota inode */ + xfs_ino_t sb_gquotino; /* group quota inode */ + uint16_t sb_qflags; /* quota flags */ + uint8_t sb_flags; /* misc. flags */ + uint8_t sb_shared_vn; /* shared version number */ + xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */ + uint32_t sb_unit; /* stripe or raid unit */ + uint32_t sb_width; /* stripe or raid width */ + uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */ + uint8_t sb_logsectlog; /* log2 of the log sector size */ + uint16_t sb_logsectsize; /* sector size for the log, bytes */ + uint32_t sb_logsunit; /* stripe unit size for the log */ + uint32_t sb_features2; /* additional feature bits */ + + /* + * bad features2 field as a result of failing to pad the sb + * structure to 64 bits. Some machines will be using this field + * for features2 bits. Easiest just to mark it bad and not use + * it for anything else. + */ + uint32_t sb_bad_features2; + uint8_t pad[304]; /* must be padded to a sector boundary */ +} __attribute__((__packed__)) xfs_sb_t; + +/* In-memory structure that stores filesystem-specific information. + * The information stored is basically retrieved from the XFS superblock + * to be used statically around the driver. + */ +struct xfs_fs_info { + uint32_t blocksize; /* Filesystem block size */ + uint8_t block_shift; /* Filesystem block size in bits */ + uint32_t dirblksize; + uint8_t dirblklog; + uint8_t inopb_shift; + uint8_t agblk_shift; + uint32_t dirleafblk; + + /* AG number bits (MSB of the inode number) */ + uint8_t ag_number_ino_shift; + + xfs_ino_t rootino; /* Root inode number for the filesystem */ + xfs_agblock_t agblocks; /* Size of each AG in blocks */ + uint8_t agblocks_shift; /* agblocks in bits */ + xfs_agnumber_t agcount; /* Number of AGs in the filesytem */ + uint16_t inodesize; /* Size of the inode in bytes */ + uint8_t inode_shift; /* Inode size in bits */ +} __attribute__((__packed__)); + +typedef struct xfs_agi { + /* + * Common allocation group header information + */ + uint32_t agi_magicnum; /* magic number == XFS_AGI_MAGIC */ + uint32_t agi_versionnum; /* header version == XFS_AGI_VERSION */ + uint32_t agi_seqno; /* sequence # starting from 0 */ + uint32_t agi_length; /* size in blocks of a.g. */ + /* + * Inode information + * Inodes are mapped by interpreting the inode number, so no + * mapping data is needed here. + */ + uint32_t agi_count; /* count of allocated inodes */ + uint32_t agi_root; /* root of inode btree */ + uint32_t agi_level; /* levels in inode btree */ + uint32_t agi_freecount; /* number of free inodes */ + uint32_t agi_newino; /* new inode just allocated */ + uint32_t agi_dirino; /* last directory inode chunk */ + /* + * Hash table of inodes which have been unlinked but are + * still being referenced. + */ + uint32_t agi_unlinked[XFS_AGI_UNLINKED_BUCKETS]; +} __attribute__((__packed__)) xfs_agi_t; + +/* + * Bmap btree record and extent descriptor. + * l0:63 is an extent flag (value 1 indicates non-normal). + * l0:9-62 are startoff. + * l0:0-8 and l1:21-63 are startblock. + * l1:0-20 are blockcount. + */ +typedef struct xfs_bmbt_rec { + uint64_t l0; + uint64_t l1; +} __attribute__((__packed__)) xfs_bmbt_rec_t; + +typedef xfs_bmbt_rec_t xfs_bmdr_rec_t; + +/* + * Possible extent states. + */ +typedef enum { + XFS_EXT_NORM, + XFS_EXT_UNWRITTEN, + XFS_EXT_DMAPI_OFFLINE, + XFS_EXT_INVALID, +} xfs_exntst_t; + +typedef struct xfs_bmbt_irec +{ + xfs_fileoff_t br_startoff; /* starting file offset */ + xfs_fsblock_t br_startblock; /* starting block number */ + xfs_filblks_t br_blockcount; /* number of blocks */ + xfs_exntst_t br_state; /* extent state */ +} __attribute__((__packed__)) xfs_bmbt_irec_t; + +static inline void bmbt_irec_get(xfs_bmbt_irec_t *dest, + const xfs_bmbt_rec_t *src) +{ + uint64_t l0, l1; + + l0 = be64_to_cpu(src->l0); + l1 = be64_to_cpu(src->l1); + + dest->br_startoff = ((xfs_fileoff_t)l0 & 0x7ffffffffffffe00ULL) >> 9; + dest->br_startblock = (((xfs_fsblock_t)l0 & 0x00000000000001ffULL) << 43) | + (((xfs_fsblock_t)l1) >> 21); + dest->br_blockcount = (xfs_filblks_t)(l1 & 0x00000000001fffffULL); + dest->br_state = (l0 & 0x8000000000000000ULL) ? + XFS_EXT_UNWRITTEN : XFS_EXT_NORM; +} + +typedef struct xfs_timestamp { + int32_t t_sec; + int32_t t_nsec; +} __attribute__((__packed__)) xfs_timestamp_t; + +/* + * Fork identifiers. + */ +#define XFS_DATA_FORK 0 +#define xFS_ATTR_FORK 1 + +typedef enum xfs_dinode_fmt { + XFS_DINODE_FMT_DEV, + XFS_DINODE_FMT_LOCAL, + XFS_DINODE_FMT_EXTENTS, + XFS_DINODE_FMT_BTREE, + XFS_DINODE_FMT_UUID, +} xfs_dinode_fmt_t; + +typedef struct xfs_dinode { + uint16_t di_magic; /* inode magic # = XFS_DINODE_MAGIC */ + uint16_t di_mode; /* mode and type of file */ + uint8_t di_version; /* inode version */ + uint8_t di_format; /* format of di_c data */ + uint16_t di_onlink; /* old number of links to file */ + uint32_t di_uid; /* owner's user id */ + uint32_t di_gid; /* owner's group id */ + uint32_t di_nlink; /* number of links to file */ + uint16_t di_projid_lo; /* lower part of owner's project id */ + uint16_t di_projid_hi; /* higher part owner's project id */ + uint8_t di_pad[6]; /* unused, zeroed space */ + uint16_t di_flushiter; /* incremented on flush */ + xfs_timestamp_t di_atime; /* time last accessed */ + xfs_timestamp_t di_mtime; /* time last modified */ + xfs_timestamp_t di_ctime; /* time created/inode modified */ + uint64_t di_size; /* number of bytes in file */ + uint64_t di_nblocks; /* # of direct & btree blocks used */ + uint32_t di_extsize; /* basic/minimum extent size for file */ + uint32_t di_nextents; /* number of extents in data fork */ + uint16_t di_anextents; /* number of extents in attribute fork*/ + uint8_t di_forkoff; /* attr fork offs, <<3 for 64b align */ + int8_t di_aformat; /* format of attr fork's data */ + uint32_t di_dmevmask; /* DMIG event mask */ + uint16_t di_dmstate; /* DMIG state info */ + uint16_t di_flags; /* random flags, XFS_DIFLAG_... */ + uint32_t di_gen; /* generation number */ + + /* di_next_unlinked is the only non-core field in the old dinode */ + uint32_t di_next_unlinked;/* agi unlinked list ptr */ + uint8_t di_literal_area[1]; +} __attribute__((packed)) xfs_dinode_t; + +/* + * Inode size for given fs. + */ +#define XFS_LITINO(fs) \ + ((int)((XFS_INFO(fs)->inodesize) - sizeof(struct xfs_dinode) - 1)) + +#define XFS_BROOT_SIZE_ADJ \ + (XFS_BTREE_LBLOCK_LEN - sizeof(xfs_bmdr_block_t)) + +/* + * Inode data & attribute fork sizes, per inode. + */ +#define XFS_DFORK_Q(dip) ((dip)->di_forkoff != 0) +#define XFS_DFORK_BOFF(dip) ((int)((dip)->di_forkoff << 3)) + +#define XFS_DFORK_DSIZE(dip, fs) \ + (XFS_DFORK_Q(dip) ? \ + XFS_DFORK_BOFF(dip) : \ + XFS_LITINO(fs)) +#define XFS_DFORK_ASIZE(dip, fs) \ + (XFS_DFORK_Q(dip) ? \ + XFS_LITINO(fs) - XFS_DFORK_BOFF(dip) : \ + 0) +#define XFS_DFORK_SIZE(dip, fs, w) \ + ((w) == XFS_DATA_FORK ? \ + XFS_DFORK_DSIZE(dip, fs) : \ + XFS_DFORK_ASIZE(dip, fs)) + +struct xfs_inode { + xfs_agblock_t i_agblock; + block_t i_ino_blk; + uint64_t i_block_offset; + uint64_t i_offset; + uint32_t i_cur_extent; + uint32_t i_btree_offset; + uint16_t i_leaf_ent_offset; +}; + +typedef struct { uint8_t i[8]; } __attribute__((__packed__)) xfs_dir2_ino8_t; +typedef struct { uint8_t i[4]; } __attribute__((__packed__)) xfs_dir2_ino4_t; + +typedef union { + xfs_dir2_ino8_t i8; + xfs_dir2_ino4_t i4; +} __attribute__((__packed__)) xfs_dir2_inou_t; + +typedef struct { uint8_t i[2]; } __attribute__((__packed__)) xfs_dir2_sf_off_t; + +typedef struct xfs_dir2_sf_hdr { + uint8_t count; /* count of entries */ + uint8_t i8count; /* count of 8-byte inode #s */ + xfs_dir2_inou_t parent; /* parent dir inode number */ +} __attribute__((__packed__)) xfs_dir2_sf_hdr_t; + +typedef struct xfs_dir2_sf_entry { + uint8_t namelen; /* actual name length */ + xfs_dir2_sf_off_t offset; /* saved offset */ + uint8_t name[1]; /* name, variable size */ + xfs_dir2_inou_t inumber; /* inode number, var. offset */ +} __attribute__((__packed__)) xfs_dir2_sf_entry_t; + +typedef struct xfs_dir2_sf { + xfs_dir2_sf_hdr_t hdr; /* shortform header */ + xfs_dir2_sf_entry_t list[1]; /* shortform entries */ +} __attribute__((__packed__)) xfs_dir2_sf_t; + +typedef xfs_ino_t xfs_intino_t; + +static inline xfs_intino_t xfs_dir2_sf_get_inumber(xfs_dir2_sf_t *sfp, + xfs_dir2_inou_t *from) +{ + return ((sfp)->hdr.i8count == 0 ? \ + (xfs_intino_t)XFS_GET_DIR_INO4((from)->i4) : \ + (xfs_intino_t)XFS_GET_DIR_INO8((from)->i8)); +} + +/* + * DIR2 Data block structures. + * + * A pure data block looks like the following drawing on disk: + * + * +-------------------------------------------------+ + * | xfs_dir2_data_hdr_t | + * +-------------------------------------------------+ + * | xfs_dir2_data_entry_t OR xfs_dir2_data_unused_t | + * | xfs_dir2_data_entry_t OR xfs_dir2_data_unused_t | + * | xfs_dir2_data_entry_t OR xfs_dir2_data_unused_t | + * | ... | + * +-------------------------------------------------+ + * | unused space | + * +-------------------------------------------------+ + * + * As all the entries are variable size structure the accessors below should + * be used to iterate over them. + * + * In addition to the pure data blocks for the data and node formats. + * most structures are also used for the combined data/freespace "block" + * format below. + */ +#define XFS_DIR2_DATA_ALIGN_LOG 3 +#define XFS_DIR2_DATA_ALIGN (1 << XFS_DIR2_DATA_ALIGN_LOG) +#define XFS_DIR2_DATA_FREE_TAG 0xffff +#define XFS_DIR2_DATA_FD_COUNT 3 + +/* + * Directory address space divided into sections. + * spaces separated by 32GB. + */ +#define XFS_DIR2_SPACE_SIZE (1ULL << (32 + XFS_DIR2_DATA_ALIGN_LOG)) + +typedef struct xfs_dir2_data_free { + uint16_t offset; + uint16_t length; +} __attribute__((__packed__)) xfs_dir2_data_free_t; + +typedef struct xfs_dir2_data_hdr { + uint32_t magic; + xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT]; +} __attribute__((__packed__)) xfs_dir2_data_hdr_t; + +typedef struct xfs_dir2_data_entry { + uint64_t inumber; /* inode number */ + uint8_t namelen; /* name length */ + uint8_t name[]; /* name types, no null */ + /* uint16_t tag; */ /* starting offset of us */ +} __attribute__((__packed__)) xfs_dir2_data_entry_t; + +typedef struct xfs_dir2_data_unused { + uint16_t freetag; /* XFS_DIR2_DATA_FREE_TAG */ + uint16_t length; /* total free length */ + /* variable offset */ + /* uint16_t tag; */ /* starting offset of us */ +} __attribute__((__packed__)) xfs_dir2_data_unused_t; + +/** + * rol32 - rotate a 32-bit value left + * @word: value to rotate + * @shift: bits to roll + */ +static inline uint32_t rol32(uint32_t word, signed int shift) +{ + return (word << shift) | (word >> (32 - shift)); +} + +#define roundup(x, y) ( \ +{ \ + const typeof(y) __y = y; \ + (((x) + (__y - 1)) / __y) * __y; \ +} \ +) + +static inline int xfs_dir2_data_entsize(int n) +{ + return (int)roundup(offsetof(struct xfs_dir2_data_entry, name[0]) + n + + (unsigned int)sizeof(uint16_t), XFS_DIR2_DATA_ALIGN); +} + +static inline uint16_t * +xfs_dir2_data_entry_tag_p(struct xfs_dir2_data_entry *dep) +{ + return (uint16_t *)((char *)dep + + xfs_dir2_data_entsize(dep->namelen) - sizeof(uint16_t)); +} + +static inline uint16_t * +xfs_dir2_data_unused_tag_p(struct xfs_dir2_data_unused *dup) +{ + return (uint16_t *)((char *)dup + + be16_to_cpu(dup->length) - sizeof(uint16_t)); +} + +typedef struct xfs_dir2_block_tail { + uint32_t count; /* count of leaf entries */ + uint32_t stale; /* count of stale lf entries */ +} __attribute__((__packed__)) xfs_dir2_block_tail_t; + +static inline struct xfs_dir2_block_tail * +xfs_dir2_block_tail_p(struct xfs_fs_info *fs_info, struct xfs_dir2_data_hdr *hdr) +{ + return ((struct xfs_dir2_block_tail *) + ((char *)hdr + fs_info->dirblksize)) - 1; +} + +static inline uint32_t +xfs_dir2_db_to_da(struct fs_info *fs, uint32_t db) +{ + return db << XFS_INFO(fs)->dirblklog; +} + +static inline int64_t +xfs_dir2_dataptr_to_byte(uint32_t dp) +{ + return (int64_t)dp << XFS_DIR2_DATA_ALIGN_LOG; +} + +static inline uint32_t +xfs_dir2_byte_to_db(struct fs_info *fs, int64_t by) +{ + return (uint32_t) + (by >> (XFS_INFO(fs)->block_shift + XFS_INFO(fs)->dirblklog)); +} + +static inline uint32_t +xfs_dir2_dataptr_to_db(struct fs_info *fs, uint32_t dp) +{ + return xfs_dir2_byte_to_db(fs, xfs_dir2_dataptr_to_byte(dp)); +} + +static inline unsigned int +xfs_dir2_byte_to_off(struct fs_info *fs, int64_t by) +{ + return (unsigned int)(by & + (( 1 << (XFS_INFO(fs)->block_shift + XFS_INFO(fs)->dirblklog)) - 1)); +} + +static inline unsigned int +xfs_dir2_dataptr_to_off(struct fs_info *fs, uint32_t dp) +{ + return xfs_dir2_byte_to_off(fs, xfs_dir2_dataptr_to_byte(dp)); +} + +#define XFS_DIR2_LEAF_SPACE 1 +#define XFS_DIR2_LEAF_OFFSET (XFS_DIR2_LEAF_SPACE * XFS_DIR2_SPACE_SIZE) +#define XFS_DIR2_LEAF_FIRSTDB(fs) \ + xfs_dir2_byte_to_db(fs, XFS_DIR2_LEAF_OFFSET) + +typedef struct xfs_da_blkinfo { + uint32_t forw; + uint32_t back; + uint16_t magic; + uint16_t pad; +} __attribute__((__packed__)) xfs_da_blkinfo_t; + +typedef struct xfs_dir2_leaf_hdr { + xfs_da_blkinfo_t info; + uint16_t count; + uint16_t stale; +} __attribute__((__packed__)) xfs_dir2_leaf_hdr_t; + +typedef struct xfs_dir2_leaf_entry { + uint32_t hashval; /* hash value of name */ + uint32_t address; /* address of data entry */ +} __attribute__((__packed__)) xfs_dir2_leaf_entry_t; + +typedef struct xfs_dir2_leaf { + xfs_dir2_leaf_hdr_t hdr; /* leaf header */ + xfs_dir2_leaf_entry_t ents[]; /* entries */ +} __attribute__((__packed__)) xfs_dir2_leaf_t; + +#define XFS_DA_NODE_MAGIC 0xfebeU /* magic number: non-leaf blocks */ +#define XFS_ATTR_LEAF_MAGIC 0xfbeeU /* magic number: attribute leaf blks */ +#define XFS_DIR2_LEAF1_MAGIC 0xd2f1U /* magic number: v2 dirlf single blks */ +#define XFS_DIR2_LEAFN_MAGIC 0xd2ffU /* magic number: V2 dirlf multi blks */ + +typedef struct xfs_da_intnode { + struct xfs_da_node_hdr { /* constant-structure header block */ + xfs_da_blkinfo_t info; /* block type, links, etc. */ + uint16_t count; /* count of active entries */ + uint16_t level; /* level above leaves (leaf == 0) */ + } hdr; + struct xfs_da_node_entry { + uint32_t hashval; /* hash value for this descendant */ + uint32_t before; /* Btree block before this key */ + } btree[1]; +} __attribute__((__packed__)) xfs_da_intnode_t; + +typedef struct xfs_da_node_hdr xfs_da_node_hdr_t; +typedef struct xfs_da_node_entry xfs_da_node_entry_t; + +static inline bool xfs_is_valid_magicnum(const xfs_sb_t *sb) +{ + return sb->sb_magicnum == *(uint32_t *)XFS_SB_MAGIC; +} + +static inline bool xfs_is_valid_agi(xfs_agi_t *agi) +{ + return agi->agi_magicnum == *(uint32_t *)XFS_AGI_MAGIC; +} + +static inline struct inode *xfs_new_inode(struct fs_info *fs) +{ + struct inode *inode; + + inode = alloc_inode(fs, 0, sizeof(struct xfs_inode)); + if (!inode) + malloc_error("xfs_inode structure"); + + return inode; +} + +static inline void fill_xfs_inode_pvt(struct fs_info *fs, struct inode *inode, + xfs_ino_t ino) +{ + XFS_PVT(inode)->i_agblock = + agnumber_to_bytes(fs, XFS_INO_TO_AGNO(fs, ino)) >> BLOCK_SHIFT(fs); + XFS_PVT(inode)->i_ino_blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs); + XFS_PVT(inode)->i_block_offset = XFS_INO_TO_OFFSET(XFS_INFO(fs), ino) << + XFS_INFO(fs)->inode_shift; +} + +/* + * Generic btree header. + * + * This is a combination of the actual format used on disk for short and long + * format btrees. The first three fields are shared by both format, but + * the pointers are different and should be used with care. + * + * To get the size of the actual short or long form headers please use + * the size macros belows. Never use sizeof(xfs_btree_block); + */ +typedef struct xfs_btree_block { + uint32_t bb_magic; /* magic number for block type */ + uint16_t bb_level; /* 0 is a leaf */ + uint16_t bb_numrecs; /* current # of data records */ + union { + struct { + uint32_t bb_leftsib; + uint32_t bb_rightsib; + } s; /* short form pointers */ + struct { + uint64_t bb_leftsib; + uint64_t bb_rightsib; + } l; /* long form pointers */ + } bb_u; /* rest */ +} xfs_btree_block_t; + +#define XFS_BTREE_SBLOCK_LEN 16 /* size of a short form block */ +#define XFS_BTREE_LBLOCK_LEN 24 /* size of a long form block */ + +/* + * Bmap root header, on-disk form only. + */ +typedef struct xfs_bmdr_block { + uint16_t bb_level; /* 0 is a leaf */ + uint16_t bb_numrecs; /* current # of data records */ +} xfs_bmdr_block_t; + +/* + * Key structure for non-leaf levels of the tree. + */ +typedef struct xfs_bmbt_key { + uint64_t br_startoff; /* starting file offset */ +} xfs_bmbt_key_t, xfs_bmdr_key_t; + +/* btree pointer type */ +typedef uint64_t xfs_bmbt_ptr_t, xfs_bmdr_ptr_t; + +/* + * Btree block header size depends on a superblock flag. + * + * (not quite yet, but soon) + */ +#define XFS_BMBT_BLOCK_LEN(fs) XFS_BTREE_LBLOCK_LEN + +#define XFS_BMBT_REC_ADDR(fs, block, index) \ + ((xfs_bmbt_rec_t *) \ + ((char *)(block) + \ + XFS_BMBT_BLOCK_LEN(fs) + \ + ((index) - 1) * sizeof(xfs_bmbt_rec_t))) + +#define XFS_BMBT_KEY_ADDR(fs, block, index) \ + ((xfs_bmbt_key_t *) \ + ((char *)(block) + \ + XFS_BMBT_BLOCK_LEN(fs) + \ + ((index) - 1) * sizeof(xfs_bmbt_key_t))) + +#define XFS_BMBT_PTR_ADDR(fs, block, index, maxrecs) \ + ((xfs_bmbt_ptr_t *) \ + ((char *)(block) + \ + XFS_BMBT_BLOCK_LEN(fs) + \ + (maxrecs) * sizeof(xfs_bmbt_key_t) + \ + ((index) - 1) * sizeof(xfs_bmbt_ptr_t))) + +#define XFS_BMDR_REC_ADDR(block, index) \ + ((xfs_bmdr_rec_t *) \ + ((char *)(block) + \ + sizeof(struct xfs_bmdr_block) + \ + ((index) - 1) * sizeof(xfs_bmdr_rec_t))) + +#define XFS_BMDR_KEY_ADDR(block, index) \ + ((xfs_bmdr_key_t *) \ + ((char *)(block) + \ + sizeof(struct xfs_bmdr_block) + \ + ((index) - 1) * sizeof(xfs_bmdr_key_t))) + +#define XFS_BMDR_PTR_ADDR(block, index, maxrecs) \ + ((xfs_bmdr_ptr_t *) \ + ((char *)(block) + \ + sizeof(struct xfs_bmdr_block) + \ + (maxrecs) * sizeof(xfs_bmdr_key_t) + \ + ((index) - 1) * sizeof(xfs_bmdr_ptr_t))) + +/* + * Calculate number of records in a bmap btree inode root. + */ +static inline int +xfs_bmdr_maxrecs(int blocklen, int leaf) +{ + blocklen -= sizeof(xfs_bmdr_block_t); + + if (leaf) + return blocklen / sizeof(xfs_bmdr_rec_t); + + return blocklen / (sizeof(xfs_bmdr_key_t) + sizeof(xfs_bmdr_ptr_t)); +} + +#endif /* XFS_H_ */ diff --git a/core/fs/xfs/xfs_ag.h b/core/fs/xfs/xfs_ag.h new file mode 100644 index 00000000..a2988b10 --- /dev/null +++ b/core/fs/xfs/xfs_ag.h @@ -0,0 +1,189 @@ +/* + * Taken from Linux kernel tree (linux/fs/xfs) + * + * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XFS_AG_H_ +#define XFS_AG_H_ + +#include "xfs_types.h" + +/* + * Allocation group header + * This is divided into three structures, placed in sequential 512-byte + * buffers after a copy of the superblock (also in a 512-byte buffer). + */ + +typedef uint32_t xfs_agino_t; + +struct xfs_buf; +struct xfs_mount; +struct xfs_trans; + +#define XFS_AGF_MAGIC "XAGF" +#define XFS_AGF_VERSION 1 +#define XFS_AGI_VERSION 1 + +#define XFS_AGF_GOOD_VERSION(v) ((v) == XFS_AGF_VERSION) +#define XFS_AGI_GOOD_VERSION(v) ((v) == XFS_AGI_VERSION) + +/* + * Btree number 0 is bno, 1 is cnt. This value gives the size of the + * arrays below. + */ +#define XFS_BTNUM_AGF ((int)XFS_BTNUM_CNTi + 1) + +/* + * The second word of agf_levels in the first a.g. overlaps the EFS + * superblock's magic number. Since the magic numbers valid for EFS + * are > 64k, our value cannot be confused for an EFS superblock's. + */ + +typedef struct xfs_agf { + /* + * Common allocation group header information + */ + uint32_t agf_magicnum; /* magic number == XFS_AGF_MAGIC */ + uint32_t agf_versionnum; /* header version == XFS_AGF_VERSION */ + uint32_t agf_seqno; /* sequence # starting from 0 */ + uint32_t agf_length; /* size in blocks of a.g. */ + /* + * Freespace information + */ + uint32_t agf_roots[XFS_BTNUM_AGF]; /* root blocks */ + uint32_t agf_spare0; /* spare field */ + uint32_t agf_levels[XFS_BTNUM_AGF]; /* btree levels */ + uint32_t agf_spare1; /* spare field */ + uint32_t agf_flfirst; /* first freelist block's index */ + uint32_t agf_fllast; /* last freelist block's index */ + uint32_t agf_flcount; /* count of blocks in freelist */ + uint32_t agf_freeblks; /* total free blocks */ + uint32_t agf_longest; /* longest free space */ + uint32_t agf_btreeblks; /* # of blocks held in AGF btrees */ +} xfs_agf_t; + +#define XFS_AGF_MAGICNUM 0x00000001 +#define XFS_AGF_VERSIONNUM 0x00000002 +#define XFS_AGF_SEQNO 0x00000004 +#define XFS_AGF_LENGTH 0x00000008 +#define XFS_AGF_ROOTS 0x00000010 +#define XFS_AGF_LEVELS 0x00000020 +#define XFS_AGF_FLFIRST 0x00000040 +#define XFS_AGF_FLLAST 0x00000080 +#define XFS_AGF_FLCOUNT 0x00000100 +#define XFS_AGF_FREEBLKS 0x00000200 +#define XFS_AGF_LONGEST 0x00000400 +#define XFS_AGF_BTREEBLKS 0x00000800 +#define XFS_AGF_NUM_BITS 12 +#define XFS_AGF_ALL_BITS ((1 << XFS_AGF_NUM_BITS) - 1) + +#define XFS_AGF_FLAGS \ + { XFS_AGF_MAGICNUM, "MAGICNUM" }, \ + { XFS_AGF_VERSIONNUM, "VERSIONNUM" }, \ + { XFS_AGF_SEQNO, "SEQNO" }, \ + { XFS_AGF_LENGTH, "LENGTH" }, \ + { XFS_AGF_ROOTS, "ROOTS" }, \ + { XFS_AGF_LEVELS, "LEVELS" }, \ + { XFS_AGF_FLFIRST, "FLFIRST" }, \ + { XFS_AGF_FLLAST, "FLLAST" }, \ + { XFS_AGF_FLCOUNT, "FLCOUNT" }, \ + { XFS_AGF_FREEBLKS, "FREEBLKS" }, \ + { XFS_AGF_LONGEST, "LONGEST" }, \ + { XFS_AGF_BTREEBLKS, "BTREEBLKS" } + +/* disk block (xfs_daddr_t) in the AG */ +#define XFS_AGF_DADDR(mp) ((xfs_daddr_t)(1 << (mp)->m_sectbb_log)) +#define XFS_AGF_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGF_DADDR(mp)) +#define XFS_BUF_TO_AGF(bp) ((xfs_agf_t *)((bp)->b_addr)) + +extern int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp, + xfs_agnumber_t agno, int flags, struct xfs_buf **bpp); + +/* + * Size of the unlinked inode hash table in the agi. + */ +#define XFS_AGI_UNLINKED_BUCKETS 64 + +#define XFS_AGI_MAGICNUM 0x00000001 +#define XFS_AGI_VERSIONNUM 0x00000002 +#define XFS_AGI_SEQNO 0x00000004 +#define XFS_AGI_LENGTH 0x00000008 +#define XFS_AGI_COUNT 0x00000010 +#define XFS_AGI_ROOT 0x00000020 +#define XFS_AGI_LEVEL 0x00000040 +#define XFS_AGI_FREECOUNT 0x00000080 +#define XFS_AGI_NEWINO 0x00000100 +#define XFS_AGI_DIRINO 0x00000200 +#define XFS_AGI_UNLINKED 0x00000400 +#define XFS_AGI_NUM_BITS 11 +#define XFS_AGI_ALL_BITS ((1 << XFS_AGI_NUM_BITS) - 1) + +/* disk block (xfs_daddr_t) in the AG */ +#define XFS_AGI_DADDR(mp) ((xfs_daddr_t)(2 << (mp)->m_sectbb_log)) +#define XFS_AGI_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGI_DADDR(mp)) +#define XFS_BUF_TO_AGI(bp) ((xfs_agi_t *)((bp)->b_addr)) + +extern int xfs_read_agi(struct xfs_mount *mp, struct xfs_trans *tp, + xfs_agnumber_t agno, struct xfs_buf **bpp); + +/* + * The third a.g. block contains the a.g. freelist, an array + * of block pointers to blocks owned by the allocation btree code. + */ +#define XFS_AGFL_DADDR(mp) ((xfs_daddr_t)(3 << (mp)->m_sectbb_log)) +#define XFS_AGFL_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGFL_DADDR(mp)) +#define XFS_AGFL_SIZE(mp) ((mp)->m_sb.sb_sectsize / sizeof(xfs_agblock_t)) +#define XFS_BUF_TO_AGFL(bp) ((xfs_agfl_t *)((bp)->b_addr)) + +typedef struct xfs_agfl { + uint32_t agfl_bno[1]; /* actually XFS_AGFL_SIZE(mp) */ +} xfs_agfl_t; + +/* + * tags for inode radix tree + */ +#define XFS_ICI_NO_TAG (-1) /* special flag for an untagged lookup + in xfs_inode_ag_iterator */ +#define XFS_ICI_RECLAIM_TAG 0 /* inode is to be reclaimed */ + +#define XFS_AG_MAXLEVELS(mp) ((mp)->m_ag_maxlevels) +#define XFS_MIN_FREELIST_RAW(bl,cl,mp) \ + (MIN(bl + 1, XFS_AG_MAXLEVELS(mp)) + MIN(cl + 1, XFS_AG_MAXLEVELS(mp))) +#define XFS_MIN_FREELIST(a,mp) \ + (XFS_MIN_FREELIST_RAW( \ + be32_to_cpu((a)->agf_levels[XFS_BTNUM_BNOi]), \ + be32_to_cpu((a)->agf_levels[XFS_BTNUM_CNTi]), mp)) +#define XFS_MIN_FREELIST_PAG(pag,mp) \ + (XFS_MIN_FREELIST_RAW( \ + (unsigned int)(pag)->pagf_levels[XFS_BTNUM_BNOi], \ + (unsigned int)(pag)->pagf_levels[XFS_BTNUM_CNTi], mp)) + +/* + * For checking for bad ranges of xfs_daddr_t's, covering multiple + * allocation groups or a single xfs_daddr_t that's a superblock copy. + */ +#define XFS_AG_CHECK_DADDR(mp,d,len) \ + ((len) == 1 ? \ + ASSERT((d) == XFS_SB_DADDR || \ + xfs_daddr_to_agbno(mp, d) != XFS_SB_DADDR) : \ + ASSERT(xfs_daddr_to_agno(mp, d) == \ + xfs_daddr_to_agno(mp, (d) + (len) - 1))) + +#endif /* XFS_AG_H_ */ diff --git a/core/fs/xfs/xfs_dinode.c b/core/fs/xfs/xfs_dinode.c new file mode 100644 index 00000000..8e2d8d04 --- /dev/null +++ b/core/fs/xfs/xfs_dinode.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cache.h> +#include <core.h> +#include <fs.h> + +#include "xfs_types.h" +#include "xfs_sb.h" +#include "xfs_ag.h" +#include "misc.h" +#include "xfs.h" + +#include "xfs_dinode.h" + +xfs_dinode_t *xfs_dinode_get_core(struct fs_info *fs, xfs_ino_t ino) +{ + block_t blk; + xfs_dinode_t *core; + uint64_t offset; + + xfs_debug("ino %lu", ino); + + blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs); + offset = XFS_INO_TO_OFFSET(XFS_INFO(fs), ino) << XFS_INFO(fs)->inode_shift; + if (offset > BLOCK_SIZE(fs)) { + xfs_error("Invalid inode offset in block!"); + xfs_debug("offset: 0x%llx", offset); + goto out; + } + + xfs_debug("blk %llu block offset 0x%llx", blk, blk << BLOCK_SHIFT(fs)); + xfs_debug("inode offset in block (in bytes) is 0x%llx", offset); + + core = (xfs_dinode_t *)((uint8_t *)get_cache(fs->fs_dev, blk) + offset); + if (be16_to_cpu(core->di_magic) != + be16_to_cpu(*(uint16_t *)XFS_DINODE_MAGIC)) { + xfs_error("Inode core's magic number does not match!"); + xfs_debug("magic number 0x%04x", (be16_to_cpu(core->di_magic))); + goto out; + } + + return core; + +out: + return NULL; +} diff --git a/core/fs/xfs/xfs_dinode.h b/core/fs/xfs/xfs_dinode.h new file mode 100644 index 00000000..80deec78 --- /dev/null +++ b/core/fs/xfs/xfs_dinode.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XFS_DINODE_H_ +#define XFS_DINODE_H_ + +xfs_dinode_t *xfs_dinode_get_core(struct fs_info *fs, xfs_ino_t ino); + +#endif /* XFS_DINODE_H_ */ diff --git a/core/fs/xfs/xfs_dir2.c b/core/fs/xfs/xfs_dir2.c new file mode 100644 index 00000000..c52196ae --- /dev/null +++ b/core/fs/xfs/xfs_dir2.c @@ -0,0 +1,759 @@ +/* + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cache.h> +#include <core.h> +#include <fs.h> + +#include "xfs_types.h" +#include "xfs_sb.h" +#include "xfs_ag.h" +#include "misc.h" +#include "xfs.h" +#include "xfs_dinode.h" + +#include "xfs_dir2.h" + +char *xfs_dir2_get_entry_name(uint8_t *start, uint8_t *end) +{ + char *s; + char *p; + + s = malloc(end - start + 1); + if (!s) + malloc_error("string"); + + p = s; + while (start < end) + *p++ = *start++; + + *p = '\0'; + + return s; +} + +uint32_t xfs_dir2_da_hashname(const uint8_t *name, int namelen) +{ + uint32_t hash; + + /* + * Do four characters at a time as long as we can. + */ + for (hash = 0; namelen >= 4; namelen -=4, name += 4) + hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^ + (name[3] << 0) ^ rol32(hash, 7 * 4); + + /* + * Now do the rest of the characters. + */ + switch (namelen) { + case 3: + return (name[0] << 14) ^ (name[1] << 7) ^ (name[2] << 0) ^ + rol32(hash, 7 * 3); + case 2: + return (name[0] << 7) ^ (name[1] << 0) ^ rol32(hash, 7 * 2); + case 1: + return (name[0] << 0) ^ rol32(hash, 7 * 1); + default: /* case 0: */ + return hash; + } +} + +void *xfs_dir2_get_dirblks(struct fs_info *fs, block_t startblock, + xfs_filblks_t c) +{ + int count = c << XFS_INFO(fs)->dirblklog; + uint8_t *p; + uint8_t *buf; + off_t offset = 0; + + buf = malloc(c * XFS_INFO(fs)->dirblksize); + if (!buf) + malloc_error("buffer memory"); + + memset(buf, 0, XFS_INFO(fs)->dirblksize); + + while (count--) { + p = (uint8_t *)get_cache(fs->fs_dev, startblock++); + memcpy(buf + offset, p, BLOCK_SIZE(fs)); + offset += BLOCK_SIZE(fs); + } + + return buf; +} + +struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent, + xfs_dinode_t *core) +{ + xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0]; + xfs_dir2_sf_entry_t *sf_entry; + uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count; + struct fs_info *fs = parent->fs; + struct inode *inode; + xfs_intino_t ino; + xfs_dinode_t *ncore = NULL; + + xfs_debug("count %hhu i8count %hhu", sf->hdr.count, sf->hdr.i8count); + + sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)&sf->list[0] - + (!sf->hdr.i8count ? 4 : 0)); + while (count--) { + uint8_t *start_name = &sf_entry->name[0]; + uint8_t *end_name = start_name + sf_entry->namelen; + char *name; + + name = xfs_dir2_get_entry_name(start_name, end_name); + + xfs_debug("entry name: %s", name); + + if (!strncmp(name, dname, strlen(dname))) { + free(name); + goto found; + } + + free(name); + + sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)sf_entry + + offsetof(struct xfs_dir2_sf_entry, + name[0]) + + sf_entry->namelen + + (sf->hdr.i8count ? 8 : 4)); + } + + return NULL; + +found: + inode = xfs_new_inode(fs); + + ino = xfs_dir2_sf_get_inumber(sf, (xfs_dir2_inou_t *)( + (uint8_t *)sf_entry + + offsetof(struct xfs_dir2_sf_entry, + name[0]) + + sf_entry->namelen)); + + xfs_debug("entry inode's number %lu", ino); + + ncore = xfs_dinode_get_core(fs, ino); + if (!ncore) { + xfs_error("Failed to get dinode!"); + goto out; + } + + fill_xfs_inode_pvt(fs, inode, ino); + + inode->ino = ino; + inode->size = be64_to_cpu(ncore->di_size); + + if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) { + inode->mode = DT_DIR; + xfs_debug("Found a directory inode!"); + } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) { + inode->mode = DT_REG; + xfs_debug("Found a file inode!"); + xfs_debug("inode size %llu", inode->size); + } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) { + inode->mode = DT_LNK; + xfs_debug("Found a symbolic link inode!"); + } + + return inode; + +out: + free(inode); + + return NULL; +} + +struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent, + xfs_dinode_t *core) +{ + xfs_bmbt_irec_t r; + block_t dir_blk; + struct fs_info *fs = parent->fs; + uint8_t *dirblk_buf; + uint8_t *p, *endp; + xfs_dir2_data_hdr_t *hdr; + struct inode *inode = NULL; + xfs_dir2_block_tail_t *btp; + xfs_dir2_data_unused_t *dup; + xfs_dir2_data_entry_t *dep; + xfs_intino_t ino; + xfs_dinode_t *ncore; + + bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]); + dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs); + + dirblk_buf = xfs_dir2_get_dirblks(fs, dir_blk, r.br_blockcount); + hdr = (xfs_dir2_data_hdr_t *)dirblk_buf; + if (be32_to_cpu(hdr->magic) != XFS_DIR2_BLOCK_MAGIC) { + xfs_error("Block directory header's magic number does not match!"); + xfs_debug("hdr->magic: 0x%lx", be32_to_cpu(hdr->magic)); + goto out; + } + + p = (uint8_t *)(hdr + 1); + + btp = xfs_dir2_block_tail_p(XFS_INFO(fs), hdr); + endp = (uint8_t *)((xfs_dir2_leaf_entry_t *)btp - be32_to_cpu(btp->count)); + + while (p < endp) { + uint8_t *start_name; + uint8_t *end_name; + char *name; + + dup = (xfs_dir2_data_unused_t *)p; + if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { + p += be16_to_cpu(dup->length); + continue; + } + + dep = (xfs_dir2_data_entry_t *)p; + + start_name = &dep->name[0]; + end_name = start_name + dep->namelen; + name = xfs_dir2_get_entry_name(start_name, end_name); + + if (!strncmp(name, dname, strlen(dname))) { + free(name); + goto found; + } + + free(name); + p += xfs_dir2_data_entsize(dep->namelen); + } + +out: + free(dirblk_buf); + + return NULL; + +found: + inode = xfs_new_inode(fs); + + ino = be64_to_cpu(dep->inumber); + + xfs_debug("entry inode's number %lu", ino); + + ncore = xfs_dinode_get_core(fs, ino); + if (!ncore) { + xfs_error("Failed to get dinode!"); + goto failed; + } + + fill_xfs_inode_pvt(fs, inode, ino); + + inode->ino = ino; + XFS_PVT(inode)->i_ino_blk = ino_to_bytes(fs, ino) >> BLOCK_SHIFT(fs); + inode->size = be64_to_cpu(ncore->di_size); + + if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) { + inode->mode = DT_DIR; + xfs_debug("Found a directory inode!"); + } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) { + inode->mode = DT_REG; + xfs_debug("Found a file inode!"); + xfs_debug("inode size %llu", inode->size); + } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) { + inode->mode = DT_LNK; + xfs_debug("Found a symbolic link inode!"); + } + + xfs_debug("entry inode's number %lu", ino); + + free(dirblk_buf); + return inode; + +failed: + free(inode); + free(dirblk_buf); + + return NULL; +} + +struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent, + xfs_dinode_t *core) +{ + xfs_dir2_leaf_t *leaf; + xfs_bmbt_irec_t irec; + block_t leaf_blk, dir_blk; + xfs_dir2_leaf_entry_t *lep; + int low; + int high; + int mid = 0; + uint32_t hash = 0; + uint32_t hashwant; + uint32_t newdb, curdb = -1; + xfs_dir2_data_entry_t *dep; + struct inode *ip; + xfs_dir2_data_hdr_t *data_hdr; + uint8_t *start_name; + uint8_t *end_name; + char *name; + xfs_intino_t ino; + xfs_dinode_t *ncore; + uint8_t *buf = NULL; + + bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + + be32_to_cpu(core->di_nextents) - 1); + leaf_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >> + BLOCK_SHIFT(parent->fs); + + leaf = (xfs_dir2_leaf_t *)xfs_dir2_get_dirblks(parent->fs, leaf_blk, + irec.br_blockcount); + if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) { + xfs_error("Single leaf block header's magic number does not match!"); + goto out; + } + + if (!leaf->hdr.count) + goto out; + + hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname)); + + /* Binary search */ + for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1; + low <= high; ) { + mid = (low + high) >> 1; + if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant) + break; + if (hash < hashwant) + low = mid + 1; + else + high = mid - 1; + } + + /* If hash is not the one we want, then the directory does not contain the + * entry we're looking for and there is nothing to do anymore. + */ + if (hash != hashwant) + goto out; + + while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant) + mid--; + + for (lep = &leaf->ents[mid]; + mid < be16_to_cpu(leaf->hdr.count) && + be32_to_cpu(lep->hashval) == hashwant; + lep++, mid++) { + /* Skip over stale leaf entries. */ + if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR) + continue; + + newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address)); + if (newdb != curdb) { + if (buf) + free(buf); + + bmbt_irec_get(&irec, + ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + newdb); + dir_blk = fsblock_to_bytes(parent->fs, irec.br_startblock) >> + BLOCK_SHIFT(parent->fs); + buf = xfs_dir2_get_dirblks(parent->fs, dir_blk, irec.br_blockcount); + data_hdr = (xfs_dir2_data_hdr_t *)buf; + if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) { + xfs_error("Leaf directory's data magic No. does not match!"); + goto out1; + } + + curdb = newdb; + } + + dep = (xfs_dir2_data_entry_t *)((char *)buf + + xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address))); + + start_name = &dep->name[0]; + end_name = start_name + dep->namelen; + name = xfs_dir2_get_entry_name(start_name, end_name); + + if (!strncmp(name, dname, strlen(dname))) { + free(name); + goto found; + } + + free(name); + } + +out1: + free(buf); +out: + free(leaf); + + return NULL; + +found: + ip = xfs_new_inode(parent->fs); + + ino = be64_to_cpu(dep->inumber); + + xfs_debug("entry inode's number %lu", ino); + + ncore = xfs_dinode_get_core(parent->fs, ino); + if (!ncore) { + xfs_error("Failed to get dinode!"); + goto failed; + } + + fill_xfs_inode_pvt(parent->fs, ip, ino); + + ip->ino = ino; + XFS_PVT(ip)->i_ino_blk = ino_to_bytes(parent->fs, ino) >> + BLOCK_SHIFT(parent->fs); + ip->size = be64_to_cpu(ncore->di_size); + + if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) { + ip->mode = DT_DIR; + xfs_debug("Found a directory inode!"); + } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) { + ip->mode = DT_REG; + xfs_debug("Found a file inode!"); + xfs_debug("inode size %llu", ip->size); + } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) { + ip->mode = DT_LNK; + xfs_debug("Found a symbolic link inode!"); + } + + xfs_debug("entry inode's number %lu", ino); + + free(buf); + free(leaf); + + return ip; + +failed: + free(ip); + free(buf); + free(leaf); + + return ip; +} + +static xfs_fsblock_t +select_child(xfs_dfiloff_t off, + xfs_bmbt_key_t *kp, + xfs_bmbt_ptr_t *pp, + int nrecs) +{ + int i; + + for (i = 0; i < nrecs; i++) { + if (be64_to_cpu(kp[i].br_startoff) == off) + return be64_to_cpu(pp[i]); + if (be64_to_cpu(kp[i].br_startoff) > off) { + if (i == 0) + return be64_to_cpu(pp[i]); + else + return be64_to_cpu(pp[i-1]); + } + } + + return be64_to_cpu(pp[nrecs - 1]); +} + +block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core, + block_t fsblkno, int *error) +{ + uint32_t idx; + xfs_bmbt_irec_t irec; + block_t bno; + block_t nextbno; + xfs_bmdr_block_t *rblock; + int fsize; + int nextents; + xfs_bmbt_ptr_t *pp; + xfs_bmbt_key_t *kp; + xfs_btree_block_t *blk; + xfs_bmbt_rec_t *xp; + + *error = 0; + if (core->di_format == XFS_DINODE_FMT_EXTENTS) { + xfs_debug("XFS_DINODE_FMT_EXTENTS"); + for (idx = 0; idx < be32_to_cpu(core->di_nextents); idx++) { + bmbt_irec_get(&irec, + ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + idx); + if (fsblkno >= irec.br_startoff && + fsblkno < irec.br_startoff + irec.br_blockcount) + break; + } + } else if (core->di_format == XFS_DINODE_FMT_BTREE) { + xfs_debug("XFS_DINODE_FMT_BTREE"); + bno = NULLFSBLOCK; + rblock = (xfs_bmdr_block_t *)&core->di_literal_area[0]; + fsize = XFS_DFORK_SIZE(core, fs, XFS_DATA_FORK); + pp = XFS_BMDR_PTR_ADDR(rblock, 1, xfs_bmdr_maxrecs(fsize, 0)); + kp = XFS_BMDR_KEY_ADDR(rblock, 1); + bno = fsblock_to_bytes(fs, + select_child(fsblkno, kp, pp, + be16_to_cpu(rblock->bb_numrecs))) >> BLOCK_SHIFT(fs); + + /* Find the leaf */ + for (;;) { + blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); + if (be16_to_cpu(blk->bb_level) == 0) + break; + pp = XFS_BMBT_PTR_ADDR(fs, blk, 1, + xfs_bmdr_maxrecs(XFS_INFO(fs)->blocksize, 0)); + kp = XFS_BMBT_KEY_ADDR(fs, blk, 1); + bno = fsblock_to_bytes(fs, + select_child(fsblkno, kp, pp, + be16_to_cpu(blk->bb_numrecs))) >> BLOCK_SHIFT(fs); + } + + /* Find the records among leaves */ + for (;;) { + nextbno = be64_to_cpu(blk->bb_u.l.bb_rightsib); + nextents = be16_to_cpu(blk->bb_numrecs); + xp = (xfs_bmbt_rec_t *)XFS_BMBT_REC_ADDR(fs, blk, 1); + for (idx = 0; idx < nextents; idx++) { + bmbt_irec_get(&irec, xp + idx); + if (fsblkno >= irec.br_startoff && + fsblkno < irec.br_startoff + irec.br_blockcount) { + nextbno = NULLFSBLOCK; + break; + } + } + if (nextbno == NULLFSBLOCK) + break; + bno = fsblock_to_bytes(fs, nextbno) >> BLOCK_SHIFT(fs); + blk = (xfs_btree_block_t *)get_cache(fs->fs_dev, bno); + } + } + + if (fsblkno < irec.br_startoff || + fsblkno >= irec.br_startoff + irec.br_blockcount) + *error = 1; + + return fsblock_to_bytes(fs, + fsblkno - irec.br_startoff + irec.br_startblock) >> + BLOCK_SHIFT(fs); +} + +struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent, + xfs_dinode_t *core) +{ + block_t fsblkno; + xfs_da_intnode_t *node = NULL; + uint32_t hashwant; + uint32_t hash = 0; + xfs_da_node_entry_t *btree; + uint16_t max; + uint16_t span; + uint16_t probe; + int error; + xfs_dir2_data_hdr_t *data_hdr; + xfs_dir2_leaf_t *leaf; + xfs_dir2_leaf_entry_t *lep; + xfs_dir2_data_entry_t *dep; + struct inode *ip; + uint8_t *start_name; + uint8_t *end_name; + char *name; + int low; + int high; + int mid = 0; + uint32_t newdb, curdb = -1; + xfs_intino_t ino; + xfs_dinode_t *ncore; + uint8_t *buf = NULL; + + hashwant = xfs_dir2_da_hashname((uint8_t *)dname, strlen(dname)); + + fsblkno = xfs_dir2_get_right_blk(parent->fs, core, + xfs_dir2_byte_to_db(parent->fs, XFS_DIR2_LEAF_OFFSET), + &error); + if (error) { + xfs_error("Cannot find right rec!"); + return NULL; + } + + node = (xfs_da_intnode_t *)xfs_dir2_get_dirblks(parent->fs, fsblkno, 1); + if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) { + xfs_error("Node's magic number does not match!"); + goto out; + } + + do { + if (!node->hdr.count) + goto out; + + /* Given a hash to lookup, you read the node's btree array and first + * "hashval" in the array that exceeds the given hash and it can then + * be found in the block pointed by the "before" value. + */ + max = be16_to_cpu(node->hdr.count); + + probe = span = max/2; + for (btree = &node->btree[probe]; + span > 4; btree = &node->btree[probe]) { + span /= 2; + hash = be32_to_cpu(btree->hashval); + + if (hash < hashwant) + probe += span; + else if (hash > hashwant) + probe -= span; + else + break; + } + + while ((probe > 0) && (be32_to_cpu(btree->hashval) >= hashwant)) { + btree--; + probe--; + } + + while ((probe < max) && (be32_to_cpu(btree->hashval) < hashwant)) { + btree++; + probe++; + } + + if (probe == max) + fsblkno = be32_to_cpu(node->btree[max-1].before); + else + fsblkno = be32_to_cpu(node->btree[probe].before); + + fsblkno = xfs_dir2_get_right_blk(parent->fs, core, fsblkno, &error); + if (error) { + xfs_error("Cannot find right rec!"); + goto out; + } + + free(node); + node = (xfs_da_intnode_t *)xfs_dir2_get_dirblks(parent->fs, + fsblkno, 1); + } while(be16_to_cpu(node->hdr.info.magic) == XFS_DA_NODE_MAGIC); + + leaf = (xfs_dir2_leaf_t*)node; + if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAFN_MAGIC) { + xfs_error("Leaf's magic number does not match!"); + goto out; + } + + if (!leaf->hdr.count) + goto out; + + for (lep = leaf->ents, low = 0, high = be16_to_cpu(leaf->hdr.count) - 1; + low <= high; ) { + mid = (low + high) >> 1; + + if ((hash = be32_to_cpu(lep[mid].hashval)) == hashwant) + break; + if (hash < hashwant) + low = mid + 1; + else + high = mid - 1; + } + + /* If hash is not the one we want, then the directory does not contain the + * entry we're looking for and there is nothing to do anymore. + */ + if (hash != hashwant) + goto out; + + while (mid > 0 && be32_to_cpu(lep[mid - 1].hashval) == hashwant) + mid--; + + for (lep = &leaf->ents[mid]; + mid < be16_to_cpu(leaf->hdr.count) && + be32_to_cpu(lep->hashval) == hashwant; + lep++, mid++) { + /* Skip over stale leaf entries. */ + if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR) + continue; + + newdb = xfs_dir2_dataptr_to_db(parent->fs, be32_to_cpu(lep->address)); + if (newdb != curdb) { + if (buf) + free(buf); + + fsblkno = xfs_dir2_get_right_blk(parent->fs, core, newdb, &error); + if (error) { + xfs_error("Cannot find data block!"); + goto out; + } + + buf = xfs_dir2_get_dirblks(parent->fs, fsblkno, 1); + data_hdr = (xfs_dir2_data_hdr_t *)buf; + if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) { + xfs_error("Leaf directory's data magic No. does not match!"); + goto out1; + } + + curdb = newdb; + } + + dep = (xfs_dir2_data_entry_t *)((char *)buf + + xfs_dir2_dataptr_to_off(parent->fs, be32_to_cpu(lep->address))); + + start_name = &dep->name[0]; + end_name = start_name + dep->namelen; + name = xfs_dir2_get_entry_name(start_name, end_name); + if (!strncmp(name, dname, strlen(dname))) { + free(name); + goto found; + } + + free(name); + } + +out1: + free(buf); + +out: + free(node); + + return NULL; + +found: + ip = xfs_new_inode(parent->fs); + ino = be64_to_cpu(dep->inumber); + ncore = xfs_dinode_get_core(parent->fs, ino); + if (!ncore) { + xfs_error("Failed to get dinode!"); + goto failed; + } + + fill_xfs_inode_pvt(parent->fs, ip, ino); + ip->ino = ino; + XFS_PVT(ip)->i_ino_blk = ino_to_bytes(parent->fs, ino) >> + BLOCK_SHIFT(parent->fs); + ip->size = be64_to_cpu(ncore->di_size); + + if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFDIR) { + ip->mode = DT_DIR; + xfs_debug("Found a directory inode!"); + } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFREG) { + ip->mode = DT_REG; + xfs_debug("Found a file inode!"); + xfs_debug("inode size %llu", ip->size); + } else if ((be16_to_cpu(ncore->di_mode) & S_IFMT) == S_IFLNK) { + ip->mode = DT_LNK; + xfs_debug("Found a symbolic link inode!"); + } + + xfs_debug("entry inode's number %lu", ino); + + free(buf); + free(node); + + return ip; + +failed: + free(ip); + free(buf); + free(node); + + return NULL; +} diff --git a/core/fs/xfs/xfs_dir2.h b/core/fs/xfs/xfs_dir2.h new file mode 100644 index 00000000..e1b96227 --- /dev/null +++ b/core/fs/xfs/xfs_dir2.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XFS_DIR2_H_ +#define XFS_DIR2_H_ + +#include <core.h> +#include <fs.h> + +#include "xfs.h" + +char *xfs_dir2_get_entry_name(uint8_t *start, uint8_t *end); +void *xfs_dir2_get_dirblks(struct fs_info *fs, block_t startblock, + xfs_filblks_t c); +uint32_t xfs_dir2_da_hashname(const uint8_t *name, int namelen); +block_t xfs_dir2_get_right_blk(struct fs_info *fs, xfs_dinode_t *core, + block_t fsblkno, int *error); + +struct inode *xfs_dir2_local_find_entry(const char *dname, struct inode *parent, + xfs_dinode_t *core); +struct inode *xfs_dir2_block_find_entry(const char *dname, struct inode *parent, + xfs_dinode_t *core); +struct inode *xfs_dir2_leaf_find_entry(const char *dname, struct inode *parent, + xfs_dinode_t *core); +struct inode *xfs_dir2_node_find_entry(const char *dname, struct inode *parent, + xfs_dinode_t *core); + +static inline bool xfs_dir2_isleaf(struct fs_info *fs, xfs_dinode_t *dip) +{ + uint64_t last = 0; + xfs_bmbt_irec_t irec; + + bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&dip->di_literal_area[0]) + + be32_to_cpu(dip->di_nextents) - 1); + last = irec.br_startoff + irec.br_blockcount; + + return (last == XFS_INFO(fs)->dirleafblk + (1 << XFS_INFO(fs)->dirblklog)); +} + +#endif /* XFS_DIR2_H_ */ diff --git a/core/fs/xfs/xfs_fs.h b/core/fs/xfs/xfs_fs.h new file mode 100644 index 00000000..587820ec --- /dev/null +++ b/core/fs/xfs/xfs_fs.h @@ -0,0 +1,501 @@ +/* + * Taken from Linux kernel tree (linux/fs/xfs) + * + * Copyright (c) 1995-2005 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XFS_FS_H_ +#define XFS_FS_H_ + +/* + * SGI's XFS filesystem's major stuff (constants, structures) + */ + +/* + * Direct I/O attribute record used with XFS_IOC_DIOINFO + * d_miniosz is the min xfer size, xfer size multiple and file seek offset + * alignment. + */ +struct dioattr { + uint32_t d_mem; /* data buffer memory alignment */ + uint32_t d_miniosz; /* min xfer size */ + uint32_t d_maxiosz; /* max xfer size */ +}; + +/* + * Structure for XFS_IOC_FSGETXATTR[A] and XFS_IOC_FSSETXATTR. + */ +struct fsxattr { + uint32_t fsx_xflags; /* xflags field value (get/set) */ + uint32_t fsx_extsize; /* extsize field value (get/set)*/ + uint32_t fsx_nextents; /* nextents field value (get) */ + uint32_t fsx_projid; /* project identifier (get/set) */ + unsigned char fsx_pad[12]; +}; + +/* + * Flags for the bs_xflags/fsx_xflags field + * There should be a one-to-one correspondence between these flags and the + * XFS_DIFLAG_s. + */ +#define XFS_XFLAG_REALTIME 0x00000001 /* data in realtime volume */ +#define XFS_XFLAG_PREALLOC 0x00000002 /* preallocated file extents */ +#define XFS_XFLAG_IMMUTABLE 0x00000008 /* file cannot be modified */ +#define XFS_XFLAG_APPEND 0x00000010 /* all writes append */ +#define XFS_XFLAG_SYNC 0x00000020 /* all writes synchronous */ +#define XFS_XFLAG_NOATIME 0x00000040 /* do not update access time */ +#define XFS_XFLAG_NODUMP 0x00000080 /* do not include in backups */ +#define XFS_XFLAG_RTINHERIT 0x00000100 /* create with rt bit set */ +#define XFS_XFLAG_PROJINHERIT 0x00000200 /* create with parents projid */ +#define XFS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */ +#define XFS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */ +#define XFS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */ +#define XFS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */ +#define XFS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */ +#define XFS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */ + +/* + * Structure for XFS_IOC_GETBMAP. + * On input, fill in bmv_offset and bmv_length of the first structure + * to indicate the area of interest in the file, and bmv_entries with + * the number of array elements given back. The first structure is + * updated on return to give the offset and length for the next call. + */ +struct getbmap { + int64_t bmv_offset; /* file offset of segment in blocks */ + int64_t bmv_block; /* starting block (64-bit daddr_t) */ + int64_t bmv_length; /* length of segment, blocks */ + int32_t bmv_count; /* # of entries in array incl. 1st */ + int32_t bmv_entries; /* # of entries filled in (output) */ +}; + +/* + * Structure for XFS_IOC_GETBMAPX. Fields bmv_offset through bmv_entries + * are used exactly as in the getbmap structure. The getbmapx structure + * has additional bmv_iflags and bmv_oflags fields. The bmv_iflags field + * is only used for the first structure. It contains input flags + * specifying XFS_IOC_GETBMAPX actions. The bmv_oflags field is filled + * in by the XFS_IOC_GETBMAPX command for each returned structure after + * the first. + */ +struct getbmapx { + int64_t bmv_offset; /* file offset of segment in blocks */ + int64_t bmv_block; /* starting block (64-bit daddr_t) */ + int64_t bmv_length; /* length of segment, blocks */ + int32_t bmv_count; /* # of entries in array incl. 1st */ + int32_t bmv_entries; /* # of entries filled in (output). */ + int32_t bmv_iflags; /* input flags (1st structure) */ + int32_t bmv_oflags; /* output flags (after 1st structure)*/ + int32_t bmv_unused1; /* future use */ + int32_t bmv_unused2; /* future use */ +}; + +/* bmv_iflags values - set by XFS_IOC_GETBMAPX caller. */ +#define BMV_IF_ATTRFORK 0x1 /* return attr fork rather than data */ +#define BMV_IF_NO_DMAPI_READ 0x2 /* Do not generate DMAPI read event */ +#define BMV_IF_PREALLOC 0x4 /* rtn status BMV_OF_PREALLOC if req */ +#define BMV_IF_DELALLOC 0x8 /* rtn status BMV_OF_DELALLOC if req */ +#define BMV_IF_NO_HOLES 0x10 /* Do not return holes */ +#define BMV_IF_VALID \ + (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC| \ + BMV_IF_DELALLOC|BMV_IF_NO_HOLES) + +/* bmv_oflags values - returned for each non-header segment */ +#define BMV_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */ +#define BMV_OF_DELALLOC 0x2 /* segment = delayed allocation */ +#define BMV_OF_LAST 0x4 /* segment is the last in the file */ + +/* + * Structure for XFS_IOC_FSSETDM. + * For use by backup and restore programs to set the XFS on-disk inode + * fields di_dmevmask and di_dmstate. These must be set to exactly and + * only values previously obtained via xfs_bulkstat! (Specifically the + * xfs_bstat_t fields bs_dmevmask and bs_dmstate.) + */ +struct fsdmidata { + uint32_t fsd_dmevmask; /* corresponds to di_dmevmask */ + __u16 fsd_padding; + __u16 fsd_dmstate; /* corresponds to di_dmstate */ +}; + +/* + * File segment locking set data type for 64 bit access. + * Also used for all the RESV/FREE interfaces. + */ +typedef struct xfs_flock64 { + __s16 l_type; + __s16 l_whence; + int64_t l_start; + int64_t l_len; /* len == 0 means until end of file */ + int32_t l_sysid; + uint32_t l_pid; + int32_t l_pad[4]; /* reserve area */ +} xfs_flock64_t; + +/* + * Output for XFS_IOC_FSGEOMETRY_V1 + */ +typedef struct xfs_fsop_geom_v1 { + uint32_t blocksize; /* filesystem (data) block size */ + uint32_t rtextsize; /* realtime extent size */ + uint32_t agblocks; /* fsblocks in an AG */ + uint32_t agcount; /* number of allocation groups */ + uint32_t logblocks; /* fsblocks in the log */ + uint32_t sectsize; /* (data) sector size, bytes */ + uint32_t inodesize; /* inode size in bytes */ + uint32_t imaxpct; /* max allowed inode space(%) */ + uint64_t datablocks; /* fsblocks in data subvolume */ + uint64_t rtblocks; /* fsblocks in realtime subvol */ + uint64_t rtextents; /* rt extents in realtime subvol*/ + uint64_t logstart; /* starting fsblock of the log */ + unsigned char uuid[16]; /* unique id of the filesystem */ + uint32_t sunit; /* stripe unit, fsblocks */ + uint32_t swidth; /* stripe width, fsblocks */ + int32_t version; /* structure version */ + uint32_t flags; /* superblock version flags */ + uint32_t logsectsize; /* log sector size, bytes */ + uint32_t rtsectsize; /* realtime sector size, bytes */ + uint32_t dirblocksize; /* directory block size, bytes */ +} xfs_fsop_geom_v1_t; + +/* + * Output for XFS_IOC_FSGEOMETRY + */ +typedef struct xfs_fsop_geom { + uint32_t blocksize; /* filesystem (data) block size */ + uint32_t rtextsize; /* realtime extent size */ + uint32_t agblocks; /* fsblocks in an AG */ + uint32_t agcount; /* number of allocation groups */ + uint32_t logblocks; /* fsblocks in the log */ + uint32_t sectsize; /* (data) sector size, bytes */ + uint32_t inodesize; /* inode size in bytes */ + uint32_t imaxpct; /* max allowed inode space(%) */ + uint64_t datablocks; /* fsblocks in data subvolume */ + uint64_t rtblocks; /* fsblocks in realtime subvol */ + uint64_t rtextents; /* rt extents in realtime subvol*/ + uint64_t logstart; /* starting fsblock of the log */ + unsigned char uuid[16]; /* unique id of the filesystem */ + uint32_t sunit; /* stripe unit, fsblocks */ + uint32_t swidth; /* stripe width, fsblocks */ + int32_t version; /* structure version */ + uint32_t flags; /* superblock version flags */ + uint32_t logsectsize; /* log sector size, bytes */ + uint32_t rtsectsize; /* realtime sector size, bytes */ + uint32_t dirblocksize; /* directory block size, bytes */ + uint32_t logsunit; /* log stripe unit, bytes */ +} xfs_fsop_geom_t; + +/* Output for XFS_FS_COUNTS */ +typedef struct xfs_fsop_counts { + uint64_t freedata; /* free data section blocks */ + uint64_t freertx; /* free rt extents */ + uint64_t freeino; /* free inodes */ + uint64_t allocino; /* total allocated inodes */ +} xfs_fsop_counts_t; + +/* Input/Output for XFS_GET_RESBLKS and XFS_SET_RESBLKS */ +typedef struct xfs_fsop_resblks { + uint64_t resblks; + uint64_t resblks_avail; +} xfs_fsop_resblks_t; + +#define XFS_FSOP_GEOM_VERSION 0 + +#define XFS_FSOP_GEOM_FLAGS_ATTR 0x0001 /* attributes in use */ +#define XFS_FSOP_GEOM_FLAGS_NLINK 0x0002 /* 32-bit nlink values */ +#define XFS_FSOP_GEOM_FLAGS_QUOTA 0x0004 /* quotas enabled */ +#define XFS_FSOP_GEOM_FLAGS_IALIGN 0x0008 /* inode alignment */ +#define XFS_FSOP_GEOM_FLAGS_DALIGN 0x0010 /* large data alignment */ +#define XFS_FSOP_GEOM_FLAGS_SHARED 0x0020 /* read-only shared */ +#define XFS_FSOP_GEOM_FLAGS_EXTFLG 0x0040 /* special extent flag */ +#define XFS_FSOP_GEOM_FLAGS_DIRV2 0x0080 /* directory version 2 */ +#define XFS_FSOP_GEOM_FLAGS_LOGV2 0x0100 /* log format version 2 */ +#define XFS_FSOP_GEOM_FLAGS_SECTOR 0x0200 /* sector sizes >1BB */ +#define XFS_FSOP_GEOM_FLAGS_ATTR2 0x0400 /* inline attributes rework */ +#define XFS_FSOP_GEOM_FLAGS_DIRV2CI 0x1000 /* ASCII only CI names */ +#define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x4000 /* lazy superblock counters */ + + +/* + * Minimum and maximum sizes need for growth checks + */ +#define XFS_MIN_AG_BLOCKS 64 +#define XFS_MIN_LOG_BLOCKS 512ULL +#define XFS_MAX_LOG_BLOCKS (1024 * 1024ULL) +#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024ULL) + +/* keep the maximum size under 2^31 by a small amount */ +#define XFS_MAX_LOG_BYTES \ + ((2 * 1024 * 1024 * 1024ULL) - XFS_MIN_LOG_BYTES) + +/* Used for sanity checks on superblock */ +#define XFS_MAX_DBLOCKS(s) ((xfs_drfsbno_t)(s)->sb_agcount * (s)->sb_agblocks) +#define XFS_MIN_DBLOCKS(s) ((xfs_drfsbno_t)((s)->sb_agcount - 1) * \ + (s)->sb_agblocks + XFS_MIN_AG_BLOCKS) + +/* + * Structures for XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG & XFS_IOC_FSGROWFSRT + */ +typedef struct xfs_growfs_data { + uint64_t newblocks; /* new data subvol size, fsblocks */ + uint32_t imaxpct; /* new inode space percentage limit */ +} xfs_growfs_data_t; + +typedef struct xfs_growfs_log { + uint32_t newblocks; /* new log size, fsblocks */ + uint32_t isint; /* 1 if new log is internal */ +} xfs_growfs_log_t; + +typedef struct xfs_growfs_rt { + uint64_t newblocks; /* new realtime size, fsblocks */ + uint32_t extsize; /* new realtime extent size, fsblocks */ +} xfs_growfs_rt_t; + + +/* + * Structures returned from ioctl XFS_IOC_FSBULKSTAT & XFS_IOC_FSBULKSTAT_SINGLE + */ +typedef struct xfs_bstime { + time_t tv_sec; /* seconds */ + int32_t tv_nsec; /* and nanoseconds */ +} xfs_bstime_t; + +typedef struct xfs_bstat { + uint64_t bs_ino; /* inode number */ + __u16 bs_mode; /* type and mode */ + __u16 bs_nlink; /* number of links */ + uint32_t bs_uid; /* user id */ + uint32_t bs_gid; /* group id */ + uint32_t bs_rdev; /* device value */ + int32_t bs_blksize; /* block size */ + int64_t bs_size; /* file size */ + xfs_bstime_t bs_atime; /* access time */ + xfs_bstime_t bs_mtime; /* modify time */ + xfs_bstime_t bs_ctime; /* inode change time */ + int64_t bs_blocks; /* number of blocks */ + uint32_t bs_xflags; /* extended flags */ + int32_t bs_extsize; /* extent size */ + int32_t bs_extents; /* number of extents */ + uint32_t bs_gen; /* generation count */ + __u16 bs_projid_lo; /* lower part of project id */ +#define bs_projid bs_projid_lo /* (previously just bs_projid) */ + __u16 bs_forkoff; /* inode fork offset in bytes */ + __u16 bs_projid_hi; /* higher part of project id */ + unsigned char bs_pad[10]; /* pad space, unused */ + uint32_t bs_dmevmask; /* DMIG event mask */ + __u16 bs_dmstate; /* DMIG state info */ + __u16 bs_aextents; /* attribute number of extents */ +} xfs_bstat_t; + +/* + * The user-level BulkStat Request interface structure. + */ +typedef struct xfs_fsop_bulkreq { + uint64_t __user *lastip; /* last inode # pointer */ + int32_t icount; /* count of entries in buffer */ + void __user *ubuffer;/* user buffer for inode desc. */ + int32_t __user *ocount; /* output count pointer */ +} xfs_fsop_bulkreq_t; + + +/* + * Structures returned from xfs_inumbers routine (XFS_IOC_FSINUMBERS). + */ +typedef struct xfs_inogrp { + uint64_t xi_startino; /* starting inode number */ + int32_t xi_alloccount; /* # bits set in allocmask */ + uint64_t xi_allocmask; /* mask of allocated inodes */ +} xfs_inogrp_t; + + +/* + * Error injection. + */ +typedef struct xfs_error_injection { + int32_t fd; + int32_t errtag; +} xfs_error_injection_t; + + +/* + * The user-level Handle Request interface structure. + */ +typedef struct xfs_fsop_handlereq { + uint32_t fd; /* fd for FD_TO_HANDLE */ + void __user *path; /* user pathname */ + uint32_t oflags; /* open flags */ + void __user *ihandle;/* user supplied handle */ + uint32_t ihandlen; /* user supplied length */ + void __user *ohandle;/* user buffer for handle */ + uint32_t __user *ohandlen;/* user buffer length */ +} xfs_fsop_handlereq_t; + +/* + * Compound structures for passing args through Handle Request interfaces + * xfs_fssetdm_by_handle, xfs_attrlist_by_handle, xfs_attrmulti_by_handle + * - ioctls: XFS_IOC_FSSETDM_BY_HANDLE, XFS_IOC_ATTRLIST_BY_HANDLE, and + * XFS_IOC_ATTRMULTI_BY_HANDLE + */ + +typedef struct xfs_fsop_setdm_handlereq { + struct xfs_fsop_handlereq hreq; /* handle information */ + struct fsdmidata __user *data; /* DMAPI data */ +} xfs_fsop_setdm_handlereq_t; + +typedef struct xfs_attrlist_cursor { + uint32_t opaque[4]; +} xfs_attrlist_cursor_t; + +typedef struct xfs_fsop_attrlist_handlereq { + struct xfs_fsop_handlereq hreq; /* handle interface structure */ + struct xfs_attrlist_cursor pos; /* opaque cookie, list offset */ + uint32_t flags; /* which namespace to use */ + uint32_t buflen; /* length of buffer supplied */ + void __user *buffer; /* returned names */ +} xfs_fsop_attrlist_handlereq_t; + +typedef struct xfs_attr_multiop { + uint32_t am_opcode; +#define ATTR_OP_GET 1 /* return the indicated attr's value */ +#define ATTR_OP_SET 2 /* set/create the indicated attr/value pair */ +#define ATTR_OP_REMOVE 3 /* remove the indicated attr */ + int32_t am_error; + void __user *am_attrname; + void __user *am_attrvalue; + uint32_t am_length; + uint32_t am_flags; +} xfs_attr_multiop_t; + +typedef struct xfs_fsop_attrmulti_handlereq { + struct xfs_fsop_handlereq hreq; /* handle interface structure */ + uint32_t opcount;/* count of following multiop */ + struct xfs_attr_multiop __user *ops; /* attr_multi data */ +} xfs_fsop_attrmulti_handlereq_t; + +/* + * per machine unique filesystem identifier types. + */ +typedef struct { uint32_t val[2]; } xfs_fsid_t; /* file system id type */ + +typedef struct xfs_fid { + __u16 fid_len; /* length of remainder */ + __u16 fid_pad; + uint32_t fid_gen; /* generation number */ + uint64_t fid_ino; /* 64 bits inode number */ +} xfs_fid_t; + +typedef struct xfs_handle { + union { + int64_t align; /* force alignment of ha_fid */ + xfs_fsid_t _ha_fsid; /* unique file system identifier */ + } ha_u; + xfs_fid_t ha_fid; /* file system specific file ID */ +} xfs_handle_t; +#define ha_fsid ha_u._ha_fsid + +#define XFS_HSIZE(handle) (((char *) &(handle).ha_fid.fid_pad \ + - (char *) &(handle)) \ + + (handle).ha_fid.fid_len) + +/* + * Flags for going down operation + */ +#define XFS_FSOP_GOING_FLAGS_DEFAULT 0x0 /* going down */ +#define XFS_FSOP_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */ +#define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ + +/* + * ioctl commands that are used by Linux filesystems + */ +#define XFS_IOC_GETXFLAGS FS_IOC_GETFLAGS +#define XFS_IOC_SETXFLAGS FS_IOC_SETFLAGS +#define XFS_IOC_GETVERSION FS_IOC_GETVERSION + +/* + * ioctl commands that replace IRIX fcntl()'s + * For 'documentation' purposed more than anything else, + * the "cmd #" field reflects the IRIX fcntl number. + */ +#define XFS_IOC_ALLOCSP _IOW ('X', 10, struct xfs_flock64) +#define XFS_IOC_FREESP _IOW ('X', 11, struct xfs_flock64) +#define XFS_IOC_DIOINFO _IOR ('X', 30, struct dioattr) +#define XFS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr) +#define XFS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr) +#define XFS_IOC_ALLOCSP64 _IOW ('X', 36, struct xfs_flock64) +#define XFS_IOC_FREESP64 _IOW ('X', 37, struct xfs_flock64) +#define XFS_IOC_GETBMAP _IOWR('X', 38, struct getbmap) +#define XFS_IOC_FSSETDM _IOW ('X', 39, struct fsdmidata) +#define XFS_IOC_RESVSP _IOW ('X', 40, struct xfs_flock64) +#define XFS_IOC_UNRESVSP _IOW ('X', 41, struct xfs_flock64) +#define XFS_IOC_RESVSP64 _IOW ('X', 42, struct xfs_flock64) +#define XFS_IOC_UNRESVSP64 _IOW ('X', 43, struct xfs_flock64) +#define XFS_IOC_GETBMAPA _IOWR('X', 44, struct getbmap) +#define XFS_IOC_FSGETXATTRA _IOR ('X', 45, struct fsxattr) +/* XFS_IOC_SETBIOSIZE ---- deprecated 46 */ +/* XFS_IOC_GETBIOSIZE ---- deprecated 47 */ +#define XFS_IOC_GETBMAPX _IOWR('X', 56, struct getbmap) +#define XFS_IOC_ZERO_RANGE _IOW ('X', 57, struct xfs_flock64) + +/* + * ioctl commands that replace IRIX syssgi()'s + */ +#define XFS_IOC_FSGEOMETRY_V1 _IOR ('X', 100, struct xfs_fsop_geom_v1) +#define XFS_IOC_FSBULKSTAT _IOWR('X', 101, struct xfs_fsop_bulkreq) +#define XFS_IOC_FSBULKSTAT_SINGLE _IOWR('X', 102, struct xfs_fsop_bulkreq) +#define XFS_IOC_FSINUMBERS _IOWR('X', 103, struct xfs_fsop_bulkreq) +#define XFS_IOC_PATH_TO_FSHANDLE _IOWR('X', 104, struct xfs_fsop_handlereq) +#define XFS_IOC_PATH_TO_HANDLE _IOWR('X', 105, struct xfs_fsop_handlereq) +#define XFS_IOC_FD_TO_HANDLE _IOWR('X', 106, struct xfs_fsop_handlereq) +#define XFS_IOC_OPEN_BY_HANDLE _IOWR('X', 107, struct xfs_fsop_handlereq) +#define XFS_IOC_READLINK_BY_HANDLE _IOWR('X', 108, struct xfs_fsop_handlereq) +#define XFS_IOC_SWAPEXT _IOWR('X', 109, struct xfs_swapext) +#define XFS_IOC_FSGROWFSDATA _IOW ('X', 110, struct xfs_growfs_data) +#define XFS_IOC_FSGROWFSLOG _IOW ('X', 111, struct xfs_growfs_log) +#define XFS_IOC_FSGROWFSRT _IOW ('X', 112, struct xfs_growfs_rt) +#define XFS_IOC_FSCOUNTS _IOR ('X', 113, struct xfs_fsop_counts) +#define XFS_IOC_SET_RESBLKS _IOWR('X', 114, struct xfs_fsop_resblks) +#define XFS_IOC_GET_RESBLKS _IOR ('X', 115, struct xfs_fsop_resblks) +#define XFS_IOC_ERROR_INJECTION _IOW ('X', 116, struct xfs_error_injection) +#define XFS_IOC_ERROR_CLEARALL _IOW ('X', 117, struct xfs_error_injection) +/* XFS_IOC_ATTRCTL_BY_HANDLE -- deprecated 118 */ +/* XFS_IOC_FREEZE -- FIFREEZE 119 */ +/* XFS_IOC_THAW -- FITHAW 120 */ +#define XFS_IOC_FSSETDM_BY_HANDLE _IOW ('X', 121, struct xfs_fsop_setdm_handlereq) +#define XFS_IOC_ATTRLIST_BY_HANDLE _IOW ('X', 122, struct xfs_fsop_attrlist_handlereq) +#define XFS_IOC_ATTRMULTI_BY_HANDLE _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq) +#define XFS_IOC_FSGEOMETRY _IOR ('X', 124, struct xfs_fsop_geom) +#define XFS_IOC_GOINGDOWN _IOR ('X', 125, __uint32_t) +/* XFS_IOC_GETFSUUID ---------- deprecated 140 */ + + +#ifndef HAVE_BBMACROS +/* + * Block I/O parameterization. A basic block (BB) is the lowest size of + * filesystem allocation, and must equal 512. Length units given to bio + * routines are in BB's. + */ +#define BBSHIFT 9 +#define BBSIZE (1<<BBSHIFT) +#define BBMASK (BBSIZE-1) +#define BTOBB(bytes) (((uint64_t)(bytes) + BBSIZE - 1) >> BBSHIFT) +#define BTOBBT(bytes) ((uint64_t)(bytes) >> BBSHIFT) +#define BBTOB(bbs) ((bbs) << BBSHIFT) +#endif + +#endif /* XFS_FS_H_ */ diff --git a/core/fs/xfs/xfs_readdir.c b/core/fs/xfs/xfs_readdir.c new file mode 100644 index 00000000..0e013e55 --- /dev/null +++ b/core/fs/xfs/xfs_readdir.c @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cache.h> +#include <core.h> +#include <fs.h> + +#include "xfs_types.h" +#include "xfs_sb.h" +#include "xfs_ag.h" +#include "misc.h" +#include "xfs.h" +#include "xfs_dinode.h" +#include "xfs_dir2.h" + +#include "xfs_readdir.h" + +static int fill_dirent(struct fs_info *fs, struct dirent *dirent, + uint32_t offset, xfs_ino_t ino, char *name, + size_t namelen) +{ + xfs_dinode_t *core; + + dirent->d_ino = ino; + dirent->d_off = offset; + dirent->d_reclen = offsetof(struct dirent, d_name) + namelen + 1; + + core = xfs_dinode_get_core(fs, ino); + if (!core) { + xfs_error("Failed to get dinode from disk (ino 0x%llx)", ino); + return -1; + } + + if (be16_to_cpu(core->di_mode) & S_IFDIR) + dirent->d_type = DT_DIR; + else if (be16_to_cpu(core->di_mode) & S_IFREG) + dirent->d_type = DT_REG; + else if (be16_to_cpu(core->di_mode) & S_IFLNK) + dirent->d_type = DT_LNK; + + memcpy(dirent->d_name, name, namelen + 1); + + return 0; +} + +int xfs_readdir_dir2_local(struct file *file, struct dirent *dirent, + xfs_dinode_t *core) +{ + xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0]; + xfs_dir2_sf_entry_t *sf_entry; + uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count; + uint32_t offset = file->offset; + uint8_t *start_name; + uint8_t *end_name; + char *name; + xfs_ino_t ino; + struct fs_info *fs = file->fs; + int retval = 0; + + xfs_debug("count %hhu i8count %hhu", sf->hdr.count, sf->hdr.i8count); + + if (file->offset + 1 > count) + return -1; + + file->offset++; + + sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)&sf->list[0] - + (!sf->hdr.i8count ? 4 : 0)); + + if (file->offset - 1) { + offset = file->offset; + while (--offset) { + sf_entry = (xfs_dir2_sf_entry_t *)( + (uint8_t *)sf_entry + + offsetof(struct xfs_dir2_sf_entry, + name[0]) + + sf_entry->namelen + + (sf->hdr.i8count ? 8 : 4)); + } + } + + start_name = &sf_entry->name[0]; + end_name = start_name + sf_entry->namelen; + + name = xfs_dir2_get_entry_name(start_name, end_name); + + ino = xfs_dir2_sf_get_inumber(sf, (xfs_dir2_inou_t *)( + (uint8_t *)sf_entry + + offsetof(struct xfs_dir2_sf_entry, + name[0]) + + sf_entry->namelen)); + + retval = fill_dirent(fs, dirent, file->offset, ino, (char *)name, + end_name - start_name); + if (retval) + xfs_error("Failed to fill in dirent structure"); + + free(name); + + return retval; +} + +int xfs_readdir_dir2_block(struct file *file, struct dirent *dirent, + xfs_dinode_t *core) +{ + xfs_bmbt_irec_t r; + block_t dir_blk; + struct fs_info *fs = file->fs; + uint8_t *dirblk_buf; + uint8_t *p; + uint32_t offset; + xfs_dir2_data_hdr_t *hdr; + xfs_dir2_block_tail_t *btp; + xfs_dir2_data_unused_t *dup; + xfs_dir2_data_entry_t *dep; + uint8_t *start_name; + uint8_t *end_name; + char *name; + xfs_ino_t ino; + int retval = 0; + + bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]); + dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs); + + dirblk_buf = xfs_dir2_get_dirblks(fs, dir_blk, r.br_blockcount); + hdr = (xfs_dir2_data_hdr_t *)dirblk_buf; + if (be32_to_cpu(hdr->magic) != XFS_DIR2_BLOCK_MAGIC) { + xfs_error("Block directory header's magic number does not match!"); + xfs_debug("hdr->magic: 0x%lx", be32_to_cpu(hdr->magic)); + + free(dirblk_buf); + + return -1; + } + + btp = xfs_dir2_block_tail_p(XFS_INFO(fs), hdr); + + if (file->offset + 1 > be32_to_cpu(btp->count)) + return -1; + + file->offset++; + + p = (uint8_t *)(hdr + 1); + + if (file->offset - 1) { + offset = file->offset; + while (--offset) { + dep = (xfs_dir2_data_entry_t *)p; + + dup = (xfs_dir2_data_unused_t *)p; + if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { + p += be16_to_cpu(dup->length); + continue; + } + + p += xfs_dir2_data_entsize(dep->namelen); + } + } + + dep = (xfs_dir2_data_entry_t *)p; + + start_name = &dep->name[0]; + end_name = start_name + dep->namelen; + name = xfs_dir2_get_entry_name(start_name, end_name); + + ino = be64_to_cpu(dep->inumber); + + retval = fill_dirent(fs, dirent, file->offset, ino, name, + end_name - start_name); + if (retval) + xfs_error("Failed to fill in dirent structure"); + + free(dirblk_buf); + free(name); + + return retval; +} + +int xfs_readdir_dir2_leaf(struct file *file, struct dirent *dirent, + xfs_dinode_t *core) +{ + xfs_bmbt_irec_t irec; + struct fs_info *fs = file->fs; + xfs_dir2_leaf_t *leaf; + block_t leaf_blk, dir_blk; + xfs_dir2_leaf_entry_t *lep; + uint32_t db; + unsigned int offset; + xfs_dir2_data_entry_t *dep; + xfs_dir2_data_hdr_t *data_hdr; + uint8_t *start_name; + uint8_t *end_name; + char *name; + xfs_intino_t ino; + uint8_t *buf = NULL; + int retval = 0; + + bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) + + be32_to_cpu(core->di_nextents) - 1); + leaf_blk = fsblock_to_bytes(fs, irec.br_startblock) >> + BLOCK_SHIFT(file->fs); + + leaf = (xfs_dir2_leaf_t *)xfs_dir2_get_dirblks(fs, leaf_blk, + irec.br_blockcount); + if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) { + xfs_error("Single leaf block header's magic number does not match!"); + goto out; + } + + if (!leaf->hdr.count) + goto out; + + if (file->offset + 1 > be16_to_cpu(leaf->hdr.count)) + goto out; + + lep = &leaf->ents[file->offset++]; + + /* Skip over stale leaf entries */ + for ( ; be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR; + lep++, file->offset++); + + db = xfs_dir2_dataptr_to_db(fs, be32_to_cpu(lep->address)); + + bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] + db); + + dir_blk = fsblock_to_bytes(fs, irec.br_startblock) >> BLOCK_SHIFT(fs); + + buf = xfs_dir2_get_dirblks(fs, dir_blk, irec.br_blockcount); + data_hdr = (xfs_dir2_data_hdr_t *)buf; + if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) { + xfs_error("Leaf directory's data magic number does not match!"); + goto out1; + } + + offset = xfs_dir2_dataptr_to_off(fs, be32_to_cpu(lep->address)); + + dep = (xfs_dir2_data_entry_t *)((uint8_t *)buf + offset); + + start_name = &dep->name[0]; + end_name = start_name + dep->namelen; + name = xfs_dir2_get_entry_name(start_name, end_name); + + ino = be64_to_cpu(dep->inumber); + + retval = fill_dirent(fs, dirent, file->offset, ino, name, + end_name - start_name); + if (retval) + xfs_error("Failed to fill in dirent structure"); + + free(name); + free(buf); + free(leaf); + + return retval; + +out1: + free(buf); + +out: + free(leaf); + + return -1; +} + +int xfs_readdir_dir2_node(struct file *file, struct dirent *dirent, + xfs_dinode_t *core) +{ + struct fs_info *fs = file->fs; + xfs_bmbt_irec_t irec; + uint32_t node_off = 0; + block_t fsblkno; + xfs_da_intnode_t *node = NULL; + struct inode *inode = file->inode; + int error; + xfs_dir2_data_hdr_t *data_hdr; + xfs_dir2_leaf_t *leaf; + xfs_dir2_leaf_entry_t *lep; + unsigned int offset; + xfs_dir2_data_entry_t *dep; + uint8_t *start_name; + uint8_t *end_name; + char *name; + uint32_t db; + uint8_t *buf = NULL; + int retval = 0; + + do { + bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] + + ++node_off); + } while (irec.br_startoff < xfs_dir2_byte_to_db(fs, XFS_DIR2_LEAF_OFFSET)); + + fsblkno = fsblock_to_bytes(fs, irec.br_startblock) >> BLOCK_SHIFT(fs); + + node = (xfs_da_intnode_t *)xfs_dir2_get_dirblks(fs, fsblkno, 1); + if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) { + xfs_error("Node's magic number does not match!"); + goto out; + } + +try_next_btree: + if (!node->hdr.count || + XFS_PVT(inode)->i_btree_offset >= be16_to_cpu(node->hdr.count)) + goto out; + + fsblkno = be32_to_cpu(node->btree[XFS_PVT(inode)->i_btree_offset].before); + fsblkno = xfs_dir2_get_right_blk(fs, core, fsblkno, &error); + if (error) { + xfs_error("Cannot find leaf rec!"); + goto out; + } + + leaf = (xfs_dir2_leaf_t*)xfs_dir2_get_dirblks(fs, fsblkno, 1); + if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAFN_MAGIC) { + xfs_error("Leaf's magic number does not match!"); + goto out1; + } + + if (!leaf->hdr.count || + XFS_PVT(inode)->i_leaf_ent_offset >= be16_to_cpu(leaf->hdr.count)) { + XFS_PVT(inode)->i_btree_offset++; + XFS_PVT(inode)->i_leaf_ent_offset = 0; + free(leaf); + goto try_next_btree; + } + + lep = &leaf->ents[XFS_PVT(inode)->i_leaf_ent_offset]; + + /* Skip over stale leaf entries */ + for ( ; XFS_PVT(inode)->i_leaf_ent_offset < be16_to_cpu(leaf->hdr.count) && + be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR; + lep++, XFS_PVT(inode)->i_leaf_ent_offset++); + + if (XFS_PVT(inode)->i_leaf_ent_offset == be16_to_cpu(leaf->hdr.count)) { + XFS_PVT(inode)->i_btree_offset++; + XFS_PVT(inode)->i_leaf_ent_offset = 0; + free(leaf); + goto try_next_btree; + } else { + XFS_PVT(inode)->i_leaf_ent_offset++; + } + + db = xfs_dir2_dataptr_to_db(fs, be32_to_cpu(lep->address)); + + fsblkno = xfs_dir2_get_right_blk(fs, core, db, &error); + if (error) { + xfs_error("Cannot find data block!"); + goto out1; + } + + buf = xfs_dir2_get_dirblks(fs, fsblkno, 1); + data_hdr = (xfs_dir2_data_hdr_t *)buf; + if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) { + xfs_error("Leaf directory's data magic No. does not match!"); + goto out2; + } + + offset = xfs_dir2_dataptr_to_off(fs, be32_to_cpu(lep->address)); + + dep = (xfs_dir2_data_entry_t *)((uint8_t *)buf + offset); + + start_name = &dep->name[0]; + end_name = start_name + dep->namelen; + name = xfs_dir2_get_entry_name(start_name, end_name); + + retval = fill_dirent(fs, dirent, 0, be64_to_cpu(dep->inumber), name, + end_name - start_name); + if (retval) + xfs_error("Failed to fill in dirent structure"); + + free(name); + free(buf); + free(leaf); + free(node); + + return retval; + +out2: + free(buf); + +out1: + free(leaf); + +out: + free(node); + + XFS_PVT(inode)->i_btree_offset = 0; + XFS_PVT(inode)->i_leaf_ent_offset = 0; + + return -1; +} diff --git a/core/fs/xfs/xfs_readdir.h b/core/fs/xfs/xfs_readdir.h new file mode 100644 index 00000000..2e564ec8 --- /dev/null +++ b/core/fs/xfs/xfs_readdir.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XFS_READDIR_H_ +#define XFS_READDIR_H_ + +int xfs_readdir_dir2_local(struct file *file, struct dirent *dirent, + xfs_dinode_t *core); +int xfs_readdir_dir2_block(struct file *file, struct dirent *dirent, + xfs_dinode_t *core); +int xfs_readdir_dir2_leaf(struct file *file, struct dirent *dirent, + xfs_dinode_t *core); +int xfs_readdir_dir2_node(struct file *file, struct dirent *dirent, + xfs_dinode_t *core); + +#endif /* XFS_READDIR_H_ */ diff --git a/core/fs/xfs/xfs_sb.h b/core/fs/xfs/xfs_sb.h new file mode 100644 index 00000000..12024ab3 --- /dev/null +++ b/core/fs/xfs/xfs_sb.h @@ -0,0 +1,206 @@ +/* + * Taken from Linux kernel tree (linux/fs/xfs) + * + * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef XFS_SB_H_ +#define XFS_SB_H_ + +#include <stddef.h> + +#include <sys/types.h> + +typedef unsigned char uuid_t[16]; + +/* + * Super block + * Fits into a sector-sized buffer at address 0 of each allocation group. + * Only the first of these is ever updated except during growfs. + */ + +struct xfs_buf; +struct xfs_mount; + +#define XFS_SB_MAGIC "XFSB" /* 'XFSB' */ +#define XFS_SB_VERSION_1 1 /* 5.3, 6.0.1, 6.1 */ +#define XFS_SB_VERSION_2 2 /* 6.2 - attributes */ +#define XFS_SB_VERSION_3 3 /* 6.2 - new inode version */ +#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */ +#define XFS_SB_VERSION_NUMBITS 0x000f +#define XFS_SB_VERSION_ALLFBITS 0xfff0 +#define XFS_SB_VERSION_SASHFBITS 0xf000 +#define XFS_SB_VERSION_REALFBITS 0x0ff0 +#define XFS_SB_VERSION_ATTRBIT 0x0010 +#define XFS_SB_VERSION_NLINKBIT 0x0020 +#define XFS_SB_VERSION_QUOTABIT 0x0040 +#define XFS_SB_VERSION_ALIGNBIT 0x0080 +#define XFS_SB_VERSION_DALIGNBIT 0x0100 +#define XFS_SB_VERSION_SHAREDBIT 0x0200 +#define XFS_SB_VERSION_LOGV2BIT 0x0400 +#define XFS_SB_VERSION_SECTORBIT 0x0800 +#define XFS_SB_VERSION_EXTFLGBIT 0x1000 +#define XFS_SB_VERSION_DIRV2BIT 0x2000 +#define XFS_SB_VERSION_BORGBIT 0x4000 /* ASCII only case-insens. */ +#define XFS_SB_VERSION_MOREBITSBIT 0x8000 +#define XFS_SB_VERSION_OKSASHFBITS \ + (XFS_SB_VERSION_EXTFLGBIT | \ + XFS_SB_VERSION_DIRV2BIT | \ + XFS_SB_VERSION_BORGBIT) +#define XFS_SB_VERSION_OKREALFBITS \ + (XFS_SB_VERSION_ATTRBIT | \ + XFS_SB_VERSION_NLINKBIT | \ + XFS_SB_VERSION_QUOTABIT | \ + XFS_SB_VERSION_ALIGNBIT | \ + XFS_SB_VERSION_DALIGNBIT | \ + XFS_SB_VERSION_SHAREDBIT | \ + XFS_SB_VERSION_LOGV2BIT | \ + XFS_SB_VERSION_SECTORBIT | \ + XFS_SB_VERSION_MOREBITSBIT) +#define XFS_SB_VERSION_OKREALBITS \ + (XFS_SB_VERSION_NUMBITS | \ + XFS_SB_VERSION_OKREALFBITS | \ + XFS_SB_VERSION_OKSASHFBITS) + +/* + * There are two words to hold XFS "feature" bits: the original + * word, sb_versionnum, and sb_features2. Whenever a bit is set in + * sb_features2, the feature bit XFS_SB_VERSION_MOREBITSBIT must be set. + * + * These defines represent bits in sb_features2. + */ +#define XFS_SB_VERSION2_REALFBITS 0x00ffffff /* Mask: features */ +#define XFS_SB_VERSION2_RESERVED1BIT 0x00000001 +#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */ +#define XFS_SB_VERSION2_RESERVED4BIT 0x00000004 +#define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */ +#define XFS_SB_VERSION2_PARENTBIT 0x00000010 /* parent pointers */ +#define XFS_SB_VERSION2_PROJID32BIT 0x00000080 /* 32 bit project id */ + +#define XFS_SB_VERSION2_OKREALFBITS \ + (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \ + XFS_SB_VERSION2_ATTR2BIT | \ + XFS_SB_VERSION2_PROJID32BIT) +#define XFS_SB_VERSION2_OKSASHFBITS \ + (0) +#define XFS_SB_VERSION2_OKREALBITS \ + (XFS_SB_VERSION2_OKREALFBITS | \ + XFS_SB_VERSION2_OKSASHFBITS ) + +/* + * Sequence number values for the fields. + */ +typedef enum { + XFS_SBS_MAGICNUM, XFS_SBS_BLOCKSIZE, XFS_SBS_DBLOCKS, XFS_SBS_RBLOCKS, + XFS_SBS_REXTENTS, XFS_SBS_UUID, XFS_SBS_LOGSTART, XFS_SBS_ROOTINO, + XFS_SBS_RBMINO, XFS_SBS_RSUMINO, XFS_SBS_REXTSIZE, XFS_SBS_AGBLOCKS, + XFS_SBS_AGCOUNT, XFS_SBS_RBMBLOCKS, XFS_SBS_LOGBLOCKS, + XFS_SBS_VERSIONNUM, XFS_SBS_SECTSIZE, XFS_SBS_INODESIZE, + XFS_SBS_INOPBLOCK, XFS_SBS_FNAME, XFS_SBS_BLOCKLOG, + XFS_SBS_SECTLOG, XFS_SBS_INODELOG, XFS_SBS_INOPBLOG, XFS_SBS_AGBLKLOG, + XFS_SBS_REXTSLOG, XFS_SBS_INPROGRESS, XFS_SBS_IMAX_PCT, XFS_SBS_ICOUNT, + XFS_SBS_IFREE, XFS_SBS_FDBLOCKS, XFS_SBS_FREXTENTS, XFS_SBS_UQUOTINO, + XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN, + XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG, + XFS_SBS_LOGSECTLOG, XFS_SBS_LOGSECTSIZE, XFS_SBS_LOGSUNIT, + XFS_SBS_FEATURES2, XFS_SBS_BAD_FEATURES2, + XFS_SBS_FIELDCOUNT +} xfs_sb_field_t; + +/* + * Mask values, defined based on the xfs_sb_field_t values. + * Only define the ones we're using. + */ +#define XFS_SB_MVAL(x) (1LL << XFS_SBS_ ## x) +#define XFS_SB_UUID XFS_SB_MVAL(UUID) +#define XFS_SB_FNAME XFS_SB_MVAL(FNAME) +#define XFS_SB_ROOTINO XFS_SB_MVAL(ROOTINO) +#define XFS_SB_RBMINO XFS_SB_MVAL(RBMINO) +#define XFS_SB_RSUMINO XFS_SB_MVAL(RSUMINO) +#define XFS_SB_VERSIONNUM XFS_SB_MVAL(VERSIONNUM) +#define XFS_SB_UQUOTINO XFS_SB_MVAL(UQUOTINO) +#define XFS_SB_GQUOTINO XFS_SB_MVAL(GQUOTINO) +#define XFS_SB_QFLAGS XFS_SB_MVAL(QFLAGS) +#define XFS_SB_SHARED_VN XFS_SB_MVAL(SHARED_VN) +#define XFS_SB_UNIT XFS_SB_MVAL(UNIT) +#define XFS_SB_WIDTH XFS_SB_MVAL(WIDTH) +#define XFS_SB_ICOUNT XFS_SB_MVAL(ICOUNT) +#define XFS_SB_IFREE XFS_SB_MVAL(IFREE) +#define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS) +#define XFS_SB_FEATURES2 XFS_SB_MVAL(FEATURES2) +#define XFS_SB_BAD_FEATURES2 XFS_SB_MVAL(BAD_FEATURES2) +#define XFS_SB_NUM_BITS ((int)XFS_SBS_FIELDCOUNT) +#define XFS_SB_ALL_BITS ((1LL << XFS_SB_NUM_BITS) - 1) +#define XFS_SB_MOD_BITS \ + (XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \ + XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \ + XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \ + XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \ + XFS_SB_BAD_FEATURES2) + + +/* + * Misc. Flags - warning - these will be cleared by xfs_repair unless + * a feature bit is set when the flag is used. + */ +#define XFS_SBF_NOFLAGS 0x00 /* no flags set */ +#define XFS_SBF_READONLY 0x01 /* only read-only mounts allowed */ + +/* + * define max. shared version we can interoperate with + */ +#define XFS_SB_MAX_SHARED_VN 0 + +#define XFS_SB_VERSION_NUM(sbp) ((sbp)->sb_versionnum & XFS_SB_VERSION_NUMBITS) + +/* + * end of superblock version macros + */ + +#define XFS_SB_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_SB_DADDR) +#define XFS_BUF_TO_SBP(bp) ((xfs_dsb_t *)((bp)->b_addr)) + +#define XFS_HDR_BLOCK(mp,d) ((xfs_agblock_t)XFS_BB_TO_FSBT(mp,d)) +#define XFS_DADDR_TO_FSB(mp,d) XFS_AGB_TO_FSB(mp, \ + xfs_daddr_to_agno(mp,d), xfs_daddr_to_agbno(mp,d)) +#define XFS_FSB_TO_DADDR(mp,fsbno) XFS_AGB_TO_DADDR(mp, \ + XFS_FSB_TO_AGNO(mp,fsbno), XFS_FSB_TO_AGBNO(mp,fsbno)) + +/* + * File system sector to basic block conversions. + */ +#define XFS_FSS_TO_BB(mp,sec) ((sec) << (mp)->m_sectbb_log) + +/* + * File system block to basic block conversions. + */ +#define XFS_FSB_TO_BB(mp,fsbno) ((fsbno) << (mp)->m_blkbb_log) +#define XFS_BB_TO_FSB(mp,bb) \ + (((bb) + (XFS_FSB_TO_BB(mp,1) - 1)) >> (mp)->m_blkbb_log) +#define XFS_BB_TO_FSBT(mp,bb) ((bb) >> (mp)->m_blkbb_log) + +/* + * File system block to byte conversions. + */ +#define XFS_FSB_TO_B(mp,fsbno) ((xfs_fsize_t)(fsbno) << (mp)->m_sb.sb_blocklog) +#define XFS_B_TO_FSB(mp,b) \ + ((((uint64_t)(b)) + (mp)->m_blockmask) >> (mp)->m_sb.sb_blocklog) +#define XFS_B_TO_FSBT(mp,b) (((uint64_t)(b)) >> (mp)->m_sb.sb_blocklog) +#define XFS_B_FSB_OFFSET(mp,b) ((b) & (mp)->m_blockmask) + +#endif /* XFS_SB_H_ */ diff --git a/core/fs/xfs/xfs_types.h b/core/fs/xfs/xfs_types.h new file mode 100644 index 00000000..92808865 --- /dev/null +++ b/core/fs/xfs/xfs_types.h @@ -0,0 +1,135 @@ +/* + * Taken from Linux kernel tree (linux/fs/xfs) + * + * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * All Rights Reserved. + * + * Copyright (c) 2012 Paulo Alcantara <pcacjr@zytor.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef XFS_TYPES_H_ +#define XFS_TYPES_H_ + +#include <stddef.h> + +#include <sys/types.h> + +typedef enum { B_FALSE,B_TRUE } boolean_t; +typedef uint32_t prid_t; /* project ID */ +typedef uint32_t inst_t; /* an instruction */ + +typedef int64_t xfs_off_t; /* <file offset> type */ +typedef unsigned long long xfs_ino_t; /* <inode> type */ +typedef int64_t xfs_daddr_t; /* <disk address> type */ +typedef char * xfs_caddr_t; /* <core address> type */ +typedef uint32_t xfs_dev_t; +typedef uint32_t xfs_nlink_t; + +/* __psint_t is the same size as a pointer */ +typedef int32_t __psint_t; +typedef uint32_t __psunsigned_t; + +typedef uint32_t xfs_agblock_t; /* blockno in alloc. group */ +typedef uint32_t xfs_extlen_t; /* extent length in blocks */ +typedef uint32_t xfs_agnumber_t; /* allocation group number */ +typedef int32_t xfs_extnum_t; /* # of extents in a file */ +typedef int16_t xfs_aextnum_t; /* # extents in an attribute fork */ +typedef int64_t xfs_fsize_t; /* bytes in a file */ +typedef uint64_t xfs_ufsize_t; /* unsigned bytes in a file */ + +typedef int32_t xfs_suminfo_t; /* type of bitmap summary info */ +typedef int32_t xfs_rtword_t; /* word type for bitmap manipulations */ + +typedef int64_t xfs_lsn_t; /* log sequence number */ +typedef int32_t xfs_tid_t; /* transaction identifier */ + +typedef uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ +typedef uint32_t xfs_dahash_t; /* dir/attr hash value */ + +/* + * These types are 64 bits on disk but are either 32 or 64 bits in memory. + * Disk based types: + */ +typedef uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */ +typedef uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */ +typedef uint64_t xfs_drtbno_t; /* extent (block) in realtime area */ +typedef uint64_t xfs_dfiloff_t; /* block number in a file */ +typedef uint64_t xfs_dfilblks_t; /* number of blocks in a file */ + +/* + * Memory based types are conditional. + */ +typedef uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef uint64_t xfs_rfsblock_t; /* blockno in filesystem (raw) */ +typedef uint64_t xfs_rtblock_t; /* extent (block) in realtime area */ +typedef int64_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */ + +typedef uint64_t xfs_fileoff_t; /* block number in a file */ +typedef int64_t xfs_sfiloff_t; /* signed block number in a file */ +typedef uint64_t xfs_filblks_t; /* number of blocks in a file */ + +/* + * Null values for the types. + */ +#define NULLDFSBNO ((xfs_dfsbno_t)-1) +#define NULLDRFSBNO ((xfs_drfsbno_t)-1) +#define NULLDRTBNO ((xfs_drtbno_t)-1) +#define NULLDFILOFF ((xfs_dfiloff_t)-1) + +#define NULLFSBLOCK ((xfs_fsblock_t)-1) +#define NULLRFSBLOCK ((xfs_rfsblock_t)-1) +#define NULLRTBLOCK ((xfs_rtblock_t)-1) +#define NULLFILEOFF ((xfs_fileoff_t)-1) + +#define NULLAGBLOCK ((xfs_agblock_t)-1) +#define NULLAGNUMBER ((xfs_agnumber_t)-1) +#define NULLEXTNUM ((xfs_extnum_t)-1) + +#define NULLCOMMITLSN ((xfs_lsn_t)-1) + +/* + * Max values for extlen, extnum, aextnum. + */ +#define MAXEXTLEN ((xfs_extlen_t)0x001fffff) /* 21 bits */ +#define MAXEXTNUM ((xfs_extnum_t)0x7fffffff) /* signed int */ +#define MAXAEXTNUM ((xfs_aextnum_t)0x7fff) /* signed short */ + +/* + * Min numbers of data/attr fork btree root pointers. + */ +#define MINDBTPTRS 3 +#define MINABTPTRS 2 + +/* + * MAXNAMELEN is the length (including the terminating null) of + * the longest permissible file (component) name. + */ +#define MAXNAMELEN 256 + +typedef enum { + XFS_LOOKUP_EQi, XFS_LOOKUP_LEi, XFS_LOOKUP_GEi +} xfs_lookup_t; + +typedef enum { + XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_BMAPi, XFS_BTNUM_INOi, + XFS_BTNUM_MAX +} xfs_btnum_t; + +struct xfs_name { + const unsigned char *name; + int len; +}; + +#endif /* XFS_TYPES_H_ */ diff --git a/core/graphics.c b/core/graphics.c index bdf48a85..834372ff 100644 --- a/core/graphics.c +++ b/core/graphics.c @@ -25,12 +25,13 @@ #include "bios.h" #include "graphics.h" -uint8_t UsingVGA = 0; +__export uint8_t UsingVGA = 0; uint16_t VGAPos; /* Pointer into VGA memory */ -uint16_t *VGAFilePtr; /* Pointer into VGAFileBuf */ +__export uint16_t *VGAFilePtr; /* Pointer into VGAFileBuf */ +__export uint16_t VGAFontSize = 16; /* Defaults to 16 byte font */ -char VGAFileBuf[VGA_FILE_BUF_SIZE]; /* Unmangled VGA image name */ -char VGAFileMBuf[FILENAME_MAX]; /* Mangled VGA image name */ +__export char VGAFileBuf[VGA_FILE_BUF_SIZE]; /* Unmangled VGA image name */ +__export char VGAFileMBuf[FILENAME_MAX]; /* Mangled VGA image name */ static uint8_t VGARowBuffer[640 + 80]; /* Decompression buffer */ static uint8_t VGAPlaneBuffer[(640/8) * 4]; /* Plane buffers */ @@ -229,7 +230,7 @@ static void outputvga(const void *in, void *out) /* * Display a graphical splash screen. */ -void vgadisplayfile(FILE *_fd) +__export void vgadisplayfile(FILE *_fd) { char *p; int size; @@ -304,7 +305,7 @@ void vgadisplayfile(FILE *_fd) /* * Disable VGA graphics. */ -void syslinux_force_text_mode(void) +__export void syslinux_force_text_mode(void) { com32sys_t ireg, oreg; @@ -356,7 +357,7 @@ void vgashowcursor(void) vgacursorcommon('_'); } -void using_vga(uint8_t vga, uint16_t pix_cols, uint16_t pix_rows) +__export void using_vga(uint8_t vga, uint16_t pix_cols, uint16_t pix_rows) { UsingVGA = vga; GXPixCols = pix_cols; diff --git a/core/highmem.inc b/core/highmem.inc deleted file mode 100644 index ea386ffc..00000000 --- a/core/highmem.inc +++ /dev/null @@ -1,158 +0,0 @@ -;; ----------------------------------------------------------------------- -;; -;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved -;; -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, Inc., 53 Temple Place Ste 330, -;; Boston MA 02111-1307, USA; either version 2 of the License, or -;; (at your option) any later version; incorporated herein by reference. -;; -;; ----------------------------------------------------------------------- - -;; -;; highmem.inc -;; -;; Probe for the size of high memory. This can be overridden by a -;; mem= command on the command line while booting a new kernel. -;; - - section .text16 - -; -; This is set up as a subroutine; it will set up the global variable -; HighMemSize. All registers are preserved. -; -highmemsize: - push es - pushfd - pushad - - push cs - pop es - -; -; First, try INT 15:E820 (get BIOS memory map) -; -; Note: we may have to scan this multiple times, because some (daft) BIOSes -; report main memory as multiple contiguous ranges... -; -get_e820: - mov dword [E820Max],-(1 << 20) ; Max amount of high memory - mov dword [E820Mem],(1 << 20) ; End of detected high memory -.start_over: - mov di,E820Buf - xor ax,ax - mov cx,10 - rep stosw ; Clear buffer - xor ebx,ebx ; Start with first record - jmp short .do_e820 ; Skip "at end" check first time! -.int_loop: and ebx,ebx ; If we're back at beginning... - jz .e820_done ; ... we're done -.do_e820: mov eax,0000E820h - mov edx,534D4150h ; "SMAP" backwards - xor ecx,ecx - mov cl,20 ; ECX <- 20 (size of buffer) - mov di,E820Buf - int 15h - jnc .no_carry - ; If carry, ebx == 0 means error, ebx != 0 means we're done - and ebx,ebx - jnz .e820_done - jmp no_e820 -.no_carry: - cmp eax,534D4150h - jne no_e820 - cmp cx,20 - jb no_e820 - -; -; Look for a memory block starting at <= 1 MB and continuing upward -; - cmp dword [E820Buf+4], byte 0 - ja .int_loop ; Start >= 4 GB? - mov eax, [E820Buf] - cmp dword [E820Buf+16],1 - je .is_ram ; Is it memory? - ; - ; Non-memory range. Remember this as a limit; some BIOSes get the length - ; of primary RAM incorrect! - ; -.not_ram: - cmp eax, (1 << 20) - jb .int_loop ; Starts in lowmem region - cmp eax,[E820Max] - jae .int_loop ; Already above limit - mov [E820Max],eax ; Set limit - jmp .int_loop - -.is_ram: - cmp eax,[E820Mem] - ja .int_loop ; Not contiguous with our starting point - add eax,[E820Buf+8] - jc .overflow - cmp dword [E820Buf+12],0 - je .nooverflow -.overflow: - or eax,-1 -.nooverflow: - cmp eax,[E820Mem] - jbe .int_loop ; All is below our baseline - mov [E820Mem],eax - jmp .start_over ; Start over in case we find an adjacent range - -.e820_done: - mov eax,[E820Mem] - cmp eax,[E820Max] - jna .not_limited - mov eax,[E820Max] -.not_limited: - cmp eax,(1 << 20) - ja got_highmem ; Did we actually find memory? - ; otherwise fall through - -; -; INT 15:E820 failed. Try INT 15:E801. -; -no_e820: - mov ax,0e801h ; Query high memory (semi-recent) - int 15h - jc no_e801 - cmp ax,3c00h - ja no_e801 ; > 3C00h something's wrong with this call - jb e801_hole ; If memory hole we can only use low part - - mov ax,bx - shl eax,16 ; 64K chunks - add eax,(16 << 20) ; Add first 16M - jmp short got_highmem - -; -; INT 15:E801 failed. Try INT 15:88. -; -no_e801: - mov ah,88h ; Query high memory (oldest) - int 15h - cmp ax,14*1024 ; Don't trust memory >15M - jna e801_hole - mov ax,14*1024 -e801_hole: - and eax,0ffffh - shl eax,10 ; Convert from kilobytes - add eax,(1 << 20) ; First megabyte -got_highmem: -%if HIGHMEM_SLOP != 0 - sub eax,HIGHMEM_SLOP -%endif - mov [HighMemSize],eax - popad - popfd - pop es - ret ; Done! - - section .bss16 - alignb 4 -E820Buf resd 5 ; INT 15:E820 data buffer -E820Mem resd 1 ; Memory detected by E820 -E820Max resd 1 ; Is E820 memory capped? -; HighMemSize is defined in com32.inc diff --git a/core/idle.c b/core/idle.c index 3f57393b..9514df88 100644 --- a/core/idle.c +++ b/core/idle.c @@ -25,7 +25,7 @@ #define TICKS_TO_IDLE 4 /* Also in idle.inc */ extern uint32_t _IdleTimer; -extern uint16_t NoHalt; +__export uint16_t NoHalt = 0; int (*idle_hook_func)(void); @@ -34,7 +34,7 @@ void reset_idle(void) _IdleTimer = jiffies(); } -void __idle(void) +__export void __idle(void) { if (jiffies() - _IdleTimer < TICKS_TO_IDLE) return; diff --git a/core/idle.inc b/core/idle.inc index c93d1773..65d6c5c8 100644 --- a/core/idle.inc +++ b/core/idle.inc @@ -22,7 +22,7 @@ reset_idle: sti ; Guard against BIOS/PXE brokenness... ret - global do_idle + global do_idle:function hidden do_idle: push eax push ds @@ -72,10 +72,8 @@ do_idle: section .data16 alignz 4 - global _IdleTimer + global _IdleTimer:data hidden _IdleTimer dd 0 - global NoHalt -NoHalt dw 0 hlt_err db 'ERROR: idle with IF=0', CR, LF, 0 diff --git a/core/include/bios.h b/core/include/bios.h index 49a75f5a..9334c41a 100644 --- a/core/include/bios.h +++ b/core/include/bios.h @@ -35,6 +35,16 @@ #define BIOS_magic 0x0472 /* BIOS reset magic */ #define BIOS_vidrows 0x0484 /* Number of screen rows */ +static inline uint16_t bios_fbm(void) +{ + return *(volatile uint16_t *)BIOS_fbm; +} + +static inline void set_bios_fbm(uint16_t mem) +{ + *(volatile uint16_t *)BIOS_fbm = mem; +} + #define serial_buf_size 4096 #define IO_DELAY_PORT 0x80 /* Invalid port (we hope!) */ diff --git a/core/include/fs.h b/core/include/fs.h index 4d6c18be..554dc97f 100644 --- a/core/include/fs.h +++ b/core/include/fs.h @@ -101,9 +101,9 @@ struct inode { const char *name; /* Name, valid for generic path search only */ int refcnt; int mode; /* FILE , DIR or SYMLINK */ - uint32_t size; - uint32_t blocks; /* How many blocks the file take */ - uint32_t ino; /* Inode number */ + uint64_t size; + uint64_t blocks; /* How many blocks the file take */ + uint64_t ino; /* Inode number */ uint32_t atime; /* Access time */ uint32_t mtime; /* Modify time */ uint32_t ctime; /* Create time */ @@ -182,7 +182,6 @@ static inline struct file *handle_to_file(uint16_t handle) return handle ? &files[handle-1] : NULL; } -#define PATH_DEFAULT "/boot/syslinux/:/boot/" extern char *PATH; /* fs.c */ diff --git a/core/isolinux.asm b/core/isolinux.asm index 39e5e5c5..673134b0 100644 --- a/core/isolinux.asm +++ b/core/isolinux.asm @@ -68,6 +68,7 @@ trackbuf resb trackbufsize ; Track buffer goes here ; Some of these are touched before the whole image ; is loaded. DO NOT move this to .bss16/.uibss. section .earlybss + global BIOSName alignb 4 FirstSecSum resd 1 ; Checksum of bytes 64-2048 ImageDwords resd 1 ; isolinux.bin size, dwords @@ -1207,24 +1208,15 @@ debug_tracer: pushad %endif ; DEBUG_TRACERS section .bss16 - global CmdOptPtr, KbdMap alignb 4 ThisKbdTo resd 1 ; Temporary holder for KbdTimeout ThisTotalTo resd 1 ; Temporary holder for TotalTimeout KernelExtPtr resw 1 ; During search, final null pointer -CmdOptPtr resw 1 ; Pointer to first option on cmd line -KbdFlags resb 1 ; Check for keyboard escapes FuncFlag resb 1 ; Escape sequences received from keyboard KernelType resb 1 ; Kernel type, from vkernel, if known -KbdMap resb 256 ; Keyboard map global KernelName KernelName resb FILENAME_MAX ; Mangled name for kernel - section .config - global PXERetry -PXERetry dw 0 ; Extra PXE retries section .data16 - global SerialNotice -SerialNotice db 1 ; Only print this once global IPAppends, numIPAppends %if IS_PXELINUX extern IPOption diff --git a/core/kaboom.c b/core/kaboom.c index 726ca2be..0b025dd0 100644 --- a/core/kaboom.c +++ b/core/kaboom.c @@ -18,7 +18,7 @@ __noreturn __bad_SEG(const volatile void *p) #undef kaboom -__noreturn _kaboom(void) +__export __noreturn _kaboom(void) { extern void kaboom(void); call16(kaboom, &zero_regs, NULL); diff --git a/core/kernel.inc b/core/kernel.inc index 245cd6db..5e1c7a39 100644 --- a/core/kernel.inc +++ b/core/kernel.inc @@ -62,9 +62,6 @@ linux_fdctab resb 12 cmd_line_here equ $ ; F800 Should be out of the way endstruc - global cmd_line -cmd_line equ core_real_mode + cmd_line_here - ; ; Old kernel command line signature ; diff --git a/core/layout.inc b/core/layout.inc index 24843923..be797ede 100644 --- a/core/layout.inc +++ b/core/layout.inc @@ -52,7 +52,6 @@ LATEBSS_START equ 0B800h ; ; 32-bit stack layout ; - global STACK32_LEN STACK32_LEN equ 64*1024 section .stack nobits write align=4096 @@ -135,7 +134,7 @@ serial_buf_size equ 4096 ; Should be a power of 2 ; extern xfer_buf_seg section .xfer_buf write nobits align=65536 - global core_xfer_buf + global core_xfer_buf:data hidden core_xfer_buf resb 65536 ; @@ -145,7 +144,7 @@ core_xfer_buf resb 65536 ; extern real_mode_seg section .real_mode write nobits align=65536 - global core_real_mode + global core_real_mode:data hidden core_real_mode resb 65536 comboot_seg equ real_mode_seg ; COMBOOT image loading zone diff --git a/core/ldlinux.asm b/core/ldlinux.asm index a2f859d0..a1f96b77 100644 --- a/core/ldlinux.asm +++ b/core/ldlinux.asm @@ -39,6 +39,8 @@ ROOT_FS_OPS: dd ext2_fs_ops extern ntfs_fs_ops dd ntfs_fs_ops + extern xfs_fs_ops + dd xfs_fs_ops extern btrfs_fs_ops dd btrfs_fs_ops dd 0 diff --git a/core/localboot.c b/core/localboot.c index 03ac866d..0f4b5820 100644 --- a/core/localboot.c +++ b/core/localboot.c @@ -34,7 +34,7 @@ extern void local_boot16(void); * Boot a specified local disk. AX specifies the BIOS disk number; or * -1 in case we should execute INT 18h ("next device.") */ -void local_boot(int16_t ax) +__export void local_boot(int16_t ax) { com32sys_t ireg, oreg; int i; diff --git a/core/localboot.inc b/core/localboot.inc index ce971ae4..b7840427 100644 --- a/core/localboot.inc +++ b/core/localboot.inc @@ -1,5 +1,5 @@ section .text16 - global local_boot16 + global local_boot16:function hidden local_boot16: mov cx,0 mov ss,cx diff --git a/core/mem/free.c b/core/mem/free.c index b0eb7146..6fb8cfdd 100644 --- a/core/mem/free.c +++ b/core/mem/free.c @@ -81,7 +81,7 @@ void bios_free(void *ptr) __free_block(ah); } -void free(void *ptr) +__export void free(void *ptr) { dprintf("free(%p) @ %p\n", ptr, __builtin_return_address(0)); diff --git a/core/mem/malloc.c b/core/mem/malloc.c index fe8af5ad..a3d6b45d 100644 --- a/core/mem/malloc.c +++ b/core/mem/malloc.c @@ -96,12 +96,12 @@ static void *_malloc(size_t size, enum heap heap, malloc_tag_t tag) return p; } -void *malloc(size_t size) +__export void *malloc(size_t size) { return _malloc(size, HEAP_MAIN, MALLOC_CORE); } -void *lmalloc(size_t size) +__export void *lmalloc(size_t size) { void *p; @@ -217,12 +217,12 @@ void *bios_realloc(void *ptr, size_t size) } } -void *realloc(void *ptr, size_t size) +__export void *realloc(void *ptr, size_t size) { return firmware->mem->realloc(ptr, size); } -void *zalloc(size_t size) +__export void *zalloc(size_t size) { void *ptr; diff --git a/core/plaincon.c b/core/plaincon.c index dfeb9784..8f8ca7ca 100644 --- a/core/plaincon.c +++ b/core/plaincon.c @@ -9,7 +9,7 @@ * Write a single character in AL to the console without * mangling any registers; handle video pages correctly. */ -void writechr(char data) +__export void writechr(char data) { com32sys_t ireg, oreg; diff --git a/core/pxelinux.asm b/core/pxelinux.asm index d927b2b2..95f76617 100644 --- a/core/pxelinux.asm +++ b/core/pxelinux.asm @@ -280,24 +280,15 @@ Kernel_EAX resd 1 Kernel_SI resw 1 section .bss16 - global CmdOptPtr, KbdMap alignb 4 ThisKbdTo resd 1 ; Temporary holder for KbdTimeout ThisTotalTo resd 1 ; Temporary holder for TotalTimeout KernelExtPtr resw 1 ; During search, final null pointer -CmdOptPtrj resw 1 ; Pointer to first option on cmd line -KbdFlags resb 1 ; Check for keyboard escapes FuncFlag resb 1 ; Escape sequences received from keyboard KernelType resb 1 ; Kernel type, from vkernel, if known -KbdMap resb 256 ; Keyboard map global KernelName KernelName resb FILENAME_MAX ; Mangled name for kernel - section .config - global PXERetry -PXERetry dw 0 ; Extra PXE retries section .data16 - global SerialNotice -SerialNotice db 1 ; Only print this once extern IPOption global IPAppends, numIPAppends alignz 2 diff --git a/core/rawcon.c b/core/rawcon.c index 1a52c954..92f0898a 100644 --- a/core/rawcon.c +++ b/core/rawcon.c @@ -10,7 +10,7 @@ #include "bios.h" #include "graphics.h" -void writechr(char data) +__export void writechr(char data) { if (UsingVGA & 0x08) syslinux_force_text_mode(); diff --git a/core/serirq.c b/core/serirq.c index e0675c9a..e230b98d 100644 --- a/core/serirq.c +++ b/core/serirq.c @@ -123,7 +123,7 @@ static inline void install_irq_vectors(uint32_t *dst, int first) } } -void sirq_install(void) +__export void sirq_install(void) { char val, val2; @@ -164,7 +164,7 @@ void sirq_install(void) outb(0xA1, 0); } -void sirq_cleanup_nowipe(void) +__export void sirq_cleanup_nowipe(void) { uint32_t *dst; int i; diff --git a/core/stack.inc b/core/stack.inc index 788db647..838d6bab 100644 --- a/core/stack.inc +++ b/core/stack.inc @@ -38,7 +38,7 @@ section .data16 alignz 4 - global BaseStack + global BaseStack:data hidden BaseStack dd StackHome ; ESP of the "home" stack pointer dw 0 ; SS of the "home" stack pointer diff --git a/core/timer.inc b/core/timer.inc index 9f42fdf2..80647983 100644 --- a/core/timer.inc +++ b/core/timer.inc @@ -32,7 +32,7 @@ timer_init: mov dword [BIOS_timer_hook],timer_irq ret - global bios_timer_cleanup + global bios_timer_cleanup:function hidden bios_timer_cleanup: ; Unhook INT 1Ch mov eax,[BIOS_timer_next] @@ -43,18 +43,18 @@ bios_timer_cleanup: ; The specified frequency is 14.31818 MHz/12/65536; this turns out ; to be a period of 54.92542 ms, or 0x36.ece8(187c) hexadecimal. ; - global timer_irq + global timer_irq:function hidden timer_irq: inc dword [cs:__jiffies] add word [cs:__ms_timer_adj],0xece8 adc dword [cs:__ms_timer],0x36 jmp 0:0 - global BIOS_timer_next + global BIOS_timer_next:data hidden BIOS_timer_next equ $-4 section .data16 alignz 4 - global __jiffies, __ms_timer + global __jiffies:data hidden, __ms_timer __jiffies dd 0 ; Clock tick timer __ms_timer dd 0 ; Millisecond timer __ms_timer_adj dw 0 ; Millisecond timer correction factor |
