diff --git a/build/jam/BuildSetup b/build/jam/BuildSetup index 4c612b0d9a..57331fd52f 100644 --- a/build/jam/BuildSetup +++ b/build/jam/BuildSetup @@ -1338,6 +1338,7 @@ if $(HAIKU_NO_WERROR) != 1 { EnableWerror src add-ons kernel file_systems layers ; EnableWerror src add-ons kernel file_systems netfs ; EnableWerror src add-ons kernel file_systems nfs ; + EnableWerror src add-ons kernel file_systems nfs4 ; # EnableWerror src add-ons kernel file_systems ntfs ; EnableWerror src add-ons kernel file_systems packagefs ; EnableWerror src add-ons kernel file_systems ramfs ; @@ -1347,6 +1348,7 @@ if $(HAIKU_NO_WERROR) != 1 { EnableWerror src add-ons kernel generic ; # EnableWerror src add-ons kernel network datalink_protocols ; EnableWerror src add-ons kernel network devices ; + EnableWerror src add-ons kernel network dns_resolver ; EnableWerror src add-ons kernel network notifications ; EnableWerror src add-ons kernel network ppp ; EnableWerror src add-ons kernel network protocols ; diff --git a/build/jam/Haiku64Image b/build/jam/Haiku64Image index 16537412f6..9b6795ba61 100644 --- a/build/jam/Haiku64Image +++ b/build/jam/Haiku64Image @@ -67,7 +67,8 @@ PRIVATE_SYSTEM_LIBS = SYSTEM_SERVERS = app_server cddb_daemon debug_server input_server midi_server mount_server net_server notification_server power_daemon print_server - print_addon_server registrar syslog_daemon + print_addon_server registrar syslog_daemon dns_resolver_server + nfs4_idmapper_server ; SYSTEM_NETWORK_DEVICES = ethernet loopback ; @@ -104,7 +105,7 @@ SYSTEM_ADD_ONS_DRIVERS_NET = 3com atheros813x ar81xx attansic_l1 attansic_l2 SYSTEM_ADD_ONS_BUS_MANAGERS = [ FFilterByBuildFeatures ata@ata pci ps2 isa scsi config_manager usb ] ; -SYSTEM_ADD_ONS_FILE_SYSTEMS = bfs iso9660 attribute_overlay write_overlay ; +SYSTEM_ADD_ONS_FILE_SYSTEMS = bfs iso9660 nfs4 attribute_overlay write_overlay ; # modules AddFilesToHaikuImage system add-ons kernel bus_managers @@ -398,6 +399,7 @@ AddFilesToHaikuImage system add-ons input_server devices : keyboard mouse tablet wacom ; AddFilesToHaikuImage system add-ons kernel network : notifications stack ; +AddFilesToHaikuImage system add-ons kernel network : dns_resolver ; AddFilesToHaikuImage system add-ons kernel network devices : $(SYSTEM_NETWORK_DEVICES) ; AddFilesToHaikuImage system add-ons kernel network datalink_protocols diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index 6dbb81f3d2..9bf91156bc 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -87,7 +87,7 @@ SYSTEM_SERVERS = [ FFilterByBuildFeatures app_server cddb_daemon debug_server input_server keystore_server mail_daemon media_addon_server media_server midi_server mount_server net_server notification_server power_daemon print_server print_addon_server registrar - syslog_daemon + syslog_daemon dns_resolver_server nfs4_idmapper_server ] ; SYSTEM_NETWORK_DEVICES = ethernet loopback ; @@ -183,7 +183,7 @@ SYSTEM_ADD_ONS_BUS_MANAGERS = [ FFilterByBuildFeatures ata@ata pci ps2@x86 isa@x86 ide@ide scsi config_manager agp_gart usb firewire acpi@x86 ] ; -SYSTEM_ADD_ONS_FILE_SYSTEMS = bfs btrfs cdda exfat ext2 fat iso9660 nfs +SYSTEM_ADD_ONS_FILE_SYSTEMS = bfs btrfs cdda exfat ext2 fat iso9660 nfs nfs4 attribute_overlay write_overlay ntfs reiserfs udf googlefs ; # wifi firmware @@ -663,6 +663,7 @@ AddFilesToHaikuImage system add-ons input_server filters : screen_saver shortcut_catcher ; AddFilesToHaikuImage system add-ons kernel network : notifications stack ; +AddFilesToHaikuImage system add-ons kernel network : dns_resolver ; AddFilesToHaikuImage system add-ons kernel network devices : $(SYSTEM_NETWORK_DEVICES) ; AddFilesToHaikuImage system add-ons kernel network datalink_protocols diff --git a/data/artwork/icons/App_Pe b/data/artwork/icons/App_Pe index b86238276f..844d4c0e38 100644 Binary files a/data/artwork/icons/App_Pe and b/data/artwork/icons/App_Pe differ diff --git a/data/artwork/icons/File_Pe_Group b/data/artwork/icons/File_Pe_Group index 4fb263b728..96b585d7f5 100644 Binary files a/data/artwork/icons/File_Pe_Group and b/data/artwork/icons/File_Pe_Group differ diff --git a/data/artwork/icons/File_Pe_Project b/data/artwork/icons/File_Pe_Project index 455955cb41..99342149c6 100644 Binary files a/data/artwork/icons/File_Pe_Project and b/data/artwork/icons/File_Pe_Project differ diff --git a/data/artwork/icons/File_SourceCode b/data/artwork/icons/File_SourceCode index 464db7b001..b9417079b9 100644 Binary files a/data/artwork/icons/File_SourceCode and b/data/artwork/icons/File_SourceCode differ diff --git a/data/catalogs/add-ons/disk_systems/intel/pt_BR.catkeys b/data/catalogs/add-ons/disk_systems/intel/pt_BR.catkeys index b1ddff193c..38e3ff755e 100644 --- a/data/catalogs/add-ons/disk_systems/intel/pt_BR.catkeys +++ b/data/catalogs/add-ons/disk_systems/intel/pt_BR.catkeys @@ -1,2 +1,2 @@ -1 portuguese (brazil) x-vnd.Haiku-IntelDiskAddOn 4191422532 -Active partition BFS_Creation_Parameter Partição ativa +1 portuguese (brazil) x-vnd.Haiku-IntelDiskAddOn 946918966 +Active partition PrimaryPartitionEditor Partição ativa diff --git a/data/catalogs/add-ons/disk_systems/ntfs/pt_BR.catkeys b/data/catalogs/add-ons/disk_systems/ntfs/pt_BR.catkeys new file mode 100644 index 0000000000..9435624d6b --- /dev/null +++ b/data/catalogs/add-ons/disk_systems/ntfs/pt_BR.catkeys @@ -0,0 +1,2 @@ +1 portuguese (brazil) x-vnd.Haiku-NTFSDiskAddOn 25755486 +Name: NTFS_Initialize_Parameter Nome: diff --git a/data/catalogs/apps/drivesetup/pl.catkeys b/data/catalogs/apps/drivesetup/pl.catkeys index 5c74339dc7..be7efed98c 100644 --- a/data/catalogs/apps/drivesetup/pl.catkeys +++ b/data/catalogs/apps/drivesetup/pl.catkeys @@ -1,11 +1,14 @@ -1 polish x-vnd.Haiku-DriveSetup 644135944 +1 polish x-vnd.Haiku-DriveSetup 1752326393 DriveSetup System name DriveSetup +Cancel AbstractParametersPanel Anuluj Delete MainWindow Usuń Are you sure you want to write the changes back to disk now?\n\nAll data on the selected partition will be irretrievably lost if you do so! MainWindow Na pewno chcesz zapisać zmiany na dysk?\n\nWszystkie dane na wybranej partycji bedą bezpowrotnie utracone! Rescan MainWindow Przeskanuj OK MainWindow OK Could not aquire partitioning information. MainWindow Nie można uzyskać informacji na temat partycji. There's no space on the partition where a child partition could be created. MainWindow Brak miejsca na partycji, gdzie można by utworzyć zagnieżdzoną partycje. +Initialize InitializeParametersPanel Inicjalizacja +OK AbstractParametersPanel OK PartitionList Unable to find the selected partition by ID. MainWindow Nie można znaleźć wybranej partycji po ID. Select a partition from the list below. DiskView Proszę wybrać partycje z listy poniżej. diff --git a/data/catalogs/apps/drivesetup/pt_BR.catkeys b/data/catalogs/apps/drivesetup/pt_BR.catkeys index 8e02ac87c8..b19b9244e4 100644 --- a/data/catalogs/apps/drivesetup/pt_BR.catkeys +++ b/data/catalogs/apps/drivesetup/pt_BR.catkeys @@ -1,11 +1,14 @@ -1 portuguese (brazil) x-vnd.Haiku-DriveSetup 644135944 +1 portuguese (brazil) x-vnd.Haiku-DriveSetup 3775412465 DriveSetup System name Configuração de Unidade +Cancel AbstractParametersPanel Cancelar Delete MainWindow Excluir Are you sure you want to write the changes back to disk now?\n\nAll data on the selected partition will be irretrievably lost if you do so! MainWindow Tem certeza que deseja aplicar todas as alterações agora?\n\nTodos os dados na partição selecionada serão perdidos de forma irrecuperável se continuar! Rescan MainWindow Re-examinar OK MainWindow OK Could not aquire partitioning information. MainWindow Não foi possível obter informações sobre a divisão em partições There's no space on the partition where a child partition could be created. MainWindow Não existe espaço suficiente na partição para criar uma partição dentro dela. +Initialize InitializeParametersPanel Inicializar +OK AbstractParametersPanel OK PartitionList Unable to find the selected partition by ID. MainWindow Não foi possível encontrar a partição selecionada pelo ID. Select a partition from the list below. DiskView Selecione a partição a partir da lista abaixo. @@ -15,6 +18,7 @@ The selected disk is read-only. MainWindow O disco selecionado está marcado co Are you sure you want to format the partition \"%s\"? You will be asked again before changes are written to the disk. MainWindow Deseja realmente formatar a partição \"%s\"? Você será consultado novamente antes que as alterações sejam gravadas no disco. Could not mount partition %s. MainWindow Não foi possível montar a partição %s. The partition %s has been successfully formatted.\n MainWindow A partição %s foi formatada com sucesso.\n +Change parameters MainWindow Modificar parâmetros The partition %s is already unmounted. MainWindow A partição %s já está desmontada. Failed to delete the partition. No changes have been written to disk. MainWindow Falha ao excluir a partição. Nenhuma alteração foi gravada no disco. Could not delete the selected partition. MainWindow Não foi possível excluir a partição selecionada. @@ -33,38 +37,53 @@ Write changes MainWindow Gravar alterações There was an error preparing the disk for modifications. MainWindow Ocorreu um erro ao preparar o disco para modificações. The partition %s is already mounted. MainWindow A partição %s já está montada. Are you sure you want to format the partition? You will be asked again before changes are written to the disk. MainWindow Deseja realmente formatar a partição? Você será consultado novamente antes que as alterações sejam gravadas no disco. +Partition name: ChangeParametersPanel Nome da partição: +Change ChangeParametersPanel Modificar Are you sure you want to write the changes back to disk now?\n\nAll data on the disk %s will be irretrievably lost if you do so! MainWindow Tem certeza que deseja aplicar todas as alterações agora?\n\nTodos os dados na partição selecionada serão perdidos de forma irrecuperável se continuar! Are you sure you want to delete the selected partition?\n\nAll data on the partition will be irretrievably lost if you do so! MainWindow Tem certeza que deseja apagar a partição selecionada?\n\nTodos os dados na partição serão perdidos de forma irrecuperável se continuar! Create… MainWindow Criar... Disk system \"%s\"\" not found! MainWindow Disco de sistema \"%s\"\" não encontrado! The disk has been successfully initialized.\n MainWindow O disco foi inicializado com sucesso.\n Could not unmount partition %s. MainWindow Não foi possível desmontar a partição %s. +Failed to change the parameters of the partition. No changes have been written to disk. MainWindow Falha ao modificar os parâmetros da partição. Nenhuma mudança foi gravada no disco. Failed to format the partition %s!\n MainWindow Falha ao formatar a partição %s!\n Mount MainWindow Montar +Partition type PartitionList Tipo de partição Are you sure you want to format a raw disk? (most people initialize the disk with a partitioning system first) You will be asked again before changes are written to the disk. MainWindow Deseja realmente formatar um disco não preparado? (a maioria das pessoas inicializa o disco com um sistema de particionamento primeiro) Você será consultado novamente antes que as alterações sejam gravadas no disco. +The panel experienced a problem! MainWindow Ocorreu um problema com a janela de diálogo! +Change parameters… MainWindow Modificar parâmetros… Device PartitionList Dispositivo Disk MainWindow Disco Are you sure you want to initialize the selected disk? All data will be lost. You will be asked again before changes are written to the disk.\n MainWindow Deseja realmente inicializar o disco selecionado? Todos os dados serão perdidos. Você será consultado novamente antes das alterações serem gravados no disco.\n +Partition size CreateParametersPanel Tamanho da partição Device DiskView Dispositivo Active PartitionList Ativo Volume name PartitionList Nome do volume Continue MainWindow Continuar Cannot delete the selected partition. MainWindow Não foi posível deletar a partição selecionada. Mount all MainWindow Montar tudo +End: %s Support Fim: %s +The panel could not return successfully. MainWindow A janela de diálogo pode não responder adequadamente. Cancel MainWindow Cancelar Delete partition MainWindow Excluir partição +Are you sure you want to change parameters of the selected partition?\n\nThe partition may no longer be recognized by other operating systems anymore! MainWindow Está seguro que deseja modificar os parâmetros da partição selecionada?\n\nA partição pode não mais ser reconhecida por outros sistemas operacionais! Eject MainWindow Ejetar Partition MainWindow Partição +Validation of the given parameters failed. MainWindow A validação dos parâmetros dados falhou. +Create CreateParametersPanel Criar File system PartitionList Sistema de arquivos Validation of the given creation parameters failed. MainWindow A validação dos parâmetros de criação inseridas falhou. +Partition type: ChangeParametersPanel Tipo de partição: Size PartitionList Tamanho Wipe (not implemented) MainWindow Limpar (não implementado) Validation of the given initialization parameters failed. MainWindow A validação dos parâmetros de criação inseridas falhou. The selected partition does not contain a partitioning system. MainWindow A partição selecionada não contém um sistema de partições. +Offset: %s Support Deslocamento: %s Are you sure you want to write the changes back to disk now?\n\nAll data on the partition %s will be irretrievably lost if you do so! MainWindow Tem certeza que deseja aplicar todas as alterações agora?\n\nTodos os dados na partição selecionada serão perdidos de forma irrecuperável se continuar! The partition %s is currently mounted. MainWindow A partição %s está atualmente montada. Surface test (not implemented) MainWindow Teste de superfície (não implementado) Format MainWindow Formatar +Could not change the parameters of the selected partition. MainWindow Não foi possível modificar os parâmetros da partição selecionada. Parameters PartitionList Parâmetros Creation of the partition has failed. MainWindow A criação da partição selecionada falhou. The currently selected partition is not empty. MainWindow A partição selecionada não está vazia. diff --git a/data/catalogs/apps/fontdemo/pt_BR.catkeys b/data/catalogs/apps/fontdemo/pt_BR.catkeys index 56c9430657..647dbe14ca 100644 --- a/data/catalogs/apps/fontdemo/pt_BR.catkeys +++ b/data/catalogs/apps/fontdemo/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-FontDemo 1300625863 +1 portuguese (brazil) x-vnd.Haiku-FontDemo 3522850500 Outline: ControlView Contorno: Size: 50 ControlView Tamanho: 50 Stop cycling ControlView Parar ciclo @@ -14,6 +14,7 @@ Rotation: 0 ControlView Rotação: 0 Drawing mode: ControlView Modo de desenho: Haiku, Inc. ControlView Haiku, Inc. Controls FontDemo Controles +FontDemo System name Demonstração de Fonte Outline: %d ControlView Contorno: %d Text: ControlView Texto: Antialiased text ControlView Texto com antisserrilhamento (suavização) diff --git a/data/catalogs/apps/launchbox/pt_BR.catkeys b/data/catalogs/apps/launchbox/pt_BR.catkeys index a0b77b9e0d..c0080ae66d 100644 --- a/data/catalogs/apps/launchbox/pt_BR.catkeys +++ b/data/catalogs/apps/launchbox/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-LaunchBox 3016105370 +1 portuguese (brazil) x-vnd.Haiku-LaunchBox 3692177981 New LaunchBox Novo Set description… LaunchBox Editar descrição... Vertical layout LaunchBox Layout vertical @@ -6,6 +6,7 @@ OK LaunchBox OK Pad 1 LaunchBox Barra 1 last chance LaunchBox última chance Quit LaunchBox Sair +Open containing folder LaunchBox Abrir conteúdo da pasta Clear button LaunchBox Botão limpar LaunchBox System name LaunchBox Ignore double-click LaunchBox Ignorar clique duplo diff --git a/data/catalogs/apps/showimage/be.catkeys b/data/catalogs/apps/showimage/be.catkeys index 60c2c96578..3ab649e42c 100644 --- a/data/catalogs/apps/showimage/be.catkeys +++ b/data/catalogs/apps/showimage/be.catkeys @@ -1,4 +1,4 @@ -1 belarusian x-vnd.Haiku-ShowImage 886873119 +1 belarusian x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Ужыць як фон… File Menus Файл Slide delay Menus Затрымка слайду @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Пакінуць рэжым поўнага экрана Close Menus Закрыць Close ClosePrompt Закрыць -Undo Menus Адмяніць Resize to (in 1/72 inches): PrintOptionsWindow Змяніць памер (у 1/72 дзюйма): Rating Menus Рэйтынг Flip left to right Menus Адлюстраваць злева направа diff --git a/data/catalogs/apps/showimage/de.catkeys b/data/catalogs/apps/showimage/de.catkeys index ec6d68ac5c..95b0ae9eda 100644 --- a/data/catalogs/apps/showimage/de.catkeys +++ b/data/catalogs/apps/showimage/de.catkeys @@ -1,4 +1,4 @@ -1 german x-vnd.Haiku-ShowImage 886873119 +1 german x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Als Hintergrund verwenden… File Menus Datei Slide delay Menus Verzögerung @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Vollbild verlassen Close Menus Schließen Close ClosePrompt Schließen -Undo Menus Rückgängig Resize to (in 1/72 inches): PrintOptionsWindow Größe ändern zu (in 1/72 Zoll): Rating Menus Bewertung Flip left to right Menus Horizontal spiegeln diff --git a/data/catalogs/apps/showimage/el.catkeys b/data/catalogs/apps/showimage/el.catkeys index dd21e80cc0..2d9efd93e6 100644 --- a/data/catalogs/apps/showimage/el.catkeys +++ b/data/catalogs/apps/showimage/el.catkeys @@ -1,4 +1,4 @@ -1 greek, modern (1453-) x-vnd.Haiku-ShowImage 886873119 +1 greek, modern (1453-) x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Ορισμός ως φόντου επιφάνειας εργασίας... File Menus Αρχείο Slide delay Menus Καθυστέρηση παρουσίασης @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Εγκατάλειψη πλήρους οθόνης Close Menus Κλείσιμο Close ClosePrompt Κλείσιμο -Undo Menus Αναίρεση Resize to (in 1/72 inches): PrintOptionsWindow Αλλαγή μεγέθους σε (στο 1/72 ίντσες): Rating Menus Βαθμολογία Flip left to right Menus Οριζόντια αναστροφή diff --git a/data/catalogs/apps/showimage/fi.catkeys b/data/catalogs/apps/showimage/fi.catkeys index 9e1552d9b0..4550136cd8 100644 --- a/data/catalogs/apps/showimage/fi.catkeys +++ b/data/catalogs/apps/showimage/fi.catkeys @@ -1,4 +1,4 @@ -1 finnish x-vnd.Haiku-ShowImage 886873119 +1 finnish x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Käytä taustana… File Menus Tiedosto Slide delay Menus Diaviive @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Jätä kokoruututila Close Menus Sulje Close ClosePrompt Sulje -Undo Menus Peru Resize to (in 1/72 inches): PrintOptionsWindow Skaalaa kokoon (1/72 tuumissa): Rating Menus Arvosana Flip left to right Menus Käännä ympäri vasemmalta oikealle diff --git a/data/catalogs/apps/showimage/fr.catkeys b/data/catalogs/apps/showimage/fr.catkeys index 15f10a9c81..49cf82af96 100644 --- a/data/catalogs/apps/showimage/fr.catkeys +++ b/data/catalogs/apps/showimage/fr.catkeys @@ -1,4 +1,4 @@ -1 french x-vnd.Haiku-ShowImage 886873119 +1 french x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Utiliser comme fond d'écran… File Menus Fichier Slide delay Menus Délai de diaporama @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Sortie du plein écran Close Menus Fermer Close ClosePrompt Fermer -Undo Menus Annuler Resize to (in 1/72 inches): PrintOptionsWindow Redimensionner (en 1/72 pouces) : Rating Menus Évaluation Flip left to right Menus Mirroir vertical diff --git a/data/catalogs/apps/showimage/hi.catkeys b/data/catalogs/apps/showimage/hi.catkeys index aab618d760..7459ccd070 100644 --- a/data/catalogs/apps/showimage/hi.catkeys +++ b/data/catalogs/apps/showimage/hi.catkeys @@ -1,4 +1,4 @@ -1 hindi x-vnd.Haiku-ShowImage 886873119 +1 hindi x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus पृष्ठभूमि के रूप में प्रयोग करें... File Menus फ़ाइल Slide delay Menus स्लाइड मे देरी @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus फूल स्क्रीन को छोड़ें Close Menus बंद करें Close ClosePrompt बंद करें -Undo Menus पूर्ववत् Resize to (in 1/72 inches): PrintOptionsWindow आकार बदलें (1/72 inches में): Rating Menus रेटिंग Flip left to right Menus फ्लिप बाएँ से दाएँ करें diff --git a/data/catalogs/apps/showimage/hu.catkeys b/data/catalogs/apps/showimage/hu.catkeys index b99b178682..bcf16331dd 100644 --- a/data/catalogs/apps/showimage/hu.catkeys +++ b/data/catalogs/apps/showimage/hu.catkeys @@ -1,4 +1,4 @@ -1 hungarian x-vnd.Haiku-ShowImage 886873119 +1 hungarian x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Beállítás háttérnek… File Menus Fájl Slide delay Menus Képváltás közti szünet @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Kilépés a teljes képernyős módból Close Menus Bezárás Close ClosePrompt Bezárás -Undo Menus Visszavonás Resize to (in 1/72 inches): PrintOptionsWindow Átméretezés (1/72 colban): Rating Menus Értékelés Flip left to right Menus Balról jobbra tükrözés diff --git a/data/catalogs/apps/showimage/ja.catkeys b/data/catalogs/apps/showimage/ja.catkeys index d445b5fb8c..b4f0ceabf4 100644 --- a/data/catalogs/apps/showimage/ja.catkeys +++ b/data/catalogs/apps/showimage/ja.catkeys @@ -1,4 +1,4 @@ -1 japanese x-vnd.Haiku-ShowImage 886873119 +1 japanese x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus デスクトップの背景にする… File Menus ファイル Slide delay Menus スライド間隔 @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus 全画面表示から戻る Close Menus 閉じる Close ClosePrompt 閉じる -Undo Menus 元に戻す Resize to (in 1/72 inches): PrintOptionsWindow リサイズ (1/72 インチ): Rating Menus 評価 Flip left to right Menus 左右反転 diff --git a/data/catalogs/apps/showimage/lt.catkeys b/data/catalogs/apps/showimage/lt.catkeys index 0cdb3d72b2..a1e801c3a1 100644 --- a/data/catalogs/apps/showimage/lt.catkeys +++ b/data/catalogs/apps/showimage/lt.catkeys @@ -1,4 +1,4 @@ -1 lithuanian x-vnd.Haiku-ShowImage 886873119 +1 lithuanian x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Naudoti kaip darbalaukio foną… File Menus Failas Slide delay Menus Skaidrių keitimo dažnis @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Normalioji veiksena Close Menus Užverti Close ClosePrompt Užverti -Undo Menus Atšaukti Resize to (in 1/72 inches): PrintOptionsWindow Keisti dydį (po 1/72 colio): Rating Menus Įvertinimas Flip left to right Menus Apversti horizontaliai diff --git a/data/catalogs/apps/showimage/nl.catkeys b/data/catalogs/apps/showimage/nl.catkeys index 71f27074d8..4a9aa94fb0 100644 --- a/data/catalogs/apps/showimage/nl.catkeys +++ b/data/catalogs/apps/showimage/nl.catkeys @@ -1,4 +1,4 @@ -1 dutch; flemish x-vnd.Haiku-ShowImage 886873119 +1 dutch; flemish x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Gebruik als achtergrond… File Menus Bestand Slide delay Menus Dia-vertraging @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Volledig scherm verlaten Close Menus Sluiten Close ClosePrompt Sluit -Undo Menus Ongedaan maken Resize to (in 1/72 inches): PrintOptionsWindow Omvormen naar (in eenheden van 1/72 inch): Rating Menus Waardering Flip left to right Menus Omklappen van links naar rechts diff --git a/data/catalogs/apps/showimage/pl.catkeys b/data/catalogs/apps/showimage/pl.catkeys index 111daaa767..58c0af23ef 100644 --- a/data/catalogs/apps/showimage/pl.catkeys +++ b/data/catalogs/apps/showimage/pl.catkeys @@ -1,4 +1,4 @@ -1 polish x-vnd.Haiku-ShowImage 886873119 +1 polish x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Użyj jako tapety… File Menus Plik Slide delay Menus Opóźnienie @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Opuść pełny ekran Close Menus Zamknij Close ClosePrompt Zamknij -Undo Menus Cofnij Resize to (in 1/72 inches): PrintOptionsWindow Ustaw rozmiar (w 1/72 cala): Rating Menus Ocena Flip left to right Menus Odbij z lewej na prawą diff --git a/data/catalogs/apps/showimage/pt_BR.catkeys b/data/catalogs/apps/showimage/pt_BR.catkeys index 7c261dc248..389acee035 100644 --- a/data/catalogs/apps/showimage/pt_BR.catkeys +++ b/data/catalogs/apps/showimage/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-ShowImage 886873119 +1 portuguese (brazil) x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Utilizar como plano de fundo… File Menus Arquivo Slide delay Menus Atraso de deslizamento @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Sair da tela cheia Close Menus Fechar Close ClosePrompt Fechar -Undo Menus Desfazer Resize to (in 1/72 inches): PrintOptionsWindow Redimensionar para (em 1/72 polegadas): Rating Menus Classificação Flip left to right Menus Inverter da esquerda para direita diff --git a/data/catalogs/apps/showimage/ro.catkeys b/data/catalogs/apps/showimage/ro.catkeys index 68bb29644c..1bb5d55acf 100644 --- a/data/catalogs/apps/showimage/ro.catkeys +++ b/data/catalogs/apps/showimage/ro.catkeys @@ -1,4 +1,4 @@ -1 romanian x-vnd.Haiku-ShowImage 2010609106 +1 romanian x-vnd.Haiku-ShowImage 840612021 Use as background… Menus Utilizează ca fundal... File Menus Fișier Slide delay Menus Întârziere imagine @@ -21,7 +21,6 @@ Could not load image! Either the file or an image translator for it does not exi First page Menus Prima pagină Close Menus Închide Close ClosePrompt Închide -Undo Menus Refă Resize to (in 1/72 inches): PrintOptionsWindow Redimensionează la (în 1/72 țoli): Flip left to right Menus Inversează stânga cu dreapta Show caption in full screen mode Menus Afișează legendă în modul ecran complet diff --git a/data/catalogs/apps/showimage/ru.catkeys b/data/catalogs/apps/showimage/ru.catkeys index 2b722d86a6..fdcb0c53d3 100644 --- a/data/catalogs/apps/showimage/ru.catkeys +++ b/data/catalogs/apps/showimage/ru.catkeys @@ -1,4 +1,4 @@ -1 russian x-vnd.Haiku-ShowImage 886873119 +1 russian x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Сделать фоном рабочего стола… File Menus Файл Slide delay Menus Задержка слайд шоу @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Выйти из полного экрана Close Menus Закрыть Close ClosePrompt Закрыть -Undo Menus Вернуть Resize to (in 1/72 inches): PrintOptionsWindow Изменить размер (1/72 дюйма): Rating Menus Рейтинг Flip left to right Menus Повернуть слева направо diff --git a/data/catalogs/apps/showimage/sk.catkeys b/data/catalogs/apps/showimage/sk.catkeys index 06f0988e54..6600173e87 100644 --- a/data/catalogs/apps/showimage/sk.catkeys +++ b/data/catalogs/apps/showimage/sk.catkeys @@ -1,4 +1,4 @@ -1 slovak x-vnd.Haiku-ShowImage 886873119 +1 slovak x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Použiť ako pozadie… File Menus Súbor Slide delay Menus Oneskorenie snímku @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Opustiť celoobrazovkový režim Close Menus Zatvoriť Close ClosePrompt Zatvoriť -Undo Menus Vrátiť späť Resize to (in 1/72 inches): PrintOptionsWindow Zmeniť veľkosť na (v 1/72 palca): Rating Menus Hodnotenie Flip left to right Menus Obrátiť zľava doprava diff --git a/data/catalogs/apps/showimage/sv.catkeys b/data/catalogs/apps/showimage/sv.catkeys index f45498776b..536c411364 100644 --- a/data/catalogs/apps/showimage/sv.catkeys +++ b/data/catalogs/apps/showimage/sv.catkeys @@ -1,4 +1,4 @@ -1 swedish x-vnd.Haiku-ShowImage 886873119 +1 swedish x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Använd som bakgrund… File Menus Arkiv Slide delay Menus Bildspelsfördröjning @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Lämna fullskärmsläge Close Menus Stäng Close ClosePrompt Stäng -Undo Menus Ångra Resize to (in 1/72 inches): PrintOptionsWindow Ändra storlek till (i måttenheten 1/72 tum): Rating Menus Betyg Flip left to right Menus Vänd åt sidan diff --git a/data/catalogs/apps/showimage/uk.catkeys b/data/catalogs/apps/showimage/uk.catkeys index 1db46cdc46..4e7f3b14c4 100644 --- a/data/catalogs/apps/showimage/uk.catkeys +++ b/data/catalogs/apps/showimage/uk.catkeys @@ -1,4 +1,4 @@ -1 ukrainian x-vnd.Haiku-ShowImage 886873119 +1 ukrainian x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus Використати як тло… File Menus Файл Slide delay Menus Затримка слайда @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus Вийти з повноекранного режиму Close Menus Закрити Close ClosePrompt Закрити -Undo Menus Відмінити Resize to (in 1/72 inches): PrintOptionsWindow Змінити розмір (в 1/72 дюйма): Rating Menus Оцінка Flip left to right Menus Пересунути горизонтально diff --git a/data/catalogs/apps/showimage/zh_Hans.catkeys b/data/catalogs/apps/showimage/zh_Hans.catkeys index 18020dcb33..d2e0a7a2b5 100644 --- a/data/catalogs/apps/showimage/zh_Hans.catkeys +++ b/data/catalogs/apps/showimage/zh_Hans.catkeys @@ -1,4 +1,4 @@ -1 english x-vnd.Haiku-ShowImage 886873119 +1 english x-vnd.Haiku-ShowImage 4011843330 Use as background… Menus 设置为桌面背景… File Menus 文件 Slide delay Menus 幻灯片延迟 @@ -25,7 +25,6 @@ The document '%s' (page %d) has been changed. Do you want to close the document? Leave full screen Menus 取消全屏 Close Menus 关闭 Close ClosePrompt 关闭 -Undo Menus 撤销 Resize to (in 1/72 inches): PrintOptionsWindow 缩放到(in 1/72 inches): Rating Menus 评级 Flip left to right Menus 自左向右翻转 diff --git a/data/catalogs/apps/terminal/pt_BR.catkeys b/data/catalogs/apps/terminal/pt_BR.catkeys index 4c6fef86c0..1a409ae55f 100644 --- a/data/catalogs/apps/terminal/pt_BR.catkeys +++ b/data/catalogs/apps/terminal/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-Terminal 766764238 +1 portuguese (brazil) x-vnd.Haiku-Terminal 2645209895 Not found. Terminal TermWindow Não localizado. Switch Terminals Terminal TermWindow Alternar Terminais Change directory Terminal TermView Mudar de pasta @@ -21,6 +21,7 @@ Font: Terminal AppearancePrefView Fonte: Copy here Terminal TermView Copiar aqui Really close? Terminal TermWindow Deseja realmente fechar? Copy Terminal TermWindow Copiar +Terminal Terminal TermWindow The title for the main window menubar entry related to terminal sessions Terminal Color scheme: Terminal AppearancePrefView Esquema de cores: Window title: Terminal TermWindow Título da janela: Unrecognized option \"%s\"\n Terminal arguments parsing Opção não reconhecida \"%s\"\n @@ -61,6 +62,7 @@ Text not found. Terminal TermWindow Texto não encontrado. Find… Terminal TermWindow Localizar… The process \"%1\" is still running.\nIf you close the Terminal, the process will be killed. Terminal TermWindow O processo \"%1\" ainda está executando.\nSe fechar o Terminal, o processo será morto. Move here Terminal TermView Mover aqui +\t%d\t-\tThe current working directory of the active process in the\n\t\t\tcurrent tab. Optionally the maximum number of path components\n\t\t\tcan be specified. E.g. '%2d' for at most two components.\n\t%T\t-\tThe Terminal application name for the current locale.\n\t%i\t-\tThe index of the window.\n\t%p\t-\tThe name of the active process in the current tab.\n\t%t\t-\tThe title of the current tab.\n\t%%\t-\tThe character '%'. Terminal ToolTips \t %d\t -\t O diretório de trabalho atual do processo ativo na\n\t \t\t aba atual. Opcionalmente o número máximo de componentes do caminho\n\t \t\t pode ser especificado. Por exemplo, '%2d' para no máximo dois componentes.\n\t %T\t -\t O nome do aplicativo Terminal para a localidade atual.\n\t %i\t -\t O índice da janela.\n\t %p\t -\t O nome do processo ativo na guia atual.\n\t %t\t -\t O título da guia atual.\n\t %%\t -\t O caractere '%'. Retro Terminal colors scheme Retrô Error! Terminal getString Erro! New tab Terminal TermWindow Nova aba diff --git a/data/catalogs/apps/webpositive/pt_BR.catkeys b/data/catalogs/apps/webpositive/pt_BR.catkeys index a2bc9cfdef..f0c9dbb99d 100644 --- a/data/catalogs/apps/webpositive/pt_BR.catkeys +++ b/data/catalogs/apps/webpositive/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-WebPositive 233049275 +1 portuguese (brazil) x-vnd.Haiku-WebPositive 3577331897 Show home button Settings Window Exibir botão home Username: Authentication Panel Nome de Usuário: Copy URL to clipboard Download Window Copiar URL para a área de transferência @@ -16,6 +16,7 @@ Start page: Settings Window Página inicial: History WebPositive Window Histórico Error opening downloads folder Download Window Erro ao abrir pasta de itens baixados Paste WebPositive Window Colar +Proxy username: Settings Window Nome de usuário do proxy: Settings Settings Window Definições %seconds seconds left Download Window %seconds segundos restantes Confirmation WebPositive Window Confirmação @@ -41,6 +42,7 @@ Quit WebPositive Window Sair Full screen WebPositive Window Tela cheia Open download error Download Window Abrir erro de download Standard font: Settings Window Fonte padrão: +Find previous occurrence of search terms WebPositive Window find bar previous button tooltip Localizar a ocorrência anterior dos termos da pesquisa Restart Download Window Reiniciar Proxy server Settings Window Servidor proxy Open containing folder Download Window Abrir pasta de contenção @@ -58,6 +60,7 @@ Cut WebPositive Window Cortar Bookmark this page WebPositive Window Marcar esta página There was an error trying to show the Bookmarks folder.\n\nError: %error WebPositive Window Don't translate variable %error Ocorreu um erro tentando exibir a pasta de marcadores.\n\nErro: %error Open downloads folder Download Window Abrir pasta de downloads +Proxy password: Settings Window Senha do proxy: Number of days to keep links in History menu: Settings Window Número de dias para manter vínculos no menu Histórico: Hide Download Window Ocultar Reset size WebPositive Window Restaurar tamanho @@ -67,6 +70,7 @@ There was an error retrieving the bookmark folder.\n\nError: %error WebPositive Over 1 day left Download Window Mais de 1 dia restante Downloads WebPositive Window Itens baixados Requesting %url WebPositive Window Solicitando %url +Find next occurrence of search terms WebPositive Window find bar next button tooltip Localizar a próxima ocorrência dos termos da pesquisa Apply Settings Window Aplicar Bookmark info WebPositive Window Informações de marcador Size: Font Selection view Tamanho: @@ -80,6 +84,7 @@ Open blank page Settings Window Abrir página em branco New tabs: Settings Window Novas guias: Cancel WebPositive Window Cancelar Open all WebPositive Window Abrir tudo +Proxy server requires authentication Settings Window Servidor proxy requer autenticação Clear URL Bar Limpar Cut URL Bar Cortar Clear WebPositive Window Limpar diff --git a/data/catalogs/kits/tracker/pt_BR.catkeys b/data/catalogs/kits/tracker/pt_BR.catkeys index 3699f5ab0d..8e7cffc9e1 100644 --- a/data/catalogs/kits/tracker/pt_BR.catkeys +++ b/data/catalogs/kits/tracker/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-libtracker 3375521561 +1 portuguese (brazil) x-vnd.Haiku-libtracker 4167158175 common B_COMMON_DIRECTORY comum OK WidgetAttributeText OK Icon view VolumeWindow Em ícones @@ -74,6 +74,7 @@ Arrange by ContainerWindow Ordenar por Mount server error AutoMounterSettings Erro do servidor de montagem Search FindPanel Localizar Preparing to empty Trash… StatusWindow Preparando para limpar a Lixeira... +You cannot put the selected item(s) into the trash. FSUtils Não é possível colocar o(s) item(ns) selecionados na lixeira. Disks Model Discos Create link ContainerWindow Criar atalho develop B_COMMON_DEVELOP_DIRECTORY desenvolvimento diff --git a/data/catalogs/preferences/appearance/be.catkeys b/data/catalogs/preferences/appearance/be.catkeys index 2c4e816658..bb295b4e35 100644 --- a/data/catalogs/preferences/appearance/be.catkeys +++ b/data/catalogs/preferences/appearance/be.catkeys @@ -1,4 +1,4 @@ -1 belarusian x-vnd.Haiku-Appearance 2950529393 +1 belarusian x-vnd.Haiku-Appearance 76206318 Plain font: Font view Просты шрыфт: Control highlight Colors tab Выдзяленне кнопак Control border Colors tab Аблямоўка кнопак @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Выкл. Choose Decorator DecorSettingsView Абраць Дэкаратар Success Colors tab Паспяхова Inactive window tab text Colors tab Тэкст неактыўнай укладкі вакна -About Decorator DecorSettingsView Пра Decorator Failure Colors tab Непаспяхова Hinting menu AntialiasingSettingsView Меню хінтынгу Document background Colors tab Фон дакумента @@ -29,7 +28,6 @@ Double: DecorSettingsView Падвойны: Window tab text Colors tab Тэкст загалоўку акна Document text Colors tab Тэкст дакументу Navigation pulse Colors tab Колер падсветкі навігацыі -Window decorator: DecorSettingsView Дэкаратар вокнаў: Selected menu item text Colors tab Тэкст пазначанага пункту меню Menu background Colors tab Фон меню List background Colors tab Фон радкоў diff --git a/data/catalogs/preferences/appearance/de.catkeys b/data/catalogs/preferences/appearance/de.catkeys index de57e8d278..0c016a73d8 100644 --- a/data/catalogs/preferences/appearance/de.catkeys +++ b/data/catalogs/preferences/appearance/de.catkeys @@ -1,4 +1,4 @@ -1 german x-vnd.Haiku-Appearance 2950529393 +1 german x-vnd.Haiku-Appearance 76206318 Plain font: Font view Normal: Control highlight Colors tab Steuerelement - Ausgewählt Control border Colors tab Steuerelement - Rahmen @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Aus Choose Decorator DecorSettingsView Dekorator wählen Success Colors tab Erfolg Inactive window tab text Colors tab Reiter - Text (inaktiv) -About Decorator DecorSettingsView Über Dekorator Failure Colors tab Fehler Hinting menu AntialiasingSettingsView Hinting-Menü Document background Colors tab Dokument - Hintergrund @@ -29,7 +28,6 @@ Double: DecorSettingsView Doppelt: Window tab text Colors tab Reiter - Text Document text Colors tab Dokument - Text Navigation pulse Colors tab Navigation - Leuchtfarbe -Window decorator: DecorSettingsView Fenster-Dekorator: Selected menu item text Colors tab Menü - Text (ausgewählt) Menu background Colors tab Menü - Hintergrund List background Colors tab Liste - Hintergrund diff --git a/data/catalogs/preferences/appearance/el.catkeys b/data/catalogs/preferences/appearance/el.catkeys index 969030c49f..0d8a549ab0 100644 --- a/data/catalogs/preferences/appearance/el.catkeys +++ b/data/catalogs/preferences/appearance/el.catkeys @@ -1,4 +1,4 @@ -1 greek, modern (1453-) x-vnd.Haiku-Appearance 1492316509 +1 greek, modern (1453-) x-vnd.Haiku-Appearance 963859935 Control highlight Colors tab Έλεγχος φωτεινότητας Control border Colors tab Έλεγχος περιγράμματος Antialiasing type: AntialiasingSettingsView Τύπος εξομάλυνσης: @@ -12,7 +12,6 @@ Off AntialiasingSettingsView Κλειστό Choose Decorator DecorSettingsView Επιλέξετε Διακοσμητή Success Colors tab Επιτυχία Inactive window tab text Colors tab Κείμενο της αδρανής καρτέλας παράθυρου -About Decorator DecorSettingsView Σχετικά Με Τον Διακοσμητή Failure Colors tab Αποτυχία Hinting menu AntialiasingSettingsView Μενού υποδείξεων Document background Colors tab Φόντο εγγράφου diff --git a/data/catalogs/preferences/appearance/fi.catkeys b/data/catalogs/preferences/appearance/fi.catkeys index 228500a08e..453267d753 100644 --- a/data/catalogs/preferences/appearance/fi.catkeys +++ b/data/catalogs/preferences/appearance/fi.catkeys @@ -1,4 +1,4 @@ -1 finnish x-vnd.Haiku-Appearance 2950529393 +1 finnish x-vnd.Haiku-Appearance 76206318 Plain font: Font view Pelkkä kirjasin: Control highlight Colors tab Kontrollin korostus Control border Colors tab Kontrollin reuna @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Pois päältä Choose Decorator DecorSettingsView Valitse koristelu Success Colors tab Onnistuminen Inactive window tab text Colors tab Epäaktiivisen ikkunavälilehden tekstin väri -About Decorator DecorSettingsView Koristeluohjelmasta Failure Colors tab Epäonnistuminen Hinting menu AntialiasingSettingsView Vinkkausvalikko Document background Colors tab Dokumentin tausta @@ -29,7 +28,6 @@ Double: DecorSettingsView Kaksikko: Window tab text Colors tab Ikkunavälilehden teksti Document text Colors tab Dokumentin teksti Navigation pulse Colors tab Navigoinnin välke -Window decorator: DecorSettingsView Ikkunan kehystäjä: Selected menu item text Colors tab Valitun valikkovalinnan teksti Menu background Colors tab Valikon tausta List background Colors tab Luettelotausta diff --git a/data/catalogs/preferences/appearance/fr.catkeys b/data/catalogs/preferences/appearance/fr.catkeys index 80db42eb66..4bc08e816d 100644 --- a/data/catalogs/preferences/appearance/fr.catkeys +++ b/data/catalogs/preferences/appearance/fr.catkeys @@ -1,4 +1,4 @@ -1 french x-vnd.Haiku-Appearance 2950529393 +1 french x-vnd.Haiku-Appearance 76206318 Plain font: Font view Police simple : Control highlight Colors tab Mise en valeur de contrôle Control border Colors tab Bordure des contrôles @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Désactivé Choose Decorator DecorSettingsView Choisir un décorateur Success Colors tab Réussite Inactive window tab text Colors tab Texte des titres de fenêtres inactives -About Decorator DecorSettingsView À propos du Décorateur Failure Colors tab Échec Hinting menu AntialiasingSettingsView Menu ajustement Document background Colors tab Arrière plan du document @@ -29,7 +28,6 @@ Double: DecorSettingsView Double : Window tab text Colors tab Texte des titres de fenêtres Document text Colors tab Texte du document Navigation pulse Colors tab Pulsation de navigation -Window decorator: DecorSettingsView Décorateur de fenêtre : Selected menu item text Colors tab Texte de l'élément sélectionné dans le menu Menu background Colors tab Arrière plan de menu List background Colors tab Arrière-plan de la liste diff --git a/data/catalogs/preferences/appearance/hi.catkeys b/data/catalogs/preferences/appearance/hi.catkeys index 987e1045cf..4e3a4e1ad2 100644 --- a/data/catalogs/preferences/appearance/hi.catkeys +++ b/data/catalogs/preferences/appearance/hi.catkeys @@ -1,4 +1,4 @@ -1 hindi x-vnd.Haiku-Appearance 1492316509 +1 hindi x-vnd.Haiku-Appearance 963859935 Control highlight Colors tab उजागर के लिए नियंत्रण Control border Colors tab नियंत्रण सीमा Antialiasing type: AntialiasingSettingsView एंटीएलिअसिंग टाइप: @@ -12,7 +12,6 @@ Off AntialiasingSettingsView बंद Choose Decorator DecorSettingsView डेकोरेटर चुनें Success Colors tab सफलता Inactive window tab text Colors tab निष्क्रिय विंडो टैब टेक्स्ट -About Decorator DecorSettingsView डेकोरेटर के बारे में Failure Colors tab असफलता Hinting menu AntialiasingSettingsView सहाईता के लिए मेनू Document background Colors tab दस्तावेज़ पृष्ठभूमि diff --git a/data/catalogs/preferences/appearance/hu.catkeys b/data/catalogs/preferences/appearance/hu.catkeys index 5c53c5115c..6a06d3017e 100644 --- a/data/catalogs/preferences/appearance/hu.catkeys +++ b/data/catalogs/preferences/appearance/hu.catkeys @@ -1,4 +1,4 @@ -1 hungarian x-vnd.Haiku-Appearance 2950529393 +1 hungarian x-vnd.Haiku-Appearance 76206318 Plain font: Font view Alap betűtípus: Control highlight Colors tab Kiválasztott vezérlőelem Control border Colors tab Vezérlőelem kerete @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Ki Choose Decorator DecorSettingsView Dekoráció kiválasztása Success Colors tab Sikerült Inactive window tab text Colors tab Inaktív ablak címszövege -About Decorator DecorSettingsView Dekoráció névjegye Failure Colors tab Nem sikerült Hinting menu AntialiasingSettingsView Körvonalmenü Document background Colors tab Dokumentum háttere @@ -29,7 +28,6 @@ Double: DecorSettingsView Dupla: Window tab text Colors tab Ablakfül szövege Document text Colors tab Dokumentumszöveg Navigation pulse Colors tab Navigációs pulzálás -Window decorator: DecorSettingsView Ablakdekoráció: Selected menu item text Colors tab Kiválasztott menüelem Menu background Colors tab Menü háttere List background Colors tab Lista háttere diff --git a/data/catalogs/preferences/appearance/ja.catkeys b/data/catalogs/preferences/appearance/ja.catkeys index 41e50d98af..19e414d95c 100644 --- a/data/catalogs/preferences/appearance/ja.catkeys +++ b/data/catalogs/preferences/appearance/ja.catkeys @@ -1,4 +1,4 @@ -1 japanese x-vnd.Haiku-Appearance 2950529393 +1 japanese x-vnd.Haiku-Appearance 76206318 Plain font: Font view 標準フォント: Control highlight Colors tab コントロールのハイライト Control border Colors tab コントロールの境界 @@ -13,7 +13,6 @@ Off AntialiasingSettingsView 無効 Choose Decorator DecorSettingsView デコレーターを選択 Success Colors tab 成功 Inactive window tab text Colors tab 非アクティブウィンドウタブの文字 -About Decorator DecorSettingsView デコレーターについて Failure Colors tab 失敗 Hinting menu AntialiasingSettingsView ヒンティングメニュー Document background Colors tab ドキュメントの背景 @@ -29,7 +28,6 @@ Double: DecorSettingsView 両方向: Window tab text Colors tab ウィンドウタブの文字 Document text Colors tab ドキュメントの文字 Navigation pulse Colors tab ナビゲーションの点滅 -Window decorator: DecorSettingsView ウィンドウデコレーター: Selected menu item text Colors tab メニュー選択項目の文字 Menu background Colors tab メニューの背景 List background Colors tab リストの背景 diff --git a/data/catalogs/preferences/appearance/lt.catkeys b/data/catalogs/preferences/appearance/lt.catkeys index 04680f6f14..955e60ccb7 100644 --- a/data/catalogs/preferences/appearance/lt.catkeys +++ b/data/catalogs/preferences/appearance/lt.catkeys @@ -1,4 +1,4 @@ -1 lithuanian x-vnd.Haiku-Appearance 3654950032 +1 lithuanian x-vnd.Haiku-Appearance 780626957 Plain font: Font view Paprastas šriftas: Control highlight Colors tab Valdiklių paryškinimas Control border Colors tab Valdiklių rėmeliai @@ -13,7 +13,6 @@ Off AntialiasingSettingsView išjungta Choose Decorator DecorSettingsView Išsirinkite dekoruoklį Success Colors tab Sėkminga baigtis Inactive window tab text Colors tab Neaktyvių langų antraščių tekstas -About Decorator DecorSettingsView Apie dekoruoklį Failure Colors tab Nesėkminga baigtis Hinting menu AntialiasingSettingsView Taškinės korekcijos meniu Document background Colors tab Dokumento fonas @@ -28,7 +27,6 @@ Double: DecorSettingsView Dvigubas: Window tab text Colors tab Veikiamojo lango antraštės tekstas Document text Colors tab Dokumento tekstas Navigation pulse Colors tab Veikiamojo elemento (židinio) žybsniai -Window decorator: DecorSettingsView Langų dekoruoklis: Selected menu item text Colors tab Veikiamojo meniu elemento tekstas Menu background Colors tab Meniu fonas List background Colors tab Sąrašo fonas diff --git a/data/catalogs/preferences/appearance/nl.catkeys b/data/catalogs/preferences/appearance/nl.catkeys index f891c198c1..af07531b8d 100644 --- a/data/catalogs/preferences/appearance/nl.catkeys +++ b/data/catalogs/preferences/appearance/nl.catkeys @@ -1,4 +1,4 @@ -1 dutch; flemish x-vnd.Haiku-Appearance 1428480647 +1 dutch; flemish x-vnd.Haiku-Appearance 2849124868 Plain font: Font view Standaardlettertype: Control highlight Colors tab Keuze-accent Control border Colors tab Keuzerand @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Uit Choose Decorator DecorSettingsView Decorator kiezen Success Colors tab Gelukt Inactive window tab text Colors tab Tabtekst niet-actief venster -About Decorator DecorSettingsView Over decorator Failure Colors tab Mislukt Hinting menu AntialiasingSettingsView Hinting-menu Document background Colors tab Achtergrond document @@ -27,7 +26,6 @@ Window border Colors tab Vensterrand Window tab text Colors tab Tekst venstertab Document text Colors tab Tekst document Navigation pulse Colors tab Puls navigatie -Window decorator: DecorSettingsView Vensterdecorator: Selected menu item text Colors tab Tekst geselecteerd menu-item Menu background Colors tab Achtergrond menu OK DecorSettingsView OK diff --git a/data/catalogs/preferences/appearance/pl.catkeys b/data/catalogs/preferences/appearance/pl.catkeys index 71ad0e2d60..4023746798 100644 --- a/data/catalogs/preferences/appearance/pl.catkeys +++ b/data/catalogs/preferences/appearance/pl.catkeys @@ -1,4 +1,4 @@ -1 polish x-vnd.Haiku-Appearance 3956654572 +1 polish x-vnd.Haiku-Appearance 1082331497 Plain font: Font view Zwykła czcionka: Control highlight Colors tab Podkreślenie kontrolki Control border Colors tab Obramowanie kontrolki @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Wyłącz Choose Decorator DecorSettingsView Wybierz Dekorator Success Colors tab Sukces Inactive window tab text Colors tab Tekst nieaktywnej zakładki okna -About Decorator DecorSettingsView O Dekoratorze Failure Colors tab Niepowodzenie Hinting menu AntialiasingSettingsView Menu hintingu Document background Colors tab Tło dokumentu @@ -28,7 +27,6 @@ Window border Colors tab Obramowanie okna Window tab text Colors tab Tekst zakładki okna Document text Colors tab Tekst dokumentu Navigation pulse Colors tab Puls nawigacji -Window decorator: DecorSettingsView Dekorator okna: Selected menu item text Colors tab Tekst zaznaczonego elementu menu Menu background Colors tab Tło menu OK DecorSettingsView OK diff --git a/data/catalogs/preferences/appearance/pt_BR.catkeys b/data/catalogs/preferences/appearance/pt_BR.catkeys index 386d361afb..c4b5ef68e5 100644 --- a/data/catalogs/preferences/appearance/pt_BR.catkeys +++ b/data/catalogs/preferences/appearance/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-Appearance 2950529393 +1 portuguese (brazil) x-vnd.Haiku-Appearance 76206318 Plain font: Font view Fonte plana: Control highlight Colors tab Realce do controle Control border Colors tab Borda do controle @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Desligado Choose Decorator DecorSettingsView Escolher Decorador Success Colors tab Sucesso Inactive window tab text Colors tab Texto da aba de janela inativa -About Decorator DecorSettingsView Sobre o Decorador Failure Colors tab Falha Hinting menu AntialiasingSettingsView Menu de sugestão Document background Colors tab Plano de fundo do documento @@ -29,7 +28,6 @@ Double: DecorSettingsView Duplo: Window tab text Colors tab Texto da aba da Janela Document text Colors tab Texto do documento Navigation pulse Colors tab Navegação de pulso -Window decorator: DecorSettingsView Decorador de janela: Selected menu item text Colors tab Texto do item do menu selecionado Menu background Colors tab Plano de fundo de menu List background Colors tab Plano de fundo da lista diff --git a/data/catalogs/preferences/appearance/ro.catkeys b/data/catalogs/preferences/appearance/ro.catkeys index cf1a2da792..b3657fb994 100644 --- a/data/catalogs/preferences/appearance/ro.catkeys +++ b/data/catalogs/preferences/appearance/ro.catkeys @@ -1,4 +1,4 @@ -1 romanian x-vnd.Haiku-Appearance 1492316509 +1 romanian x-vnd.Haiku-Appearance 963859935 Control highlight Colors tab Punct principal de control Control border Colors tab Margine de control Antialiasing type: AntialiasingSettingsView Tip de anti-alias: @@ -12,7 +12,6 @@ Off AntialiasingSettingsView Oprit Choose Decorator DecorSettingsView Alege decorator Success Colors tab Succes Inactive window tab text Colors tab Text de fereastră tab inactivă -About Decorator DecorSettingsView Despre Decorator Failure Colors tab Eşec Hinting menu AntialiasingSettingsView Meniu de sugestii Document background Colors tab Fundalul documentului diff --git a/data/catalogs/preferences/appearance/ru.catkeys b/data/catalogs/preferences/appearance/ru.catkeys index 3b571e4a7a..311686e2a4 100644 --- a/data/catalogs/preferences/appearance/ru.catkeys +++ b/data/catalogs/preferences/appearance/ru.catkeys @@ -1,4 +1,4 @@ -1 russian x-vnd.Haiku-Appearance 3266657197 +1 russian x-vnd.Haiku-Appearance 392334122 Plain font: Font view Простой шрифт: Control highlight Colors tab Подсветка элемента Control border Colors tab Граница элемента @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Выключить Choose Decorator DecorSettingsView Выберите декоратор Success Colors tab Успех Inactive window tab text Colors tab Текст заголовка неактивного окна -About Decorator DecorSettingsView Об этом декораторе Failure Colors tab Неудача Hinting menu AntialiasingSettingsView Корректировка (хинтинг) Document background Colors tab Фон документа @@ -29,7 +28,6 @@ Double: DecorSettingsView Двойной: Window tab text Colors tab Текст заголовка окна Document text Colors tab Текст документа Navigation pulse Colors tab Навигационная пульсация -Window decorator: DecorSettingsView Оконный декоратор: Selected menu item text Colors tab Текст выделенного пункта меню Menu background Colors tab Фон меню OK DecorSettingsView ОК diff --git a/data/catalogs/preferences/appearance/sk.catkeys b/data/catalogs/preferences/appearance/sk.catkeys index 97d918baf3..0a390d085b 100644 --- a/data/catalogs/preferences/appearance/sk.catkeys +++ b/data/catalogs/preferences/appearance/sk.catkeys @@ -1,4 +1,4 @@ -1 slovak x-vnd.Haiku-Appearance 2950529393 +1 slovak x-vnd.Haiku-Appearance 76206318 Plain font: Font view Základné písmo: Control highlight Colors tab Zvýraznenie ovládacieho prvku Control border Colors tab Okraj ovládacieho prvku @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Vypnutý Choose Decorator DecorSettingsView Vyberte dekorátor Success Colors tab Úspešné Inactive window tab text Colors tab Text záložky neaktívneho okna -About Decorator DecorSettingsView O dekorátore Failure Colors tab Zlyhanie Hinting menu AntialiasingSettingsView Menu s tipmi Document background Colors tab Pozadie dokumentu @@ -29,7 +28,6 @@ Double: DecorSettingsView Dvojité: Window tab text Colors tab Text záložky okna Document text Colors tab Text dokumentu Navigation pulse Colors tab Pulz navigácie -Window decorator: DecorSettingsView Dekorátor okna: Selected menu item text Colors tab Text položky zvoleného menu Menu background Colors tab Pozadie menu List background Colors tab Pozadie zoznamu diff --git a/data/catalogs/preferences/appearance/sv.catkeys b/data/catalogs/preferences/appearance/sv.catkeys index 89d3b0765c..b363651a35 100644 --- a/data/catalogs/preferences/appearance/sv.catkeys +++ b/data/catalogs/preferences/appearance/sv.catkeys @@ -1,4 +1,4 @@ -1 swedish x-vnd.Haiku-Appearance 2950529393 +1 swedish x-vnd.Haiku-Appearance 76206318 Plain font: Font view Vanlig font: Control highlight Colors tab Framhävd kontroll Control border Colors tab Kontrollkant @@ -13,7 +13,6 @@ Off AntialiasingSettingsView Av Choose Decorator DecorSettingsView Välj dekor Success Colors tab Framgång Inactive window tab text Colors tab Fliktext för inaktiva fönster -About Decorator DecorSettingsView Om Dekor Failure Colors tab Misslyckande Hinting menu AntialiasingSettingsView Betoningsmeny Document background Colors tab Dokument bakgrund @@ -29,7 +28,6 @@ Double: DecorSettingsView Dubbel Window tab text Colors tab Fönsterflik text Document text Colors tab Dokument text Navigation pulse Colors tab Navigeringspuls -Window decorator: DecorSettingsView Fönsterdekor: Selected menu item text Colors tab Textfärg för valt menyalternativ Menu background Colors tab Menybakgrund List background Colors tab Lista bakgrund diff --git a/data/catalogs/preferences/appearance/uk.catkeys b/data/catalogs/preferences/appearance/uk.catkeys index db5a4d764d..eb9036900f 100644 --- a/data/catalogs/preferences/appearance/uk.catkeys +++ b/data/catalogs/preferences/appearance/uk.catkeys @@ -1,4 +1,4 @@ -1 ukrainian x-vnd.Haiku-Appearance 1492316509 +1 ukrainian x-vnd.Haiku-Appearance 963859935 Control highlight Colors tab Підсвітка елемента Control border Colors tab Межа елемента Antialiasing type: AntialiasingSettingsView Тип зглажування: @@ -12,7 +12,6 @@ Off AntialiasingSettingsView Слабе Choose Decorator DecorSettingsView Вибір декоратора Success Colors tab Успіх Inactive window tab text Colors tab Текст заголовку неактивного вікна -About Decorator DecorSettingsView Про Декоратор Failure Colors tab Невдача Hinting menu AntialiasingSettingsView Меню коректування Document background Colors tab Тло документа diff --git a/data/catalogs/preferences/appearance/zh_Hans.catkeys b/data/catalogs/preferences/appearance/zh_Hans.catkeys index 758da32a71..885bcdec8b 100644 --- a/data/catalogs/preferences/appearance/zh_Hans.catkeys +++ b/data/catalogs/preferences/appearance/zh_Hans.catkeys @@ -1,4 +1,4 @@ -1 english x-vnd.Haiku-Appearance 2950529393 +1 english x-vnd.Haiku-Appearance 76206318 Plain font: Font view 常规字体 Control highlight Colors tab 控件亮度 Control border Colors tab 控件边界 @@ -13,7 +13,6 @@ Off AntialiasingSettingsView 关闭 Choose Decorator DecorSettingsView 选择装饰 Success Colors tab 成功 Inactive window tab text Colors tab 激活窗口标签 -About Decorator DecorSettingsView 关于 Decorator Failure Colors tab 失败 Hinting menu AntialiasingSettingsView 提示菜单 Document background Colors tab 文档背景 @@ -29,7 +28,6 @@ Double: DecorSettingsView 双行: Window tab text Colors tab 窗口标签文本 Document text Colors tab 文件文本 Navigation pulse Colors tab 导航引导 -Window decorator: DecorSettingsView 窗口装饰: Selected menu item text Colors tab 选择菜单文本 Menu background Colors tab 菜单背景 List background Colors tab 列表背景 diff --git a/data/catalogs/preferences/bluetooth/be.catkeys b/data/catalogs/preferences/bluetooth/be.catkeys index 12110d1ddd..f8319ff6cd 100644 --- a/data/catalogs/preferences/bluetooth/be.catkeys +++ b/data/catalogs/preferences/bluetooth/be.catkeys @@ -1,4 +1,4 @@ -1 belarusian x-vnd.Haiku-BluetoothPrefs 3926935355 +1 belarusian x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window Пра праграму Bluetooth… Handheld Settings view Наладоннік Default inquiry time: Settings view Час стандартнага запыту: @@ -36,7 +36,6 @@ Identify us as... Settings view Ідэнтыфікаваць сябе як... As blocked Remote devices Як блакаваны Bluetooth System name Bluetooth Disconnect Remote devices Адключыцца -Pick LocalDevice... Settings view Выбраць прыладу (LocalDevice)… Defaults Window Прадвызначаныя Inquiry Inquiry panel Запыт Desktop Settings view Рабочы стол diff --git a/data/catalogs/preferences/bluetooth/de.catkeys b/data/catalogs/preferences/bluetooth/de.catkeys index e0ed7d69cf..66eee460a4 100644 --- a/data/catalogs/preferences/bluetooth/de.catkeys +++ b/data/catalogs/preferences/bluetooth/de.catkeys @@ -1,4 +1,4 @@ -1 german x-vnd.Haiku-BluetoothPrefs 3926935355 +1 german x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window Über Bluetooth… Handheld Settings view Handheld Default inquiry time: Settings view Suchdauer: @@ -36,7 +36,6 @@ Identify us as... Settings view Rolle... As blocked Remote devices Blockieren Bluetooth System name Bluetooth Disconnect Remote devices Trennen -Pick LocalDevice... Settings view Gerät... Defaults Window Standardwerte Inquiry Inquiry panel Suchen Desktop Settings view Desktop diff --git a/data/catalogs/preferences/bluetooth/el.catkeys b/data/catalogs/preferences/bluetooth/el.catkeys index 3dc8da04ed..05dba2803d 100644 --- a/data/catalogs/preferences/bluetooth/el.catkeys +++ b/data/catalogs/preferences/bluetooth/el.catkeys @@ -1,4 +1,4 @@ -1 greek, modern (1453-) x-vnd.Haiku-BluetoothPrefs 2526531136 +1 greek, modern (1453-) x-vnd.Haiku-BluetoothPrefs 228189009 About Bluetooth… Window Περί Bluetooth... Handheld Settings view Φορητός Default inquiry time: Settings view Προκαθορισμένος χρόνος έρευνας: @@ -35,7 +35,6 @@ Identify us as... Settings view Αναγνωρίστε μας ως... As blocked Remote devices Ως αποκλεισμένο Bluetooth System name Bluetooth Disconnect Remote devices Αποσύνδεση -Pick LocalDevice... Settings view Επιλογή τοπικής συσκευής Defaults Window Προεπιλογές Inquiry Inquiry panel Αίτηση Desktop Settings view Επιφάνεια εργασίας diff --git a/data/catalogs/preferences/bluetooth/fi.catkeys b/data/catalogs/preferences/bluetooth/fi.catkeys index 1817999cb0..55606a87a3 100644 --- a/data/catalogs/preferences/bluetooth/fi.catkeys +++ b/data/catalogs/preferences/bluetooth/fi.catkeys @@ -1,4 +1,4 @@ -1 finnish x-vnd.Haiku-BluetoothPrefs 3926935355 +1 finnish x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window Bluetooth-ohjelmasta… Handheld Settings view Kädessäpidettävät Default inquiry time: Settings view Oletuskyselyn pituus: @@ -36,7 +36,6 @@ Identify us as... Settings view Tunnista meidät nimellä... As blocked Remote devices Estettynä Bluetooth System name Bluetooth-asetukset Disconnect Remote devices Katkaise yhteys -Pick LocalDevice... Settings view Valitse paikallinen laite... Defaults Window Oletusasetukset Inquiry Inquiry panel Tiedustelu Desktop Settings view Työpöytäasetukset diff --git a/data/catalogs/preferences/bluetooth/fr.catkeys b/data/catalogs/preferences/bluetooth/fr.catkeys index b463c1947a..02ab0c5ab2 100644 --- a/data/catalogs/preferences/bluetooth/fr.catkeys +++ b/data/catalogs/preferences/bluetooth/fr.catkeys @@ -1,4 +1,4 @@ -1 french x-vnd.Haiku-BluetoothPrefs 3926935355 +1 french x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window À propos de Bluetooth… Handheld Settings view Appareil de poche Default inquiry time: Settings view Temps de requête par défaut : @@ -36,7 +36,6 @@ Identify us as... Settings view S'identifier comme… As blocked Remote devices Comme bloqué Bluetooth System name Bluetooth Disconnect Remote devices Déconnexion -Pick LocalDevice... Settings view Choisir un appareil local… Defaults Window Défauts Inquiry Inquiry panel Examiner Desktop Settings view Bureau diff --git a/data/catalogs/preferences/bluetooth/hi.catkeys b/data/catalogs/preferences/bluetooth/hi.catkeys index 77e32f7074..086b1501ca 100644 --- a/data/catalogs/preferences/bluetooth/hi.catkeys +++ b/data/catalogs/preferences/bluetooth/hi.catkeys @@ -1,4 +1,4 @@ -1 hindi x-vnd.Haiku-BluetoothPrefs 3926935355 +1 hindi x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window ब्लूटूथ के बारे में ... Handheld Settings view हैंडहेल्ड Default inquiry time: Settings view डिफ़ॉल्ट जांच का समय: @@ -36,7 +36,6 @@ Identify us as... Settings view हमें पहचानें के र As blocked Remote devices के रूप में बंद Bluetooth System name ब्लूटूथ Disconnect Remote devices डिसकन्नेक्ट -Pick LocalDevice... Settings view उठाओ लोकाल्देविस ... Defaults Window चूक Inquiry Inquiry panel जांच Desktop Settings view डेस्कटॉप diff --git a/data/catalogs/preferences/bluetooth/hu.catkeys b/data/catalogs/preferences/bluetooth/hu.catkeys index 229276205a..bdd4c10eb8 100644 --- a/data/catalogs/preferences/bluetooth/hu.catkeys +++ b/data/catalogs/preferences/bluetooth/hu.catkeys @@ -1,4 +1,4 @@ -1 hungarian x-vnd.Haiku-BluetoothPrefs 3926935355 +1 hungarian x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window A Bluetooth-ról… Handheld Settings view Mobiltelefon Default inquiry time: Settings view Alapértelmezett lekérdezési időkorlát: @@ -36,7 +36,6 @@ Identify us as... Settings view Azonosítása mint… As blocked Remote devices Blokkoltként Bluetooth System name Bluetooth Disconnect Remote devices Kapcsolatbontás -Pick LocalDevice... Settings view Helyi eszköz kiválasztása… Defaults Window Alapértelmezés Inquiry Inquiry panel Lekérdezés Desktop Settings view Asztal diff --git a/data/catalogs/preferences/bluetooth/ja.catkeys b/data/catalogs/preferences/bluetooth/ja.catkeys index 0986553db5..1e7df16295 100644 --- a/data/catalogs/preferences/bluetooth/ja.catkeys +++ b/data/catalogs/preferences/bluetooth/ja.catkeys @@ -1,4 +1,4 @@ -1 japanese x-vnd.Haiku-BluetoothPrefs 3926935355 +1 japanese x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window Bluetoothについて… Handheld Settings view ハンドヘルド機器 Default inquiry time: Settings view デフォルトの問い合わせ時間: @@ -36,7 +36,6 @@ Identify us as... Settings view 自機名… As blocked Remote devices ブロック状態 Bluetooth System name Bluetooth Disconnect Remote devices 切断 -Pick LocalDevice... Settings view ローカルデバイスの選択… Defaults Window デフォルト Inquiry Inquiry panel 問い合わせ Desktop Settings view デスクトップ diff --git a/data/catalogs/preferences/bluetooth/lt.catkeys b/data/catalogs/preferences/bluetooth/lt.catkeys index c9dc8a25b1..7818528221 100644 --- a/data/catalogs/preferences/bluetooth/lt.catkeys +++ b/data/catalogs/preferences/bluetooth/lt.catkeys @@ -1,4 +1,4 @@ -1 lithuanian x-vnd.Haiku-BluetoothPrefs 3926935355 +1 lithuanian x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window Apie… Handheld Settings view delninukas Default inquiry time: Settings view Numatytasis žvalgymo laikas: @@ -36,7 +36,6 @@ Identify us as... Settings view pasirinkite… As blocked Remote devices Kaip blokuotas Bluetooth System name Bluetooth Disconnect Remote devices Atsijungti -Pick LocalDevice... Settings view pasirinkite vietinį įrenginį… Defaults Window Numatytai Inquiry Inquiry panel Žvalgyti Desktop Settings view stalinis kompiuteris diff --git a/data/catalogs/preferences/bluetooth/nl.catkeys b/data/catalogs/preferences/bluetooth/nl.catkeys index b07a414526..3a03dd63ca 100644 --- a/data/catalogs/preferences/bluetooth/nl.catkeys +++ b/data/catalogs/preferences/bluetooth/nl.catkeys @@ -1,4 +1,4 @@ -1 dutch; flemish x-vnd.Haiku-BluetoothPrefs 3926935355 +1 dutch; flemish x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window Over Bluetooth… Handheld Settings view Handheld Default inquiry time: Settings view Standaard onderzoekstijd: @@ -36,7 +36,6 @@ Identify us as... Settings view Identificeer ons als... As blocked Remote devices Als geblokkeerd Bluetooth System name Bluetooth Disconnect Remote devices Verbreek verbinding -Pick LocalDevice... Settings view Kies een lokaal apparaat... Defaults Window Standaardwaarden Inquiry Inquiry panel Onderzoek Desktop Settings view Bureaublad diff --git a/data/catalogs/preferences/bluetooth/pl.catkeys b/data/catalogs/preferences/bluetooth/pl.catkeys index 51c6227c9e..ff89c01a8f 100644 --- a/data/catalogs/preferences/bluetooth/pl.catkeys +++ b/data/catalogs/preferences/bluetooth/pl.catkeys @@ -1,4 +1,4 @@ -1 polish x-vnd.Haiku-BluetoothPrefs 3926935355 +1 polish x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window O aplikacji Bluetooth… Handheld Settings view Handheld Default inquiry time: Settings view Domyślny czas zapytania: @@ -36,7 +36,6 @@ Identify us as... Settings view Identyfikuj nas jako... As blocked Remote devices Jako zablokowany Bluetooth System name Bluetooth Disconnect Remote devices Rozłączony -Pick LocalDevice... Settings view Wybierz urządzenie lokalne... Defaults Window Domyślne Inquiry Inquiry panel Zapytaj Desktop Settings view Pulpit diff --git a/data/catalogs/preferences/bluetooth/pt_BR.catkeys b/data/catalogs/preferences/bluetooth/pt_BR.catkeys index bfc7400df3..26cf922223 100644 --- a/data/catalogs/preferences/bluetooth/pt_BR.catkeys +++ b/data/catalogs/preferences/bluetooth/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-BluetoothPrefs 3926935355 +1 portuguese (brazil) x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window Sobre Bluetooth… Handheld Settings view Handheld Default inquiry time: Settings view Tempo de consulta padrão: @@ -36,7 +36,6 @@ Identify us as... Settings view Identificar-nos como… As blocked Remote devices Como bloqueado Bluetooth System name Bluetooth Disconnect Remote devices Desconectar -Pick LocalDevice... Settings view Escolher dispositivo local... Defaults Window Padrões Inquiry Inquiry panel Inquérito Desktop Settings view Desktop diff --git a/data/catalogs/preferences/bluetooth/ro.catkeys b/data/catalogs/preferences/bluetooth/ro.catkeys index 9b606f90a1..5de1e47a2b 100644 --- a/data/catalogs/preferences/bluetooth/ro.catkeys +++ b/data/catalogs/preferences/bluetooth/ro.catkeys @@ -1,4 +1,4 @@ -1 romanian x-vnd.Haiku-BluetoothPrefs 2526531136 +1 romanian x-vnd.Haiku-BluetoothPrefs 228189009 About Bluetooth… Window Despre Bluetooth... Handheld Settings view Calculator de buzunar Default inquiry time: Settings view Timp implicit de întrebare: @@ -35,7 +35,6 @@ Identify us as... Settings view Identificare ca... As blocked Remote devices Ca blocat Bluetooth System name Bluetooth Disconnect Remote devices Deconectează -Pick LocalDevice... Settings view Selectează dispozitiv local... Defaults Window Implicite Inquiry Inquiry panel Întrebare Desktop Settings view Desktop diff --git a/data/catalogs/preferences/bluetooth/ru.catkeys b/data/catalogs/preferences/bluetooth/ru.catkeys index fdc09c8026..5145226bac 100644 --- a/data/catalogs/preferences/bluetooth/ru.catkeys +++ b/data/catalogs/preferences/bluetooth/ru.catkeys @@ -1,4 +1,4 @@ -1 russian x-vnd.Haiku-BluetoothPrefs 3926935355 +1 russian x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window О программе… Handheld Settings view Переносной Default inquiry time: Settings view Время запроса по умолчанию: @@ -36,7 +36,6 @@ Identify us as... Settings view Идентифицировать нас как As blocked Remote devices Как блокированный Bluetooth System name Bluetooth Disconnect Remote devices Отключить -Pick LocalDevice... Settings view Выбор локального устройства… Defaults Window По умолчанию Inquiry Inquiry panel Запрос Desktop Settings view Рабочий стол diff --git a/data/catalogs/preferences/bluetooth/sk.catkeys b/data/catalogs/preferences/bluetooth/sk.catkeys index d8aba20455..946fb78d1c 100644 --- a/data/catalogs/preferences/bluetooth/sk.catkeys +++ b/data/catalogs/preferences/bluetooth/sk.catkeys @@ -1,4 +1,4 @@ -1 slovak x-vnd.Haiku-BluetoothPrefs 3926935355 +1 slovak x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window O aplikácii Bluetooth… Handheld Settings view Handheld Default inquiry time: Settings view Predvolený čas zisťovania: @@ -36,7 +36,6 @@ Identify us as... Settings view Identifikovať sa ako... As blocked Remote devices Ako blokované Bluetooth System name Bluetooth Disconnect Remote devices Odpojiť -Pick LocalDevice... Settings view Vybrať lokálne zariadenie... Defaults Window Predvolené Inquiry Inquiry panel Zistenie Desktop Settings view Plocha diff --git a/data/catalogs/preferences/bluetooth/sv.catkeys b/data/catalogs/preferences/bluetooth/sv.catkeys index a3a7e117f2..7450e2a136 100644 --- a/data/catalogs/preferences/bluetooth/sv.catkeys +++ b/data/catalogs/preferences/bluetooth/sv.catkeys @@ -1,4 +1,4 @@ -1 swedish x-vnd.Haiku-BluetoothPrefs 3926935355 +1 swedish x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window Om Bluetooth… Handheld Settings view Handhållen Default inquiry time: Settings view Förvald frågotid: @@ -36,7 +36,6 @@ Identify us as... Settings view Identifiera som... As blocked Remote devices Som blockerad Bluetooth System name Bluetooth Disconnect Remote devices Koppla från -Pick LocalDevice... Settings view Välj lokal enhet... Defaults Window Förval Inquiry Inquiry panel Förfrågan Desktop Settings view Skrivbord diff --git a/data/catalogs/preferences/bluetooth/uk.catkeys b/data/catalogs/preferences/bluetooth/uk.catkeys index 1505d8261b..b825e7d7bb 100644 --- a/data/catalogs/preferences/bluetooth/uk.catkeys +++ b/data/catalogs/preferences/bluetooth/uk.catkeys @@ -1,4 +1,4 @@ -1 ukrainian x-vnd.Haiku-BluetoothPrefs 2526531136 +1 ukrainian x-vnd.Haiku-BluetoothPrefs 228189009 About Bluetooth… Window Про Bluetooth… Handheld Settings view Переносний Default inquiry time: Settings view Час запиту по замовчуванню: @@ -35,7 +35,6 @@ Identify us as... Settings view Визначити нас як… As blocked Remote devices Як заблокований Bluetooth System name Bluetooth Disconnect Remote devices Роз’єднати -Pick LocalDevice... Settings view Вибір локального приладу… Defaults Window По замовчуванню Inquiry Inquiry panel Запит Desktop Settings view Робочий стіл diff --git a/data/catalogs/preferences/bluetooth/zh_Hans.catkeys b/data/catalogs/preferences/bluetooth/zh_Hans.catkeys index f39c58694c..a6ad1e2074 100644 --- a/data/catalogs/preferences/bluetooth/zh_Hans.catkeys +++ b/data/catalogs/preferences/bluetooth/zh_Hans.catkeys @@ -1,4 +1,4 @@ -1 english x-vnd.Haiku-BluetoothPrefs 3926935355 +1 english x-vnd.Haiku-BluetoothPrefs 1628593228 About Bluetooth… Window 关于蓝牙... Handheld Settings view 手持设备 Default inquiry time: Settings view 默认查询时间: @@ -36,7 +36,6 @@ Identify us as... Settings view 身份识别... As blocked Remote devices 阻塞状态 Bluetooth System name 蓝牙 Disconnect Remote devices 断开连接 -Pick LocalDevice... Settings view 选择本地设备... Defaults Window 默认 Inquiry Inquiry panel 查询 Desktop Settings view 桌面电脑 diff --git a/data/catalogs/preferences/network/pt_BR.catkeys b/data/catalogs/preferences/network/pt_BR.catkeys index ed0770a887..1c935884a0 100644 --- a/data/catalogs/preferences/network/pt_BR.catkeys +++ b/data/catalogs/preferences/network/pt_BR.catkeys @@ -1,22 +1,27 @@ -1 portuguese (brazil) x-vnd.Haiku-Network 365183238 +1 portuguese (brazil) x-vnd.Haiku-Network 1341378870 Choose automatically EthernetSettingsView Escolher automaticamente Gateway: EthernetSettingsView Gateway: Netmask: EthernetSettingsView Máscara de rede: DHCP EthernetSettingsView DHCP DNS #2: EthernetSettingsView DNS #2: Apply EthernetSettingsView Aplicar +Netmask is invalid EthernetSettingsView Máscara de rede é inválida OK EthernetSettingsView OK DNS #1: EthernetSettingsView DNS #1: IP address: EthernetSettingsView Endereço de IP: Adapter: EthernetSettingsView Adaptador: Domain: EthernetSettingsView Domínio: +Gateway is invalid EthernetSettingsView O Gateway é inválido +DNS #1 is invalid EthernetSettingsView DNS #1 é inválido Revert EthernetSettingsView Reverter EthernetSettingsView Network System name Rede Mode: EthernetSettingsView Modo: +IP address is invalid EthernetSettingsView O endereço IP é inválido Network: EthernetSettingsView Rede: The net_server needs to run for the auto configuration! EthernetSettingsView É preciso abrir o net_server para efetuar a configuração automática! Disabled EthernetSettingsView Desativado Auto-configuring failed: EthernetSettingsView Auto-configuração falhou: Static EthernetSettingsView Estático +DNS #2 is invalid EthernetSettingsView DNS #2 é inválido EthernetSettingsView diff --git a/data/catalogs/preferences/notifications/pt_BR.catkeys b/data/catalogs/preferences/notifications/pt_BR.catkeys index cd56072f22..6c7df4f95d 100644 --- a/data/catalogs/preferences/notifications/pt_BR.catkeys +++ b/data/catalogs/preferences/notifications/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-Notifications 814394708 +1 portuguese (brazil) x-vnd.Haiku-Notifications 2177286129 An error occurred saving the preferences.\nIt's possible you are running out of disk space. GeneralView Um erro ocorreu salvando as preferências.\nÉ possível que esteja executando fora do espaço de disco. Notifications GeneralView Notificações seconds of inactivity GeneralView segundos de inatividade @@ -17,6 +17,7 @@ Cannot disable notifications because the server can't be reached. GeneralView N Progress NotificationView Progresso Last Received NotificationView Último Recebido General PrefletView Geral +Apply PrefletWin Aplicar Display PrefletView Tela Can't enable notifications at startup time, you probably don't have write permission to the boot settings directory. GeneralView Não é possível habilitar notificações em tempo de inicialização, provavelmente não há permissão de escrita para o diretório de definições de inicialização. Search: NotificationView Pesquisar: diff --git a/data/catalogs/preferences/time/pt_BR.catkeys b/data/catalogs/preferences/time/pt_BR.catkeys index e8a2116993..741b6eb541 100644 --- a/data/catalogs/preferences/time/pt_BR.catkeys +++ b/data/catalogs/preferences/time/pt_BR.catkeys @@ -1,4 +1,4 @@ -1 portuguese (brazil) x-vnd.Haiku-Time 3544635877 +1 portuguese (brazil) x-vnd.Haiku-Time 3259467657 GMT (UNIX compatible) Time GMT (compatível com UNIX) OK Time OK Asia Time Ásia @@ -11,6 +11,7 @@ Preview time: Time Pré-visualização de tempo: Synchronize Time Sincronizar Revert Time Reverter Pacific Time Pacífico +Show day of week Time Exibir dia da semana Add Time Adicionar Date and time Time Data e hora about Time sobre @@ -26,6 +27,7 @@ Time Time Hora Indian Time Índico Sending request failed Time Falha ao enviar solicitação Arctic Time Ártico +Display time with seconds Time Mostrar a hora com os segundos Time System name Hora America Time América Reset Time Restaurar @@ -33,6 +35,8 @@ Synchronize at boot Time Sincronizar ao inicializar Time & Date, written by:\n\n\tAndrew Edward McCall\n\tMike Berg\n\tJulun\n\tPhilippe Saint-Pierre\n\nCopyright 2004-2012, Haiku. Time Hora & Data, escrito por:\n\n\t Andrew Edward McCall\n\t Mike Berg\n\t Julun\n\t Philippe Saint-Pierre\n\nDireitos reservados 2004-2012, Haiku. Received invalid time Time Recebida hora inválida Antarctica Time Antártica +Show time zone Time Mostrar fuso horário +Show clock in Deskbar Time Exibir relógio na Deskbar The following error occured while synchronizing:r\n%s: %s Time Ocorreu o seguinte erro ao sincronizar:r\n%s: %s Time Current time: Time Hora atual: diff --git a/headers/os/drivers/fs_interface.h b/headers/os/drivers/fs_interface.h index 03d867b131..1b3f0be8f7 100644 --- a/headers/os/drivers/fs_interface.h +++ b/headers/os/drivers/fs_interface.h @@ -242,6 +242,14 @@ struct fs_vnode_ops { fs_vnode* _superVnode, ino_t* _nodeID); status_t (*get_super_vnode)(fs_volume* volume, fs_vnode* vnode, fs_volume* superVolume, fs_vnode* superVnode); + + /* lock operations */ + status_t (*test_lock)(fs_volume* volume, fs_vnode* vnode, void* cookie, + struct flock* lock); + status_t (*acquire_lock)(fs_volume* volume, fs_vnode* vnode, void* cookie, + const struct flock* lock, bool wait); + status_t (*release_lock)(fs_volume* volume, fs_vnode* vnode, void* cookie, + const struct flock* lock); }; struct file_system_module_info { diff --git a/headers/private/kernel/util/AVLTreeMap.h b/headers/private/kernel/util/AVLTreeMap.h index ea436be3c1..051f4e7fdd 100644 --- a/headers/private/kernel/util/AVLTreeMap.h +++ b/headers/private/kernel/util/AVLTreeMap.h @@ -269,6 +269,7 @@ _AVL_TREE_MAP_CLASS_NAME::MakeEmpty() { AVLTreeNode* root = fTree.Root(); _FreeTree(root); + fTree.MakeEmpty(); } diff --git a/headers/private/net/dns_resolver.h b/headers/private/net/dns_resolver.h new file mode 100644 index 0000000000..d868ca0381 --- /dev/null +++ b/headers/private/net/dns_resolver.h @@ -0,0 +1,58 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef DNS_RESOLVER_H +#define DNS_RESOLVER_H + + +#include +#include + +#include + + +#define DNS_RESOLVER_MODULE_NAME "network/dns_resolver/v1" + + +struct dns_resolver_module { + module_info module; + status_t (*getaddrinfo)(const char* node, const char* service, + const struct addrinfo* hints, struct addrinfo** res); +}; + + +static inline int +kgetaddrinfo(const char* node, const char* service, + const struct addrinfo* hints, struct addrinfo** res) +{ + dns_resolver_module* dns; + status_t result = get_module(DNS_RESOLVER_MODULE_NAME, + reinterpret_cast(&dns)); + if (result != B_OK) + return result; + + result = dns->getaddrinfo(node, service, hints, res); + + put_module(DNS_RESOLVER_MODULE_NAME); + + return result; +} + + +static inline void +kfreeaddrinfo(struct addrinfo* res) +{ + free(res); +} + + +#define getaddrinfo kgetaddrinfo +#define freeaddrinfo kfreeaddrinfo + + +#endif // DNS_RESOLVER_H + diff --git a/src/add-ons/accelerants/intel_extreme/mode.cpp b/src/add-ons/accelerants/intel_extreme/mode.cpp index f9bf68f43c..76742c1d5d 100644 --- a/src/add-ons/accelerants/intel_extreme/mode.cpp +++ b/src/add-ons/accelerants/intel_extreme/mode.cpp @@ -123,122 +123,6 @@ set_i2c_signals(void* cookie, int clock, int data) } -void -set_frame_buffer_base() -{ - intel_shared_info &sharedInfo = *gInfo->shared_info; - display_mode &mode = sharedInfo.current_mode; - uint32 baseRegister; - uint32 surfaceRegister; - - if (gInfo->head_mode & HEAD_MODE_A_ANALOG) { - baseRegister = INTEL_DISPLAY_A_BASE; - surfaceRegister = INTEL_DISPLAY_A_SURFACE; - } else { - baseRegister = INTEL_DISPLAY_B_BASE; - surfaceRegister = INTEL_DISPLAY_B_SURFACE; - } - - if (sharedInfo.device_type.InGroup(INTEL_TYPE_96x) - || sharedInfo.device_type.InGroup(INTEL_TYPE_G4x) - || sharedInfo.device_type.InGroup(INTEL_TYPE_ILK) - || sharedInfo.device_type.InGroup(INTEL_TYPE_SNB) - || sharedInfo.device_type.InGroup(INTEL_TYPE_IVB)) { - write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row - + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); - read32(baseRegister); - write32(surfaceRegister, sharedInfo.frame_buffer_offset); - read32(surfaceRegister); - } else { - write32(baseRegister, sharedInfo.frame_buffer_offset - + mode.v_display_start * sharedInfo.bytes_per_row - + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); - read32(baseRegister); - } -} - - -/*! Creates the initial mode list of the primary accelerant. - It's called from intel_init_accelerant(). -*/ -status_t -create_mode_list(void) -{ - i2c_bus bus; - bus.cookie = (void*)INTEL_I2C_IO_A; - bus.set_signals = &set_i2c_signals; - bus.get_signals = &get_i2c_signals; - ddc2_init_timing(&bus); - - status_t error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL); - if (error == B_OK) { - edid_dump(&gInfo->edid_info); - gInfo->has_edid = true; - } else { - TRACE("getting EDID on port A (analog) failed : %s. " - "Trying on port C (lvds)\n", strerror(error)); - bus.cookie = (void*)INTEL_I2C_IO_C; - error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL); - if (error == B_OK) { - edid_dump(&gInfo->edid_info); - gInfo->has_edid = true; - } else { - TRACE("getting EDID on port C failed : %s\n", - strerror(error)); - - // We could not read any EDID info. Fallback to creating a list with - // only the mode set up by the BIOS. - // TODO: support lower modes via scaling and windowing - if ((gInfo->head_mode & HEAD_MODE_LVDS_PANEL) != 0 - && (gInfo->head_mode & HEAD_MODE_A_ANALOG) == 0) { - size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1) - & ~(B_PAGE_SIZE - 1); - - display_mode* list; - area_id area = create_area("intel extreme modes", - (void**)&list, B_ANY_ADDRESS, size, B_NO_LOCK, - B_READ_AREA | B_WRITE_AREA); - if (area < B_OK) - return area; - - memcpy(list, &gInfo->lvds_panel_mode, sizeof(display_mode)); - - gInfo->mode_list_area = area; - gInfo->mode_list = list; - gInfo->shared_info->mode_list_area = gInfo->mode_list_area; - gInfo->shared_info->mode_count = 1; - return B_OK; - } - } - } - - // Otherwise return the 'real' list of modes - display_mode* list; - uint32 count = 0; - gInfo->mode_list_area = create_display_modes("intel extreme modes", - gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL, - &list, &count); - if (gInfo->mode_list_area < B_OK) - return gInfo->mode_list_area; - - gInfo->mode_list = list; - gInfo->shared_info->mode_list_area = gInfo->mode_list_area; - gInfo->shared_info->mode_count = count; - - return B_OK; -} - - -void -wait_for_vblank(void) -{ - acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, - 25000); - // With the output turned off via DPMS, we might not get any interrupts - // anymore that's why we don't wait forever for it. -} - - static void get_pll_limits(pll_limits &limits) { @@ -398,7 +282,7 @@ compute_pll_divisors(const display_mode ¤t, pll_divisors& divisors, } -void +static void retrieve_current_mode(display_mode& mode, uint32 pllRegister) { uint32 pll = read32(pllRegister); @@ -551,19 +435,6 @@ retrieve_current_mode(display_mode& mode, uint32 pllRegister) } -/*! Store away panel information if identified on startup - (used for pipe B->lvds). -*/ -void -save_lvds_mode(void) -{ - // dump currently programmed mode. - display_mode biosMode; - retrieve_current_mode(biosMode, INTEL_DISPLAY_B_PLL); - gInfo->lvds_panel_mode = biosMode; -} - - static void get_color_space_format(const display_mode &mode, uint32 &colorMode, uint32 &bytesPerRow, uint32 &bitsPerPixel) @@ -633,6 +504,143 @@ sanitize_display_mode(display_mode& mode) } +// #pragma mark - + + +void +set_frame_buffer_base() +{ + intel_shared_info &sharedInfo = *gInfo->shared_info; + display_mode &mode = sharedInfo.current_mode; + uint32 baseRegister; + uint32 surfaceRegister; + + if (gInfo->head_mode & HEAD_MODE_A_ANALOG) { + baseRegister = INTEL_DISPLAY_A_BASE; + surfaceRegister = INTEL_DISPLAY_A_SURFACE; + } else { + baseRegister = INTEL_DISPLAY_B_BASE; + surfaceRegister = INTEL_DISPLAY_B_SURFACE; + } + + if (sharedInfo.device_type.InGroup(INTEL_TYPE_96x) + || sharedInfo.device_type.InGroup(INTEL_TYPE_G4x) + || sharedInfo.device_type.InGroup(INTEL_TYPE_ILK) + || sharedInfo.device_type.InGroup(INTEL_TYPE_SNB) + || sharedInfo.device_type.InGroup(INTEL_TYPE_IVB)) { + write32(baseRegister, mode.v_display_start * sharedInfo.bytes_per_row + + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); + read32(baseRegister); + write32(surfaceRegister, sharedInfo.frame_buffer_offset); + read32(surfaceRegister); + } else { + write32(baseRegister, sharedInfo.frame_buffer_offset + + mode.v_display_start * sharedInfo.bytes_per_row + + mode.h_display_start * (sharedInfo.bits_per_pixel + 7) / 8); + read32(baseRegister); + } +} + + +/*! Creates the initial mode list of the primary accelerant. + It's called from intel_init_accelerant(). +*/ +status_t +create_mode_list(void) +{ + i2c_bus bus; + bus.cookie = (void*)INTEL_I2C_IO_A; + bus.set_signals = &set_i2c_signals; + bus.get_signals = &get_i2c_signals; + ddc2_init_timing(&bus); + + status_t error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL); + if (error == B_OK) { + edid_dump(&gInfo->edid_info); + gInfo->has_edid = true; + } else { + TRACE("getting EDID on port A (analog) failed : %s. " + "Trying on port C (lvds)\n", strerror(error)); + bus.cookie = (void*)INTEL_I2C_IO_C; + error = ddc2_read_edid1(&bus, &gInfo->edid_info, NULL, NULL); + if (error == B_OK) { + edid_dump(&gInfo->edid_info); + gInfo->has_edid = true; + } else { + TRACE("getting EDID on port C failed : %s\n", + strerror(error)); + + // We could not read any EDID info. Fallback to creating a list with + // only the mode set up by the BIOS. + // TODO: support lower modes via scaling and windowing + if ((gInfo->head_mode & HEAD_MODE_LVDS_PANEL) != 0 + && (gInfo->head_mode & HEAD_MODE_A_ANALOG) == 0) { + size_t size = (sizeof(display_mode) + B_PAGE_SIZE - 1) + & ~(B_PAGE_SIZE - 1); + + display_mode* list; + area_id area = create_area("intel extreme modes", + (void**)&list, B_ANY_ADDRESS, size, B_NO_LOCK, + B_READ_AREA | B_WRITE_AREA); + if (area < B_OK) + return area; + + memcpy(list, &gInfo->lvds_panel_mode, sizeof(display_mode)); + + gInfo->mode_list_area = area; + gInfo->mode_list = list; + gInfo->shared_info->mode_list_area = gInfo->mode_list_area; + gInfo->shared_info->mode_count = 1; + return B_OK; + } + } + } + + // Otherwise return the 'real' list of modes + display_mode* list; + uint32 count = 0; + gInfo->mode_list_area = create_display_modes("intel extreme modes", + gInfo->has_edid ? &gInfo->edid_info : NULL, NULL, 0, NULL, 0, NULL, + &list, &count); + if (gInfo->mode_list_area < B_OK) + return gInfo->mode_list_area; + + gInfo->mode_list = list; + gInfo->shared_info->mode_list_area = gInfo->mode_list_area; + gInfo->shared_info->mode_count = count; + + return B_OK; +} + + +void +wait_for_vblank(void) +{ + acquire_sem_etc(gInfo->shared_info->vblank_sem, 1, B_RELATIVE_TIMEOUT, + 25000); + // With the output turned off via DPMS, we might not get any interrupts + // anymore that's why we don't wait forever for it. +} + + +/*! Store away panel information if identified on startup + (used for pipe B->lvds). +*/ +void +save_lvds_mode(void) +{ + // dump currently programmed mode. + display_mode biosMode; + retrieve_current_mode(biosMode, INTEL_DISPLAY_B_PLL); + + sanitize_display_mode(biosMode); + // The BIOS mode may not be a valid mode, as LVDS output does not + // really care about the sync values + + gInfo->lvds_panel_mode = biosMode; +} + + // #pragma mark - @@ -667,14 +675,14 @@ intel_propose_display_mode(display_mode* target, const display_mode* low, // configurations. for (uint32 i = 0; i < gInfo->shared_info->mode_count; i++) { display_mode *mode = &gInfo->mode_list[i]; - + // TODO: improve this, ie. adapt pixel clock to allowed values!!! - + if (target->virtual_width != mode->virtual_width || target->virtual_height != mode->virtual_height || target->space != mode->space) continue; - + *target = *mode; return B_OK; } diff --git a/src/add-ons/kernel/file_systems/Jamfile b/src/add-ons/kernel/file_systems/Jamfile index 24fe5eb6a1..25bb03651a 100644 --- a/src/add-ons/kernel/file_systems/Jamfile +++ b/src/add-ons/kernel/file_systems/Jamfile @@ -11,6 +11,7 @@ SubInclude HAIKU_TOP src add-ons kernel file_systems googlefs ; SubInclude HAIKU_TOP src add-ons kernel file_systems iso9660 ; SubInclude HAIKU_TOP src add-ons kernel file_systems netfs ; SubInclude HAIKU_TOP src add-ons kernel file_systems nfs ; +SubInclude HAIKU_TOP src add-ons kernel file_systems nfs4 ; SubInclude HAIKU_TOP src add-ons kernel file_systems ntfs ; SubInclude HAIKU_TOP src add-ons kernel file_systems packagefs ; SubInclude HAIKU_TOP src add-ons kernel file_systems ramfs ; diff --git a/src/add-ons/kernel/file_systems/nfs4/Connection.cpp b/src/add-ons/kernel/file_systems/nfs4/Connection.cpp new file mode 100644 index 0000000000..937e45ed1b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Connection.cpp @@ -0,0 +1,798 @@ +/* + * Copyright 2012-2013 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Connection.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + + +#define NFS4_PORT 2049 + +#define LAST_FRAGMENT 0x80000000 +#define MAX_PACKET_SIZE 65535 + +#define NFS_MIN_PORT 665 + + +bool +PeerAddress::operator==(const PeerAddress& address) +{ + return memcmp(&fAddress, &address.fAddress, sizeof(fAddress)) == 0 + && fProtocol == address.fProtocol; +} + + +bool +PeerAddress::operator<(const PeerAddress& address) +{ + int compare = memcmp(&fAddress, &address.fAddress, sizeof(fAddress)); + return compare < 0 || (compare == 0 && fProtocol < address.fProtocol); +} + + +PeerAddress& +PeerAddress::operator=(const PeerAddress& address) +{ + fAddress = address.fAddress; + fProtocol = address.fProtocol; + return *this; +} + + +PeerAddress::PeerAddress() + : + fProtocol(0) +{ + memset(&fAddress, 0, sizeof(fAddress)); +} + + +PeerAddress::PeerAddress(int networkFamily) + : + fProtocol(0) +{ + ASSERT(networkFamily == AF_INET || networkFamily == AF_INET6); + + memset(&fAddress, 0, sizeof(fAddress)); + + fAddress.ss_family = networkFamily; + switch (networkFamily) { + case AF_INET: + fAddress.ss_len = sizeof(sockaddr_in); + break; + case AF_INET6: + fAddress.ss_len = sizeof(sockaddr_in6); + break; + } +} + + +const char* +PeerAddress::ProtocolString() const +{ + static const char* tcpName = "tcp"; + static const char* udpName = "udp"; + static const char* unknown = ""; + + switch (fProtocol) { + case IPPROTO_TCP: + return tcpName; + case IPPROTO_UDP: + return udpName; + default: + return unknown; + } +} + + +void +PeerAddress::SetProtocol(const char* protocol) +{ + ASSERT(protocol != NULL); + + if (strcmp(protocol, "tcp") == 0) + fProtocol = IPPROTO_TCP; + else if (strcmp(protocol, "udp") == 0) + fProtocol = IPPROTO_UDP; +} + + +char* +PeerAddress::UniversalAddress() const +{ + char* uAddr = reinterpret_cast(malloc(INET6_ADDRSTRLEN + 16)); + if (uAddr == NULL) + return NULL; + + if (inet_ntop(fAddress.ss_family, InAddr(), uAddr, AddressSize()) == NULL) + return NULL; + + char port[16]; + sprintf(port, ".%d.%d", Port() >> 8, Port() & 0xff); + strcat(uAddr, port); + + return uAddr; +} + + +socklen_t +PeerAddress::AddressSize() const +{ + switch (Family()) { + case AF_INET: + return sizeof(sockaddr_in); + case AF_INET6: + return sizeof(sockaddr_in6); + default: + return 0; + } +} + + +uint16 +PeerAddress::Port() const +{ + uint16 port; + + switch (Family()) { + case AF_INET: + port = reinterpret_cast(&fAddress)->sin_port; + break; + case AF_INET6: + port = reinterpret_cast(&fAddress)->sin6_port; + break; + default: + port = 0; + } + + return ntohs(port); +} + + +void +PeerAddress::SetPort(uint16 port) +{ + port = htons(port); + + switch (Family()) { + case AF_INET: + reinterpret_cast(&fAddress)->sin_port = port; + break; + case AF_INET6: + reinterpret_cast(&fAddress)->sin6_port = port; + break; + } +} + +const void* +PeerAddress::InAddr() const +{ + switch (Family()) { + case AF_INET: + return &reinterpret_cast(&fAddress)->sin_addr; + case AF_INET6: + return &reinterpret_cast(&fAddress)->sin6_addr; + default: + return NULL; + } +} + + +size_t +PeerAddress::InAddrSize() const +{ + switch (Family()) { + case AF_INET: + return sizeof(in_addr); + case AF_INET6: + return sizeof(in6_addr); + default: + return 0; + } +} + + +AddressResolver::AddressResolver(const char* name) + : + fHead(NULL), + fCurrent(NULL), + fForcedPort(htons(NFS4_PORT)), + fForcedProtocol(IPPROTO_TCP) +{ + fStatus = ResolveAddress(name); +} + + +AddressResolver::~AddressResolver() +{ + freeaddrinfo(fHead); +} + + +status_t +AddressResolver::ResolveAddress(const char* name) +{ + ASSERT(name != NULL); + + if (fHead != NULL) { + freeaddrinfo(fHead); + fHead = NULL; + fCurrent = NULL; + } + + // getaddrinfo() is very expensive when called from kernel, so we do not + // want to call it unless there is no other choice. + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + if (inet_aton(name, &addr.sin_addr) == 1) { + addr.sin_len = sizeof(addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(NFS4_PORT); + + memcpy(&fAddress.fAddress, &addr, sizeof(addr)); + fAddress.fProtocol = IPPROTO_TCP; + return B_OK; + } + + status_t result = getaddrinfo(name, NULL, NULL, &fHead); + fCurrent = fHead; + + return result; +} + + +void +AddressResolver::ForceProtocol(const char* protocol) +{ + ASSERT(protocol != NULL); + + if (strcmp(protocol, "tcp") == 0) + fForcedProtocol = IPPROTO_TCP; + else if (strcmp(protocol, "udp") == 0) + fForcedProtocol = IPPROTO_UDP; + + fAddress.SetProtocol(protocol); +} + + +void +AddressResolver::ForcePort(uint16 port) +{ + fForcedPort = htons(port); + fAddress.SetPort(port); +} + + +status_t +AddressResolver::GetNextAddress(PeerAddress* address) +{ + ASSERT(address != NULL); + + if (fStatus != B_OK) + return fStatus; + + if (fHead == NULL) { + *address = fAddress; + fStatus = B_NAME_NOT_FOUND; + return B_OK; + } + + address->fProtocol = fForcedProtocol; + + while (fCurrent != NULL) { + if (fCurrent->ai_family == AF_INET) { + memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in)); + reinterpret_cast(&address->fAddress)->sin_port + = fForcedPort; + } else if (fCurrent->ai_family == AF_INET6) { + memcpy(&address->fAddress, fCurrent->ai_addr, sizeof(sockaddr_in6)); + reinterpret_cast(&address->fAddress)->sin6_port + = fForcedPort; + } else { + fCurrent = fCurrent->ai_next; + continue; + } + + fCurrent = fCurrent->ai_next; + return B_OK; + } + + return B_NAME_NOT_FOUND; +} + + +Connection::Connection(const PeerAddress& address) + : + ConnectionBase(address) +{ +} + + +ConnectionListener::ConnectionListener(const PeerAddress& address) + : + ConnectionBase(address) +{ +} + + +ConnectionBase::ConnectionBase(const PeerAddress& address) + : + fWaitCancel(create_sem(0, NULL)), + fSocket(-1), + fPeerAddress(address) +{ + mutex_init(&fSocketLock, NULL); +} + + +ConnectionStream::ConnectionStream(const PeerAddress& address) + : + Connection(address) +{ +} + + +ConnectionPacket::ConnectionPacket(const PeerAddress& address) + : + Connection(address) +{ +} + + +ConnectionBase::~ConnectionBase() +{ + if (fSocket != -1) + close(fSocket); + mutex_destroy(&fSocketLock); + delete_sem(fWaitCancel); +} + + +status_t +ConnectionBase::GetLocalAddress(PeerAddress* address) +{ + ASSERT(address != NULL); + + address->fProtocol = fPeerAddress.fProtocol; + + socklen_t addressSize = sizeof(address->fAddress); + return getsockname(fSocket, (struct sockaddr*)&address->fAddress, + &addressSize); +} + + +status_t +ConnectionStream::Send(const void* buffer, uint32 size) +{ + ASSERT(buffer != NULL); + + status_t result; + + uint32* buf = reinterpret_cast(malloc(size + sizeof(uint32))); + if (buf == NULL) + return B_NO_MEMORY; + MemoryDeleter _(buf); + + buf[0] = htonl(size | LAST_FRAGMENT); + memcpy(buf + 1, buffer, size); + + // More than one threads may send data and ksend is allowed to send partial + // data. Need a lock here. + uint32 sent = 0; + mutex_lock(&fSocketLock); + do { + result = send(fSocket, buf + sent, size + sizeof(uint32) - sent, 0); + sent += result; + } while (result > 0 && sent < size + sizeof(uint32)); + mutex_unlock(&fSocketLock); + if (result < 0) { + result = errno; + return result; + } else if (result == 0) + return B_IO_ERROR; + + return B_OK; +} + + +status_t +ConnectionPacket::Send(const void* buffer, uint32 size) +{ + ASSERT(buffer != NULL); + ASSERT(size < 65535); + + // send on DGRAM sockets is atomic. No need to lock. + status_t result = send(fSocket, buffer, size, 0); + if (result < 0) + return errno; + return B_OK; +} + + +status_t +ConnectionStream::Receive(void** _buffer, uint32* _size) +{ + ASSERT(_buffer != NULL); + ASSERT(_size != NULL); + + status_t result; + + uint32 size = 0; + void* buffer = NULL; + + uint32 record_size; + bool last_one = false; + + object_wait_info object[2]; + object[0].object = fWaitCancel; + object[0].type = B_OBJECT_TYPE_SEMAPHORE; + object[0].events = B_EVENT_ACQUIRE_SEMAPHORE; + + object[1].object = fSocket; + object[1].type = B_OBJECT_TYPE_FD; + object[1].events = B_EVENT_READ; + + do { + object[0].events = B_EVENT_ACQUIRE_SEMAPHORE; + object[1].events = B_EVENT_READ; + + result = wait_for_objects(object, 2); + if (result < B_OK + || (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) { + free(buffer); + return ECONNABORTED; + } else if ((object[1].events & B_EVENT_READ) == 0) + continue; + + // There is only one listener thread per connection. No need to lock. + uint32 received = 0; + do { + result = recv(fSocket, ((uint8*)&record_size) + received, + sizeof(record_size) - received, 0); + received += result; + } while (result > 0 && received < sizeof(record_size)); + if (result < 0) { + result = errno; + free(buffer); + return result; + } else if (result == 0) { + free(buffer); + return ECONNABORTED; + } + + record_size = ntohl(record_size); + ASSERT(record_size > 0); + + last_one = static_cast(record_size) < 0; + record_size &= LAST_FRAGMENT - 1; + + void* ptr = realloc(buffer, size + record_size); + if (ptr == NULL) { + free(buffer); + return B_NO_MEMORY; + } else + buffer = ptr; + MemoryDeleter bufferDeleter(buffer); + + received = 0; + do { + result = recv(fSocket, (uint8*)buffer + size + received, + record_size - received, 0); + received += result; + } while (result > 0 && received < record_size); + if (result < 0) + return errno; + else if (result == 0) + return ECONNABORTED; + + bufferDeleter.Detach(); + size += record_size; + } while (!last_one); + + + *_buffer = buffer; + *_size = size; + + return B_OK; +} + + +status_t +ConnectionPacket::Receive(void** _buffer, uint32* _size) +{ + ASSERT(_buffer != NULL); + ASSERT(_size != NULL); + + status_t result; + int32 size = MAX_PACKET_SIZE; + void* buffer = malloc(size); + + if (buffer == NULL) + return B_NO_MEMORY; + + object_wait_info object[2]; + object[0].object = fWaitCancel; + object[0].type = B_OBJECT_TYPE_SEMAPHORE; + object[0].events = B_EVENT_ACQUIRE_SEMAPHORE; + + object[1].object = fSocket; + object[1].type = B_OBJECT_TYPE_FD; + object[1].events = B_EVENT_READ; + + do { + object[0].events = B_EVENT_ACQUIRE_SEMAPHORE; + object[1].events = B_EVENT_READ; + + result = wait_for_objects(object, 2); + if (result < B_OK + || (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) { + free(buffer); + return ECONNABORTED; + } else if ((object[1].events & B_EVENT_READ) == 0) + continue; + break; + } while (true); + + // There is only one listener thread per connection. No need to lock. + size = recv(fSocket, buffer, size, 0); + if (size < 0) { + result = errno; + free(buffer); + return result; + } else if (size == 0) { + free(buffer); + return ECONNABORTED; + } + + *_buffer = buffer; + *_size = size; + + return B_OK; +} + + +Connection* +Connection::CreateObject(const PeerAddress& address) +{ + switch (address.fProtocol) { + case IPPROTO_TCP: + return new(std::nothrow) ConnectionStream(address); + case IPPROTO_UDP: + return new(std::nothrow) ConnectionPacket(address); + default: + return NULL; + } +} + + +status_t +Connection::Connect(Connection **_connection, const PeerAddress& address) +{ + ASSERT(_connection != NULL); + + Connection* conn = CreateObject(address); + if (conn == NULL) + return B_NO_MEMORY; + + status_t result; + if (conn->fWaitCancel < B_OK) { + result = conn->fWaitCancel; + delete conn; + return result; + } + + result = conn->Connect(); + if (result != B_OK) { + delete conn; + return result; + } + + *_connection = conn; + + return B_OK; +} + + +status_t +Connection::SetTo(Connection **_connection, int socket, + const PeerAddress& address) +{ + ASSERT(_connection != NULL); + ASSERT(socket != -1); + + Connection* conn = CreateObject(address); + if (conn == NULL) + return B_NO_MEMORY; + + status_t result; + if (conn->fWaitCancel < B_OK) { + result = conn->fWaitCancel; + delete conn; + return result; + } + + conn->fSocket = socket; + + *_connection = conn; + + return B_OK; +} + + +status_t +Connection::Connect() +{ + switch (fPeerAddress.fProtocol) { + case IPPROTO_TCP: + fSocket = socket(fPeerAddress.Family(), SOCK_STREAM, IPPROTO_TCP); + break; + case IPPROTO_UDP: + fSocket = socket(fPeerAddress.Family(), SOCK_DGRAM, IPPROTO_UDP); + break; + default: + return B_BAD_VALUE; + } + if (fSocket < 0) + return errno; + + status_t result; + uint16 port, attempt = 0; + + PeerAddress address(fPeerAddress.Family()); + + do { + port = rand() % (IPPORT_RESERVED - NFS_MIN_PORT); + port += NFS_MIN_PORT; + + if (attempt == 9) + port = 0; + attempt++; + + address.SetPort(port); + result = bind(fSocket, (sockaddr*)&address.fAddress, + address.AddressSize()); + } while (attempt <= 10 && result != B_OK); + + if (attempt > 10) { + close(fSocket); + return result; + } + + result = connect(fSocket, (sockaddr*)&fPeerAddress.fAddress, + fPeerAddress.AddressSize()); + if (result != 0) { + result = errno; + close(fSocket); + return result; + } + + return B_OK; +} + + +status_t +Connection::Reconnect() +{ + release_sem(fWaitCancel); + close(fSocket); + acquire_sem(fWaitCancel); + return Connect(); +} + + +void +ConnectionBase::Disconnect() +{ + release_sem(fWaitCancel); + + close(fSocket); + fSocket = -1; +} + + +status_t +ConnectionListener::Listen(ConnectionListener** listener, int networkFamily, + uint16 port) +{ + ASSERT(listener != NULL); + ASSERT(networkFamily == AF_INET || networkFamily == AF_INET6); + + int sock = socket(networkFamily, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) + return errno; + + PeerAddress address(networkFamily); + address.SetPort(port); + address.fProtocol = IPPROTO_TCP; + + status_t result = bind(sock, (sockaddr*)&address.fAddress, + address.AddressSize()); + if (result != B_OK) { + close(sock); + return errno; + } + + if (listen(sock, 5) != B_OK) { + close(sock); + return errno; + } + + *listener = new(std::nothrow) ConnectionListener(address); + if (*listener == NULL) { + close(sock); + return B_NO_MEMORY; + } + + if ((*listener)->fWaitCancel < B_OK) { + result = (*listener)->fWaitCancel; + close(sock); + delete *listener; + return result; + } + + (*listener)->fSocket = sock; + + return B_OK; +} + + +status_t +ConnectionListener::AcceptConnection(Connection** connection) +{ + ASSERT(connection != NULL); + + object_wait_info object[2]; + object[0].object = fWaitCancel; + object[0].type = B_OBJECT_TYPE_SEMAPHORE; + object[0].events = B_EVENT_ACQUIRE_SEMAPHORE; + + object[1].object = fSocket; + object[1].type = B_OBJECT_TYPE_FD; + object[1].events = B_EVENT_READ; + + do { + object[0].events = B_EVENT_ACQUIRE_SEMAPHORE; + object[1].events = B_EVENT_READ; + + status_t result = wait_for_objects(object, 2); + if (result < B_OK + || (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) { + return ECONNABORTED; + } else if ((object[1].events & B_EVENT_READ) == 0) + continue; + break; + } while (true); + + sockaddr_storage addr; + socklen_t length = sizeof(addr); + int sock = accept(fSocket, reinterpret_cast(&addr), &length); + if (sock < 0) + return errno; + + PeerAddress address; + address.fProtocol = IPPROTO_TCP; + address.fAddress = addr; + + status_t result = Connection::SetTo(connection, sock, address); + if (result != B_OK) { + close(sock); + return result; + } + + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Connection.h b/src/add-ons/kernel/file_systems/nfs4/Connection.h new file mode 100644 index 0000000000..3c01daa6dd --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Connection.h @@ -0,0 +1,146 @@ +/* + * Copyright 2012-2013 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef CONNECTION_H +#define CONNECTION_H + + +#include + +#include +#include + + +struct PeerAddress { + sockaddr_storage fAddress; + int fProtocol; + + bool operator==(const PeerAddress& address); + bool operator<(const PeerAddress& address); + + PeerAddress& operator=(const PeerAddress& address); + + PeerAddress(); + PeerAddress(int networkFamily); + + inline int Family() const; + + const char* ProtocolString() const; + void SetProtocol(const char* protocol); + + char* UniversalAddress() const; + + socklen_t AddressSize() const; + + void SetPort(uint16 port); + uint16 Port() const; + + const void* InAddr() const; + size_t InAddrSize() const; +}; + + +inline int +PeerAddress::Family() const +{ + return fAddress.ss_family; +} + + +struct addrinfo; + +class AddressResolver { +public: + AddressResolver(const char* name); + ~AddressResolver(); + + status_t GetNextAddress(PeerAddress* address); + + void ForceProtocol(const char* protocol); + void ForcePort(uint16 port); + +protected: + status_t ResolveAddress(const char* name); + +private: + addrinfo* fHead; + addrinfo* fCurrent; + + PeerAddress fAddress; + + uint16 fForcedPort; + int fForcedProtocol; + + status_t fStatus; +}; + +class ConnectionBase { +public: + ConnectionBase(const PeerAddress& address); + virtual ~ConnectionBase(); + + status_t GetLocalAddress(PeerAddress* address); + + void Disconnect(); + +protected: + sem_id fWaitCancel; + int fSocket; + mutex fSocketLock; + + const PeerAddress fPeerAddress; +}; + +class Connection : public ConnectionBase { +public: + static status_t Connect(Connection **connection, + const PeerAddress& address); + static status_t SetTo(Connection **connection, int socket, + const PeerAddress& address); + + virtual status_t Send(const void* buffer, uint32 size) = 0; + virtual status_t Receive(void** buffer, uint32* size) = 0; + + status_t Reconnect(); + +protected: + static Connection* CreateObject(const PeerAddress& address); + + Connection(const PeerAddress& address); + status_t Connect(); + +}; + +class ConnectionStream : public Connection { +public: + ConnectionStream(const PeerAddress& address); + + virtual status_t Send(const void* buffer, uint32 size); + virtual status_t Receive(void** buffer, uint32* size); +}; + +class ConnectionPacket : public Connection { +public: + ConnectionPacket(const PeerAddress& address); + + virtual status_t Send(const void* buffer, uint32 size); + virtual status_t Receive(void** buffer, uint32* size); +}; + +class ConnectionListener : public ConnectionBase { +public: + static status_t Listen(ConnectionListener** listener, int networkFamily, + uint16 port = 0); + + status_t AcceptConnection(Connection** connection); + +protected: + ConnectionListener(const PeerAddress& address); +}; + +#endif // CONNECTION_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp b/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp new file mode 100644 index 0000000000..e756e75301 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Cookie.h" + +#include "Inode.h" +#include "Request.h" + + +LockOwner::LockOwner(uint32 owner) + : + fSequence(0), + fOwner(owner), + fUseCount(0), + fNext(NULL), + fPrev(NULL) +{ + memset(fStateId, 0, sizeof(fStateId)); + mutex_init(&fLock, NULL); +} + + +LockOwner::~LockOwner() +{ + mutex_destroy(&fLock); +} + + +LockInfo::LockInfo(LockOwner* owner) + : + fOwner(owner) +{ + ASSERT(owner != NULL); + fOwner->fUseCount++; +} + + +LockInfo::~LockInfo() +{ + fOwner->fUseCount--; +} + + +bool +LockInfo::operator==(const struct flock& lock) const +{ + bool eof = lock.l_len + lock.l_start == OFF_MAX; + uint64 start = static_cast(lock.l_start); + uint64 len = static_cast(lock.l_len); + + return fStart == start && (fLength == len + || (eof && fLength == UINT64_MAX)); +} + + +bool +LockInfo::operator==(const LockInfo& lock) const +{ + return fOwner == lock.fOwner && fStart == lock.fStart + && fLength == lock.fLength && fType == lock.fType; +} + + +Cookie::Cookie() + : + fRequests(NULL), + fSnoozeCancel(create_sem(1, NULL)) +{ + acquire_sem(fSnoozeCancel); + mutex_init(&fRequestLock, NULL); +} + + +Cookie::~Cookie() +{ + delete_sem(fSnoozeCancel); + mutex_destroy(&fRequestLock); +} + + +status_t +Cookie::RegisterRequest(RPC::Request* req) +{ + ASSERT(req != NULL); + + RequestEntry* ent = new RequestEntry; + if (ent == NULL) + return B_NO_MEMORY; + + MutexLocker _(fRequestLock); + ent->fRequest = req; + ent->fNext = fRequests; + fRequests = ent; + + return B_OK; +} + + +status_t +Cookie::UnregisterRequest(RPC::Request* req) +{ + ASSERT(req != NULL); + + MutexLocker _(fRequestLock); + RequestEntry* ent = fRequests; + RequestEntry* prev = NULL; + while (ent != NULL) { + if (ent->fRequest == req) { + if (prev == NULL) + fRequests = ent->fNext; + else + prev->fNext = ent->fNext; + delete ent; + } + + prev = ent; + ent = ent->fNext; + } + + return B_OK; +} + + +status_t +Cookie::CancelAll() +{ + release_sem(fSnoozeCancel); + + MutexLocker _(fRequestLock); + RequestEntry* ent = fRequests; + while (ent != NULL) { + fFileSystem->Server()->WakeCall(ent->fRequest); + ent = ent->fNext; + } + + return B_OK; +} + + +OpenFileCookie::OpenFileCookie() + : + fLocks(NULL) +{ +} + + +void +OpenFileCookie::AddLock(LockInfo* lock) +{ + ASSERT(lock != NULL); + + lock->fCookieNext = fLocks; + fLocks = lock; +} + + +void +OpenFileCookie::RemoveLock(LockInfo* lock, LockInfo* prev) +{ + if (prev != NULL) + prev->fCookieNext = lock->fCookieNext; + else { + ASSERT(prev == NULL && fLocks == lock); + fLocks = lock->fCookieNext; + } +} + + +OpenDirCookie::~OpenDirCookie() +{ + if (fSnapshot != NULL) + fSnapshot->ReleaseReference(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Cookie.h b/src/add-ons/kernel/file_systems/nfs4/Cookie.h new file mode 100644 index 0000000000..a3d36c9c8c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Cookie.h @@ -0,0 +1,106 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef COOKIE_H +#define COOKIE_H + + +#include + +#include "DirectoryCache.h" +#include "FileSystem.h" + + +struct OpenState; + +struct LockOwner { + uint64 fClientId; + + uint32 fStateId[3]; + uint32 fStateSeq; + uint32 fSequence; + + uint32 fOwner; + + uint32 fUseCount; + + mutex fLock; + + LockOwner* fNext; + LockOwner* fPrev; + + LockOwner(uint32 owner); + ~LockOwner(); +}; + +struct LockInfo { + LockOwner* fOwner; + + uint64 fStart; + uint64 fLength; + LockType fType; + + LockInfo* fNext; + LockInfo* fCookieNext; + + LockInfo(LockOwner* owner); + ~LockInfo(); + + bool operator==(const struct flock& lock) const; + bool operator==(const LockInfo& lock) const; +}; + +struct Cookie { + struct RequestEntry { + RPC::Request* fRequest; + RequestEntry* fNext; + }; + + FileSystem* fFileSystem; + RequestEntry* fRequests; + mutex fRequestLock; + + sem_id fSnoozeCancel; + + Cookie(); + virtual ~Cookie(); + + status_t RegisterRequest(RPC::Request* req); + status_t UnregisterRequest(RPC::Request* req); + status_t CancelAll(); +}; + +struct OpenStateCookie : public Cookie { + OpenState* fOpenState; + uint32 fMode; +}; + +struct OpenFileCookie : public OpenStateCookie { + LockInfo* fLocks; + + void AddLock(LockInfo* lock); + void RemoveLock(LockInfo* lock, LockInfo* prev); + + OpenFileCookie(); +}; + +struct OpenDirCookie : public Cookie { + int fSpecial; + DirectoryCacheSnapshot* fSnapshot; + NameCacheEntry* fCurrent; + bool fEOF; + + bool fAttrDir; + + ~OpenDirCookie(); +}; + +struct OpenAttrCookie : public OpenStateCookie { }; + + +#endif // COOKIE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp b/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp new file mode 100644 index 0000000000..2fce4fde6c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Delegation.h" + +#include "Inode.h" +#include "Request.h" + + +Delegation::Delegation(const OpenDelegationData& data, Inode* inode, + uint64 clientID, bool attribute) + : + fClientID(clientID), + fData(data), + fInode(inode), + fAttribute(attribute) +{ + ASSERT(inode != NULL); +} + + +status_t +Delegation::GiveUp(bool truncate) +{ + if (!fAttribute && !truncate) + fInode->SyncAndCommit(true); + + ReturnDelegation(); + + return B_OK; +} + + +status_t +Delegation::ReturnDelegation() +{ + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.DelegReturn(fData.fStateID, fData.fStateSeq); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv, NULL, fInode->GetOpenState())) + continue; + + reply.PutFH(); + + return reply.DelegReturn(); + } while (true); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Delegation.h b/src/add-ons/kernel/file_systems/nfs4/Delegation.h new file mode 100644 index 0000000000..807d52617c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Delegation.h @@ -0,0 +1,65 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef DELEGATION_H +#define DELEGATION_H + + +#include +#include + +#include "NFS4Object.h" + + +class Inode; + +class Delegation : public NFS4Object, + public DoublyLinkedListLinkImpl { +public: + Delegation(const OpenDelegationData& data, Inode* inode, + uint64 clientID, bool attr = false); + + status_t GiveUp(bool truncate = false); + + inline void SetData(const OpenDelegationData& data); + inline Inode* GetInode(); + inline OpenDelegation Type(); + +protected: + status_t ReturnDelegation(); + +private: + uint64 fClientID; + OpenDelegationData fData; + Inode* fInode; + bool fAttribute; +}; + + +inline void +Delegation::SetData(const OpenDelegationData& data) +{ + fData = data; +} + + +inline Inode* +Delegation::GetInode() +{ + return fInode; +} + + +inline OpenDelegation +Delegation::Type() +{ + return fData.fType; +} + + +#endif // DELEGATION_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp new file mode 100644 index 0000000000..c40707a35d --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp @@ -0,0 +1,345 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "DirectoryCache.h" + +#include +#include + +#include "Inode.h" + + + +NameCacheEntry::NameCacheEntry(const char* name, ino_t node) + : + fNode(node), + fName(strdup(name)) +{ + ASSERT(name != NULL); +} + + +NameCacheEntry::NameCacheEntry(const NameCacheEntry& entry) + : + fNode(entry.fNode), + fName(strdup(entry.fName)) +{ +} + + +NameCacheEntry::~NameCacheEntry() +{ + free(const_cast(fName)); +} + + +DirectoryCacheSnapshot::DirectoryCacheSnapshot() +{ + mutex_init(&fLock, NULL); +} + + +DirectoryCacheSnapshot::DirectoryCacheSnapshot( + const DirectoryCacheSnapshot& snapshot) +{ + mutex_init(&fLock, NULL); + + MutexLocker _(snapshot.fLock); + NameCacheEntry* entry = snapshot.fEntries.Head(); + NameCacheEntry* new_entry; + while (entry) { + new_entry = new NameCacheEntry(*entry); + if (new_entry == NULL) + break; + + fEntries.Add(new_entry); + + entry = snapshot.fEntries.GetNext(entry); + } +} + + +DirectoryCacheSnapshot::~DirectoryCacheSnapshot() +{ + while (!fEntries.IsEmpty()) { + NameCacheEntry* current = fEntries.RemoveHead(); + delete current; + } + + mutex_destroy(&fLock); +} + + +DirectoryCache::DirectoryCache(Inode* inode, bool attr) + : + fRevalidated(false), + fDirectoryCache(NULL), + fInode(inode), + fAttrDir(attr), + fTrashed(true) +{ + ASSERT(inode != NULL); + + mutex_init(&fLock, NULL); +} + + +DirectoryCache::~DirectoryCache() +{ + mutex_destroy(&fLock); +} + + +void +DirectoryCache::Reset() +{ + Trash(); + fExpireTime = system_time() + kExpirationTime; + fTrashed = false; +} + + +void +DirectoryCache::Trash() +{ + while (!fNameCache.IsEmpty()) { + NameCacheEntry* current = fNameCache.RemoveHead(); + entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), + current->fName); + delete current; + } + + _SetSnapshot(NULL); + + fTrashed = true; +} + + +status_t +DirectoryCache::AddEntry(const char* name, ino_t node, bool created) +{ + ASSERT(name != NULL); + + NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node); + if (entry == NULL) + return B_NO_MEMORY; + if (entry->fName == NULL) { + delete entry; + return B_NO_MEMORY; + } + + fNameCache.Add(entry); + + if (created && fDirectoryCache != NULL) { + MutexLocker _(fDirectoryCache->fLock); + NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node); + if (entry == NULL) + return B_NO_MEMORY; + if (entry->fName == NULL) { + delete entry; + return B_NO_MEMORY; + } + + fDirectoryCache->fEntries.Add(entry); + } + + if (!fAttrDir) { + return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(), + name, node); + } + + return B_OK; +} + + +void +DirectoryCache::RemoveEntry(const char* name) +{ + ASSERT(name != NULL); + + SinglyLinkedList::Iterator iterator + = fNameCache.GetIterator(); + NameCacheEntry* previous = NULL; + NameCacheEntry* current = iterator.Next(); + while (current != NULL) { + if (strcmp(current->fName, name) == 0) { + fNameCache.Remove(previous, current); + delete current; + break; + } + + previous = current; + current = iterator.Next(); + } + + if (fDirectoryCache != NULL) { + MutexLocker _(fDirectoryCache->fLock); + iterator = fDirectoryCache->fEntries.GetIterator(); + previous = NULL; + current = iterator.Next(); + while (current != NULL) { + if (strcmp(current->fName, name) == 0) { + fDirectoryCache->fEntries.Remove(previous, current); + delete current; + break; + } + + previous = current; + current = iterator.Next(); + } + } + + if (!fAttrDir) { + entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), + name); + } +} + + +void +DirectoryCache::_SetSnapshot(DirectoryCacheSnapshot* snapshot) +{ + if (fDirectoryCache != NULL) + fDirectoryCache->ReleaseReference(); + fDirectoryCache = snapshot; +} + + +status_t +DirectoryCache::_LoadSnapshot(bool trash) +{ + DirectoryCacheSnapshot* oldSnapshot = fDirectoryCache; + if (oldSnapshot != NULL) + oldSnapshot->AcquireReference(); + + if (trash) + Trash(); + + DirectoryCacheSnapshot* newSnapshot; + status_t result = fInode->GetDirSnapshot(&newSnapshot, NULL, &fChange, + fAttrDir); + if (result != B_OK) { + if (oldSnapshot != NULL) + oldSnapshot->ReleaseReference(); + return result; + } + newSnapshot->AcquireReference(); + + _SetSnapshot(newSnapshot); + fExpireTime = system_time() + kExpirationTime; + + fTrashed = false; + + if (oldSnapshot != NULL) + NotifyChanges(oldSnapshot, newSnapshot); + + if (oldSnapshot != NULL) + oldSnapshot->ReleaseReference(); + + newSnapshot->ReleaseReference(); + return B_OK; +} + + +status_t +DirectoryCache::Revalidate() +{ + if (fExpireTime < system_time()) + return B_OK; + + uint64 change; + if (fInode->GetChangeInfo(&change, fAttrDir) != B_OK) { + Trash(); + return B_ERROR; + } + + if (change == fChange) { + fExpireTime = system_time() + kExpirationTime; + return B_OK; + } + + return _LoadSnapshot(true); +} + + +void +DirectoryCache::NotifyChanges(DirectoryCacheSnapshot* oldSnapshot, + DirectoryCacheSnapshot* newSnapshot) +{ + ASSERT(newSnapshot != NULL); + ASSERT(oldSnapshot != NULL); + + MutexLocker _(newSnapshot->fLock); + + SinglyLinkedList::Iterator oldIt + = oldSnapshot->fEntries.GetIterator(); + NameCacheEntry* oldCurrent; + + SinglyLinkedList::Iterator newIt + = newSnapshot->fEntries.GetIterator(); + NameCacheEntry* newCurrent = newIt.Next(); + while (newCurrent != NULL) { + oldIt = oldSnapshot->fEntries.GetIterator(); + oldCurrent = oldIt.Next(); + + bool found = false; + NameCacheEntry* prev = NULL; + while (oldCurrent != NULL) { + if (oldCurrent->fNode == newCurrent->fNode + && strcmp(oldCurrent->fName, newCurrent->fName) == 0) { + found = true; + break; + } + + prev = oldCurrent; + oldCurrent = oldIt.Next(); + } + + if (!found) { + if (fAttrDir) { + notify_attribute_changed(fInode->GetFileSystem()->DevId(), + fInode->ID(), newCurrent->fName, B_ATTR_CREATED); + } else { + notify_entry_created(fInode->GetFileSystem()->DevId(), + fInode->ID(), newCurrent->fName, newCurrent->fNode); + + do { + FileInfo fi; + fi.fFileId = newCurrent->fNode; + fi.fParent = fInode->fInfo.fHandle; + status_t result = fi.CreateName(fInode->fInfo.fPath, + newCurrent->fName); + if (result != B_OK) + break; + + fInode->GetFileSystem()->InoIdMap()->AddEntry(fi, + Inode::FileIdToInoT(newCurrent->fNode), true); + } while (false); + } + } else + oldSnapshot->fEntries.Remove(prev, oldCurrent); + + newCurrent = newIt.Next(); + } + + oldIt = oldSnapshot->fEntries.GetIterator(); + oldCurrent = oldIt.Next(); + + while (oldCurrent != NULL) { + if (fAttrDir) { + notify_attribute_changed(fInode->GetFileSystem()->DevId(), + fInode->ID(), newCurrent->fName, B_ATTR_REMOVED); + } else { + notify_entry_removed(fInode->GetFileSystem()->DevId(), fInode->ID(), + oldCurrent->fName, oldCurrent->fNode); + } + oldCurrent = oldIt.Next(); + } +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h new file mode 100644 index 0000000000..357ec6e213 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h @@ -0,0 +1,175 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef DIRECTORYCACHE_H +#define DIRECTORYCACHE_H + + +#include +#include +#include +#include +#include + + +class Inode; + +struct NameCacheEntry : + public SinglyLinkedListLinkImpl { + ino_t fNode; + const char* fName; + + NameCacheEntry(const char* name, ino_t node); + NameCacheEntry(const NameCacheEntry& entry); + ~NameCacheEntry(); +}; + +struct DirectoryCacheSnapshot : public KernelReferenceable { + SinglyLinkedList fEntries; + mutable mutex fLock; + + DirectoryCacheSnapshot(); + DirectoryCacheSnapshot( + const DirectoryCacheSnapshot& snapshot); + ~DirectoryCacheSnapshot(); +}; + +class DirectoryCache { +public: + DirectoryCache(Inode* inode, bool attr = false); + ~DirectoryCache(); + + inline void Lock(); + inline void Unlock(); + + void Reset(); + void Trash(); + inline bool Valid(); + + status_t AddEntry(const char* name, ino_t node, + bool created = false); + void RemoveEntry(const char* name); + + inline status_t GetSnapshot(DirectoryCacheSnapshot** snapshot); + + inline SinglyLinkedList& EntriesList(); + + status_t Revalidate(); + + inline status_t ValidateChangeInfo(uint64 change); + inline void SetChangeInfo(uint64 change); + inline uint64 ChangeInfo(); + + inline Inode* GetInode(); + + static const bigtime_t kExpirationTime = 15000000; + + bool fRevalidated; +protected: + void NotifyChanges(DirectoryCacheSnapshot* oldSnapshot, + DirectoryCacheSnapshot* newSnapshot); + +private: + void _SetSnapshot(DirectoryCacheSnapshot* snapshot); + status_t _LoadSnapshot(bool trash); + + SinglyLinkedList fNameCache; + + DirectoryCacheSnapshot* fDirectoryCache; + + Inode* fInode; + + bool fAttrDir; + bool fTrashed; + mutex fLock; + + uint64 fChange; + bigtime_t fExpireTime; +}; + + +inline void +DirectoryCache::Lock() +{ + mutex_lock(&fLock); +} + + +inline void +DirectoryCache::Unlock() +{ + mutex_unlock(&fLock); +} + + +inline bool +DirectoryCache::Valid() +{ + return !fTrashed; +} + + +inline status_t +DirectoryCache::GetSnapshot(DirectoryCacheSnapshot** snapshot) +{ + ASSERT(snapshot != NULL); + + status_t result = B_OK; + if (fDirectoryCache == NULL) + result = _LoadSnapshot(false); + *snapshot = fDirectoryCache; + return result; +} + + +inline SinglyLinkedList& +DirectoryCache::EntriesList() +{ + return fNameCache; +} + + +inline status_t +DirectoryCache::ValidateChangeInfo(uint64 change) +{ + if (fTrashed || change != fChange) { + Trash(); + fChange = change; + fExpireTime = system_time() + kExpirationTime; + fTrashed = false; + + return B_ERROR; + } + + return B_OK; +} + + +inline void +DirectoryCache::SetChangeInfo(uint64 change) +{ + fExpireTime = system_time() + kExpirationTime; + fChange = change; +} + + +inline uint64 +DirectoryCache::ChangeInfo() +{ + return fChange; +} + + +inline Inode* +DirectoryCache::GetInode() +{ + return fInode; +} + + +#endif // DIRECTORYCACHE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp b/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp new file mode 100644 index 0000000000..5b7a57d776 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/FileInfo.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "FileInfo.h" + +#include "FileSystem.h" +#include "Request.h" + + +status_t +FileInfo::ParsePath(RequestBuilder& req, uint32& count, const char* _path) +{ + ASSERT(_path != NULL); + + char* path = strdup(_path); + if (path == NULL) + return B_NO_MEMORY; + + char* pathStart = path; + char* pathEnd; + + while (pathStart != NULL) { + pathEnd = strchr(pathStart, '/'); + if (pathEnd != NULL) + *pathEnd = '\0'; + + if (pathEnd != pathStart) { + if (!strcmp(pathStart, "..")) { + req.LookUpUp(); + count++; + } else if (strcmp(pathStart, ".")) { + req.LookUp(pathStart); + count++; + } + } + + if (pathEnd != NULL && pathEnd[1] != '\0') + pathStart = pathEnd + 1; + else + pathStart = NULL; + } + free(path); + + return B_OK; +} + + +status_t +FileInfo::CreateName(const char* dirPath, const char* name) +{ + ASSERT(name != NULL); + + free(const_cast(fName)); + fName = strdup(name); + if (fName == NULL) + return B_NO_MEMORY; + + free(const_cast(fPath)); + fPath = NULL; + if (dirPath != NULL) { + char* path = reinterpret_cast(malloc(strlen(name) + 2 + + strlen(dirPath))); + if (path == NULL) + return B_NO_MEMORY; + + strcpy(path, dirPath); + strcat(path, "/"); + strcat(path, name); + + fPath = path; + } else + fPath = strdup(name); + + if (fPath == NULL) + return B_NO_MEMORY; + + return B_OK; +} + + +status_t +FileInfo::UpdateFileHandles(FileSystem* fs) +{ + ASSERT(fs != NULL); + + Request request(fs->Server(), fs); + RequestBuilder& req = request.Builder(); + + req.PutRootFH(); + + uint32 lookupCount = 0; + status_t result; + + result = ParsePath(req, lookupCount, fs->Path()); + if (result != B_OK) + return result; + + result = ParsePath(req, lookupCount, fPath); + if (result != B_OK) + return result; + + if (fs->IsAttrSupported(FATTR4_FILEID)) { + AttrValue attr; + attr.fAttribute = FATTR4_FILEID; + attr.fFreePointer = false; + attr.fData.fValue64 = fFileId; + req.Verify(&attr, 1); + } + + req.GetFH(); + req.LookUpUp(); + req.GetFH(); + + result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + reply.PutRootFH(); + for (uint32 i = 0; i < lookupCount; i++) + reply.LookUp(); + + if (fs->IsAttrSupported(FATTR4_FILEID)) { + result = reply.Verify(); + if (result != B_OK) + return result; + } + + reply.GetFH(&fHandle); + if (reply.LookUpUp() == B_ENTRY_NOT_FOUND) { + fParent = fHandle; + return B_OK; + } + + return reply.GetFH(&fParent); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/FileInfo.h b/src/add-ons/kernel/file_systems/nfs4/FileInfo.h new file mode 100644 index 0000000000..4182dcc331 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/FileInfo.h @@ -0,0 +1,188 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef FILEINFO_H +#define FILEINFO_H + + +#include +#include + +#include + + +#define NFS4_FHSIZE 128 + +struct FileHandle { + uint8 fSize; + uint8 fData[NFS4_FHSIZE]; + + inline FileHandle(); + inline FileHandle(const FileHandle& fh); + inline FileHandle& operator=(const FileHandle& fh); + + + inline bool operator!=(const FileHandle& handle) const; + inline bool operator>(const FileHandle& handle) const; + inline bool operator<(const FileHandle& handle) const; +}; + + +class FileSystem; +class RequestBuilder; + +// Complete information needed to identify a file in any situation. +// Unfortunately just a FileHandle is not enough even when they are persistent +// since OPEN requires both parent FileHandle and file name (just like LOOKUP). +struct FileInfo { + uint64 fFileId; + FileHandle fHandle; + + FileHandle fParent; + const char* fName; + const char* fPath; + + FileHandle fAttrDir; + + inline FileInfo(); + inline ~FileInfo(); + inline FileInfo(const FileInfo& fi); + inline FileInfo& operator=(const FileInfo& fi); + + status_t UpdateFileHandles(FileSystem* fs); + + static status_t ParsePath(RequestBuilder& req, uint32& count, + const char* _path); + + status_t CreateName(const char* dirPath, const char* name); +}; + +struct FileSystemId { + uint64 fMajor; + uint64 fMinor; + + inline bool operator==(const FileSystemId& fsid) const; + inline bool operator!=(const FileSystemId& fsid) const; +}; + + +inline +FileHandle::FileHandle() + : + fSize(0) +{ +} + + +inline +FileHandle::FileHandle(const FileHandle& fh) + : + fSize(fh.fSize) +{ + memcpy(fData, fh.fData, fSize); +} + + +inline FileHandle& +FileHandle::operator=(const FileHandle& fh) +{ + fSize = fh.fSize; + memcpy(fData, fh.fData, fSize); + return *this; +} + + +inline bool +FileHandle::operator!=(const FileHandle& handle) const +{ + if (fSize != handle.fSize) + return true; + return memcmp(fData, handle.fData, fSize) != 0; +} + + +inline bool +FileHandle::operator>(const FileHandle& handle) const +{ + if (fSize > handle.fSize) + return true; + return memcmp(fData, handle.fData, fSize) > 0; +} + + +inline bool +FileHandle::operator<(const FileHandle& handle) const +{ + if (fSize < handle.fSize) + return true; + return memcmp(fData, handle.fData, fSize) < 0; +} + + +inline +FileInfo::FileInfo() + : + fFileId(0), + fName(NULL), + fPath(NULL) +{ +} + + +inline +FileInfo::~FileInfo() +{ + free(const_cast(fName)); + free(const_cast(fPath)); +} + + +inline +FileInfo::FileInfo(const FileInfo& fi) + : + fFileId(fi.fFileId), + fHandle(fi.fHandle), + fParent(fi.fParent), + fName(strdup(fi.fName)), + fPath(strdup(fi.fPath)) +{ +} + + +inline FileInfo& +FileInfo::operator=(const FileInfo& fi) +{ + fFileId = fi.fFileId; + fHandle = fi.fHandle; + fParent = fi.fParent; + + free(const_cast(fName)); + fName = strdup(fi.fName); + + free(const_cast(fPath)); + fPath = strdup(fi.fPath); + + return *this; +} + + +inline bool +FileSystemId::operator==(const FileSystemId& fsid) const +{ + return fMajor == fsid.fMajor && fMinor == fsid.fMinor; +} + + +inline bool +FileSystemId::operator!=(const FileSystemId& fsid) const +{ + return !operator==(fsid); +} + + +#endif // FILEHINFO_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp b/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp new file mode 100644 index 0000000000..309f572d28 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp @@ -0,0 +1,393 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "FileSystem.h" + +#include + +#include +#include + +#include "Request.h" +#include "RootInode.h" + + +extern RPC::ServerManager* gRPCServerManager; +extern RPC::ProgramData* CreateNFS4Server(RPC::Server* serv); + + +FileSystem::FileSystem(const MountConfiguration& configuration) + : + fOpenCount(0), + fOpenOwnerSequence(0), + fNamedAttrs(true), + fPath(NULL), + fRoot(NULL), + fId(1), + fConfiguration(configuration) +{ + fOpenOwner = rand(); + fOpenOwner <<= 32; + fOpenOwner |= rand(); + + mutex_init(&fOpenOwnerLock, NULL); + mutex_init(&fOpenLock, NULL); + mutex_init(&fDelegationLock, NULL); + mutex_init(&fCreateFileLock, NULL); +} + + +FileSystem::~FileSystem() +{ + NFS4Server* server = reinterpret_cast(fServer->PrivateData()); + if (server != NULL) + server->RemoveFileSystem(this); + + mutex_destroy(&fDelegationLock); + mutex_destroy(&fOpenLock); + mutex_destroy(&fOpenOwnerLock); + mutex_destroy(&fCreateFileLock); + + free(const_cast(fPath)); + delete fRoot; +} + + +static const char* +GetPath(const char* root, const char* path) +{ + ASSERT(path != NULL); + + int slash = 0; + int i; + for (i = 0; path[i] != '\0'; i++) { + if (path[i] == '/') + slash = i; + + if (root == NULL) + break; + + if (path[i] != root[i] || root[i] == '\0') + break; + } + + if (path[i] == '\0') + return NULL; + + return path + slash; +} + + +status_t +FileSystem::Mount(FileSystem** _fs, RPC::Server* serv, const char* fsPath, + dev_t id, const MountConfiguration& configuration) +{ + ASSERT(_fs != NULL); + ASSERT(serv != NULL); + ASSERT(fsPath != NULL); + + FileSystem* fs = new(std::nothrow) FileSystem(configuration); + if (fs == NULL) + return B_NO_MEMORY; + ObjectDeleter fsDeleter(fs); + + Request request(serv, fs); + RequestBuilder& req = request.Builder(); + + req.PutRootFH(); + + uint32 lookupCount = 0; + status_t result = FileInfo::ParsePath(req, lookupCount, fsPath); + if (result != B_OK) + return result; + + req.GetFH(); + req.Access(); + + Attribute attr[] = { FATTR4_SUPPORTED_ATTRS, FATTR4_FH_EXPIRE_TYPE, + FATTR4_FSID, FATTR4_FS_LOCATIONS }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + reply.PutRootFH(); + + for (uint32 i = 0; i < lookupCount; i++) + reply.LookUp(); + + FileHandle fh; + reply.GetFH(&fh); + + uint32 allowed; + result = reply.Access(NULL, &allowed); + if (result != B_OK) + return result; + else if ((allowed & (ACCESS4_READ | ACCESS4_LOOKUP)) + != (ACCESS4_READ | ACCESS4_LOOKUP)) + return B_PERMISSION_DENIED; + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK || count < 2) + return result; + + // FATTR4_SUPPORTED_ATTRS is mandatory + memcpy(fs->fSupAttrs, &values[0].fData.fValue64, sizeof(fs->fSupAttrs)); + + // FATTR4_FH_EXPIRE_TYPE is mandatory + fs->fExpireType = values[1].fData.fValue32; + + // FATTR4_FSID is mandatory + FileSystemId* fsid + = reinterpret_cast(values[2].fData.fPointer); + + if (count == 4 && values[3].fAttribute == FATTR4_FS_LOCATIONS) { + FSLocations* locs + = reinterpret_cast(values[3].fData.fLocations); + + fs->fPath = strdup(locs->fRootPath); + } else + fs->fPath = NULL; + + FileInfo fi; + const char* name; + if (fsPath != NULL && fsPath[0] == '/') + fsPath++; + + fs->fServer = serv; + fs->fDevId = id; + fs->fFsId = *fsid; + + fi.fHandle = fh; + fi.fParent = fh; + fi.fPath = strdup(GetPath(fs->fPath, fsPath)); + + if (fi.fPath != NULL) { + name = strrchr(fi.fPath, '/'); + if (name != NULL) { + name++; + fi.fName = strdup(name); + } + } + + delete[] values; + + Inode* inode; + result = Inode::CreateInode(fs, fi, &inode); + if (result != B_OK) + return result; + + name = strrchr(fsPath, '/'); + if (name != NULL) { + name++; + reinterpret_cast(inode)->SetName(name); + } else if (fsPath[0] != '\0') + reinterpret_cast(inode)->SetName(fsPath); + else { + char* address = serv->ID().UniversalAddress(); + if (address != NULL) + reinterpret_cast(inode)->SetName(address); + else + reinterpret_cast(inode)->SetName("NFS4 Share"); + free(address); + } + + fs->fRoot = reinterpret_cast(inode); + + fs->NFSServer()->AddFileSystem(fs); + *_fs = fs; + + fsDeleter.Detach(); + return B_OK; +} + + +status_t +FileSystem::GetInode(ino_t id, Inode** _inode) +{ + ASSERT(_inode != NULL); + + FileInfo fi; + status_t result = fInoIdMap.GetFileInfo(&fi, id); + ASSERT(result != B_ENTRY_NOT_FOUND); + + if (result != B_OK) + return result; + + Inode* inode; + result = Inode::CreateInode(this, fi, &inode); + if (result != B_OK) + return result; + + *_inode = inode; + return B_OK; +} + + +status_t +FileSystem::Migrate(const RPC::Server* serv) +{ + ASSERT(serv != NULL); + + MutexLocker _(fOpenLock); + if (serv != fServer) + return B_OK; + + if (!fRoot->ProbeMigration()) + return B_OK; + + AttrValue* values; + status_t result = fRoot->GetLocations(&values); + if (result != B_OK) + return result; + + FSLocations* locs + = reinterpret_cast(values[0].fData.fLocations); + + RPC::Server* server = fServer; + for (uint32 i = 0; i < locs->fCount; i++) { + for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) { + AddressResolver resolver(locs->fLocations[i].fLocations[j]); + + if (gRPCServerManager->Acquire(&fServer, &resolver, + CreateNFS4Server) == B_OK) { + + free(const_cast(fPath)); + fPath = strdup(locs->fLocations[i].fRootPath); + + if (fPath == NULL) { + gRPCServerManager->Release(fServer); + fServer = server; + + delete[] values; + return B_NO_MEMORY; + } + + break; + } + } + } + + delete[] values; + + if (server == fServer) { + gRPCServerManager->Release(server); + return B_ERROR; + } + + NFS4Server* old = reinterpret_cast(server->PrivateData()); + old->RemoveFileSystem(this); + NFSServer()->AddFileSystem(this); + + gRPCServerManager->Release(server); + + return B_OK; +} + + +DoublyLinkedList& +FileSystem::OpenFilesLock() +{ + mutex_lock(&fOpenLock); + return fOpenFiles; +} + + +void +FileSystem::OpenFilesUnlock() +{ + mutex_unlock(&fOpenLock); +} + + +void +FileSystem::AddOpenFile(OpenState* state) +{ + ASSERT(state != NULL); + + MutexLocker _(fOpenLock); + + fOpenFiles.InsertBefore(fOpenFiles.Head(), state); + + NFSServer()->IncUsage(); +} + + +void +FileSystem::RemoveOpenFile(OpenState* state) +{ + ASSERT(state != NULL); + + MutexLocker _(fOpenLock); + + fOpenFiles.Remove(state); + + NFSServer()->DecUsage(); +} + + +DoublyLinkedList& +FileSystem::DelegationsLock() +{ + mutex_lock(&fDelegationLock); + return fDelegationList; +} + + +void +FileSystem::DelegationsUnlock() +{ + mutex_unlock(&fDelegationLock); +} + + +void +FileSystem::AddDelegation(Delegation* delegation) +{ + ASSERT(delegation != NULL); + + MutexLocker _(fDelegationLock); + + fDelegationList.InsertBefore(fDelegationList.Head(), delegation); + + fHandleToDelegation.Remove(delegation->fInfo.fHandle); + fHandleToDelegation.Insert(delegation->fInfo.fHandle, delegation); +} + + +void +FileSystem::RemoveDelegation(Delegation* delegation) +{ + ASSERT(delegation != NULL); + + MutexLocker _(fDelegationLock); + + fDelegationList.Remove(delegation); + fHandleToDelegation.Remove(delegation->fInfo.fHandle); +} + + +Delegation* +FileSystem::GetDelegation(const FileHandle& handle) +{ + MutexLocker _(fDelegationLock); + + AVLTreeMap::Iterator it; + it = fHandleToDelegation.Find(handle); + if (!it.HasCurrent()) + return NULL; + + return it.Current(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/FileSystem.h b/src/add-ons/kernel/file_systems/nfs4/FileSystem.h new file mode 100644 index 0000000000..2d961b4f59 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/FileSystem.h @@ -0,0 +1,247 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef FILESYSTEM_H +#define FILESYSTEM_H + + +#include "Delegation.h" +#include "InodeIdMap.h" +#include "NFS4Defs.h" +#include "NFS4Server.h" + + +class Inode; +class RootInode; + +struct MountConfiguration { + bool fHard; + int fRetryLimit; + bigtime_t fRequestTimeout; + + bool fEmulateNamedAttrs; + bool fCacheMetadata; +}; + +class FileSystem : public DoublyLinkedListLinkImpl { +public: + static status_t Mount(FileSystem** pfs, RPC::Server* serv, + const char* path, dev_t id, + const MountConfiguration& configuration); + ~FileSystem(); + + status_t GetInode(ino_t id, Inode** inode); + inline RootInode* Root(); + + status_t Migrate(const RPC::Server* serv); + + DoublyLinkedList& OpenFilesLock(); + void OpenFilesUnlock(); + inline uint32 OpenFilesCount(); + void AddOpenFile(OpenState* state); + void RemoveOpenFile(OpenState* state); + + DoublyLinkedList& DelegationsLock(); + void DelegationsUnlock(); + void AddDelegation(Delegation* delegation); + void RemoveDelegation(Delegation* delegation); + Delegation* GetDelegation(const FileHandle& handle); + + inline bool IsAttrSupported(Attribute attr) const; + inline uint32 ExpireType() const; + + inline RPC::Server* Server(); + inline NFS4Server* NFSServer(); + + inline const char* Path() const; + inline const FileSystemId& FsId() const; + + inline uint64 AllocFileId(); + + inline dev_t DevId() const; + inline InodeIdMap* InoIdMap(); + + inline uint64 OpenOwner() const; + inline uint32 OpenOwnerSequenceLock(); + inline void OpenOwnerSequenceUnlock(uint32 sequence); + + inline bool NamedAttrs(); + inline void SetNamedAttrs(bool attrs); + + inline const MountConfiguration& GetConfiguration(); + + inline mutex& CreateFileLock(); +private: + FileSystem(const MountConfiguration& config); + + mutex fCreateFileLock; + + mutex fDelegationLock; + DoublyLinkedList fDelegationList; + AVLTreeMap fHandleToDelegation; + + DoublyLinkedList fOpenFiles; + uint32 fOpenCount; + mutex fOpenLock; + + uint64 fOpenOwner; + uint32 fOpenOwnerSequence; + mutex fOpenOwnerLock; + + uint32 fExpireType; + uint32 fSupAttrs[2]; + bool fNamedAttrs; + + FileSystemId fFsId; + const char* fPath; + + RootInode* fRoot; + + RPC::Server* fServer; + + vint64 fId; + dev_t fDevId; + + InodeIdMap fInoIdMap; + + MountConfiguration fConfiguration; +}; + + +inline RootInode* +FileSystem::Root() +{ + return fRoot; +} + + +inline uint32 +FileSystem::OpenFilesCount() +{ + return fOpenCount; +} + + +inline bool +FileSystem::IsAttrSupported(Attribute attr) const +{ + return sIsAttrSet(attr, fSupAttrs, 2); +} + + +inline uint32 +FileSystem::ExpireType() const +{ + return fExpireType; +} + + +inline RPC::Server* +FileSystem::Server() +{ + ASSERT(fServer != NULL); + return fServer; +} + + +inline NFS4Server* +FileSystem::NFSServer() +{ + ASSERT(fServer->PrivateData() != NULL); + return reinterpret_cast(fServer->PrivateData()); +} + + +inline const char* +FileSystem::Path() const +{ + ASSERT(fPath != NULL); + return fPath; +} + + +inline const FileSystemId& +FileSystem::FsId() const +{ + return fFsId; +} + + +inline uint64 +FileSystem::AllocFileId() +{ + return atomic_add64(&fId, 1); +} + + +inline dev_t +FileSystem::DevId() const +{ + return fDevId; +} + + +inline InodeIdMap* +FileSystem::InoIdMap() +{ + return &fInoIdMap; +} + + +inline uint64 +FileSystem::OpenOwner() const +{ + return fOpenOwner; +} + + +inline uint32 +FileSystem::OpenOwnerSequenceLock() +{ + mutex_lock(&fOpenOwnerLock); + return fOpenOwnerSequence; +} + + +inline void +FileSystem::OpenOwnerSequenceUnlock(uint32 sequence) +{ + fOpenOwnerSequence = sequence; + mutex_unlock(&fOpenOwnerLock); +} + + +inline bool +FileSystem::NamedAttrs() +{ + return fNamedAttrs; +} + + +inline void +FileSystem::SetNamedAttrs(bool attrs) +{ + fNamedAttrs = attrs; +} + + +inline const MountConfiguration& +FileSystem::GetConfiguration() +{ + return fConfiguration; +} + + +inline mutex& +FileSystem::CreateFileLock() +{ + return fCreateFileLock; +} + + +#endif // FILESYSTEM_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/IdMap.cpp b/src/add-ons/kernel/file_systems/nfs4/IdMap.cpp new file mode 100644 index 0000000000..a779469eb7 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/IdMap.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "IdMap.h" + +#include +#include +#include +#include + +#include "idmapper/IdMapper.h" + + +IdMap* gIdMapper = NULL; +mutex gIdMapperLock; + + +IdMap::IdMap() +{ + mutex_init(&fLock, NULL); + fInitStatus = _Repair(); +} + + +IdMap::~IdMap() +{ + delete_port(fRequestPort); + delete_port(fReplyPort); + mutex_destroy(&fLock); +} + + +uid_t +IdMap::GetUserId(const char* owner) +{ + ASSERT(owner != NULL); + return _GetValue(owner, MsgNameToUID); +} + + +gid_t +IdMap::GetGroupId(const char* ownerGroup) +{ + ASSERT(ownerGroup != NULL); + return _GetValue(ownerGroup, MsgNameToGID); +} + + +char* +IdMap::GetOwner(uid_t user) +{ + return reinterpret_cast(_GetBuffer(user, MsgUIDToName)); +} + + +char* +IdMap::GetOwnerGroup(gid_t group) +{ + return reinterpret_cast(_GetBuffer(group, MsgGIDToName)); +} + + +template +T +IdMap::_GetValue(const char* buffer, int32 code) +{ + ASSERT(buffer != NULL); + + MutexLocker _(fLock); + do { + status_t result = write_port(fRequestPort, MsgNameToUID, buffer, + strlen(buffer) + 1); + if (result != B_OK) { + if (_Repair() != B_OK) + return 0; + continue; + } + + int32 code; + T value; + result = read_port(fReplyPort, &code, &value, sizeof(T)); + if (result < B_OK) { + if (_Repair() != B_OK) + return 0; + continue; + } + + if (code != MsgReply) + return 0; + + return value; + } while (true); +} + + +template +void* +IdMap::_GetBuffer(T value, int32 code) +{ + MutexLocker _(fLock); + do { + status_t result = write_port(fRequestPort, code, &value, sizeof(value)); + if (result != B_OK) { + if (_Repair() != B_OK) + return NULL; + continue; + } + + ssize_t size = port_buffer_size(fReplyPort); + if (size < B_OK) { + if (_Repair() != B_OK) + return NULL; + continue; + } + + int32 code; + void* buffer = malloc(size); + if (buffer == NULL) + return NULL; + MemoryDeleter bufferDeleter(buffer); + + size = read_port(fReplyPort, &code, buffer, size); + if (size < B_OK) { + if (_Repair() != B_OK) + return 0; + continue; + } + + if (code != MsgReply) + return NULL; + + bufferDeleter.Detach(); + return buffer; + } while (true); +} + + +status_t +IdMap::_Repair() +{ + status_t result = B_OK; + + fRequestPort = create_port(1, kRequestPortName); + if (fRequestPort < B_OK) + return fRequestPort; + + fReplyPort = create_port(1, kReplyPortName); + if (fReplyPort < B_OK) { + delete_port(fRequestPort); + return fReplyPort; + } + + char path[256]; + if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, static_cast(-1), + false, path, sizeof(path)) != B_OK) { + delete_port(fReplyPort); + delete_port(fRequestPort); + return B_NAME_NOT_FOUND; + } + strlcat(path, "/nfs4_idmapper_server", sizeof(path)); + + const char* args[] = { path, NULL }; + thread_id thread = load_image_etc(1, args, NULL, B_NORMAL_PRIORITY, + B_SYSTEM_TEAM, 0); + if (thread < B_OK) { + delete_port(fReplyPort); + delete_port(fRequestPort); + return thread; + } + + set_port_owner(fRequestPort, thread); + set_port_owner(fReplyPort, thread); + + result = resume_thread(thread); + if (result != B_OK) { + kill_thread(thread); + delete_port(fReplyPort); + delete_port(fRequestPort); + return result; + } + + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/IdMap.h b/src/add-ons/kernel/file_systems/nfs4/IdMap.h new file mode 100644 index 0000000000..daf66446cb --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/IdMap.h @@ -0,0 +1,60 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef IDMAP_H +#define IDMAP_H + + +#include +#include +#include + + +class IdMap { +public: + IdMap(); + ~IdMap(); + + uid_t GetUserId(const char* owner); + gid_t GetGroupId(const char* ownerGroup); + + char* GetOwner(uid_t user); + char* GetOwnerGroup(gid_t group); + + inline status_t InitStatus(); + +private: + status_t _Repair(); + + template + void* _GetBuffer(T value, int32 code); + + template + T _GetValue(const char* buffer, int32 code); + + status_t fInitStatus; + + mutex fLock; + + port_id fRequestPort; + port_id fReplyPort; +}; + + +inline status_t +IdMap::InitStatus() +{ + return fInitStatus; +} + + +extern IdMap* gIdMapper; +extern mutex gIdMapperLock; + + +#endif // IDMAP_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp new file mode 100644 index 0000000000..fe3d60d69c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp @@ -0,0 +1,981 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Inode.h" + +#include +#include + +#include +#include +#include + +#include "IdMap.h" +#include "Request.h" +#include "RootInode.h" + + +Inode::Inode() + : + fMetaCache(this), + fCache(NULL), + fAttrCache(NULL), + fDelegation(NULL), + fFileCache(NULL), + fMaxFileSize(0), + fOpenState(NULL), + fWriteDirty(false), + fAIOWait(create_sem(1, NULL)), + fAIOCount(0) +{ + rw_lock_init(&fDelegationLock, NULL); + mutex_init(&fStateLock, NULL); + mutex_init(&fFileCacheLock, NULL); + rw_lock_init(&fWriteLock, NULL); + mutex_init(&fAIOLock, NULL); +} + + +status_t +Inode::CreateInode(FileSystem* fs, const FileInfo& fi, Inode** _inode) +{ + ASSERT(fs != NULL); + ASSERT(_inode != NULL); + + Inode* inode = NULL; + if (fs->Root() == NULL) + inode = new(std::nothrow) RootInode; + else + inode = new(std::nothrow) Inode; + + if (inode == NULL) + return B_NO_MEMORY; + + inode->fInfo = fi; + inode->fFileSystem = fs; + + uint64 size; + do { + RPC::Server* serv = fs->Server(); + Request request(serv, fs); + RequestBuilder& req = request.Builder(); + + req.PutFH(inode->fInfo.fHandle); + + Attribute attr[] = { FATTR4_TYPE, FATTR4_CHANGE, FATTR4_SIZE, + FATTR4_FSID, FATTR4_FILEID }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (inode->HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK || count < 4) + return result; + + if (fi.fFileId == 0) { + if (count < 5 || values[4].fAttribute != FATTR4_FILEID) + inode->fInfo.fFileId = fs->AllocFileId(); + else + inode->fInfo.fFileId = values[4].fData.fValue64; + } else + inode->fInfo.fFileId = fi.fFileId; + + // FATTR4_TYPE is mandatory + inode->fType = values[0].fData.fValue32; + + if (inode->fType == NF4DIR) + inode->fCache = new DirectoryCache(inode); + inode->fAttrCache = new DirectoryCache(inode, true); + + // FATTR4_CHANGE is mandatory + inode->fChange = values[1].fData.fValue64; + + // FATTR4_SIZE is mandatory + size = values[2].fData.fValue64; + + // FATTR4_FSID is mandatory + FileSystemId* fsid + = reinterpret_cast(values[3].fData.fPointer); + if (*fsid != fs->FsId()) { + delete[] values; + return B_ENTRY_NOT_FOUND; + } + + delete[] values; + + *_inode = inode; + + break; + } while (true); + + if (inode->fType == NF4REG) + inode->fFileCache = file_cache_create(fs->DevId(), inode->ID(), size); + + return B_OK; +} + + +Inode::~Inode() +{ + if (fDelegation != NULL) + RecallDelegation(); + + if (fFileCache != NULL) + file_cache_delete(fFileCache); + + delete fCache; + delete fAttrCache; + + delete_sem(fAIOWait); + mutex_destroy(&fAIOLock); + mutex_destroy(&fStateLock); + mutex_destroy(&fFileCacheLock); + rw_lock_destroy(&fDelegationLock); + rw_lock_destroy(&fWriteLock); + + ASSERT(fAIOCount == 0); +} + + +status_t +Inode::RevalidateFileCache() +{ + if (fDelegation != NULL) + return B_OK; + + uint64 change; + status_t result = GetChangeInfo(&change); + if (result != B_OK) + return result; + + MutexLocker _(fFileCacheLock); + if (change == fChange) + return B_OK; + + struct stat st; + result = Stat(&st); + if (result != B_OK) + return result; + + SyncAndCommit(true); + file_cache_delete(fFileCache); + + fFileCache = file_cache_create(fFileSystem->DevId(), ID(), st.st_size); + + change = fChange; + return B_OK; +} + + +status_t +Inode::LookUp(const char* name, ino_t* id) +{ + ASSERT(name != NULL); + ASSERT(id != NULL); + + if (fType != NF4DIR) + return B_NOT_A_DIRECTORY; + + uint64 change; + uint64 fileID; + FileHandle handle; + status_t result = NFS4Inode::LookUp(name, &change, &fileID, &handle); + if (result != B_OK) + return result; + + *id = FileIdToInoT(fileID); + + result = ChildAdded(name, fileID, handle); + if (result != B_OK) + return result; + + fCache->Lock(); + if (!fCache->Valid()) { + fCache->Reset(); + fCache->SetChangeInfo(change); + } else + fCache->ValidateChangeInfo(change); + + fCache->AddEntry(name, *id); + fCache->Unlock(); + + return B_OK; +} + + +status_t +Inode::Link(Inode* dir, const char* name) +{ + ASSERT(dir != NULL); + ASSERT(name != NULL); + + ChangeInfo changeInfo; + status_t result = NFS4Inode::Link(dir, name, &changeInfo); + if (result != B_OK) + return result; + + fFileSystem->Root()->MakeInfoInvalid(); + + FileInfo fi = fInfo; + fi.fParent = dir->fInfo.fHandle; + result = fi.CreateName(fInfo.fPath, name); + if (result != B_OK) + return result; + + fFileSystem->InoIdMap()->AddEntry(fi, fInfo.fFileId); + + dir->fCache->Lock(); + if (dir->fCache->Valid()) { + if (changeInfo.fAtomic + && dir->fCache->ChangeInfo() == changeInfo.fBefore) { + dir->fCache->AddEntry(name, fInfo.fFileId, true); + dir->fCache->SetChangeInfo(changeInfo.fAfter); + } else + dir->fCache->Trash(); + } + dir->fCache->Unlock(); + + notify_entry_created(fFileSystem->DevId(), dir->ID(), name, ID()); + + return B_OK; +} + + +status_t +Inode::Remove(const char* name, FileType type, ino_t* id) +{ + ASSERT(name != NULL); + + MemoryDeleter nameDeleter; + if (type == NF4NAMEDATTR) { + status_t result = LoadAttrDirHandle(); + if (result != B_OK) + return result; + + name = AttrToFileName(name); + if (name == NULL) + return B_NO_MEMORY; + nameDeleter.SetTo(const_cast(name)); + } + + ChangeInfo changeInfo; + uint64 fileID; + status_t result = NFS4Inode::RemoveObject(name, type, &changeInfo, &fileID); + if (result != B_OK) + return result; + + DirectoryCache* cache = type != NF4NAMEDATTR ? fCache : fAttrCache; + cache->Lock(); + if (cache->Valid()) { + if (changeInfo.fAtomic + && fCache->ChangeInfo() == changeInfo.fBefore) { + cache->RemoveEntry(name); + cache->SetChangeInfo(changeInfo.fAfter); + } else if (cache->ChangeInfo() != changeInfo.fBefore) + cache->Trash(); + } + cache->Unlock(); + + fFileSystem->Root()->MakeInfoInvalid(); + if (id != NULL) + *id = FileIdToInoT(fileID); + + if (type == NF4NAMEDATTR) { + notify_attribute_changed(fFileSystem->DevId(), ID(), name, + B_ATTR_REMOVED); + } else { + notify_entry_removed(fFileSystem->DevId(), ID(), name, + FileIdToInoT(fileID)); + } + + return B_OK; +} + + +status_t +Inode::Rename(Inode* from, Inode* to, const char* fromName, const char* toName, + bool attribute, ino_t* id) +{ + ASSERT(from != NULL); + ASSERT(fromName != NULL); + ASSERT(to != NULL); + ASSERT(toName != NULL); + + if (from->fFileSystem != to->fFileSystem) + return B_DONT_DO_THAT; + + MemoryDeleter fromNameDeleter; + MemoryDeleter toNameDeleter; + if (attribute) { + status_t result = from->LoadAttrDirHandle(); + if (result != B_OK) + return result; + + result = to->LoadAttrDirHandle(); + if (result != B_OK) + return result; + + fromName = from->AttrToFileName(fromName); + toName = to->AttrToFileName(toName); + + fromNameDeleter.SetTo(const_cast(fromName)); + toNameDeleter.SetTo(const_cast(toName)); + if (fromName == NULL || toName == NULL) + return B_NO_MEMORY; + } + + ChangeInfo fromChange, toChange; + uint64 fileID; + status_t result = NFS4Inode::RenameNode(from, to, fromName, toName, + &fromChange, &toChange, &fileID, attribute); + if (result != B_OK) + return result; + + from->fFileSystem->Root()->MakeInfoInvalid(); + + DirectoryCache* cache = attribute ? from->fAttrCache : from->fCache; + cache->Lock(); + if (cache->Valid()) { + if (fromChange.fAtomic + && cache->ChangeInfo() == fromChange.fBefore) { + cache->RemoveEntry(fromName); + cache->SetChangeInfo(fromChange.fAfter); + } else if (cache->ChangeInfo() != fromChange.fBefore) + cache->Trash(); + } + cache->Unlock(); + + if (id != NULL) + *id = FileIdToInoT(fileID); + + cache = attribute ? to->fAttrCache : to->fCache; + cache->Lock(); + if (cache->Valid()) { + if (toChange.fAtomic + && cache->ChangeInfo() == toChange.fBefore) { + cache->AddEntry(toName, fileID, true); + cache->SetChangeInfo(toChange.fAfter); + } else if (to->fCache->ChangeInfo() != toChange.fBefore) + cache->Trash(); + } + cache->Unlock(); + + if (attribute) { + notify_attribute_changed(from->fFileSystem->DevId(), from->ID(), + fromName, B_ATTR_REMOVED); + notify_attribute_changed(to->fFileSystem->DevId(), to->ID(), toName, + B_ATTR_CREATED); + } else { + notify_entry_moved(from->fFileSystem->DevId(), from->ID(), fromName, + to->ID(), toName, FileIdToInoT(fileID)); + } + + return B_OK; +} + + +status_t +Inode::CreateLink(const char* name, const char* path, int mode, ino_t* id) +{ + return CreateObject(name, path, mode, NF4LNK, id); +} + + +status_t +Inode::CreateObject(const char* name, const char* path, int mode, FileType type, + ino_t* id) +{ + ASSERT(name != NULL); + ASSERT(type != NF4LNK || path != NULL); + + ChangeInfo changeInfo; + uint64 fileID; + FileHandle handle; + + status_t result = NFS4Inode::CreateObject(name, path, mode, type, &changeInfo, + &fileID, &handle); + if (result != B_OK) + return B_OK; + + fFileSystem->Root()->MakeInfoInvalid(); + + result = ChildAdded(name, fileID, handle); + if (result != B_OK) + return B_OK; + + fCache->Lock(); + if (fCache->Valid()) { + if (changeInfo.fAtomic && fCache->ChangeInfo() == changeInfo.fBefore) { + fCache->AddEntry(name, fileID, true); + fCache->SetChangeInfo(changeInfo.fAfter); + } else + fCache->Trash(); + } + fCache->Unlock(); + + notify_entry_created(fFileSystem->DevId(), ID(), name, + FileIdToInoT(fileID)); + + *id = FileIdToInoT(fileID); + return B_OK; +} + + +status_t +Inode::Access(int mode) +{ + int acc = 0; + + uint32 allowed; + bool cache = fFileSystem->GetConfiguration().fCacheMetadata; + status_t result = fMetaCache.GetAccess(geteuid(), &allowed); + if (result != B_OK || !cache) { + result = NFS4Inode::Access(&allowed); + if (result != B_OK) + return result; + fMetaCache.SetAccess(geteuid(), allowed); + } + + if ((allowed & ACCESS4_READ) != 0) + acc |= R_OK; + + if ((allowed & ACCESS4_LOOKUP) != 0) + acc |= X_OK | R_OK; + + if ((allowed & ACCESS4_EXECUTE) != 0) + acc |= X_OK; + + if ((allowed & ACCESS4_MODIFY) != 0) + acc |= W_OK; + + if ((mode & acc) != mode) + return B_NOT_ALLOWED; + + return B_OK; +} + + +status_t +Inode::Stat(struct stat* st, OpenAttrCookie* attr) +{ + ASSERT(st != NULL); + + if (attr != NULL) + return GetStat(st, attr); + + bool cache = fFileSystem->GetConfiguration().fCacheMetadata; + if (!cache) + return GetStat(st, NULL); + + status_t result = fMetaCache.GetStat(st); + if (result != B_OK) { + struct stat temp; + result = GetStat(&temp); + if (result != B_OK) + return result; + fMetaCache.SetStat(temp); + fMetaCache.GetStat(st); + } + + return B_OK; +} + + +status_t +Inode::GetStat(struct stat* st, OpenAttrCookie* attr) +{ + ASSERT(st != NULL); + + AttrValue* values; + uint32 count; + status_t result = NFS4Inode::GetStat(&values, &count, attr); + if (result != B_OK) + return result; + + // FATTR4_SIZE is mandatory + if (count < 1 || values[0].fAttribute != FATTR4_SIZE) { + delete[] values; + return B_BAD_VALUE; + } + st->st_size = values[0].fData.fValue64; + + uint32 next = 1; + st->st_mode = Type(); + if (count >= next && values[next].fAttribute == FATTR4_MODE) { + st->st_mode |= values[next].fData.fValue32; + next++; + } else + st->st_mode = 777; + + if (count >= next && values[next].fAttribute == FATTR4_NUMLINKS) { + st->st_nlink = values[next].fData.fValue32; + next++; + } else + st->st_nlink = 1; + + if (count >= next && values[next].fAttribute == FATTR4_OWNER) { + char* owner = reinterpret_cast(values[next].fData.fPointer); + if (owner != NULL && isdigit(owner[0])) + st->st_uid = atoi(owner); + else + st->st_uid = gIdMapper->GetUserId(owner); + next++; + } else + st->st_uid = 0; + + if (count >= next && values[next].fAttribute == FATTR4_OWNER_GROUP) { + char* group = reinterpret_cast(values[next].fData.fPointer); + if (group != NULL && isdigit(group[0])) + st->st_gid = atoi(group); + else + st->st_gid = gIdMapper->GetGroupId(group); + next++; + } else + st->st_gid = 0; + + if (count >= next && values[next].fAttribute == FATTR4_TIME_ACCESS) { + memcpy(&st->st_atim, values[next].fData.fPointer, + sizeof(timespec)); + next++; + } else + memset(&st->st_atim, 0, sizeof(timespec)); + + if (count >= next && values[next].fAttribute == FATTR4_TIME_CREATE) { + memcpy(&st->st_crtim, values[next].fData.fPointer, + sizeof(timespec)); + next++; + } else + memset(&st->st_crtim, 0, sizeof(timespec)); + + if (count >= next && values[next].fAttribute == FATTR4_TIME_METADATA) { + memcpy(&st->st_ctim, values[next].fData.fPointer, + sizeof(timespec)); + next++; + } else + memset(&st->st_ctim, 0, sizeof(timespec)); + + if (count >= next && values[next].fAttribute == FATTR4_TIME_MODIFY) { + memcpy(&st->st_mtim, values[next].fData.fPointer, + sizeof(timespec)); + next++; + } else + memset(&st->st_mtim, 0, sizeof(timespec)); + delete[] values; + + st->st_blksize = fFileSystem->Root()->IOSize(); + st->st_blocks = st->st_size / st->st_blksize; + st->st_blocks += st->st_size % st->st_blksize == 0 ? 0 : 1; + + return B_OK; +} + + +status_t +Inode::WriteStat(const struct stat* st, uint32 mask, OpenAttrCookie* cookie) +{ + ASSERT(st != NULL); + + status_t result; + AttrValue attr[6]; + uint32 i = 0; + + if ((mask & B_STAT_SIZE) != 0) { + attr[i].fAttribute = FATTR4_SIZE; + attr[i].fFreePointer = false; + attr[i].fData.fValue64 = st->st_size; + i++; + } + + if ((mask & B_STAT_MODE) != 0) { + attr[i].fAttribute = FATTR4_MODE; + attr[i].fFreePointer = false; + attr[i].fData.fValue32 = st->st_mode; + i++; + } + + if ((mask & B_STAT_UID) != 0) { + attr[i].fAttribute = FATTR4_OWNER; + attr[i].fFreePointer = true; + attr[i].fData.fPointer = gIdMapper->GetOwner(st->st_uid); + i++; + } + + if ((mask & B_STAT_GID) != 0) { + attr[i].fAttribute = FATTR4_OWNER_GROUP; + attr[i].fFreePointer = true; + attr[i].fData.fPointer = gIdMapper->GetOwnerGroup(st->st_gid); + i++; + } + + if ((mask & B_STAT_ACCESS_TIME) != 0) { + attr[i].fAttribute = FATTR4_TIME_ACCESS_SET; + attr[i].fFreePointer = true; + attr[i].fData.fPointer = malloc(sizeof(st->st_atim)); + memcpy(attr[i].fData.fPointer, &st->st_atim, sizeof(st->st_atim)); + i++; + } + + if ((mask & B_STAT_MODIFICATION_TIME) != 0) { + attr[i].fAttribute = FATTR4_TIME_MODIFY_SET; + attr[i].fFreePointer = true; + attr[i].fData.fPointer = malloc(sizeof(st->st_mtim)); + memcpy(attr[i].fData.fPointer, &st->st_mtim, sizeof(st->st_mtim)); + i++; + } + + if (cookie == NULL) { + MutexLocker stateLocker(fStateLock); + ASSERT(fOpenState != NULL); + result = NFS4Inode::WriteStat(fOpenState, attr, i); + stateLocker.Unlock(); + + fMetaCache.InvalidateStat(); + if ((mask & B_STAT_MODE) != 0 || (mask & B_STAT_UID) != 0 + || (mask & B_STAT_GID) != 0) { + fMetaCache.InvalidateAccess(); + } + } else + result = NFS4Inode::WriteStat(cookie->fOpenState, attr, i); + + return result; +} + + +inline status_t +Inode::CheckLockType(short ltype, uint32 mode) +{ + switch (ltype) { + case F_UNLCK: + return B_OK; + + case F_RDLCK: + if ((mode & O_RDONLY) == 0 && (mode & O_RDWR) == 0) + return EBADF; + return B_OK; + + case F_WRLCK: + if ((mode & O_WRONLY) == 0 && (mode & O_RDWR) == 0) + return EBADF; + return B_OK; + + default: + return B_BAD_VALUE; + } +} + + +status_t +Inode::TestLock(OpenFileCookie* cookie, struct flock* lock) +{ + ASSERT(cookie != NULL); + ASSERT(lock != NULL); + + if (lock->l_type == F_UNLCK) + return B_OK; + + status_t result = CheckLockType(lock->l_type, cookie->fMode); + if (result != B_OK) + return result; + + LockType ltype = sGetLockType(lock->l_type, false); + uint64 position = lock->l_start; + uint64 length = lock->l_len; + bool conflict; + + result = NFS4Inode::TestLock(cookie, <ype, &position, &length, conflict); + if (result != B_OK) + return result; + + if (conflict) { + lock->l_type = sLockTypeToHaiku(ltype); + lock->l_start = static_cast(position); + if (length >= OFF_MAX) + lock->l_len = OFF_MAX; + else + lock->l_len = static_cast(length); + } else + lock->l_type = F_UNLCK; + + return B_OK; +} + + +status_t +Inode::AcquireLock(OpenFileCookie* cookie, const struct flock* lock, + bool wait) +{ + ASSERT(cookie != NULL); + ASSERT(lock != NULL); + + OpenState* state = cookie->fOpenState; + + status_t result = CheckLockType(lock->l_type, cookie->fMode); + if (result != B_OK) + return result; + + thread_info info; + get_thread_info(find_thread(NULL), &info); + + MutexLocker locker(state->fOwnerLock); + LockOwner* owner = state->GetLockOwner(info.team); + if (owner == NULL) + return B_NO_MEMORY; + + LockInfo* linfo = new LockInfo(owner); + if (linfo == NULL) + return B_NO_MEMORY; + locker.Unlock(); + + linfo->fStart = lock->l_start; + if (lock->l_len + lock->l_start == OFF_MAX) + linfo->fLength = UINT64_MAX; + else + linfo->fLength = lock->l_len; + linfo->fType = sGetLockType(lock->l_type, wait); + + result = NFS4Inode::AcquireLock(cookie, linfo, wait); + if (result != B_OK) + return result; + + MutexLocker _(state->fLocksLock); + state->AddLock(linfo); + cookie->AddLock(linfo); + + return B_OK; +} + + +status_t +Inode::ReleaseLock(OpenFileCookie* cookie, const struct flock* lock) +{ + ASSERT(cookie != NULL); + ASSERT(lock != NULL); + + SyncAndCommit(); + + LockInfo* prev = NULL; + + thread_info info; + get_thread_info(find_thread(NULL), &info); + uint32 owner = info.team; + + OpenState* state = cookie->fOpenState; + MutexLocker locker(state->fLocksLock); + LockInfo* linfo = state->fLocks; + while (linfo != NULL) { + if (linfo->fOwner->fOwner == owner && *linfo == *lock) { + state->RemoveLock(linfo, prev); + break; + } + + prev = linfo; + linfo = linfo->fNext; + } + + prev = NULL; + linfo = cookie->fLocks; + while (linfo != NULL) { + if (linfo->fOwner->fOwner == owner && *linfo == *lock) { + cookie->RemoveLock(linfo, prev); + break; + } + + prev = linfo; + linfo = linfo->fCookieNext; + } + locker.Unlock(); + + if (linfo == NULL) + return B_BAD_VALUE; + + status_t result = NFS4Inode::ReleaseLock(cookie, linfo); + if (result != B_OK) + return result; + + state->DeleteLock(linfo); + + return B_OK; +} + + +status_t +Inode::ReleaseAllLocks(OpenFileCookie* cookie) +{ + ASSERT(cookie != NULL); + + SyncAndCommit(); + + OpenState* state = cookie->fOpenState; + MutexLocker _(state->fLocksLock); + LockInfo* linfo = cookie->fLocks; + while (linfo != NULL) { + cookie->RemoveLock(linfo, NULL); + + LockInfo* prev = NULL; + LockInfo* stateLock = state->fLocks; + while (stateLock != NULL) { + if (*linfo == *stateLock) { + state->RemoveLock(stateLock, prev); + break; + } + + prev = stateLock; + stateLock = stateLock->fNext; + } + + NFS4Inode::ReleaseLock(cookie, linfo); + state->DeleteLock(linfo); + + linfo = cookie->fLocks; + } + + return B_OK; +} + + +status_t +Inode::ChildAdded(const char* name, uint64 fileID, + const FileHandle& fileHandle) +{ + ASSERT(name != NULL); + + fFileSystem->Root()->MakeInfoInvalid(); + + FileInfo fi; + fi.fFileId = fileID; + fi.fHandle = fileHandle; + fi.fParent = fInfo.fHandle; + status_t result = fi.CreateName(fInfo.fPath, name); + if (result != B_OK) + return result; + + return fFileSystem->InoIdMap()->AddEntry(fi, FileIdToInoT(fileID)); +} + + +const char* +Inode::Name() const +{ + return fInfo.fName; +} + + +void +Inode::SetDelegation(Delegation* delegation) +{ + ASSERT(delegation != NULL); + + WriteLocker _(fDelegationLock); + + fMetaCache.InvalidateStat(); + struct stat st; + Stat(&st); + fMetaCache.LockValid(); + + fDelegation = delegation; + fOpenState->AcquireReference(); + fOpenState->fDelegation = delegation; + fFileSystem->AddDelegation(delegation); +} + + +void +Inode::RecallDelegation(bool truncate) +{ + WriteLocker _(fDelegationLock); + if (fDelegation == NULL) + return; + ReturnDelegation(truncate); +} + + +void +Inode::RecallReadDelegation() +{ + WriteLocker _(fDelegationLock); + if (fDelegation == NULL || fDelegation->Type() != OPEN_DELEGATE_READ) + return; + ReturnDelegation(false); +} + + +void +Inode::ReturnDelegation(bool truncate) +{ + ASSERT(fDelegation != NULL); + + fDelegation->GiveUp(truncate); + + fMetaCache.UnlockValid(); + fFileSystem->RemoveDelegation(fDelegation); + + MutexLocker stateLocker(fStateLock); + fOpenState->fDelegation = NULL; + ReleaseOpenState(); + + delete fDelegation; + fDelegation = NULL; +} + + +void +Inode::ReleaseOpenState() +{ + ASSERT(fOpenState != NULL); + + if (fOpenState->ReleaseReference() == 1) { + ASSERT(fAIOCount == 0); + fOpenState = NULL; + } +} + + +status_t +Inode::SyncAndCommit(bool force) +{ + if (!force && fDelegation != NULL) + return B_OK; + + file_cache_sync(fFileCache); + WaitAIOComplete(); + return Commit(); +} + + +void +Inode::BeginAIOOp() +{ + MutexLocker _(fAIOLock); + fAIOCount++; + if (fAIOCount == 1) + acquire_sem(fAIOWait); +} + + +void +Inode::EndAIOOp() +{ + MutexLocker _(fAIOLock); + ASSERT(fAIOCount > 0); + fAIOCount--; + if (fAIOCount == 0) + release_sem(fAIOWait); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.h b/src/add-ons/kernel/file_systems/nfs4/Inode.h new file mode 100644 index 0000000000..0c5e8449fe --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.h @@ -0,0 +1,258 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef INODE_H +#define INODE_H + + +#include "DirectoryCache.h" +#include "MetadataCache.h" +#include "NFS4Inode.h" +#include "OpenState.h" + + +class Delegation; + +class Inode : public NFS4Inode { +public: + static status_t CreateInode(FileSystem* fs, const FileInfo& fi, + Inode** inode); + virtual ~Inode(); + + inline ino_t ID() const; + inline mode_t Type() const; + virtual const char* Name() const; + inline FileSystem* GetFileSystem() const; + + inline void SetOpenState(OpenState* state); + + inline void* FileCache(); + status_t RevalidateFileCache(); + + inline uint64 MaxFileSize(); + + inline uint64 Change(); + inline bool Dirty(); + + inline OpenState* GetOpenState(); + + void SetDelegation(Delegation* delegation); + void RecallDelegation(bool truncate = false); + void RecallReadDelegation(); + + status_t LookUp(const char* name, ino_t* id); + + status_t Access(int mode); + + status_t Commit(); + status_t SyncAndCommit(bool force = false); + + status_t CreateObject(const char* name, const char* path, + int mode, FileType type, ino_t* id); + + status_t CreateLink(const char* name, const char* path, + int mode, ino_t* id); + + status_t Link(Inode* dir, const char* name); + status_t Remove(const char* name, FileType type, + ino_t* id = NULL); + static status_t Rename(Inode* from, Inode* to, + const char* fromName, const char* toName, + bool attribute = false, ino_t* id = NULL); + + status_t Stat(struct stat* st, + OpenAttrCookie* attr = NULL); + status_t WriteStat(const struct stat* st, uint32 mask, + OpenAttrCookie* attr = NULL); + + status_t Create(const char* name, int mode, int perms, + OpenFileCookie* cookie, + OpenDelegationData* data, ino_t* id); + status_t Open(int mode, OpenFileCookie* cookie); + status_t Close(OpenFileCookie* cookie); + + status_t OpenAttr(const char* name, int mode, + OpenAttrCookie* cookie, bool create, + int32 type = 0); + status_t CloseAttr(OpenAttrCookie* cookie); + + status_t Read(OpenFileCookie* cookie, off_t pos, + void* buffer, size_t* length); + status_t Write(OpenFileCookie* cookie, off_t pos, + const void* buffer, size_t* _length); + + status_t ReadDirect(OpenStateCookie* cookie, off_t pos, + void* buffer, size_t* length, bool* eof); + status_t WriteDirect(OpenStateCookie* cookie, off_t pos, + const void* buffer, size_t* _length); + + status_t CreateDir(const char* name, int mode, + ino_t* id); + status_t OpenDir(OpenDirCookie* cookie); + status_t ReadDir(void* buffer, uint32 size, + uint32* count, OpenDirCookie* cookie); + + status_t OpenAttrDir(OpenDirCookie* cookie); + + status_t TestLock(OpenFileCookie* cookie, + struct flock* lock); + status_t AcquireLock(OpenFileCookie* cookie, + const struct flock* lock, bool wait); + status_t ReleaseLock(OpenFileCookie* cookie, + const struct flock* lock); + status_t ReleaseAllLocks(OpenFileCookie* cookie); + + status_t GetDirSnapshot(DirectoryCacheSnapshot** + _snapshot, OpenDirCookie* cookie, + uint64* _change, bool attribute); + + status_t LoadAttrDirHandle(); + + static inline ino_t FileIdToInoT(uint64 fileid); + + void BeginAIOOp(); + void EndAIOOp(); + inline void WaitAIOComplete(); +protected: + Inode(); + + void ReleaseOpenState(); + + status_t CreateState(const char* name, int mode, + int perms, OpenState* state, + OpenDelegationData* data); + + void ReturnDelegation(bool truncate); + + status_t ReadDirUp(struct dirent* de, uint32 pos, + uint32 size); + status_t FillDirEntry(struct dirent* de, ino_t id, + const char* name, uint32 pos, uint32 size); + + status_t ChildAdded(const char* name, uint64 fileID, + const FileHandle& fileHandle); + + status_t GetStat(struct stat* st, + OpenAttrCookie* attr = NULL); + + char* AttrToFileName(const char* path); + + static inline status_t CheckLockType(short ltype, uint32 mode); + +private: + uint32 fType; + + MetadataCache fMetaCache; + DirectoryCache* fCache; + DirectoryCache* fAttrCache; + + rw_lock fDelegationLock; + Delegation* fDelegation; + + uint64 fChange; + void* fFileCache; + mutex fFileCacheLock; + uint64 fMaxFileSize; + + OpenState* fOpenState; + mutex fStateLock; + + rw_lock fWriteLock; + bool fWriteDirty; + + sem_id fAIOWait; + uint32 fAIOCount; + mutex fAIOLock; +}; + + +inline void +Inode::WaitAIOComplete() +{ + acquire_sem(fAIOWait); + release_sem(fAIOWait); +} + + +inline ino_t +Inode::FileIdToInoT(uint64 fileid) +{ + if (sizeof(ino_t) >= sizeof(uint64)) + return fileid; + else + return (ino_t)fileid ^ (fileid >> + (sizeof(uint64) - sizeof(ino_t)) * 8); +} + + +inline ino_t +Inode::ID() const +{ + return FileIdToInoT(fInfo.fFileId); +} + + +inline mode_t +Inode::Type() const +{ + return sNFSFileTypeToHaiku[fType]; +} + + +inline FileSystem* +Inode::GetFileSystem() const +{ + ASSERT(fFileSystem != NULL); + return fFileSystem; +} + + +inline void* +Inode::FileCache() +{ + return fFileCache; +} + + +inline void +Inode::SetOpenState(OpenState* state) +{ + ASSERT(state != NULL); + fOpenState = state; +} + + +inline uint64 +Inode::MaxFileSize() +{ + return fMaxFileSize; +} + + +inline uint64 +Inode::Change() +{ + return fChange; +} + + +inline bool +Inode::Dirty() +{ + return fWriteDirty; +} + + +inline OpenState* +Inode::GetOpenState() +{ + return fOpenState; +} + + +#endif // INODE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp b/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp new file mode 100644 index 0000000000..1b3851704c --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp @@ -0,0 +1,416 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Inode.h" + +#include +#include + +#include "IdMap.h" +#include "Request.h" +#include "RootInode.h" + + +status_t +Inode::CreateDir(const char* name, int mode, ino_t* id) +{ + return CreateObject(name, NULL, mode, NF4DIR, id); +} + + +status_t +Inode::OpenDir(OpenDirCookie* cookie) +{ + ASSERT(cookie != NULL); + + if (fType != NF4DIR) + return B_NOT_A_DIRECTORY; + + status_t result = Access(R_OK); + if (result != B_OK) + return result; + + cookie->fFileSystem = fFileSystem; + cookie->fSpecial = 0; + cookie->fSnapshot = NULL; + cookie->fCurrent = NULL; + cookie->fEOF = false; + cookie->fAttrDir = false; + + return B_OK; +} + + +status_t +Inode::OpenAttrDir(OpenDirCookie* cookie) +{ + ASSERT(cookie != NULL); + + cookie->fFileSystem = fFileSystem; + cookie->fSpecial = 0; + cookie->fSnapshot = NULL; + cookie->fCurrent = NULL; + cookie->fEOF = false; + cookie->fAttrDir = true; + + return LoadAttrDirHandle(); +} + + +status_t +Inode::LoadAttrDirHandle() +{ + if (fInfo.fAttrDir.fSize != 0) + return B_OK; + + FileHandle handle; + status_t result; + + if (fFileSystem->NamedAttrs()) { + result = NFS4Inode::OpenAttrDir(&handle); + if (result == B_OK) { + fInfo.fAttrDir = handle; + return B_OK; + } + + if (result != B_UNSUPPORTED) + return result; + + fFileSystem->SetNamedAttrs(false); + } + + if (!fFileSystem->GetConfiguration().fEmulateNamedAttrs) + return B_UNSUPPORTED; + + char* attrDir + = reinterpret_cast(malloc(strlen(Name()) + 32)); + if (attrDir == NULL) + return B_NO_MEMORY; + strcpy(attrDir, "."); + strcat(attrDir, Name()); + strcat(attrDir, "-haiku-attrs"); + + result = NFS4Inode::LookUp(attrDir, NULL, NULL, &handle, true); + if (result == B_ENTRY_NOT_FOUND) { + ChangeInfo change; + struct stat st; + Stat(&st); + st.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; + result = NFS4Inode::CreateObject(attrDir, NULL, st.st_mode, NF4DIR, + &change, NULL, &handle, true); + } + + free(attrDir); + + if (result != B_OK) + return result; + + fInfo.fAttrDir = handle; + return B_OK; +} + + +status_t +Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos, + uint32 size) +{ + ASSERT(de != NULL); + ASSERT(name != NULL); + + uint32 nameSize = strlen(name) + 1; + const uint32 entSize = sizeof(struct dirent); + + if (pos + entSize + nameSize > size) + return B_BUFFER_OVERFLOW; + + de->d_dev = fFileSystem->DevId(); + de->d_ino = id; + de->d_reclen = entSize + nameSize; + if (de->d_reclen % 8 != 0) + de->d_reclen += 8 - de->d_reclen % 8; + + strcpy(de->d_name, name); + + return B_OK; +} + + +status_t +Inode::ReadDirUp(struct dirent* de, uint32 pos, uint32 size) +{ + ASSERT(de != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.LookUpUp(); + req.GetFH(); + + if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { + Attribute attr[] = { FATTR4_FILEID }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + } + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + result = reply.LookUpUp(); + if (result != B_OK) + return result; + + FileHandle fh; + reply.GetFH(&fh); + + uint64 fileId; + if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { + AttrValue* values; + uint32 count; + reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + fileId = values[0].fData.fValue64; + delete[] values; + } else + fileId = fFileSystem->AllocFileId(); + + return FillDirEntry(de, FileIdToInoT(fileId), "..", pos, size); + } while (true); +} + + +static char* +FileToAttrName(const char* path) +{ + ASSERT(path != NULL); + + char* name = strdup(path); + if (name == NULL) + return NULL; + + char* current = strpbrk(name, "#$"); + while (current != NULL) { + switch (*current) { + case '#': + *current = '/'; + break; + case '$': + *current = ':'; + break; + } + current = strpbrk(name, "#$"); + } + + return name; +} + + +status_t +Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot, + OpenDirCookie* cookie, uint64* _change, bool attribute) +{ + ASSERT(_snapshot != NULL); + + DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot; + if (snapshot == NULL) + return B_NO_MEMORY; + + uint64 change = 0; + uint64 dirCookie = 0; + uint64 dirCookieVerf = 0; + bool eof = false; + + while (!eof) { + uint32 count; + DirEntry* dirents; + + status_t result = ReadDirOnce(&dirents, &count, cookie, &eof, &change, + &dirCookie, &dirCookieVerf, attribute); + if (result != B_OK) { + delete snapshot; + return result; + } + + uint32 i; + for (i = 0; i < count; i++) { + + // FATTR4_FSID is mandatory + void* data = dirents[i].fAttrs[0].fData.fPointer; + FileSystemId* fsid = reinterpret_cast(data); + if (*fsid != fFileSystem->FsId()) + continue; + + if (strstr(dirents[i].fName, "-haiku-attrs") != NULL) + continue; + + ino_t id; + if (!attribute) { + if (dirents[i].fAttrCount == 2) + id = FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64); + else + id = FileIdToInoT(fFileSystem->AllocFileId()); + } else + id = 0; + + const char* name = dirents[i].fName; + if (attribute) + name = FileToAttrName(name); + if (name == NULL) { + delete snapshot; + delete[] dirents; + return B_NO_MEMORY; + } + + NameCacheEntry* entry = new NameCacheEntry(name, id); + if (attribute) + free(const_cast(name)); + + if (entry == NULL || entry->fName == NULL) { + if (entry != NULL) + delete entry; + delete snapshot; + delete[] dirents; + return B_NO_MEMORY; + } + snapshot->fEntries.Add(entry); + } + + delete[] dirents; + } + + *_snapshot = snapshot; + *_change = change; + + return B_OK; +} + + +status_t +Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, + OpenDirCookie* cookie) +{ + ASSERT(_buffer != NULL); + ASSERT(_count != NULL); + ASSERT(cookie != NULL); + + if (cookie->fEOF) { + *_count = 0; + return B_OK; + } + + status_t result; + DirectoryCache* cache = cookie->fAttrDir ? fAttrCache : fCache; + if (cookie->fSnapshot == NULL) { + cache->Lock(); + result = cache->Revalidate(); + if (result != B_OK) { + cache->Unlock(); + return result; + } + + DirectoryCacheSnapshot* snapshot; + result = cache->GetSnapshot(&snapshot); + if (result != B_OK) { + cache->Unlock(); + return result; + } + + cookie->fSnapshot = new DirectoryCacheSnapshot(*snapshot); + cache->Unlock(); + + if (cookie->fSnapshot == NULL) + return B_NO_MEMORY; + } + + char* buffer = reinterpret_cast(_buffer); + uint32 pos = 0; + uint32 i = 0; + bool overflow = false; + + if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) { + struct dirent* de = reinterpret_cast(buffer + pos); + + status_t result; + result = FillDirEntry(de, fInfo.fFileId, ".", pos, size); + + if (result == B_BUFFER_OVERFLOW) + overflow = true; + else if (result == B_OK) { + pos += de->d_reclen; + i++; + cookie->fSpecial++; + } else + return result; + } + + if (cookie->fSpecial == 1 && i < *_count && !cookie->fAttrDir) { + struct dirent* de = reinterpret_cast(buffer + pos); + + status_t result; + result = ReadDirUp(de, pos, size); + if (result == B_ENTRY_NOT_FOUND) { + result = FillDirEntry(de, FileIdToInoT(fInfo.fFileId), "..", pos, + size); + } + + if (result == B_BUFFER_OVERFLOW) + overflow = true; + else if (result == B_OK) { + pos += de->d_reclen; + i++; + cookie->fSpecial++; + } else + return result; + } + + MutexLocker _(cookie->fSnapshot->fLock); + for (; !overflow && i < *_count; i++) { + struct dirent* de = reinterpret_cast(buffer + pos); + NameCacheEntry* temp = cookie->fCurrent; + + if (cookie->fCurrent == NULL) + cookie->fCurrent = cookie->fSnapshot->fEntries.Head(); + else { + cookie->fCurrent + = cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent); + } + + if (cookie->fCurrent == NULL) { + cookie->fEOF = true; + break; + } + + if (FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName, + pos, size) == B_BUFFER_OVERFLOW) { + cookie->fCurrent = temp; + overflow = true; + break; + } + + pos += de->d_reclen; + } + + if (i == 0 && overflow) + return B_BUFFER_OVERFLOW; + + *_count = i; + + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h b/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h new file mode 100644 index 0000000000..43ae7994b2 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/InodeIdMap.h @@ -0,0 +1,126 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef INODEIDMAP_H +#define INODEIDMAP_H + + +#include +#include +#include +#include + +#include "FileInfo.h" + +struct InodeIdMapEntry { + FileInfo fFileInfo; + bool fRemoved; +}; + +class InodeIdMap { +public: + inline InodeIdMap(); + inline ~InodeIdMap(); + + inline status_t AddEntry(const FileInfo& fi, + ino_t id, bool weak = false); + inline status_t MarkRemoved(ino_t id); + inline status_t RemoveEntry(ino_t id); + inline status_t GetFileInfo(FileInfo* fi, ino_t id); + +protected: + inline bool _IsEntryRemoved(ino_t id); + +private: + AVLTreeMap fMap; + mutex fLock; + +}; + + +inline +InodeIdMap::InodeIdMap() +{ + mutex_init(&fLock, NULL); +} + + +inline +InodeIdMap::~InodeIdMap() +{ + mutex_destroy(&fLock); +} + + +inline status_t +InodeIdMap::AddEntry(const FileInfo& fi, ino_t id, bool weak) +{ + InodeIdMapEntry entry; + + MutexLocker _(fLock); + if (!weak || _IsEntryRemoved(id)) + fMap.Remove(id); + + entry.fFileInfo = fi; + entry.fRemoved = false; + + return fMap.Insert(id, entry); +} + + +inline status_t +InodeIdMap::MarkRemoved(ino_t id) +{ + MutexLocker _(fLock); + AVLTreeMap::Iterator it = fMap.Find(id); + if (!it.HasCurrent()) + return B_ENTRY_NOT_FOUND; + + it.CurrentValuePointer()->fRemoved = true; + return B_OK; +} + + +inline status_t +InodeIdMap::RemoveEntry(ino_t id) +{ + MutexLocker _(fLock); + if (_IsEntryRemoved(id)) + return fMap.Remove(id); + return B_OK; +} + + +inline status_t +InodeIdMap::GetFileInfo(FileInfo* fi, ino_t id) +{ + ASSERT(fi != NULL); + + MutexLocker _(fLock); + AVLTreeMap::Iterator it = fMap.Find(id); + if (!it.HasCurrent()) + return B_ENTRY_NOT_FOUND; + + *fi = it.Current().fFileInfo; + return B_OK; +} + + +// Caller must hold fLock +inline bool +InodeIdMap::_IsEntryRemoved(ino_t id) +{ + AVLTreeMap::Iterator it = fMap.Find(id); + if (!it.HasCurrent()) + return true; + + return it.Current().fRemoved; +} + + +#endif // INODEIDMAP_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp b/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp new file mode 100644 index 0000000000..974827413b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp @@ -0,0 +1,446 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Inode.h" + +#include + +#include +#include +#include + +#include "IdMap.h" +#include "Request.h" +#include "RootInode.h" + + +status_t +Inode::CreateState(const char* name, int mode, int perms, OpenState* state, + OpenDelegationData* delegationData) { + ASSERT(name != NULL); + ASSERT(state != NULL); + ASSERT(delegationData != NULL); + + uint64 fileID; + FileHandle handle; + ChangeInfo changeInfo; + + status_t result = CreateFile(name, mode, perms, state, &changeInfo, + &fileID, &handle, delegationData); + if (result != B_OK) + return result; + + FileInfo fi; + fi.fFileId = fileID; + fi.fHandle = handle; + fi.fParent = fInfo.fHandle; + fi.CreateName(fInfo.fPath, name); + + fFileSystem->InoIdMap()->AddEntry(fi, FileIdToInoT(fileID)); + + fCache->Lock(); + if (fCache->Valid()) { + if (changeInfo.fAtomic + && fCache->ChangeInfo() == changeInfo.fBefore) { + fCache->AddEntry(name, fileID, true); + fCache->SetChangeInfo(changeInfo.fAfter); + } else + fCache->Trash(); + } + fCache->Unlock(); + + state->fFileSystem = fFileSystem; + state->fInfo = fi; + state->fMode = mode & O_RWMASK; + + return B_OK; +} + + +status_t +Inode::Create(const char* name, int mode, int perms, OpenFileCookie* cookie, + OpenDelegationData* data, ino_t* id) +{ + ASSERT(name != NULL); + ASSERT(cookie != NULL); + ASSERT(data != NULL); + + cookie->fMode = mode; + cookie->fLocks = NULL; + + OpenState* state = new OpenState; + status_t result = CreateState(name, mode, perms, state, data); + if (result != B_OK) + return result; + + cookie->fOpenState = state; + cookie->fFileSystem = fFileSystem; + + *id = FileIdToInoT(state->fInfo.fFileId); + + fFileSystem->AddOpenFile(state); + fFileSystem->Root()->MakeInfoInvalid(); + + notify_entry_created(fFileSystem->DevId(), ID(), name, *id); + + return B_OK; +} + + +status_t +Inode::Open(int mode, OpenFileCookie* cookie) +{ + ASSERT(cookie != NULL); + + MutexLocker locker(fStateLock); + + OpenDelegationData data; + data.fType = OPEN_DELEGATE_NONE; + if (fOpenState == NULL) { + RevalidateFileCache(); + + OpenState* state = new OpenState; + if (state == NULL) + return B_NO_MEMORY; + + state->fInfo = fInfo; + state->fFileSystem = fFileSystem; + state->fMode = mode & O_RWMASK; + status_t result = OpenFile(state, mode, &data); + if (result != B_OK) + return result; + + fFileSystem->AddOpenFile(state); + fOpenState = state; + cookie->fOpenState = state; + locker.Unlock(); + } else { + fOpenState->AcquireReference(); + cookie->fOpenState = fOpenState; + locker.Unlock(); + + int newMode = mode & O_RWMASK; + int oldMode = fOpenState->fMode & O_RWMASK; + if (oldMode != newMode && oldMode != O_RDWR) { + if (oldMode == O_RDONLY) + RecallReadDelegation(); + + status_t result = OpenFile(fOpenState, O_RDWR, &data); + if (result != B_OK) { + locker.Lock(); + ReleaseOpenState(); + return result; + } + fOpenState->fMode = O_RDWR; + } else { + int newMode = mode & O_RWMASK; + uint32 allowed = 0; + if (newMode == O_RDWR || newMode == O_RDONLY) + allowed |= R_OK; + if (newMode == O_RDWR || newMode == O_WRONLY) + allowed |= W_OK; + + status_t result = Access(allowed); + if (result != B_OK) { + locker.Lock(); + ReleaseOpenState(); + return result; + } + } + } + + if ((mode & O_TRUNC) == O_TRUNC) { + struct stat st; + st.st_size = 0; + WriteStat(&st, B_STAT_SIZE); + file_cache_set_size(fFileCache, 0); + } + + cookie->fFileSystem = fFileSystem; + cookie->fMode = mode; + cookie->fLocks = NULL; + + if (data.fType != OPEN_DELEGATE_NONE) { + Delegation* delegation + = new(std::nothrow) Delegation(data, this, fOpenState->fClientID); + if (delegation != NULL) { + delegation->fInfo = fOpenState->fInfo; + delegation->fFileSystem = fFileSystem; + SetDelegation(delegation); + } + } + + return B_OK; +} + + +status_t +Inode::Close(OpenFileCookie* cookie) +{ + ASSERT(cookie != NULL); + ASSERT(fOpenState == cookie->fOpenState); + + SyncAndCommit(); + + MutexLocker _(fStateLock); + ReleaseOpenState(); + + return B_OK; +} + + +char* +Inode::AttrToFileName(const char* path) +{ + ASSERT(path != NULL); + + char* name = strdup(path); + if (name == NULL) + return NULL; + + char* current = strpbrk(name, "/:"); + while (current != NULL) { + switch (*current) { + case '/': + *current = '#'; + break; + case ':': + *current = '$'; + break; + } + current = strpbrk(name, "/:"); + } + + return name; +} + + +status_t +Inode::OpenAttr(const char* _name, int mode, OpenAttrCookie* cookie, + bool create, int32 type) +{ + ASSERT(_name != NULL); + ASSERT(cookie != NULL); + + (void)type; + + status_t result = LoadAttrDirHandle(); + if (result != B_OK) + return result; + + char* name = AttrToFileName(_name); + if (name == NULL) + return B_NO_MEMORY; + MemoryDeleter nameDeleter(name); + + OpenDelegationData data; + data.fType = OPEN_DELEGATE_NONE; + + OpenState* state = new OpenState; + if (state == NULL) + return B_NO_MEMORY; + + state->fInfo.fName = strdup(name); + state->fInfo.fParent = fInfo.fAttrDir; + state->fFileSystem = fFileSystem; + result = NFS4Inode::OpenAttr(state, name, mode, &data, create); + if (result != B_OK) { + delete state; + return result; + } + + fFileSystem->AddOpenFile(state); + + cookie->fOpenState = state; + cookie->fFileSystem = fFileSystem; + cookie->fMode = mode; + + if (data.fType != OPEN_DELEGATE_NONE) { + Delegation* delegation + = new(std::nothrow) Delegation(data, this, state->fClientID, true); + if (delegation != NULL) { + delegation->fInfo = state->fInfo; + delegation->fFileSystem = fFileSystem; + state->fDelegation = delegation; + fFileSystem->AddDelegation(delegation); + } + } + + if (create || (mode & O_TRUNC) == O_TRUNC) { + struct stat st; + st.st_size = 0; + WriteStat(&st, B_STAT_SIZE, cookie); + } + + return B_OK; +} + + +status_t +Inode::CloseAttr(OpenAttrCookie* cookie) +{ + ASSERT(cookie != NULL); + + if (cookie->fOpenState->fDelegation != NULL) { + cookie->fOpenState->fDelegation->GiveUp(); + fFileSystem->RemoveDelegation(cookie->fOpenState->fDelegation); + } + + delete cookie->fOpenState->fDelegation; + delete cookie->fOpenState; + return B_OK; +} + + +status_t +Inode::ReadDirect(OpenStateCookie* cookie, off_t pos, void* buffer, + size_t* _length, bool* eof) +{ + ASSERT(cookie != NULL || fOpenState != NULL); + ASSERT(buffer != NULL); + ASSERT(_length != NULL); + ASSERT(eof != NULL); + + *eof = false; + uint32 size = 0; + + uint32 ioSize = fFileSystem->Root()->IOSize(); + *_length = min_c(ioSize, *_length); + + status_t result; + OpenState* state = cookie != NULL ? cookie->fOpenState : fOpenState; + while (size < *_length && !*eof) { + uint32 len = *_length - size; + result = ReadFile(cookie, state, pos + size, &len, + reinterpret_cast(buffer) + size, eof); + if (result != B_OK) { + if (size == 0) + return result; + else + break; + } + + size += len; + } + + *_length = size; + + return B_OK; +} + + +status_t +Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length) +{ + ASSERT(cookie != NULL); + ASSERT(buffer != NULL); + ASSERT(_length != NULL); + + bool eof = false; + if ((cookie->fMode & O_NOCACHE) != 0) + return ReadDirect(cookie, pos, buffer, _length, &eof); + return file_cache_read(fFileCache, cookie, pos, buffer, _length); +} + + +status_t +Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer, + size_t* _length) +{ + ASSERT(cookie != NULL || fOpenState != NULL); + ASSERT(_buffer != NULL); + ASSERT(_length != NULL); + + uint32 size = 0; + const char* buffer = reinterpret_cast(_buffer); + + uint32 ioSize = fFileSystem->Root()->IOSize(); + *_length = min_c(ioSize, *_length); + + bool attribute = false; + OpenState* state = fOpenState; + if (cookie != NULL) { + attribute = cookie->fOpenState->fInfo.fHandle != fInfo.fHandle; + state = cookie->fOpenState; + } + + if (!attribute) { + ReadLocker _(fWriteLock); + fWriteDirty = true; + } + + while (size < *_length) { + uint32 len = *_length - size; + status_t result = WriteFile(cookie, state, pos + size, &len, + buffer + size, attribute); + if (result != B_OK) { + if (size == 0) + return result; + else + break; + } + + size += len; + } + + *_length = size; + + fMetaCache.GrowFile(size + pos); + fFileSystem->Root()->MakeInfoInvalid(); + + return B_OK; +} + + +status_t +Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer, + size_t* _length) +{ + ASSERT(cookie != NULL); + ASSERT(_buffer != NULL); + ASSERT(_length != NULL); + + struct stat st; + status_t result = Stat(&st); + if (result != B_OK) + return result; + + if ((cookie->fMode & O_APPEND) != 0) + pos = st.st_size; + + uint64 fileSize = max_c(st.st_size, pos + *_length); + fMaxFileSize = max_c(fMaxFileSize, fileSize); + + if ((cookie->fMode & O_NOCACHE) != 0) { + WriteDirect(cookie, pos, _buffer, _length); + Commit(); + } + + result = file_cache_set_size(fFileCache, fileSize); + if (result != B_OK) + return result; + + return file_cache_write(fFileCache, cookie, pos, _buffer, _length); +} + + +status_t +Inode::Commit() +{ + WriteLocker _(fWriteLock); + + if (!fWriteDirty) + return B_OK; + status_t result = CommitWrites(); + if (result != B_OK) + return result; + fWriteDirty = false; + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Jamfile b/src/add-ons/kernel/file_systems/nfs4/Jamfile new file mode 100644 index 0000000000..3d678a9001 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Jamfile @@ -0,0 +1,43 @@ +SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 ; + +UsePrivateKernelHeaders ; +UsePrivateHeaders shared ; + +KernelAddon nfs4 : + Cookie.cpp + Connection.cpp + Delegation.cpp + DirectoryCache.cpp + FileInfo.cpp + FileSystem.cpp + IdMap.cpp + Inode.cpp + InodeDir.cpp + InodeRegular.cpp + kernel_interface.cpp + MetadataCache.cpp + NFS4Inode.cpp + NFS4Object.cpp + NFS4Server.cpp + OpenState.cpp + ReplyBuilder.cpp + ReplyInterpreter.cpp + Request.cpp + RequestBuilder.cpp + RequestInterpreter.cpp + RootInode.cpp + RPCAuth.cpp + RPCCall.cpp + RPCCallback.cpp + RPCCallbackReply.cpp + RPCCallbackRequest.cpp + RPCCallbackServer.cpp + RPCReply.cpp + RPCServer.cpp + VnodeToInode.cpp + WorkQueue.cpp + XDR.cpp +; + +SubInclude HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ; + diff --git a/src/add-ons/kernel/file_systems/nfs4/MetadataCache.cpp b/src/add-ons/kernel/file_systems/nfs4/MetadataCache.cpp new file mode 100644 index 0000000000..570f6da44b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/MetadataCache.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "MetadataCache.h" + +#include + +#include "Inode.h" + + +MetadataCache::MetadataCache(Inode* inode) + : + fExpire(0), + fForceValid(false), + fInode(inode), + fInited(false) +{ + ASSERT(inode != NULL); + mutex_init(&fLock, NULL); +} + + +MetadataCache::~MetadataCache() +{ + mutex_destroy(&fLock); +} + + +status_t +MetadataCache::GetStat(struct stat* st) +{ + ASSERT(st != NULL); + + MutexLocker _(fLock); + if (fForceValid || fExpire > time(NULL)) { + // Do not touch other members of struct stat + st->st_size = fStatCache.st_size; + st->st_mode = fStatCache.st_mode; + st->st_nlink = fStatCache.st_nlink; + st->st_uid = fStatCache.st_uid; + st->st_gid = fStatCache.st_gid; + st->st_atim = fStatCache.st_atim; + st->st_ctim = fStatCache.st_ctim; + st->st_crtim = fStatCache.st_crtim; + st->st_mtim = fStatCache.st_mtim; + st->st_blksize = fStatCache.st_blksize; + st->st_blocks = fStatCache.st_blocks; + return B_OK; + } + + return B_ERROR; +} + + +void +MetadataCache::SetStat(const struct stat& st) +{ + MutexLocker _(fLock); + if (fInited) + NotifyChanges(&fStatCache, &st); + + fStatCache = st; + fExpire = time(NULL) + kExpirationTime; + fInited = true; +} + + +void +MetadataCache::GrowFile(size_t newSize) +{ + MutexLocker _(fLock); + fStatCache.st_size = max_c(newSize, fStatCache.st_size); +} + + +status_t +MetadataCache::GetAccess(uid_t uid, uint32* allowed) +{ + ASSERT(allowed != NULL); + + MutexLocker _(fLock); + AVLTreeMap::Iterator it = fAccessCache.Find(uid); + if (!it.HasCurrent()) + return B_ENTRY_NOT_FOUND; + + if (!fForceValid) + it.CurrentValuePointer()->fForceValid = false; + + if (!it.Current().fForceValid && it.Current().fExpire < time(NULL)) { + it.Remove(); + return B_ERROR; + } + + *allowed = it.Current().fAllowed; + + return B_OK; +} + + +void +MetadataCache::SetAccess(uid_t uid, uint32 allowed) +{ + MutexLocker _(fLock); + AVLTreeMap::Iterator it = fAccessCache.Find(uid); + if (it.HasCurrent()) + it.Remove(); + + AccessEntry entry; + entry.fAllowed = allowed; + entry.fExpire = time(NULL) + kExpirationTime; + entry.fForceValid = fForceValid; + + fAccessCache.Insert(uid, entry); +} + + +status_t +MetadataCache::LockValid() +{ + MutexLocker _(fLock); + if (fForceValid || fExpire > time(NULL)) { + fForceValid = true; + return B_OK; + } + + return B_ERROR; +} + + +void +MetadataCache::UnlockValid() +{ + MutexLocker _(fLock); + fExpire = time(NULL) + kExpirationTime; + fForceValid = false; +} + + +void +MetadataCache::NotifyChanges(const struct stat* oldStat, + const struct stat* newStat) +{ + ASSERT(oldStat != NULL); + ASSERT(newStat != NULL); + + uint32 flags = 0; + if (oldStat->st_size != newStat->st_size) + flags |= B_STAT_SIZE; + if (oldStat->st_mode != newStat->st_mode) + flags |= B_STAT_MODE; + if (oldStat->st_uid != newStat->st_uid) + flags |= B_STAT_UID; + if (oldStat->st_gid != newStat->st_gid) + flags |= B_STAT_GID; + + if (memcmp(&oldStat->st_atim, &newStat->st_atim, + sizeof(struct timespec) == 0)) + flags |= B_STAT_ACCESS_TIME; + + if (memcmp(&oldStat->st_ctim, &newStat->st_ctim, + sizeof(struct timespec) == 0)) + flags |= B_STAT_CHANGE_TIME; + + if (memcmp(&oldStat->st_crtim, &newStat->st_crtim, + sizeof(struct timespec) == 0)) + flags |= B_STAT_CREATION_TIME; + + if (memcmp(&oldStat->st_mtim, &newStat->st_mtim, + sizeof(struct timespec) == 0)) + flags |= B_STAT_MODIFICATION_TIME; + + notify_stat_changed(fInode->GetFileSystem()->DevId(), fInode->ID(), flags); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/MetadataCache.h b/src/add-ons/kernel/file_systems/nfs4/MetadataCache.h new file mode 100644 index 0000000000..82e44c6496 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/MetadataCache.h @@ -0,0 +1,95 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef METADATACACHE_H +#define METADATACACHE_H + + +#include +#include +#include +#include +#include + + +class Inode; + +struct AccessEntry { + time_t fExpire; + bool fForceValid; + + uint32 fAllowed; +}; + +class MetadataCache { +public: + MetadataCache(Inode* inode); + ~MetadataCache(); + + status_t GetStat(struct stat* st); + void SetStat(const struct stat& st); + void GrowFile(size_t newSize); + + status_t GetAccess(uid_t uid, uint32* allowed); + void SetAccess(uid_t uid, uint32 allowed); + + status_t LockValid(); + void UnlockValid(); + + inline void InvalidateStat(); + inline void InvalidateAccess(); + + inline void Invalidate(); + + static const time_t kExpirationTime = 60; + +protected: + void NotifyChanges(const struct stat* oldStat, + const struct stat* newStat); + +private: + struct stat fStatCache; + time_t fExpire; + bool fForceValid; + + Inode* fInode; + bool fInited; + + AVLTreeMap fAccessCache; + + mutex fLock; +}; + + +inline void +MetadataCache::InvalidateStat() +{ + MutexLocker _(fLock); + if (!fForceValid) + fExpire = 0; +} + + +inline void +MetadataCache::InvalidateAccess() +{ + MutexLocker _(fLock); + if (!fForceValid) + fAccessCache.MakeEmpty(); +} + + +inline void +MetadataCache::Invalidate() +{ + InvalidateStat(); + InvalidateAccess(); +} + + +#endif // METADATACACHE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h new file mode 100644 index 0000000000..aab14e6596 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Defs.h @@ -0,0 +1,371 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef NFS4DEFS_H +#define NFS4DEFS_H + + +#include +#include + +#include + + +enum Procedure { + ProcNull = 0, + ProcCompound = 1 +}; + +enum CallbackProcedure { + CallbackProcNull = 0, + CallbackProcCompound = 1 +}; + +enum CallbackOpcode { + OpCallbackGetAttr = 3, + OpCallbackRecall = 4 +}; + +enum Opcode { + OpAccess = 3, + OpClose = 4, + OpCommit = 5, + OpCreate = 6, + OpDelegReturn = 8, + OpGetAttr = 9, + OpGetFH = 10, + OpLink = 11, + OpLock = 12, + OpLockT = 13, + OpLockU = 14, + OpLookUp = 15, + OpLookUpUp = 16, + OpNverify = 17, + OpOpen = 18, + OpOpenAttrDir = 19, + OpOpenConfirm = 20, + OpPutFH = 22, + OpPutRootFH = 24, + OpRead = 25, + OpReadDir = 26, + OpReadLink = 27, + OpRemove = 28, + OpRename = 29, + OpRenew = 30, + OpSaveFH = 32, + OpSetAttr = 34, + OpSetClientID = 35, + OpSetClientIDConfirm = 36, + OpVerify = 37, + OpWrite = 38, + OpReleaseLockOwner = 39 +}; + +enum Access { + ACCESS4_READ = 0x00000001, + ACCESS4_LOOKUP = 0x00000002, + ACCESS4_MODIFY = 0x00000004, + ACCESS4_EXTEND = 0x00000008, + ACCESS4_DELETE = 0x00000010, + ACCESS4_EXECUTE = 0x00000020 +}; + +enum Attribute { + // Mandatory Attributes + FATTR4_SUPPORTED_ATTRS = 0, + FATTR4_TYPE = 1, + FATTR4_FH_EXPIRE_TYPE = 2, + FATTR4_CHANGE = 3, + FATTR4_SIZE = 4, + FATTR4_LINK_SUPPORT = 5, + FATTR4_SYMLINK_SUPPORT = 6, + FATTR4_NAMED_ATTR = 7, + FATTR4_FSID = 8, + FATTR4_UNIQUE_HANDLES = 9, + FATTR4_LEASE_TIME = 10, + FATTR4_RDATTR_ERROR = 11, + FATTR4_FILEHANDLE = 19, + + // Recommended Attributes + FATTR4_ACL = 12, + FATTR4_ACLSUPPORT = 13, + FATTR4_ARCHIVE = 14, + FATTR4_CANSETTIME = 15, + FATTR4_CASE_INSENSITIVE = 16, + FATTR4_CASE_PRESERVING = 17, + FATTR4_CHOWN_RESTRICTED = 18, + FATTR4_FILEID = 20, + FATTR4_FILES_AVAIL = 21, + FATTR4_FILES_FREE = 22, + FATTR4_FILES_TOTAL = 23, + FATTR4_FS_LOCATIONS = 24, + FATTR4_HIDDEN = 25, + FATTR4_HOMOGENEOUS = 26, + FATTR4_MAXFILESIZE = 27, + FATTR4_MAXLINK = 28, + FATTR4_MAXNAME = 29, + FATTR4_MAXREAD = 30, + FATTR4_MAXWRITE = 31, + FATTR4_MIMETYPE = 32, + FATTR4_MODE = 33, + FATTR4_NO_TRUNC = 34, + FATTR4_NUMLINKS = 35, + FATTR4_OWNER = 36, + FATTR4_OWNER_GROUP = 37, + FATTR4_QUOTA_AVAIL_HARD = 38, + FATTR4_QUOTA_AVAIL_SOFT = 39, + FATTR4_QUOTA_USED = 40, + FATTR4_RAWDEV = 41, + FATTR4_SPACE_AVAIL = 42, + FATTR4_SPACE_FREE = 43, + FATTR4_SPACE_TOTAL = 44, + FATTR4_SPACE_USED = 45, + FATTR4_SYSTEM = 46, + FATTR4_TIME_ACCESS = 47, + FATTR4_TIME_ACCESS_SET = 48, + FATTR4_TIME_BACKUP = 49, + FATTR4_TIME_CREATE = 50, + FATTR4_TIME_DELTA = 51, + FATTR4_TIME_METADATA = 52, + FATTR4_TIME_MODIFY = 53, + FATTR4_TIME_MODIFY_SET = 54, + FATTR4_MOUNTED_ON_FILEID = 55, + FATTR4_MAXIMUM_ATTR_ID +}; + +enum CallbackAttr { + CallbackAttrSize = 1, + CallbackAttrChange = 2 +}; + +static inline bool sIsAttrSet(Attribute attr, const uint32* bitmap, + uint32 count) +{ + if ((uint32)attr / 32 >= count) + return false; + + return (bitmap[attr / 32] & 1 << attr % 32) != 0; +} + + +enum FileType { + NF4REG = 1, /* Regular File */ + NF4DIR = 2, /* Directory */ + NF4BLK = 3, /* Special File - block device */ + NF4CHR = 4, /* Special File - character device */ + NF4LNK = 5, /* Symbolic Link */ + NF4SOCK = 6, /* Special File - socket */ + NF4FIFO = 7, /* Special File - fifo */ + NF4ATTRDIR = 8, /* Attribute Directory */ + NF4NAMEDATTR = 9 /* Named Attribute */ +}; + +static const mode_t sNFSFileTypeToHaiku[] = { + S_IFREG, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, + S_IFDIR, S_IFREG +}; + +enum FileHandleExpiryType { + FH4_PERSISTENT = 0x00, + FH4_NOEXPIRE_WITH_OPEN = 0x01, + FH4_VOLATILE_ANY = 0x02, + FH4_VOL_MIGRATION = 0x04, + FH4_VOL_RENAME = 0x08 +}; + +enum OpenAccess { + OPEN4_SHARE_ACCESS_READ = 1, + OPEN4_SHARE_ACCESS_WRITE = 2, + OPEN4_SHARE_ACCESS_BOTH = 3 +}; + + +static inline OpenAccess +sModeToAccess(int mode) +{ + switch (mode & O_RWMASK) { + case O_RDONLY: + return OPEN4_SHARE_ACCESS_READ; + case O_WRONLY: + return OPEN4_SHARE_ACCESS_WRITE; + case O_RDWR: + return OPEN4_SHARE_ACCESS_BOTH; + } + + return OPEN4_SHARE_ACCESS_READ; +} + + +enum OpenCreate { + OPEN4_NOCREATE = 0, + OPEN4_CREATE = 1 +}; + +enum OpenCreateHow { + UNCHECKED4 = 0, + GUARDED4 = 1, + EXCLUSIVE4 = 2 +}; + +enum OpenClaim { + CLAIM_NULL = 0, + CLAIM_PREVIOUS = 1, + CLAIM_DELEGATE_CUR = 2, + CLAIM_DELEGATE_PREV = 3 +}; + +enum OpenDelegation { + OPEN_DELEGATE_NONE = 0, + OPEN_DELEGATE_READ = 1, + OPEN_DELEGATE_WRITE = 2 +}; + +struct OpenDelegationData { + OpenDelegation fType; + + uint32 fStateSeq; + uint32 fStateID[3]; + + bool fRecall; + uint64 fSpaceLimit; +}; + +enum OpenFlags { + OPEN4_RESULT_CONFIRM = 2, + OPEN4_RESULT_LOCKTYPE_POSIX = 4 +}; + +enum { + NFS_LIMIT_SIZE = 1, + NFS_LIMIT_BLOCKS = 2 +}; + +struct ChangeInfo { + bool fAtomic; + uint64 fBefore; + uint64 fAfter; +}; + +enum WriteStable { + UNSTABLE4 = 0, + DATA_SYNC4 = 1, + FILE_SYNC4 = 2 +}; + +enum LockType { + READ_LT = 1, + WRITE_LT = 2, + READW_LT = 3, + WRITEW_LT = 4 +}; + + +static inline LockType +sGetLockType(short type, bool wait) { + switch (type) { + case F_RDLCK: return wait ? READW_LT : READ_LT; + case F_WRLCK: return wait ? WRITEW_LT : WRITE_LT; + default: return READ_LT; + } +} + + +static inline short +sLockTypeToHaiku(LockType type) { + switch (type) { + case READ_LT: + case READW_LT: + return F_RDLCK; + + case WRITE_LT: + case WRITEW_LT: + return F_WRLCK; + + default: return F_UNLCK; + } +} + + +enum Errors { + NFS4_OK = 0, + NFS4ERR_PERM = 1, + NFS4ERR_NOENT = 2, + NFS4ERR_IO = 5, + NFS4ERR_NXIO = 6, + NFS4ERR_ACCESS = 13, + NFS4ERR_EXIST = 17, + NFS4ERR_XDEV = 18, + NFS4ERR_NOTDIR = 20, + NFS4ERR_ISDIR = 21, + NFS4ERR_INVAL = 22, + NFS4ERR_FBIG = 27, + NFS4ERR_NOSPC = 28, + NFS4ERR_ROFS = 30, + NFS4ERR_MLINK = 31, + NFS4ERR_NAMETOOLONG = 63, + NFS4ERR_NOTEMPTY = 66, + NFS4ERR_DQUOT = 69, + NFS4ERR_STALE = 70, + NFS4ERR_BADHANDLE = 10001, + NFS4ERR_BAD_COOKIE = 10003, + NFS4ERR_NOTSUPP = 10004, + NFS4ERR_TOOSMALL = 10005, + NFS4ERR_SERVERFAULT = 10006, + NFS4ERR_BADTYPE = 10007, + NFS4ERR_DELAY = 10008, + NFS4ERR_SAME = 10009, + NFS4ERR_DENIED = 10010, + NFS4ERR_EXPIRED = 10011, + NFS4ERR_LOCKED = 10012, + NFS4ERR_GRACE = 10013, + NFS4ERR_FHEXPIRED = 10014, + NFS4ERR_SHARE_DENIED = 10015, + NFS4ERR_WRONGSEC = 10016, + NFS4ERR_CLID_INUSE = 10017, + NFS4ERR_RESOURCE = 10018, + NFS4ERR_MOVED = 10019, + NFS4ERR_NOFILEHANDLE = 10020, + NFS4ERR_MINOR_VERS_MISMATCH = 10021, + NFS4ERR_STALE_CLIENTID = 10022, + NFS4ERR_STALE_STATEID = 10023, + NFS4ERR_OLD_STATEID = 10024, + NFS4ERR_BAD_STATEID = 10025, + NFS4ERR_BAD_SEQID = 10026, + NFS4ERR_NOT_SAME = 10027, + NFS4ERR_LOCK_RANGE = 10028, + NFS4ERR_SYMLINK = 10029, + NFS4ERR_RESTOREFH = 10030, + NFS4ERR_LEASE_MOVED = 10031, + NFS4ERR_ATTRNOTSUPP = 10032, + NFS4ERR_NO_GRACE = 10033, + NFS4ERR_RECLAIM_BAD = 10034, + NFS4ERR_RECLAIM_CONFLICT = 10035, + NFS4ERR_BADXDR = 10036, + NFS4ERR_LOCKS_HELD = 10037, + NFS4ERR_OPENMODE = 10038, + NFS4ERR_BADOWNER = 10039, + NFS4ERR_BADCHAR = 10040, + NFS4ERR_BADNAME = 10041, + NFS4ERR_BAD_RANGE = 10042, + NFS4ERR_LOCK_NOTSUPP = 10043, + NFS4ERR_OP_ILLEGAL = 10044, + NFS4ERR_DEADLOCK = 10045, + NFS4ERR_FILE_OPEN = 10046, + NFS4ERR_ADMIN_REVOKED = 10047, + NFS4ERR_CB_PATH_DOWN = 10048 +}; + + +static inline bigtime_t +sSecToBigTime(uint32 sec) +{ + return static_cast(sec) * 1000000; +} + + +#endif // NFS4DEFS_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp new file mode 100644 index 0000000000..6118c42175 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp @@ -0,0 +1,1178 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "IdMap.h" +#include "Inode.h" +#include "NFS4Inode.h" +#include "Request.h" + + +status_t +NFS4Inode::GetChangeInfo(uint64* change, bool attrDir) +{ + ASSERT(change != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (attrDir) + req.PutFH(fInfo.fAttrDir); + else + req.PutFH(fInfo.fHandle); + + Attribute attr[] = { FATTR4_CHANGE }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK || count < 1) + return result; + + // FATTR4_CHANGE is mandatory + *change = values[0].fData.fValue64; + delete[] values; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::CommitWrites() +{ + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Commit(0, 0); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + return reply.Commit(); + } while (true); +} + + +status_t +NFS4Inode::Access(uint32* allowed) +{ + ASSERT(allowed != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Access(); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + return reply.Access(NULL, allowed); + } while (true); +} + + +status_t +NFS4Inode::LookUp(const char* name, uint64* change, uint64* fileID, + FileHandle* handle, bool parent) +{ + ASSERT(name != NULL); + ASSERT(change != NULL); + ASSERT(fileID != NULL); + ASSERT(handle != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (parent) + req.PutFH(fInfo.fParent); + else + req.PutFH(fInfo.fHandle); + + if (change != NULL) { + Attribute dirAttr[] = { FATTR4_CHANGE }; + req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute)); + } + + if (!strcmp(name, "..")) + req.LookUpUp(); + else + req.LookUp(name); + + req.GetFH(); + + Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + AttrValue* values; + uint32 count; + if (change != NULL) { + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + *change = values[0].fData.fValue64; + delete[] values; + } + + if (!strcmp(name, "..")) + result = reply.LookUpUp(); + else + result = reply.LookUp(); + if (result != B_OK) + return result; + + reply.GetFH(handle); + + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + // FATTR4_FSID is mandatory + FileSystemId* fsid + = reinterpret_cast(values[0].fData.fPointer); + if (*fsid != fFileSystem->FsId()) { + delete[] values; + return B_ENTRY_NOT_FOUND; + } + + if (fileID != NULL) { + if (count < 2 || values[1].fAttribute != FATTR4_FILEID) + *fileID = fFileSystem->AllocFileId(); + else + *fileID = values[1].fData.fValue64; + } + + delete[] values; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::Link(Inode* dir, const char* name, ChangeInfo* changeInfo) +{ + ASSERT(dir != NULL); + ASSERT(name != NULL); + ASSERT(changeInfo != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.SaveFH(); + req.PutFH(dir->fInfo.fHandle); + req.Link(name); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + reply.SaveFH(); + reply.PutFH(); + + return reply.Link(&changeInfo->fBefore, &changeInfo->fAfter, + changeInfo->fAtomic); + } while (true); +} + + +status_t +NFS4Inode::ReadLink(void* buffer, size_t* length) +{ + ASSERT(buffer != NULL); + ASSERT(length != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.ReadLink(); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + uint32 size; + result = reply.ReadLink(buffer, &size, *length); + *length = static_cast(size); + + return result; + } while (true); +} + + +status_t +NFS4Inode::GetStat(AttrValue** values, uint32* count, OpenAttrCookie* cookie) +{ + ASSERT(values != NULL); + ASSERT(count != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (cookie != NULL) + req.PutFH(cookie->fOpenState->fInfo.fHandle); + else + req.PutFH(fInfo.fHandle); + + Attribute attr[] = { FATTR4_SIZE, FATTR4_MODE, FATTR4_NUMLINKS, + FATTR4_OWNER, FATTR4_OWNER_GROUP, + FATTR4_TIME_ACCESS, FATTR4_TIME_CREATE, + FATTR4_TIME_METADATA, FATTR4_TIME_MODIFY }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(cookie); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + return reply.GetAttr(values, count); + } while (true); +} + + +status_t +NFS4Inode::WriteStat(OpenState* state, AttrValue* attrs, uint32 attrCount) +{ + ASSERT(attrs != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (state != NULL) { + req.PutFH(state->fInfo.fHandle); + req.SetAttr(state->fStateID, state->fStateSeq, attrs, attrCount); + } else { + req.PutFH(fInfo.fHandle); + req.SetAttr(NULL, 0, attrs, attrCount); + } + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + result = reply.SetAttr(); + + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::RenameNode(Inode* from, Inode* to, const char* fromName, + const char* toName, ChangeInfo* fromChange, ChangeInfo* toChange, + uint64* fileID, bool attribute) +{ + ASSERT(from != NULL); + ASSERT(to != NULL); + ASSERT(fromName != NULL); + ASSERT(toName != NULL); + ASSERT(fromChange != NULL); + ASSERT(toChange != NULL); + + do { + RPC::Server* serv = from->fFileSystem->Server(); + Request request(serv, from->fFileSystem); + RequestBuilder& req = request.Builder(); + + if (attribute) + req.PutFH(from->fInfo.fAttrDir); + else + req.PutFH(from->fInfo.fHandle); + req.SaveFH(); + if (attribute) + req.PutFH(to->fInfo.fAttrDir); + else + req.PutFH(to->fInfo.fHandle); + req.Rename(fromName, toName); + + if (!attribute) { + req.LookUp(toName); + Attribute attr[] = { FATTR4_FILEID }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + } + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + // FileHandle has expired + if (reply.NFS4Error() == NFS4ERR_FHEXPIRED) { + from->fInfo.UpdateFileHandles(from->fFileSystem); + to->fInfo.UpdateFileHandles(to->fFileSystem); + continue; + } + + // filesystem has been moved + if (reply.NFS4Error() == NFS4ERR_MOVED) { + from->fFileSystem->Migrate(serv); + continue; + } + + // need to wait + if (reply.NFS4Error() == NFS4ERR_DELAY) { + snooze_etc(sSecToBigTime(5), B_SYSTEM_TIMEBASE, B_RELATIVE_TIMEOUT); + continue; + } + + reply.PutFH(); + reply.SaveFH(); + reply.PutFH(); + + result = reply.Rename(&fromChange->fBefore, &fromChange->fAfter, + fromChange->fAtomic, &toChange->fBefore, &toChange->fAfter, + toChange->fAtomic); + if (result != B_OK) + return result; + + if (!attribute) { + result = reply.LookUp(); + if (result != B_OK) + return result; + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + if (count == 0) + *fileID = from->fFileSystem->AllocFileId(); + else + *fileID = values[0].fData.fValue64; + + delete[] values; + } + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::CreateFile(const char* name, int mode, int perms, OpenState* state, + ChangeInfo* changeInfo, uint64* fileID, FileHandle* handle, + OpenDelegationData* delegation) +{ + ASSERT(name != NULL); + ASSERT(state != NULL); + ASSERT(changeInfo != NULL); + ASSERT(handle != NULL); + ASSERT(delegation != NULL); + + bool confirm; + status_t result; + + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + state->fClientID = fFileSystem->NFSServer()->ClientId(); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + + AttrValue cattr[2]; + uint32 i = 0; + if ((mode & O_TRUNC) == O_TRUNC) { + cattr[i].fAttribute = FATTR4_SIZE; + cattr[i].fFreePointer = false; + cattr[i].fData.fValue64 = 0; + i++; + } + cattr[i].fAttribute = FATTR4_MODE; + cattr[i].fFreePointer = false; + cattr[i].fData.fValue32 = perms; + i++; + + req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), + state->fClientID, OPEN4_CREATE, fFileSystem->OpenOwner(), name, + cattr, i, (mode & O_EXCL) == O_EXCL); + + req.GetFH(); + + if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { + Attribute attr[] = { FATTR4_FILEID }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + } + + result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + if (HandleErrors(reply.NFS4Error(), serv, NULL, state, &sequence)) + continue; + + reply.PutFH(); + + result = reply.Open(state->fStateID, &state->fStateSeq, &confirm, + delegation, changeInfo); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + reply.GetFH(handle); + + if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + *fileID = values[0].fData.fValue64; + + delete[] values; + } else + *fileID = fFileSystem->AllocFileId(); + + break; + } while (true); + + state->fOpened = true; + + if (confirm) + result = ConfirmOpen(*handle, state, &sequence); + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; +} + + +status_t +NFS4Inode::OpenFile(OpenState* state, int mode, OpenDelegationData* delegation) +{ + ASSERT(state != NULL); + ASSERT(delegation != NULL); + + bool confirm; + status_t result; + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + state->fClientID = fFileSystem->NFSServer()->ClientId(); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + // Since we are opening the file using a pair (parentFH, name) we + // need to check for race conditions. + if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) { + req.PutFH(fInfo.fParent); + req.LookUp(fInfo.fName); + AttrValue attr; + attr.fAttribute = FATTR4_FILEID; + attr.fFreePointer = false; + attr.fData.fValue64 = fInfo.fFileId; + req.Verify(&attr, 1); + } else if (fFileSystem->ExpireType() == FH4_PERSISTENT) { + req.PutFH(fInfo.fParent); + req.LookUp(fInfo.fName); + AttrValue attr; + attr.fAttribute = FATTR4_FILEHANDLE; + attr.fFreePointer = true; + attr.fData.fPointer = malloc(sizeof(fInfo.fHandle)); + memcpy(attr.fData.fPointer, &fInfo.fHandle, sizeof(fInfo.fHandle)); + req.Verify(&attr, 1); + } + + req.PutFH(fInfo.fParent); + req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), state->fClientID, + OPEN4_NOCREATE, fFileSystem->OpenOwner(), fInfo.fName); + req.GetFH(); + + result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + if (HandleErrors(reply.NFS4Error(), serv, NULL, state, &sequence)) + continue; + + // Verify if the file we want to open is the file this Inode + // represents. + if (fFileSystem->IsAttrSupported(FATTR4_FILEID) + || fFileSystem->ExpireType() == FH4_PERSISTENT) { + reply.PutFH(); + result = reply.LookUp(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + result = reply.Verify(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + if (result != B_OK && reply.NFS4Error() == NFS4ERR_NOT_SAME) + return B_ENTRY_NOT_FOUND; + else if (result != B_OK) + return result; + } + + reply.PutFH(); + result = reply.Open(state->fStateID, &state->fStateSeq, &confirm, + delegation); + + FileHandle handle; + reply.GetFH(&handle); + + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + break; + } while (true); + + state->fOpened = true; + + if (confirm) + result = ConfirmOpen(fInfo.fHandle, state, &sequence); + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; +} + + +status_t +NFS4Inode::OpenAttr(OpenState* state, const char* name, int mode, + OpenDelegationData* delegation, bool create) +{ + ASSERT(name != NULL); + ASSERT(state != NULL); + ASSERT(delegation != NULL); + + bool confirm; + status_t result; + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + state->fClientID = fFileSystem->NFSServer()->ClientId(); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fAttrDir); + req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), state->fClientID, + create ? OPEN4_CREATE : OPEN4_NOCREATE, fFileSystem->OpenOwner(), + name); + req.GetFH(); + + result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + if (HandleErrors(reply.NFS4Error(), serv, NULL, state, &sequence)) + continue; + + reply.PutFH(); + result = reply.Open(state->fStateID, &state->fStateSeq, &confirm, + delegation); + + reply.GetFH(&state->fInfo.fHandle); + + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + break; + } while (true); + + state->fOpened = true; + + if (confirm) + result = ConfirmOpen(state->fInfo.fHandle, state, &sequence); + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; +} + + +status_t +NFS4Inode::ReadFile(OpenStateCookie* cookie, OpenState* state, uint64 position, + uint32* length, void* buffer, bool* eof) +{ + ASSERT(state != NULL); + ASSERT(length != NULL); + ASSERT(buffer != NULL); + ASSERT(eof != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(state->fInfo.fHandle); + req.Read(state->fStateID, state->fStateSeq, position, *length); + + status_t result = request.Send(cookie); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv, cookie, state)) + continue; + + reply.PutFH(); + result = reply.Read(buffer, length, eof); + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::WriteFile(OpenStateCookie* cookie, OpenState* state, uint64 position, + uint32* length, const void* buffer, bool commit) +{ + ASSERT(state != NULL); + ASSERT(length != NULL); + ASSERT(buffer != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(state->fInfo.fHandle); + + req.Write(state->fStateID, state->fStateSeq, buffer, position, *length, + commit); + + status_t result = request.Send(cookie); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv, cookie, state)) + continue; + + reply.PutFH(); + + result = reply.Write(length); + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::CreateObject(const char* name, const char* path, int mode, + FileType type, ChangeInfo* changeInfo, uint64* fileID, FileHandle* handle, + bool parent) +{ + ASSERT(name != NULL); + ASSERT(changeInfo != NULL); + ASSERT(handle != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (parent) + req.PutFH(fInfo.fParent); + else + req.PutFH(fInfo.fHandle); + + uint32 i = 0; + AttrValue cattr[1]; + cattr[i].fAttribute = FATTR4_MODE; + cattr[i].fFreePointer = false; + cattr[i].fData.fValue32 = mode; + i++; + + switch (type) { + case NF4DIR: + req.Create(NF4DIR, name, cattr, i); + break; + case NF4LNK: + req.Create(NF4LNK, name, cattr, i, path); + break; + default: + return B_BAD_VALUE; + } + + req.GetFH(); + + if (fileID != NULL) { + Attribute attr[] = { FATTR4_FILEID }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + } + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + result = reply.Create(&changeInfo->fBefore, &changeInfo->fAfter, + changeInfo->fAtomic); + if (result != B_OK) + return result; + + result = reply.GetFH(handle); + if (result != B_OK) + return result; + + if (fileID != NULL) { + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + if (count == 0) + *fileID = fFileSystem->AllocFileId(); + else + *fileID = values[0].fData.fValue64; + + delete[] values; + } + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::RemoveObject(const char* name, FileType type, ChangeInfo* changeInfo, + uint64* fileID) +{ + ASSERT(name != NULL); + ASSERT(changeInfo != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.LookUp(name); + AttrValue attr; + attr.fAttribute = FATTR4_TYPE; + attr.fFreePointer = false; + attr.fData.fValue32 = NF4DIR; + if (type == NF4DIR) + req.Verify(&attr, 1); + else + req.Nverify(&attr, 1); + + if (type != NF4NAMEDATTR) { + Attribute idAttr[] = { FATTR4_FILEID }; + req.GetAttr(idAttr, sizeof(idAttr) / sizeof(Attribute)); + } + + req.PutFH(fInfo.fHandle); + req.Remove(name); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + result = reply.LookUp(); + if (result != B_OK) + return result; + + if (type == NF4DIR) + result = reply.Verify(); + else + result = reply.Nverify(); + + if (result == NFS4ERR_SAME && type != NF4DIR) + return B_IS_A_DIRECTORY; + if (result == NFS4ERR_NOT_SAME && type == NF4DIR) + return B_NOT_A_DIRECTORY; + if (result != B_OK) + return result; + + if (type != NF4NAMEDATTR) { + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + if (count == 0) + *fileID = fFileSystem->AllocFileId(); + else + *fileID = values[0].fData.fValue64; + delete[] values; + } + + reply.PutFH(); + return reply.Remove(&changeInfo->fBefore, &changeInfo->fAfter, + changeInfo->fAtomic); + } while (true); +} + + +status_t +NFS4Inode::ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie, + bool* eof, uint64* change, uint64* dirCookie, uint64* dirCookieVerf, + bool attribute) +{ + ASSERT(dirents != NULL); + ASSERT(count != NULL); + ASSERT(eof != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + if (attribute) + req.PutFH(fInfo.fAttrDir); + else + req.PutFH(fInfo.fHandle); + + Attribute dirAttr[] = { FATTR4_CHANGE }; + if (*change == 0) + req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute)); + + Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID }; + req.ReadDir(*count, *dirCookie, *dirCookieVerf, attr, + sizeof(attr) / sizeof(Attribute)); + + req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute)); + + status_t result = request.Send(cookie); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + AttrValue* before = NULL; + uint32 attrCount; + if (*change == 0) { + result = reply.GetAttr(&before, &attrCount); + if (result != B_OK) + return result; + } + + result = reply.ReadDir(dirCookie, dirCookieVerf, dirents, count, eof); + if (result != B_OK) { + delete[] before; + return result; + } + + AttrValue* after; + result = reply.GetAttr(&after, &attrCount); + if (result != B_OK) { + delete[] before; + return result; + } + + if ((*change == 0 + && before[0].fData.fValue64 == after[0].fData.fValue64) + || *change == after[0].fData.fValue64) + *change = after[0].fData.fValue64; + else + return B_ERROR; + + delete[] before; + delete[] after; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::OpenAttrDir(FileHandle* handle) +{ + ASSERT(handle != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.OpenAttrDir(true); + req.GetFH(); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + result = reply.OpenAttrDir(); + if (result != B_OK) + return result; + + return reply.GetFH(handle); + } while (true); +} + + +status_t +NFS4Inode::TestLock(OpenFileCookie* cookie, LockType* type, uint64* position, + uint64* length, bool& conflict) +{ + ASSERT(cookie != NULL); + ASSERT(type != NULL); + ASSERT(position != NULL); + ASSERT(length != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.LockT(*type, *position, *length, cookie->fOpenState); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + if (HandleErrors(reply.NFS4Error(), serv, cookie)) + continue; + + reply.PutFH(); + result = reply.LockT(position, length, type); + if (reply.NFS4Error() == NFS4ERR_DENIED) { + conflict = true; + result = B_OK; + } else if (reply.NFS4Error() == NFS4_OK) + conflict = false; + + return result; + } while (true); + + return B_OK; +} + + +status_t +NFS4Inode::AcquireLock(OpenFileCookie* cookie, LockInfo* lockInfo, bool wait) +{ + ASSERT(cookie != NULL); + ASSERT(lockInfo != NULL); + + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + MutexLocker ownerLocker(lockInfo->fOwner->fLock); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Lock(cookie->fOpenState, lockInfo, &sequence); + + status_t result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + reply.PutFH(); + result = reply.Lock(lockInfo); + + ownerLocker.Unlock(); + if (wait && reply.NFS4Error() == NFS4ERR_DENIED) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + snooze_etc(sSecToBigTime(5), B_SYSTEM_TIMEBASE, + B_RELATIVE_TIMEOUT); + sequence = fFileSystem->OpenOwnerSequenceLock(); + continue; + } + if (HandleErrors(reply.NFS4Error(), serv, cookie, NULL, &sequence)) + continue; + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + + +status_t +NFS4Inode::ReleaseLock(OpenFileCookie* cookie, LockInfo* lockInfo) +{ + ASSERT(cookie != NULL); + ASSERT(lockInfo != NULL); + + do { + MutexLocker ownerLocker(lockInfo->fOwner->fLock); + + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.LockU(lockInfo); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + reply.PutFH(); + result = reply.LockU(lockInfo); + + ownerLocker.Unlock(); + if (HandleErrors(reply.NFS4Error(), serv, cookie)) + continue; + + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h new file mode 100644 index 0000000000..4aff6886c9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h @@ -0,0 +1,89 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef NFS4INODE_H +#define NFS4INODE_H + + +#include + +#include + +#include "Cookie.h" +#include "FileInfo.h" +#include "FileSystem.h" +#include "NFS4Object.h" +#include "ReplyInterpreter.h" + + +class NFS4Inode : public NFS4Object { +public: + status_t GetChangeInfo(uint64* change, bool attrDir = false); + status_t ReadLink(void* buffer, size_t* length); + +protected: + status_t Access(uint32* allowed); + + status_t CommitWrites(); + + status_t LookUp(const char* name, uint64* change, uint64* fileID, + FileHandle* handle, bool parent = false); + + status_t Link(Inode* dir, const char* name, + ChangeInfo* changeInfo); + + static status_t RenameNode(Inode* from, Inode* to, const char* fromName, + const char* toName, ChangeInfo* fromChange, + ChangeInfo* toChange, uint64* fileID, + bool attribute = false); + + status_t GetStat(AttrValue** values, uint32* count, + OpenAttrCookie* attr = NULL); + status_t WriteStat(OpenState* state, AttrValue* attrs, + uint32 attrCount); + + status_t CreateFile(const char* name, int mode, int perms, + OpenState* state, ChangeInfo* changeInfo, + uint64* fileID, FileHandle* handle, + OpenDelegationData* delegation); + status_t OpenFile(OpenState* state, int mode, + OpenDelegationData* delegation); + + status_t OpenAttr(OpenState* state, const char* name, int mode, + OpenDelegationData* delegation, bool create); + + status_t ReadFile(OpenStateCookie* cookie, OpenState* state, + uint64 position, uint32* length, void* buffer, + bool* eof); + status_t WriteFile(OpenStateCookie* cookie, OpenState* state, + uint64 position, uint32* length, + const void* buffer, bool commit = false); + + status_t CreateObject(const char* name, const char* path, + int mode, FileType type, ChangeInfo* changeInfo, + uint64* fileID, FileHandle* handle, + bool parent = false); + status_t RemoveObject(const char* name, FileType type, + ChangeInfo* changeInfo, uint64* fileID); + + status_t ReadDirOnce(DirEntry** dirents, uint32* count, + OpenDirCookie* cookie, bool* eof, uint64* change, + uint64* dirCookie, uint64* dirCookieVerf, + bool attribute); + + status_t OpenAttrDir(FileHandle* handle); + + status_t TestLock(OpenFileCookie* cookie, LockType* type, + uint64* position, uint64* length, bool& conflict); + status_t AcquireLock(OpenFileCookie* cookie, LockInfo* lockInfo, + bool wait); + status_t ReleaseLock(OpenFileCookie* cookie, LockInfo* lockInfo); +}; + + +#endif // NFS4INODE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp new file mode 100644 index 0000000000..69381930f9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp @@ -0,0 +1,204 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Cookie.h" +#include "FileSystem.h" +#include "NFS4Object.h" +#include "OpenState.h" +#include "Request.h" + + +bool +NFS4Object::HandleErrors(uint32 nfs4Error, RPC::Server* serv, + OpenStateCookie* cookie, OpenState* state, uint32* sequence) +{ + uint32 leaseTime; + + // No request send by the client should cause any of the following errors. + ASSERT(nfs4Error != NFS4ERR_CLID_INUSE); + ASSERT(nfs4Error != NFS4ERR_NOFILEHANDLE); + ASSERT(nfs4Error != NFS4ERR_BAD_STATEID); + ASSERT(nfs4Error != NFS4ERR_RESTOREFH); + ASSERT(nfs4Error != NFS4ERR_LOCKS_HELD); + ASSERT(nfs4Error != NFS4ERR_OP_ILLEGAL); + + if (cookie != NULL) + state = cookie->fOpenState; + + switch (nfs4Error) { + case NFS4_OK: + return false; + + // retransmission of CLOSE caused seqid to fall back + case NFS4ERR_BAD_SEQID: + ASSERT(sequence != NULL); + (*sequence)++; + return true; + + // server needs more time, we need to wait + case NFS4ERR_LOCKED: + case NFS4ERR_DELAY: + if (sequence != NULL) + fFileSystem->OpenOwnerSequenceUnlock(*sequence); + + if (cookie == NULL) { + snooze_etc(sSecToBigTime(5), B_SYSTEM_TIMEBASE, + B_RELATIVE_TIMEOUT); + + if (sequence != NULL) + *sequence = fFileSystem->OpenOwnerSequenceLock(); + + return true; + } + + if ((cookie->fMode & O_NONBLOCK) == 0) { + status_t result = acquire_sem_etc(cookie->fSnoozeCancel, 1, + B_RELATIVE_TIMEOUT, sSecToBigTime(5)); + + if (sequence != NULL) + *sequence = fFileSystem->OpenOwnerSequenceLock(); + + if (result != B_TIMED_OUT) { + if (result == B_OK) + release_sem(cookie->fSnoozeCancel); + return false; + } + return true; + } + + if (sequence != NULL) + *sequence = fFileSystem->OpenOwnerSequenceLock(); + return false; + + // server is in grace period, we need to wait + case NFS4ERR_GRACE: + leaseTime = fFileSystem->NFSServer()->LeaseTime(); + if (sequence != NULL) + fFileSystem->OpenOwnerSequenceUnlock(*sequence); + + if (cookie == NULL) { + snooze_etc(sSecToBigTime(leaseTime) / 3, B_SYSTEM_TIMEBASE, + B_RELATIVE_TIMEOUT); + if (sequence != NULL) + *sequence = fFileSystem->OpenOwnerSequenceLock(); + return true; + } + + if ((cookie->fMode & O_NONBLOCK) == 0) { + status_t result = acquire_sem_etc(cookie->fSnoozeCancel, 1, + B_RELATIVE_TIMEOUT, sSecToBigTime(leaseTime) / 3); + + if (sequence != NULL) + *sequence = fFileSystem->OpenOwnerSequenceLock(); + + if (result != B_TIMED_OUT) { + if (result == B_OK) + release_sem(cookie->fSnoozeCancel); + return false; + } + return true; + } + + if (sequence != NULL) + *sequence = fFileSystem->OpenOwnerSequenceLock(); + return false; + + // server has rebooted, reclaim share and try again + case NFS4ERR_STALE_CLIENTID: + case NFS4ERR_STALE_STATEID: + if (state != NULL) { + if (sequence != NULL) + fFileSystem->OpenOwnerSequenceUnlock(*sequence); + + fFileSystem->NFSServer()->ServerRebooted(state->fClientID); + if (sequence != NULL) + *sequence = fFileSystem->OpenOwnerSequenceLock(); + + return true; + } + return false; + + // File Handle has expired, is invalid or the node has been deleted + case NFS4ERR_NOFILEHANDLE: + case NFS4ERR_BADHANDLE: + case NFS4ERR_FHEXPIRED: + case NFS4ERR_STALE: + if (fInfo.UpdateFileHandles(fFileSystem) == B_OK) + return true; + return false; + + // filesystem has been moved + case NFS4ERR_LEASE_MOVED: + case NFS4ERR_MOVED: + fFileSystem->Migrate(serv); + return true; + + // lease has expired + case NFS4ERR_EXPIRED: + if (state != NULL) { + fFileSystem->NFSServer()->ClientId(state->fClientID, true); + return true; + } + return false; + + default: + return false; + } +} + + +status_t +NFS4Object::ConfirmOpen(const FileHandle& fh, OpenState* state, + uint32* sequence) +{ + ASSERT(state != NULL); + ASSERT(sequence != NULL); + + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + + RequestBuilder& req = request.Builder(); + + req.PutFH(fh); + req.OpenConfirm(*sequence, state->fStateID, state->fStateSeq); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + *sequence += IncrementSequence(reply.NFS4Error()); + + if (HandleErrors(reply.NFS4Error(), serv, NULL, state)) + continue; + + reply.PutFH(); + result = reply.OpenConfirm(&state->fStateSeq); + if (result != B_OK) + return result; + + return B_OK; + } while (true); +} + + +uint32 +NFS4Object::IncrementSequence(uint32 error) +{ + if (error != NFS4ERR_STALE_CLIENTID && error != NFS4ERR_STALE_STATEID + && error != NFS4ERR_BAD_STATEID && error != NFS4ERR_BAD_SEQID + && error != NFS4ERR_BADXDR && error != NFS4ERR_RESOURCE + && error != NFS4ERR_NOFILEHANDLE) + return 1; + + return 0; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Object.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.h new file mode 100644 index 0000000000..d0ac9cb83a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef NFS4OBJECT_H +#define NFS4OBJECT_H + +#include "FileInfo.h" +#include "NFS4Defs.h" +#include "RPCServer.h" + + +class OpenStateCookie; +class OpenState; + +class NFS4Object { +public: + bool HandleErrors(uint32 nfs4Error, RPC::Server* serv, + OpenStateCookie* cookie = NULL, + OpenState* state = NULL, uint32* sequence = NULL); + + status_t ConfirmOpen(const FileHandle& fileHandle, + OpenState* state, uint32* sequence); + + static uint32 IncrementSequence(uint32 error); + + inline NFS4Object(); + + FileInfo fInfo; + FileSystem* fFileSystem; +}; + + +inline +NFS4Object::NFS4Object() + : + fFileSystem(NULL) +{ +} + + +#endif // NFS4OBJECT_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp new file mode 100644 index 0000000000..e92ede74ba --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp @@ -0,0 +1,425 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "FileSystem.h" +#include "Inode.h" +#include "NFS4Server.h" +#include "Request.h" +#include "WorkQueue.h" + + +NFS4Server::NFS4Server(RPC::Server* serv) + : + fThreadCancel(true), + fWaitCancel(create_sem(0, NULL)), + fLeaseTime(0), + fClientIdLastUse(0), + fUseCount(0), + fServer(serv) +{ + ASSERT(serv != NULL); + + mutex_init(&fClientIdLock, NULL); + mutex_init(&fFSLock, NULL); + mutex_init(&fThreadStartLock, NULL); + +} + + +NFS4Server::~NFS4Server() +{ + fThreadCancel = true; + fUseCount = 0; + release_sem(fWaitCancel); + status_t result; + wait_for_thread(fThread, &result); + + delete_sem(fWaitCancel); + mutex_destroy(&fClientIdLock); + mutex_destroy(&fFSLock); + mutex_destroy(&fThreadStartLock); +} + + +uint64 +NFS4Server::ServerRebooted(uint64 clientId) +{ + if (clientId != fClientId) + return fClientId; + + fClientId = ClientId(clientId, true); + + // reclaim all opened files and held locks from all filesystems + MutexLocker _(fFSLock); + FileSystem* fs = fFileSystems.Head(); + while (fs != NULL) { + DoublyLinkedList::Iterator iterator + = fs->OpenFilesLock().GetIterator(); + OpenState* current = iterator.Next(); + while (current != NULL) { + current->Reclaim(fClientId); + + current = iterator.Next(); + } + fs->OpenFilesUnlock(); + + fs = fFileSystems.GetNext(fs); + } + + return fClientId; +} + + +void +NFS4Server::AddFileSystem(FileSystem* fs) +{ + ASSERT(fs != NULL); + + MutexLocker _(fFSLock); + fFileSystems.Add(fs); + + fUseCount += fs->OpenFilesCount(); + if (fs->OpenFilesCount() > 0) + _StartRenewing(); +} + + +void +NFS4Server::RemoveFileSystem(FileSystem* fs) +{ + ASSERT(fs != NULL); + + MutexLocker _(fFSLock); + fFileSystems.Remove(fs); + fUseCount -= fs->OpenFilesCount(); +} + + +uint64 +NFS4Server::ClientId(uint64 prevId, bool forceNew) +{ + MutexLocker _(fClientIdLock); + if ((fUseCount == 0 && fClientIdLastUse + (time_t)LeaseTime() < time(NULL)) + || (forceNew && fClientId == prevId)) { + + Request request(fServer, NULL); + request.Builder().SetClientID(fServer); + + status_t result = request.Send(); + if (result != B_OK) + return fClientId; + + uint64 ver; + result = request.Reply().SetClientID(&fClientId, &ver); + if (result != B_OK) + return fClientId; + + request.Reset(); + request.Builder().SetClientIDConfirm(fClientId, ver); + + result = request.Send(); + if (result != B_OK) + return fClientId; + + result = request.Reply().SetClientIDConfirm(); + if (result != B_OK) + return fClientId; + } + + fClientIdLastUse = time(NULL); + return fClientId; +} + + +status_t +NFS4Server::FileSystemMigrated() +{ + // reclaim all opened files and held locks from all filesystems + MutexLocker _(fFSLock); + FileSystem* fs = fFileSystems.Head(); + while (fs != NULL) { + fs->Migrate(fServer); + fs = fFileSystems.GetNext(fs); + } + + return B_OK; +} + + +status_t +NFS4Server::_GetLeaseTime() +{ + Request request(fServer, NULL); + request.Builder().PutRootFH(); + Attribute attr[] = { FATTR4_LEASE_TIME }; + request.Builder().GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + reply.PutRootFH(); + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + // FATTR4_LEASE_TIME is mandatory + if (count < 1 || values[0].fAttribute != FATTR4_LEASE_TIME) { + delete[] values; + return B_BAD_VALUE; + } + + fLeaseTime = values[0].fData.fValue32; + + return B_OK; +} + + +status_t +NFS4Server::_StartRenewing() +{ + if (!fThreadCancel) + return B_OK; + + MutexLocker _(fThreadStartLock); + + if (!fThreadCancel) + return B_OK; + + if (fLeaseTime == 0) { + status_t result = _GetLeaseTime(); + if (result != B_OK) + return result; + } + + fThreadCancel = false; + fThread = spawn_kernel_thread(&NFS4Server::_RenewalThreadStart, + "NFSv4 Renewal", B_NORMAL_PRIORITY, this); + if (fThread < B_OK) + return fThread; + + status_t result = resume_thread(fThread); + if (result != B_OK) { + kill_thread(fThread); + return result; + } + + return B_OK; +} + + +status_t +NFS4Server::_Renewal() +{ + while (!fThreadCancel) { + // TODO: operations like OPEN, READ, CLOSE, etc also renew leases + status_t result = acquire_sem_etc(fWaitCancel, 1, + B_RELATIVE_TIMEOUT, sSecToBigTime(fLeaseTime - 2)); + if (result != B_TIMED_OUT) { + if (result == B_OK) + release_sem(fWaitCancel); + return B_OK; + } + + uint64 clientId = fClientId; + + if (fUseCount == 0) { + MutexLocker _(fFSLock); + if (fUseCount == 0) { + fThreadCancel = true; + return B_OK; + } + } + + Request request(fServer, NULL); + request.Builder().Renew(clientId); + request.Send(); + + switch (request.Reply().NFS4Error()) { + case NFS4ERR_CB_PATH_DOWN: + RecallAll(); + break; + case NFS4ERR_STALE_CLIENTID: + ServerRebooted(clientId); + break; + case NFS4ERR_LEASE_MOVED: + FileSystemMigrated(); + break; + } + } + + return B_OK; +} + + +status_t +NFS4Server::_RenewalThreadStart(void* ptr) +{ + ASSERT(ptr != NULL); + NFS4Server* server = reinterpret_cast(ptr); + return server->_Renewal(); +} + + +status_t +NFS4Server::ProcessCallback(RPC::CallbackRequest* request, + Connection* connection) +{ + ASSERT(request != NULL); + ASSERT(connection != NULL); + + RequestInterpreter req(request); + ReplyBuilder reply(request->XID()); + + status_t result; + uint32 count = req.OperationCount(); + + for (uint32 i = 0; i < count; i++) { + switch (req.Operation()) { + case OpCallbackGetAttr: + result = CallbackGetAttr(&req, &reply); + break; + case OpCallbackRecall: + result = CallbackRecall(&req, &reply); + break; + default: + result = B_NOT_SUPPORTED; + } + + if (result != B_OK) + break; + } + + XDR::WriteStream& stream = reply.Reply()->Stream(); + connection->Send(stream.Buffer(), stream.Size()); + + return B_OK; +} + + +status_t +NFS4Server::CallbackRecall(RequestInterpreter* request, ReplyBuilder* reply) +{ + ASSERT(request != NULL); + ASSERT(reply != NULL); + + uint32 stateID[3]; + uint32 stateSeq; + bool truncate; + FileHandle handle; + + status_t result = request->Recall(&handle, truncate, &stateSeq, stateID); + if (result != B_OK) + return result; + + MutexLocker locker(fFSLock); + + Delegation* delegation = NULL; + FileSystem* current = fFileSystems.Head(); + while (current != NULL) { + delegation = current->GetDelegation(handle); + if (delegation != NULL) + break; + + current = fFileSystems.GetNext(current); + } + locker.Unlock(); + + if (delegation == NULL) { + reply->Recall(B_FILE_NOT_FOUND); + return B_FILE_NOT_FOUND; + } + + DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs; + args->fDelegation = delegation; + args->fTruncate = truncate; + gWorkQueue->EnqueueJob(DelegationRecall, args); + + reply->Recall(B_OK); + + return B_OK; +} + + +status_t +NFS4Server::CallbackGetAttr(RequestInterpreter* request, ReplyBuilder* reply) +{ + ASSERT(request != NULL); + ASSERT(reply != NULL); + + FileHandle handle; + int mask; + + status_t result = request->GetAttr(&handle, &mask); + if (result != B_OK) + return result; + return B_OK; + + MutexLocker locker(fFSLock); + + Delegation* delegation = NULL; + FileSystem* current = fFileSystems.Head(); + while (current != NULL) { + delegation = current->GetDelegation(handle); + if (delegation != NULL) + break; + + current = fFileSystems.GetNext(current); + } + locker.Unlock(); + + if (delegation == NULL) { + reply->GetAttr(B_FILE_NOT_FOUND, 0, 0, 0); + return B_FILE_NOT_FOUND; + } + + struct stat st; + delegation->GetInode()->Stat(&st); + + uint64 change; + change = delegation->GetInode()->Change(); + if (delegation->GetInode()->Dirty()) + change++; + reply->GetAttr(B_OK, mask, st.st_size, change); + + return B_OK; +} + + +status_t +NFS4Server::RecallAll() +{ + MutexLocker _(fFSLock); + FileSystem* fs = fFileSystems.Head(); + while (fs != NULL) { + DoublyLinkedList& list = fs->DelegationsLock(); + DoublyLinkedList::Iterator iterator = list.GetIterator(); + + Delegation* current = iterator.Next(); + while (current != NULL) { + DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs; + args->fDelegation = current; + args->fTruncate = false; + gWorkQueue->EnqueueJob(DelegationRecall, args); + + current = iterator.Next(); + } + fs->DelegationsUnlock(); + + fs = fFileSystems.GetNext(fs); + } + + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Server.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.h new file mode 100644 index 0000000000..624b0fe26a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.h @@ -0,0 +1,106 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef NFS4SERVER_H +#define NFS4SERVER_H + + +#include + +#include "ReplyBuilder.h" +#include "RequestInterpreter.h" +#include "RPCServer.h" + + +class FileSystem; +class OpenFileCookie; + +class NFS4Server : public RPC::ProgramData { +public: + NFS4Server(RPC::Server* serv); + virtual ~NFS4Server(); + + uint64 ServerRebooted(uint64 clientId); + status_t FileSystemMigrated(); + + void AddFileSystem(FileSystem* fs); + void RemoveFileSystem(FileSystem* fs); + + inline void IncUsage(); + inline void DecUsage(); + + uint64 ClientId(uint64 prevId = 0, bool forceNew = false); + + inline uint32 LeaseTime(); + + virtual status_t ProcessCallback(RPC::CallbackRequest* request, + Connection* connection); + + status_t CallbackRecall(RequestInterpreter* request, + ReplyBuilder* reply); + status_t RecallAll(); + + status_t CallbackGetAttr(RequestInterpreter* request, + ReplyBuilder* reply); +private: + status_t _GetLeaseTime(); + + status_t _StartRenewing(); + status_t _Renewal(); + static status_t _RenewalThreadStart(void* ptr); + + thread_id fThread; + bool fThreadCancel; + sem_id fWaitCancel; + mutex fThreadStartLock; + + uint32 fLeaseTime; + + uint64 fClientId; + bool fClientIdInit; + time_t fClientIdLastUse; + mutex fClientIdLock; + + uint32 fUseCount; + DoublyLinkedList fFileSystems; + mutex fFSLock; + + RPC::Server* fServer; +}; + + +inline void +NFS4Server::IncUsage() +{ + MutexLocker _(fFSLock); + fUseCount++; + _StartRenewing(); + fClientIdLastUse = time(NULL); +} + + +inline void +NFS4Server::DecUsage() +{ + MutexLocker _(fFSLock); + fClientIdLastUse = time(NULL); + fUseCount--; +} + + +inline uint32 +NFS4Server::LeaseTime() +{ + if (fLeaseTime == 0) + _GetLeaseTime(); + + return fLeaseTime; +} + + +#endif // NFS4SERVER_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/OpenState.cpp b/src/add-ons/kernel/file_systems/nfs4/OpenState.cpp new file mode 100644 index 0000000000..21ac8d5b78 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/OpenState.cpp @@ -0,0 +1,322 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "OpenState.h" + +#include + +#include "FileSystem.h" +#include "Request.h" +#include "WorkQueue.h" + + +OpenState::OpenState() + : + fOpened(false), + fDelegation(NULL), + fLocks(NULL), + fLockOwners(NULL) +{ + mutex_init(&fLock, NULL); + + mutex_init(&fLocksLock, NULL); + mutex_init(&fOwnerLock, NULL); +} + + +OpenState::~OpenState() +{ + if (fOpened) + fFileSystem->RemoveOpenFile(this); + Close(); + + mutex_destroy(&fLock); + + mutex_destroy(&fLocksLock); + mutex_destroy(&fOwnerLock); +} + + +LockOwner* +OpenState::GetLockOwner(uint32 owner) +{ + LockOwner* current = fLockOwners; + while (current != NULL) { + if (current->fOwner == owner) + return current; + + current = current->fNext; + } + + current = new LockOwner(owner); + if (current == NULL) + return NULL; + + current->fNext = fLockOwners; + if (fLockOwners != NULL) + fLockOwners->fPrev = current; + fLockOwners = current; + + return current; +} + + +// Caller must hold fLocksLock +void +OpenState::AddLock(LockInfo* lock) +{ + lock->fNext = fLocks; + fLocks = lock; +} + + +// Caller must hold fLocksLock +void +OpenState::RemoveLock(LockInfo* lock, LockInfo* prev) +{ + if (prev != NULL) + prev->fNext = lock->fNext; + else + fLocks = lock->fNext; +} + + +void +OpenState::DeleteLock(LockInfo* lock) +{ + MutexLocker _(fOwnerLock); + + LockOwner* owner = lock->fOwner; + delete lock; + + if (owner->fUseCount == 0) { + if (owner->fPrev) + owner->fPrev->fNext = owner->fNext; + else + fLockOwners = owner->fNext; + if (owner->fNext) + owner->fNext->fPrev = owner->fPrev; + + _ReleaseLockOwner(owner); + delete owner; + } +} + + +status_t +OpenState::_ReleaseLockOwner(LockOwner* owner) +{ + ASSERT(owner != NULL); + + do { + RPC::Server* server = fFileSystem->Server(); + Request request(server, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.ReleaseLockOwner(this, owner); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), server)) + continue; + + return reply.ReleaseLockOwner(); + } while (true); +} + + +status_t +OpenState::Reclaim(uint64 newClientID) +{ + if (!fOpened) + return B_OK; + + MutexLocker _(fLock); + + if (fClientID == newClientID) + return B_OK; + fClientID = newClientID; + + _ReclaimOpen(newClientID); + _ReclaimLocks(newClientID); + + return B_OK; +} + + +status_t +OpenState::_ReclaimOpen(uint64 newClientID) +{ + bool confirm; + OpenDelegationData delegation; + delegation.fType = OPEN_DELEGATE_NONE; + delegation.fRecall = false; + + status_t result; + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + OpenDelegation delegType = fDelegation != NULL ? fDelegation->Type() + : OPEN_DELEGATE_NONE; + do { + RPC::Server* server = fFileSystem->Server(); + Request request(server, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Open(CLAIM_PREVIOUS, sequence, sModeToAccess(fMode), newClientID, + OPEN4_NOCREATE, fFileSystem->OpenOwner(), NULL, NULL, 0, false, + delegType); + + result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID + && HandleErrors(reply.NFS4Error(), server, NULL, NULL, &sequence)) { + continue; + } + + reply.PutFH(); + + result = reply.Open(fStateID, &fStateSeq, &confirm, &delegation); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + break; + } while (true); + + if (fDelegation != NULL) + fDelegation->SetData(delegation); + + if (delegation.fRecall) { + DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs; + args->fDelegation = fDelegation; + args->fTruncate = false; + gWorkQueue->EnqueueJob(DelegationRecall, args); + } + + if (confirm) + result = ConfirmOpen(fInfo.fHandle, this, &sequence); + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; +} + + +status_t +OpenState::_ReclaimLocks(uint64 newClientID) +{ + MutexLocker _(fLocksLock); + LockInfo* linfo = fLocks; + while (linfo != NULL) { + MutexLocker locker(linfo->fOwner->fLock); + + if (linfo->fOwner->fClientId != newClientID) { + memset(linfo->fOwner->fStateId, 0, sizeof(linfo->fOwner->fStateId)); + linfo->fOwner->fClientId = newClientID; + } + + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + RPC::Server* server = fFileSystem->Server(); + Request request(server, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Lock(this, linfo, &sequence, true); + + status_t result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + break; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + if (reply.NFS4Error() != NFS4ERR_STALE_CLIENTID + && reply.NFS4Error() != NFS4ERR_STALE_STATEID + && HandleErrors(reply.NFS4Error(), server, NULL, NULL, + &sequence)) { + continue; + } + + reply.PutFH(); + reply.Lock(linfo); + + fFileSystem->OpenOwnerSequenceUnlock(sequence); + break; + } while (true); + locker.Unlock(); + + linfo = linfo->fNext; + } + + return B_OK; +} + + +status_t +OpenState::Close() +{ + if (!fOpened) + return B_OK; + + MutexLocker _(fLock); + fOpened = false; + + uint32 sequence = fFileSystem->OpenOwnerSequenceLock(); + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Close(sequence, fStateID, fStateSeq); + + status_t result = request.Send(); + if (result != B_OK) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return result; + } + + ReplyInterpreter& reply = request.Reply(); + + sequence += IncrementSequence(reply.NFS4Error()); + + // RFC 3530 8.10.1. Some servers does not do anything to help client + // recognize retried CLOSE requests so we just assume that BAD_STATEID + // on CLOSE request is just a result of retransmission. + if (reply.NFS4Error() == NFS4ERR_BAD_STATEID) { + fFileSystem->OpenOwnerSequenceUnlock(sequence); + return B_OK; + } + + if (HandleErrors(reply.NFS4Error(), serv, NULL, this, &sequence)) + continue; + fFileSystem->OpenOwnerSequenceUnlock(sequence); + + reply.PutFH(); + + return reply.Close(); + } while (true); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/OpenState.h b/src/add-ons/kernel/file_systems/nfs4/OpenState.h new file mode 100644 index 0000000000..f810528903 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/OpenState.h @@ -0,0 +1,60 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef OPENSTATE_H +#define OPENSTATE_H + + +#include +#include +#include + +#include "Cookie.h" +#include "NFS4Object.h" + + +struct OpenState : public NFS4Object, public KernelReferenceable, + public DoublyLinkedListLinkImpl { + OpenState(); + ~OpenState(); + + uint64 fClientID; + + int fMode; + mutex fLock; + + uint32 fStateID[3]; + uint32 fStateSeq; + + bool fOpened; + Delegation* fDelegation; + + LockInfo* fLocks; + mutex fLocksLock; + + LockOwner* fLockOwners; + mutex fOwnerLock; + + LockOwner* GetLockOwner(uint32 owner); + + void AddLock(LockInfo* lock); + void RemoveLock(LockInfo* lock, LockInfo* prev); + void DeleteLock(LockInfo* lock); + + status_t Reclaim(uint64 newClientID); + + status_t Close(); + +private: + status_t _ReclaimOpen(uint64 newClientID); + status_t _ReclaimLocks(uint64 newClientID); + status_t _ReleaseLockOwner(LockOwner* owner); +}; + + +#endif // OPENSTATE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCAuth.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCAuth.cpp new file mode 100644 index 0000000000..b10f2abba0 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCAuth.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RPCAuth.h" + +#include +#include +#include + +#include +#include + +#include "RPCDefs.h" + + +using namespace RPC; + + +Auth::Auth() +{ +} + + +const Auth* +Auth::CreateNone() +{ + Auth* auth = new(std::nothrow) Auth; + if (auth == NULL) + return NULL; + + auth->fStream.AddInt(AUTH_NONE); + auth->fStream.AddOpaque(NULL, 0); + if (auth->fStream.Error() != B_OK) { + delete auth; + return NULL; + } + + return auth; +} + + +const Auth* +Auth::CreateSys() +{ + Auth* auth = new(std::nothrow) Auth; + if (auth == NULL) + return NULL; + + XDR::WriteStream xdr; + xdr.AddUInt(time(NULL)); + + char hostname[255]; + if (gethostname(hostname, 255) < 0) + strcpy(hostname, "unknown"); + xdr.AddString(hostname, 255); + + xdr.AddUInt(getuid()); + xdr.AddUInt(getgid()); + + int count = getgroups(0, NULL); + gid_t* groups = (gid_t*)malloc(count * sizeof(gid_t)); + int len = getgroups(count, groups); + if (len > 0) { + len = min_c(len, 16); + xdr.AddUInt(len); + for (int i = 0; i < len; i++) + xdr.AddUInt((uint32)groups[i]); + } else + xdr.AddUInt(0); + free(groups); + if (xdr.Error() != B_OK) { + delete auth; + return NULL; + } + + auth->fStream.AddInt(AUTH_SYS); + auth->fStream.AddOpaque(xdr); + if (auth->fStream.Error() != B_OK) { + delete auth; + return NULL; + } + + return auth; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCAuth.h b/src/add-ons/kernel/file_systems/nfs4/RPCAuth.h new file mode 100644 index 0000000000..d45a7cf460 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCAuth.h @@ -0,0 +1,41 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef RPCAUTH_H +#define RPCAUTH_H + + +#include "XDR.h" + + +namespace RPC { + +class Auth { +public: + inline const XDR::WriteStream& Stream() const; + + static const Auth* CreateNone(); + static const Auth* CreateSys(); + +private: + Auth(); + + XDR::WriteStream fStream; +}; + + +inline const XDR::WriteStream& +Auth::Stream() const +{ + return fStream; +} + +} // namespace RPC + + +#endif // RPCAUTH_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCall.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCall.cpp new file mode 100644 index 0000000000..d0c009945a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCall.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RPCCall.h" + +#include +#include + +#include "RPCDefs.h" + + +using namespace RPC; + + +Call::Call() +{ +} + + +Call* +Call::Create(uint32 proc, const Auth* creds, const Auth* ver) +{ + ASSERT(creds != NULL); + ASSERT(ver != NULL); + + Call* call = new(std::nothrow) Call; + if (call == NULL) + return NULL; + + // XID will be determined and set by RPC::Server + call->fXIDPosition = call->fStream.Current(); + call->fStream.AddUInt(0); + + call->fStream.AddInt(CALL); + call->fStream.AddUInt(VERSION); + call->fStream.AddUInt(PROGRAM_NFS); + call->fStream.AddUInt(NFS_VERSION); + call->fStream.AddUInt(proc); + + call->fStream.Append(creds->Stream()); + delete creds; + + call->fStream.Append(ver->Stream()); + delete ver; + + if (call->fStream.Error() != B_OK) { + delete call; + return NULL; + } + + return call; +} + + +Call::~Call() +{ +} + + +void +Call::SetXID(uint32 xid) +{ + fStream.InsertUInt(fXIDPosition, xid); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCall.h b/src/add-ons/kernel/file_systems/nfs4/RPCCall.h new file mode 100644 index 0000000000..f1739bd4d1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCall.h @@ -0,0 +1,46 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef RPCCALL_H +#define RPCCALL_H + + +#include "RPCAuth.h" +#include "XDR.h" + + +namespace RPC { + +class Call { +public: + static Call* Create(uint32 proc, const Auth* creds, + const Auth* ver); + ~Call(); + + void SetXID(uint32 x); + + inline XDR::WriteStream& Stream(); + +private: + Call(); + + XDR::Stream::Position fXIDPosition; + + XDR::WriteStream fStream; +}; + + +inline XDR::WriteStream& +Call::Stream() +{ + return fStream; +} + +} // namespace RPC + + +#endif // RPCCALL_H diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallback.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCallback.cpp new file mode 100644 index 0000000000..baf6246759 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallback.cpp @@ -0,0 +1,33 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RPCCallback.h" + +#include "RPCCallbackRequest.h" +#include "RPCServer.h" + + +using namespace RPC; + + +Callback::Callback(Server* server) + : + fServer(server) +{ +} + + +status_t +Callback::EnqueueRequest(CallbackRequest* request, Connection* connection) +{ + ASSERT(request != NULL); + ASSERT(connection != NULL); + return fServer->PrivateData()->ProcessCallback(request, connection); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallback.h b/src/add-ons/kernel/file_systems/nfs4/RPCCallback.h new file mode 100644 index 0000000000..9f579be9cf --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallback.h @@ -0,0 +1,73 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef RPCCALLBACK_H +#define RPCCALLBACK_H + + +#include "Connection.h" + + +namespace RPC { + +class CallbackServer; +class CallbackRequest; +class Server; + +class Callback { +public: + Callback(Server* server); + + inline void SetID(int32 id); + inline int32 ID(); + + inline void SetCBServer(CallbackServer* server); + inline CallbackServer* CBServer(); + + status_t EnqueueRequest(CallbackRequest* request, + Connection* connection); + +private: + CallbackServer* fCBServer; + Server* fServer; + int32 fID; +}; + + +inline void +Callback::SetID(int32 id) +{ + fID = id; +} + + +inline int32 +Callback::ID() +{ + return fID; +} + + +inline void +Callback::SetCBServer(CallbackServer* server) +{ + fCBServer = server; +} + + +inline CallbackServer* +Callback::CBServer() +{ + return fCBServer; +} + + +} // namespace RPC + + +#endif // RPCCALLBACK_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.cpp new file mode 100644 index 0000000000..595bb0989f --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RPCCallbackReply.h" + +#include + +#include "RPCDefs.h" + + +using namespace RPC; + + +CallbackReply::CallbackReply() +{ +} + + +CallbackReply* +CallbackReply::Create(uint32 xid, AcceptStat rpcError) +{ + CallbackReply* reply = new(std::nothrow) CallbackReply; + if (reply == NULL) + return NULL; + + reply->fStream.AddUInt(xid); + + reply->fStream.AddInt(REPLY); + reply->fStream.AddUInt(MSG_ACCEPTED); + + reply->fStream.AddInt(AUTH_NONE); + reply->fStream.AddOpaque(NULL, 0); + + reply->fStream.AddUInt(rpcError); + + return reply; +} + + +CallbackReply::~CallbackReply() +{ +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.h b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.h new file mode 100644 index 0000000000..485d5fa8a8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackReply.h @@ -0,0 +1,42 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef RPCCALLBACKREPLY_H +#define RPCCALLBACKREPLY_H + + +#include "RPCDefs.h" +#include "XDR.h" + + +namespace RPC { + +class CallbackReply { +public: + static CallbackReply* Create(uint32 xid, + AcceptStat rpcError = SUCCESS); + ~CallbackReply(); + + inline XDR::WriteStream& Stream(); + +private: + CallbackReply(); + + XDR::WriteStream fStream; +}; + + +inline XDR::WriteStream& +CallbackReply::Stream() +{ + return fStream; +} + +} // namespace RPC + + +#endif // RPCCALLBACKREPLY_H diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.cpp new file mode 100644 index 0000000000..54b9ef7b6f --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RPCCallbackRequest.h" + +#include + +#include + +#include "NFS4Defs.h" +#include "RPCDefs.h" + + +using namespace RPC; + + +CallbackRequest::CallbackRequest(void* buffer, int size) + : + fError(B_BAD_VALUE), + fRPCError(GARBAGE_ARGS), + fStream(buffer, size), + fBuffer(buffer) +{ + ASSERT(buffer != NULL); + + fXID = fStream.GetUInt(); + + if (fStream.GetUInt() != CALL) + return; + + if (fStream.GetUInt() != VERSION) + return; + + if (fStream.GetUInt() != PROGRAM_NFS_CB) { + fRPCError = PROG_UNAVAIL; + return; + } + + if (fStream.GetUInt() != NFS_CB_VERSION) { + fRPCError = PROG_MISMATCH; + return; + } + + fProcedure = fStream.GetUInt(); + + fStream.GetUInt(); + fStream.GetOpaque(NULL); + + fStream.GetUInt(); + fStream.GetOpaque(NULL); + + if (fProcedure == CallbackProcCompound) { + fStream.GetOpaque(NULL); // TODO: tag may be important + if (fStream.GetUInt() != 0) + return; + + fID = fStream.GetUInt(); + + fRPCError = SUCCESS; + fError = B_OK; + } else if (fProcedure == CallbackProcNull) { + fRPCError = SUCCESS; + fError = B_OK; + } else + fRPCError = PROC_UNAVAIL; +} + + +CallbackRequest::~CallbackRequest() +{ + free(fBuffer); +} + + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.h b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.h new file mode 100644 index 0000000000..19e2e006c5 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackRequest.h @@ -0,0 +1,93 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef RPCCALLBACKREQUEST_H +#define RPCCALLBACKREQUEST_H + + +#include "RPCDefs.h" +#include "XDR.h" + + +namespace RPC { + +class CallbackRequest { +public: + CallbackRequest(void* buffer, int size); + ~CallbackRequest(); + + inline uint32 XID(); + inline uint32 ID(); + + inline uint32 Procedure(); + + inline status_t Error(); + inline AcceptStat RPCError(); + + inline XDR::ReadStream& Stream(); + +private: + uint32 fXID; + uint32 fID; + + uint32 fProcedure; + + status_t fError; + AcceptStat fRPCError; + + XDR::ReadStream fStream; + void* fBuffer; +}; + + +inline uint32 +CallbackRequest::XID() +{ + return fXID; +} + + +inline uint32 +CallbackRequest::ID() +{ + return fID; +} + + +inline uint32 +CallbackRequest::Procedure() +{ + return fProcedure; +} + + +inline status_t +CallbackRequest::Error() +{ + return fError; +} + + +inline AcceptStat +CallbackRequest::RPCError() +{ + return fRPCError; +} + + +inline XDR::ReadStream& +CallbackRequest::Stream() +{ + return fStream; +} + + +} // namespace RPC + + +#endif // RPCCALLBACKREQUEST_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.cpp new file mode 100644 index 0000000000..c296289d84 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.cpp @@ -0,0 +1,374 @@ +/* + * Copyright 2012-2013 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RPCCallbackServer.h" + +#include "NFS4Defs.h" +#include "RPCCallback.h" +#include "RPCCallbackReply.h" +#include "RPCCallbackRequest.h" +#include "RPCServer.h" + + +using namespace RPC; + + +CallbackServer* gRPCCallbackServer = NULL; +CallbackServer* gRPCCallbackServer6 = NULL; + + +CallbackServer::CallbackServer(int networkFamily) + : + fConnectionList(NULL), + fListener(NULL), + fThreadRunning(false), + fCallbackArray(NULL), + fArraySize(0), + fFreeSlot(-1), + fNetworkFamily(networkFamily) +{ + mutex_init(&fConnectionLock, NULL); + mutex_init(&fThreadLock, NULL); + rw_lock_init(&fArrayLock, NULL); +} + + +CallbackServer::~CallbackServer() +{ + StopServer(); + + free(fCallbackArray); + rw_lock_destroy(&fArrayLock); + mutex_destroy(&fThreadLock); + mutex_destroy(&fConnectionLock); +} + + +CallbackServer* +CallbackServer::Get(Server* server) +{ + ASSERT(server != NULL); + + int family = server->ID().Family(); + ASSERT(family == AF_INET || family == AF_INET6); + + int idx; + switch (family) { + case AF_INET: + idx = 0; + break; + case AF_INET6: + idx = 1; + break; + default: + return NULL; + } + + MutexLocker _(fServerCreationLock); + if (fServers[idx] == NULL) + fServers[idx] = new CallbackServer(family); + return fServers[idx]; +} + + +void +CallbackServer::ShutdownAll() +{ + MutexLocker _(fServerCreationLock); + for (unsigned int i = 0; i < sizeof(fServers) / sizeof(fServers[0]); i++) + delete fServers[i]; + memset(&fServers, 0, sizeof(fServers)); +} + + +mutex CallbackServer::fServerCreationLock = MUTEX_INITIALIZER(NULL); +CallbackServer* CallbackServer::fServers[2] = { NULL, NULL }; + + +status_t +CallbackServer::RegisterCallback(Callback* callback) +{ + ASSERT(callback != NULL); + + status_t result = StartServer(); + if (result != B_OK) + return result; + + WriteLocker _(fArrayLock); + if (fFreeSlot == -1) { + uint32 newSize = max_c(fArraySize * 2, 4); + uint32 size = newSize * sizeof(CallbackSlot); + CallbackSlot* array = reinterpret_cast(malloc(size)); + if (array == NULL) + return B_NO_MEMORY; + + if (fCallbackArray != NULL) + memcpy(array, fCallbackArray, fArraySize * sizeof(CallbackSlot)); + + for (uint32 i = fArraySize; i < newSize; i++) + array[i].fNext = i + 1; + + array[newSize - 1].fNext = -1; + + fCallbackArray = array; + fFreeSlot = fArraySize; + fArraySize = newSize; + } + + int32 id = fFreeSlot; + fFreeSlot = fCallbackArray[id].fNext; + + fCallbackArray[id].fCallback = callback; + callback->SetID(id); + callback->SetCBServer(this); + + return B_OK; +} + + +status_t +CallbackServer::UnregisterCallback(Callback* callback) +{ + ASSERT(callback != NULL); + ASSERT(callback->CBServer() == this); + + int32 id = callback->ID(); + + WriteLocker _(fArrayLock); + fCallbackArray[id].fNext = fFreeSlot; + fFreeSlot = id; + + callback->SetCBServer(NULL); + return B_OK; +} + + +status_t +CallbackServer::StartServer() +{ + MutexLocker _(fThreadLock); + if (fThreadRunning) + return B_OK; + + status_t result = ConnectionListener::Listen(&fListener, fNetworkFamily); + if (result != B_OK) + return result; + + fThread = spawn_kernel_thread(&CallbackServer::ListenerThreadLauncher, + "NFSv4 Callback Listener", B_NORMAL_PRIORITY, this); + if (fThread < B_OK) + return fThread; + + fThreadRunning = true; + + result = resume_thread(fThread); + if (result != B_OK) { + kill_thread(fThread); + fThreadRunning = false; + return result; + } + + return B_OK; +} + + +status_t +CallbackServer::StopServer() +{ + MutexLocker _(&fThreadLock); + if (!fThreadRunning) + return B_OK; + + fListener->Disconnect(); + status_t result; + wait_for_thread(fThread, &result); + + MutexLocker locker(fConnectionLock); + while (fConnectionList != NULL) { + ConnectionEntry* entry = fConnectionList; + fConnectionList = entry->fNext; + entry->fConnection->Disconnect(); + + status_t result; + wait_for_thread(entry->fThread, &result); + + delete entry->fConnection; + delete entry; + } + + delete fListener; + + fThreadRunning = false; + return B_OK; +} + + +status_t +CallbackServer::NewConnection(Connection* connection) +{ + ASSERT(connection != NULL); + + ConnectionEntry* entry = new ConnectionEntry; + entry->fConnection = connection; + entry->fPrev = NULL; + + MutexLocker locker(fConnectionLock); + entry->fNext = fConnectionList; + if (fConnectionList != NULL) + fConnectionList->fPrev = entry; + fConnectionList = entry; + locker.Unlock(); + + void** arguments = reinterpret_cast(malloc(sizeof(void*) * 2)); + if (arguments == NULL) + return B_NO_MEMORY; + + arguments[0] = this; + arguments[1] = entry; + + thread_id thread; + thread = spawn_kernel_thread(&CallbackServer::ConnectionThreadLauncher, + "NFSv4 Callback Connection", B_NORMAL_PRIORITY, arguments); + if (thread < B_OK) { + ReleaseConnection(entry); + free(arguments); + return thread; + } + + entry->fThread = thread; + + status_t result = resume_thread(thread); + if (result != B_OK) { + kill_thread(thread); + ReleaseConnection(entry); + free(arguments); + return result; + } + + return B_OK; +} + + +status_t +CallbackServer::ReleaseConnection(ConnectionEntry* entry) +{ + ASSERT(entry != NULL); + + MutexLocker _(fConnectionLock); + if (entry->fNext != NULL) + entry->fNext->fPrev = entry->fPrev; + if (entry->fPrev != NULL) + entry->fPrev->fNext = entry->fNext; + else + fConnectionList = entry->fNext; + + delete entry->fConnection; + delete entry; + return B_OK; +} + + +status_t +CallbackServer::ConnectionThreadLauncher(void* object) +{ + ASSERT(object != NULL); + + void** objects = reinterpret_cast(object); + CallbackServer* server = reinterpret_cast(objects[0]); + ConnectionEntry* entry = reinterpret_cast(objects[1]); + free(objects); + + return server->ConnectionThread(entry); +} + + +status_t +CallbackServer::ConnectionThread(ConnectionEntry* entry) +{ + ASSERT(entry != NULL); + + Connection* connection = entry->fConnection; + CallbackReply* reply; + + while (fThreadRunning) { + uint32 size; + void* buffer; + status_t result = connection->Receive(&buffer, &size); + if (result != B_OK) { + if (result != ECONNABORTED) + ReleaseConnection(entry); + return result; + } + + CallbackRequest* request = new CallbackRequest(buffer, size); + if (request == NULL || request->Error() != B_OK) { + free(buffer); + continue; + } else if (request != NULL && request->Error() != B_OK) { + reply = CallbackReply::Create(request->XID(), request->RPCError()); + if (reply != NULL) { + connection->Send(reply->Stream().Buffer(), + reply->Stream().Size()); + delete reply; + } + free(buffer); + continue; + } + + switch (request->Procedure()) { + case CallbackProcCompound: + GetCallback(request->ID())->EnqueueRequest(request, connection); + break; + + case CallbackProcNull: + reply = CallbackReply::Create(request->XID()); + if (reply != NULL) { + connection->Send(reply->Stream().Buffer(), + reply->Stream().Size()); + delete reply; + } + + default: + free(buffer); + } + } + + return B_OK; +} + + +status_t +CallbackServer::ListenerThreadLauncher(void* object) +{ + ASSERT(object != NULL); + + CallbackServer* server = reinterpret_cast(object); + return server->ListenerThread(); +} + + +status_t +CallbackServer::ListenerThread() +{ + while (fThreadRunning) { + Connection* connection; + + status_t result = fListener->AcceptConnection(&connection); + if (result != B_OK) { + fThreadRunning = false; + return result; + } + result = NewConnection(connection); + if (result != B_OK) + delete connection; + } + + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.h b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.h new file mode 100644 index 0000000000..fce7354b02 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCCallbackServer.h @@ -0,0 +1,108 @@ +/* + * Copyright 2012-2013 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef RPCCALLBACKSERVER_H +#define RPCCALLBACKSERVER_H + + +#include + +#include "Connection.h" + + +namespace RPC { + +class Callback; +class Server; + +struct ConnectionEntry { + Connection* fConnection; + thread_id fThread; + + ConnectionEntry* fNext; + ConnectionEntry* fPrev; +}; + +union CallbackSlot { + Callback* fCallback; + int32 fNext; +}; + +class CallbackServer { +public: + CallbackServer(int networkFamily); + ~CallbackServer(); + + static CallbackServer* Get(Server* server); + static void ShutdownAll(); + + status_t RegisterCallback(Callback* callback); + status_t UnregisterCallback(Callback* callback); + + inline PeerAddress LocalID(); + +protected: + status_t StartServer(); + status_t StopServer(); + + status_t NewConnection(Connection* connection); + status_t ReleaseConnection(ConnectionEntry* entry); + + static status_t ListenerThreadLauncher(void* object); + status_t ListenerThread(); + + static status_t ConnectionThreadLauncher(void* object); + status_t ConnectionThread(ConnectionEntry* entry); + + inline Callback* GetCallback(int32 id); + +private: + static mutex fServerCreationLock; + static CallbackServer* fServers[2]; + + mutex fConnectionLock; + ConnectionEntry* fConnectionList; + ConnectionListener* fListener; + + mutex fThreadLock; + thread_id fThread; + bool fThreadRunning; + + rw_lock fArrayLock; + CallbackSlot* fCallbackArray; + uint32 fArraySize; + int32 fFreeSlot; + + int fNetworkFamily; +}; + + +inline PeerAddress +CallbackServer::LocalID() +{ + PeerAddress address; + + ASSERT(fListener != NULL); + fListener->GetLocalAddress(&address); + return address; +} + + +inline Callback* +CallbackServer::GetCallback(int32 id) +{ + ReadLocker _(fArrayLock); + if (id >= 0 && static_cast(id) < fArraySize) + return fCallbackArray[id].fCallback; + return NULL; +} + + +} // namespace RPC + +#endif // RPCCALLBACKSERVER_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCDefs.h b/src/add-ons/kernel/file_systems/nfs4/RPCDefs.h new file mode 100644 index 0000000000..3363eaeb66 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCDefs.h @@ -0,0 +1,61 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef RPCDEFS_H +#define RPCDEFS_H + + +namespace RPC { + +enum { + VERSION = 2 +}; + +enum { + PROGRAM_NFS = 100003, + PROGRAM_NFS_CB = 0x40000000 +}; + +enum { + NFS_VERSION = 4, + NFS_CB_VERSION = 1 +}; + +enum { + CALL = 0, + REPLY = 1 +}; + +enum { + MSG_ACCEPTED = 0, + MSG_DENIED = 1 +}; + +enum AcceptStat { + SUCCESS = 0, /* RPC executed successfully */ + PROG_UNAVAIL = 1, /* remote hasn't exported program */ + PROG_MISMATCH = 2, /* remote can't support version # */ + PROC_UNAVAIL = 3, /* program can't support procedure */ + GARBAGE_ARGS = 4, /* procedure can't decode params */ + SYSTEM_ERR = 5 /* e.g. memory allocation failure */ +}; + +enum RejectStat { + RPC_MISMATCH = 0, /* RPC version number != 2 */ + AUTH_ERROR = 1 /* remote can't authenticate caller */ +}; + +enum AuthFlavour { + AUTH_NONE = 0, + AUTH_SYS = 1 +}; + +} // namespace RPC + + +#endif // RPCDEFS_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCReply.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCReply.cpp new file mode 100644 index 0000000000..bca2eaa5bf --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCReply.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RPCReply.h" + +#include +#include + +#include "RPCDefs.h" + + +using namespace RPC; + + +Reply::Reply(void* buffer, int size) + : + fError(B_OK), + fStream(buffer, size), + fBuffer(buffer) +{ + ASSERT(buffer != NULL); + + fXID = fStream.GetUInt(); + if (fStream.GetInt() != REPLY) { + fError = B_BAD_VALUE; + return; + } + + if (fStream.GetInt() == MSG_ACCEPTED) { + fStream.GetInt(); + fStream.GetOpaque(NULL); + + switch (fStream.GetInt()) { + case SUCCESS: + return; + case PROG_UNAVAIL: + case PROG_MISMATCH: + case PROC_UNAVAIL: + fError = B_DEVICE_NOT_FOUND; + return; + case GARBAGE_ARGS: + fError = B_MISMATCHED_VALUES; + return; + case SYSTEM_ERR: + fError = B_ERROR; + return; + default: + fError = B_BAD_VALUE; + return; + } + } else { // MSG_DENIED + if (fStream.GetInt() == RPC_MISMATCH) { + fError = B_DEVICE_NOT_FOUND; + return; + } else { // AUTH_ERROR + fError = B_PERMISSION_DENIED; + return; + } + } +} + + +Reply::~Reply() +{ + free(fBuffer); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCReply.h b/src/add-ons/kernel/file_systems/nfs4/RPCReply.h new file mode 100644 index 0000000000..50a200b3b8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCReply.h @@ -0,0 +1,60 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef RPCREPLY_H +#define RPCREPLY_H + + +#include "XDR.h" + + +namespace RPC { + +class Reply { +public: + Reply(void* buffer, int size); + ~Reply(); + + inline uint32 GetXID(); + + inline status_t Error(); + inline XDR::ReadStream& Stream(); + +private: + uint32 fXID; + + status_t fError; + + XDR::ReadStream fStream; + void* fBuffer; +}; + +inline uint32 +Reply::GetXID() +{ + return fXID; +} + + +inline status_t +Reply::Error() +{ + return fError; +} + + +inline XDR::ReadStream& +Reply::Stream() +{ + return fStream; +} + +} // namespace RPC + + +#endif // RPCREPLY_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp b/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp new file mode 100644 index 0000000000..811d3d4a5a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCServer.cpp @@ -0,0 +1,529 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RPCServer.h" + +#include + +#include + +#include "RPCCallbackServer.h" +#include "RPCReply.h" + + +using namespace RPC; + + +RequestManager::RequestManager() + : + fQueueHead(NULL), + fQueueTail(NULL) +{ + mutex_init(&fLock, NULL); +} + + +RequestManager::~RequestManager() +{ + mutex_destroy(&fLock); +} + + +void +RequestManager::AddRequest(Request* request) +{ + ASSERT(request != NULL); + + MutexLocker _(fLock); + if (fQueueTail != NULL) + fQueueTail->fNext = request; + else + fQueueHead = request; + fQueueTail = request; + request->fNext = NULL; +} + + +Request* +RequestManager::FindRequest(uint32 xid) +{ + MutexLocker _(fLock); + Request* req = fQueueHead; + Request* prev = NULL; + while (req != NULL) { + if (req->fXID == xid) { + if (prev != NULL) + prev->fNext = req->fNext; + if (fQueueTail == req) + fQueueTail = prev; + if (fQueueHead == req) + fQueueHead = req->fNext; + + return req; + } + + prev = req; + req = req->fNext; + } + + return NULL; +} + + +Server::Server(Connection* connection, PeerAddress* address) + : + fConnection(connection), + fAddress(address), + fPrivateData(NULL), + fCallback(NULL), + fRepairCount(0), + fXID(rand() << 1) +{ + ASSERT(connection != NULL); + ASSERT(address != NULL); + + mutex_init(&fCallbackLock, NULL); + mutex_init(&fRepairLock, NULL); + + _StartListening(); +} + + +Server::~Server() +{ + if (fCallback != NULL) + fCallback->CBServer()->UnregisterCallback(fCallback); + delete fCallback; + mutex_destroy(&fCallbackLock); + mutex_destroy(&fRepairLock); + + delete fPrivateData; + + fThreadCancel = true; + fConnection->Disconnect(); + + status_t result; + wait_for_thread(fThread, &result); + + delete fConnection; +} + + +status_t +Server::_StartListening() +{ + fThreadCancel = false; + fThreadError = B_OK; + fThread = spawn_kernel_thread(&Server::_ListenerThreadStart, + "NFSv4 Listener", B_NORMAL_PRIORITY, this); + if (fThread < B_OK) + return fThread; + + status_t result = resume_thread(fThread); + if (result != B_OK) { + kill_thread(fThread); + return result; + } + + return B_OK; +} + + +status_t +Server::SendCallAsync(Call* call, Reply** reply, Request** request) +{ + ASSERT(call != NULL); + ASSERT(reply != NULL); + ASSERT(request != NULL); + + if (fThreadError != B_OK && Repair() != B_OK) + return fThreadError; + + Request* req = new(std::nothrow) Request; + if (req == NULL) + return B_NO_MEMORY; + + uint32 xid = _GetXID(); + call->SetXID(xid); + req->fXID = xid; + req->fReply = reply; + req->fEvent.Init(&req->fEvent, NULL); + req->fDone = false; + req->fError = B_OK; + req->fNext = NULL; + + fRequests.AddRequest(req); + + *request = req; + return ResendCallAsync(call, req); +} + + +status_t +Server::ResendCallAsync(Call* call, Request* request) +{ + ASSERT(call != NULL); + ASSERT(request != NULL); + + if (fThreadError != B_OK && Repair() != B_OK) { + fRequests.FindRequest(request->fXID); + delete request; + return fThreadError; + } + + XDR::WriteStream& stream = call->Stream(); + status_t result = fConnection->Send(stream.Buffer(), stream.Size()); + if (result != B_OK) { + fRequests.FindRequest(request->fXID); + delete request; + return result; + } + + return B_OK; +} + + +status_t +Server::WakeCall(Request* request) +{ + ASSERT(request != NULL); + + Request* req = fRequests.FindRequest(request->fXID); + if (req == NULL) + return B_OK; + + request->fError = B_FILE_ERROR; + *request->fReply = NULL; + request->fDone = true; + request->fEvent.NotifyAll(); + + return B_OK; +} + + +status_t +Server::Repair() +{ + uint32 thisRepair = fRepairCount; + + MutexLocker _(fRepairLock); + if (fRepairCount != thisRepair) + return B_OK; + + fThreadCancel = true; + + status_t result = fConnection->Reconnect(); + if (result != B_OK) + return result; + + wait_for_thread(fThread, &result); + result = _StartListening(); + + if (result == B_OK) + fRepairCount++; + + return result; +} + + +Callback* +Server::GetCallback() +{ + MutexLocker _(fCallbackLock); + + if (fCallback == NULL) { + fCallback = new(std::nothrow) Callback(this); + if (fCallback == NULL) + return NULL; + + CallbackServer* server = CallbackServer::Get(this); + if (server == NULL) { + delete fCallback; + return NULL; + } + + if (server->RegisterCallback(fCallback) != B_OK) { + delete fCallback; + return NULL; + } + } + + return fCallback; +} + + +uint32 +Server::_GetXID() +{ + return static_cast(atomic_add(&fXID, 1)); +} + + +status_t +Server::_Listener() +{ + status_t result; + uint32 size; + void* buffer = NULL; + + while (!fThreadCancel) { + result = fConnection->Receive(&buffer, &size); + if (result == B_NO_MEMORY) + continue; + else if (result != B_OK) { + fThreadError = result; + return result; + } + + ASSERT(buffer != NULL && size > 0); + Reply* reply = new(std::nothrow) Reply(buffer, size); + if (reply == NULL) { + free(buffer); + continue; + } + + Request* req = fRequests.FindRequest(reply->GetXID()); + if (req != NULL) { + *req->fReply = reply; + req->fDone = true; + req->fEvent.NotifyAll(); + } else + delete reply; + } + + return B_OK; +} + + +status_t +Server::_ListenerThreadStart(void* object) +{ + ASSERT(object != NULL); + + Server* server = reinterpret_cast(object); + return server->_Listener(); +} + + +ServerManager::ServerManager() + : + fRoot(NULL) +{ + mutex_init(&fLock, NULL); +} + + +ServerManager::~ServerManager() +{ + mutex_destroy(&fLock); +} + + +status_t +ServerManager::Acquire(Server** _server, AddressResolver* resolver, + ProgramData* (*createPrivateData)(Server*)) +{ + PeerAddress address; + status_t result; + + while ((result = resolver->GetNextAddress(&address)) == B_OK) { + result = _Acquire(_server, address, createPrivateData); + if (result == B_OK) + break; + } + + return result; +} + + +status_t +ServerManager::_Acquire(Server** _server, const PeerAddress& address, + ProgramData* (*createPrivateData)(Server*)) +{ + ASSERT(_server != NULL); + ASSERT(createPrivateData != NULL); + + status_t result; + + MutexLocker locker(fLock); + ServerNode* node = _Find(address); + if (node != NULL) { + node->fRefCount++; + *_server = node->fServer; + + return B_OK; + } + + node = new(std::nothrow) ServerNode; + if (node == NULL) + return B_NO_MEMORY; + + node->fID = address; + + Connection* conn; + result = Connection::Connect(&conn, address); + if (result != B_OK) { + delete node; + return result; + } + + node->fServer = new Server(conn, &node->fID); + if (node->fServer == NULL) { + delete node; + delete conn; + return B_NO_MEMORY; + } + node->fServer->SetPrivateData(createPrivateData(node->fServer)); + + node->fRefCount = 1; + node->fLeft = node->fRight = NULL; + + ServerNode* nd = _Insert(node); + if (nd != node) { + nd->fRefCount++; + + delete node->fServer; + delete node; + *_server = nd->fServer; + return B_OK; + } + + *_server = node->fServer; + return B_OK; +} + + +void +ServerManager::Release(Server* server) +{ + ASSERT(server != NULL); + + MutexLocker _(fLock); + ServerNode* node = _Find(server->ID()); + if (node != NULL) { + node->fRefCount--; + + if (node->fRefCount == 0) { + _Delete(node); + delete node->fServer; + delete node; + } + } +} + + +ServerNode* +ServerManager::_Find(const PeerAddress& address) +{ + ServerNode* node = fRoot; + while (node != NULL) { + if (node->fID == address) + return node; + if (node->fID < address) + node = node->fRight; + else + node = node->fLeft; + } + + return node; +} + + +void +ServerManager::_Delete(ServerNode* node) +{ + ASSERT(node != NULL); + + bool found = false; + ServerNode* previous = NULL; + ServerNode* current = fRoot; + while (current != NULL) { + if (current->fID == node->fID) { + found = true; + break; + } + + if (current->fID < node->fID) { + previous = current; + current = current->fRight; + } else { + previous = current; + current = current->fLeft; + } + } + + if (!found) + return; + + if (previous == NULL) + fRoot = NULL; + else if (current->fLeft == NULL && current->fRight == NULL) { + if (previous->fID < node->fID) + previous->fRight = NULL; + else + previous->fLeft = NULL; + } else if (current->fLeft != NULL && current->fRight == NULL) { + if (previous->fID < node->fID) + previous->fRight = current->fLeft; + else + previous->fLeft = current->fLeft; + } else if (current->fLeft == NULL && current->fRight != NULL) { + if (previous->fID < node->fID) + previous->fRight = current->fRight; + else + previous->fLeft = current->fRight; + } else { + ServerNode* left_prev = current; + ServerNode* left = current->fLeft; + + while (left->fLeft != NULL) { + left_prev = left; + left = left->fLeft; + } + + if (previous->fID < node->fID) + previous->fRight = left; + else + previous->fLeft = left; + + + left_prev->fLeft = NULL; + } +} + + +ServerNode* +ServerManager::_Insert(ServerNode* node) +{ + ASSERT(node != NULL); + + ServerNode* previous = NULL; + ServerNode* current = fRoot; + while (current != NULL) { + if (current->fID == node->fID) + return current; + if (current->fID < node->fID) { + previous = current; + current = current->fRight; + } else { + previous = current; + current = current->fLeft; + } + } + + if (previous == NULL) + fRoot = node; + else if (previous->fID < node->fID) + previous->fRight = node; + else + previous->fLeft = node; + + return node; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RPCServer.h b/src/add-ons/kernel/file_systems/nfs4/RPCServer.h new file mode 100644 index 0000000000..35c52f8bc1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RPCServer.h @@ -0,0 +1,197 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef RPCSERVER_H +#define RPCSERVER_H + + +#include +#include + +#include "Connection.h" +#include "RPCCall.h" +#include "RPCCallback.h" +#include "RPCReply.h" + + +namespace RPC { + +struct Request { + uint32 fXID; + ConditionVariable fEvent; + + bool fDone; + Reply** fReply; + status_t fError; + + Request* fNext; +}; + +class RequestManager { +public: + RequestManager(); + ~RequestManager(); + + void AddRequest(Request* request); + Request* FindRequest(uint32 xid); + +private: + mutex fLock; + + // Neither SinglyLinkedList nor DoublyLinkedList is what we want + // here. And DoublyLinkedQueue is not even a queue. + Request* fQueueHead; + Request* fQueueTail; + +}; + +class ProgramData { +public: + virtual status_t ProcessCallback(CallbackRequest* request, + Connection* connection) = 0; + + virtual ~ProgramData() { } +}; + +class Server { +public: + Server(Connection* connection, + PeerAddress* address); + virtual ~Server(); + + status_t SendCallAsync(Call* call, Reply** reply, + Request** request); + status_t ResendCallAsync(Call* call, + Request* request); + + inline status_t WaitCall(Request* request, + bigtime_t time); + inline status_t CancelCall(Request* request); + status_t WakeCall(Request* request); + + status_t Repair(); + + inline const PeerAddress& ID() const; + inline PeerAddress LocalID() const; + + inline ProgramData* PrivateData(); + inline void SetPrivateData(ProgramData* privateData); + + Callback* GetCallback(); + +private: + inline uint32 _GetXID(); + + status_t _StartListening(); + + status_t _Listener(); + static status_t _ListenerThreadStart(void* object); + + thread_id fThread; + bool fThreadCancel; + status_t fThreadError; + + RequestManager fRequests; + Connection* fConnection; + const PeerAddress* fAddress; + + ProgramData* fPrivateData; + + mutex fCallbackLock; + Callback* fCallback; + + uint32 fRepairCount; + mutex fRepairLock; + + vint32 fXID; +}; + + +inline status_t +Server::WaitCall(Request* request, bigtime_t time) +{ + if (request->fDone) + return B_OK; + return request->fEvent.Wait(B_RELATIVE_TIMEOUT, time); +} + + +inline status_t +Server::CancelCall(Request* request) +{ + fRequests.FindRequest(request->fXID); + return B_OK; +} + + +inline const PeerAddress& +Server::ID() const +{ + return *fAddress; +} + + +inline PeerAddress +Server::LocalID() const +{ + PeerAddress addr; + memset(&addr, 0, sizeof(addr)); + fConnection->GetLocalAddress(&addr); + return addr; +} + + +inline ProgramData* +Server::PrivateData() +{ + return fPrivateData; +} + + +inline void +Server::SetPrivateData(ProgramData* privateData) +{ + delete fPrivateData; + fPrivateData = privateData; +} + + +struct ServerNode { + PeerAddress fID; + Server* fServer; + int fRefCount; + + ServerNode* fLeft; + ServerNode* fRight; +}; + +class ServerManager { +public: + ServerManager(); + ~ServerManager(); + + status_t Acquire(Server** _server, AddressResolver* resolver, + ProgramData* (*createPrivateData)(Server*)); + void Release(Server* server); + +private: + status_t _Acquire(Server** _server, const PeerAddress& address, + ProgramData* (*createPrivateData)(Server*)); + + ServerNode* _Find(const PeerAddress& address); + void _Delete(ServerNode* node); + ServerNode* _Insert(ServerNode* node); + + ServerNode* fRoot; + mutex fLock; +}; + +} // namespace RPC + + +#endif // RPCSERVER_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.cpp b/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.cpp new file mode 100644 index 0000000000..152d5de25b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "ReplyBuilder.h" + +#include "NFS4Defs.h" +#include "RPCCallbackReply.h" + + +ReplyBuilder::ReplyBuilder(uint32 xid) + : + fStatus(B_OK), + fOpCount(0), + fReply(RPC::CallbackReply::Create(xid)) +{ + _InitHeader(); +} + + +ReplyBuilder::~ReplyBuilder() +{ + delete fReply; +} + + +void +ReplyBuilder::_InitHeader() +{ + fStatusPosition = fReply->Stream().Current(); + fReply->Stream().AddUInt(0); + + fReply->Stream().AddOpaque(NULL, 0); + + fOpCountPosition = fReply->Stream().Current(); + fReply->Stream().AddUInt(0); + +} + + +RPC::CallbackReply* +ReplyBuilder::Reply() +{ + fReply->Stream().InsertUInt(fStatusPosition, _HaikuErrorToNFS4(fStatus)); + fReply->Stream().InsertUInt(fOpCountPosition, fOpCount); + + if (fReply == NULL || fReply->Stream().Error() == B_OK) + return fReply; + else + return NULL; +} + + +status_t +ReplyBuilder::GetAttr(status_t status, int mask, uint64 size, uint64 change) +{ + if (fStatus != B_OK) + return B_ERROR; + + fReply->Stream().AddUInt(OpCallbackGetAttr); + fReply->Stream().AddUInt(_HaikuErrorToNFS4(fStatus)); + fStatus = status; + + if (status == B_OK) { + uint32 bitmap = 0; + if ((mask & CallbackAttrChange) != 0) + bitmap |= 1 << FATTR4_CHANGE; + if ((mask & CallbackAttrSize) != 0) + bitmap |= 1 << FATTR4_SIZE; + fReply->Stream().AddUInt(1); + fReply->Stream().AddUInt(bitmap); + + XDR::WriteStream str; + if ((mask & CallbackAttrChange) != 0) + str.AddUHyper(change); + + if ((mask & CallbackAttrSize) != 0) + str.AddUHyper(size); + fReply->Stream().AddOpaque(str); + } + + fOpCount++; + + return B_OK; +} + + +status_t +ReplyBuilder::Recall(status_t status) +{ + if (fStatus != B_OK) + return B_ERROR; + + fReply->Stream().AddUInt(OpCallbackRecall); + fReply->Stream().AddUInt(_HaikuErrorToNFS4(fStatus)); + fStatus = status; + + fOpCount++; + + return B_OK; +} + + +uint32 +ReplyBuilder::_HaikuErrorToNFS4(status_t error) +{ + switch (error) { + case B_OK: return NFS4_OK; + case B_FILE_NOT_FOUND: return NFS4ERR_BADHANDLE; + case B_NOT_SUPPORTED: return NFS4ERR_OP_ILLEGAL; + default: return NFS4ERR_RESOURCE; + } +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.h b/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.h new file mode 100644 index 0000000000..2a12a0d522 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyBuilder.h @@ -0,0 +1,45 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef REPLYBUILDER_H +#define REPLYBUILDER_H + + +#include + +#include "RPCCallbackReply.h" +#include "XDR.h" + + +class ReplyBuilder { +public: + ReplyBuilder(uint32 xid); + ~ReplyBuilder(); + + RPC::CallbackReply* Reply(); + + status_t GetAttr(status_t status, int mask, + uint64 size, uint64 change); + status_t Recall(status_t status); + +private: + void _InitHeader(); + + static uint32 _HaikuErrorToNFS4(status_t error); + + status_t fStatus; + XDR::Stream::Position fStatusPosition; + + uint32 fOpCount; + XDR::Stream::Position fOpCountPosition; + + RPC::CallbackReply* fReply; +}; + + +#endif // REPLYBUILDER_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp new file mode 100644 index 0000000000..271cabb490 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp @@ -0,0 +1,845 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "ReplyInterpreter.h" + +#include + +#include + +#include "Cookie.h" + + +FSLocation::~FSLocation() +{ + free(const_cast(fRootPath)); + for (uint32 i = 0; i < fCount; i++) + free(const_cast(fLocations[i])); + delete[] fLocations; +} + + +FSLocations::~FSLocations() +{ + free(const_cast(fRootPath)); + delete[] fLocations; +} + + +AttrValue::AttrValue() + : + fAttribute(0), + fFreePointer(false) +{ +} + + +AttrValue::~AttrValue() +{ + if (fFreePointer) + free(fData.fPointer); + if (fAttribute == FATTR4_FS_LOCATIONS) + delete fData.fLocations; +} + + +DirEntry::DirEntry() + : + fName(NULL), + fAttrs(NULL), + fAttrCount(0) +{ +} + + +DirEntry::~DirEntry() +{ + free(const_cast(fName)); + delete[] fAttrs; +} + + +ReplyInterpreter::ReplyInterpreter(RPC::Reply* reply) + : + fNFS4Error(NFS4_OK), + fDecodeError(false), + fReply(reply) +{ + if (reply != NULL) + _ParseHeader(); +} + + +ReplyInterpreter::~ReplyInterpreter() +{ + delete fReply; +} + + +void +ReplyInterpreter::_ParseHeader() +{ + fNFS4Error = fReply->Stream().GetUInt(); + fReply->Stream().GetOpaque(NULL); + fReply->Stream().GetUInt(); +} + + +status_t +ReplyInterpreter::Access(uint32* supported, uint32* allowed) +{ + status_t res = _OperationError(OpAccess); + if (res != B_OK) + return res; + + uint32 support = fReply->Stream().GetUInt(); + uint32 allow = fReply->Stream().GetUInt(); + + if (supported != NULL) + *supported = support; + if (allowed != NULL) + *allowed = allow; + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Close() +{ + status_t res = _OperationError(OpClose); + if (res != B_OK) + return res; + + fReply->Stream().GetUInt(); + fReply->Stream().GetUInt(); + fReply->Stream().GetUInt(); + fReply->Stream().GetUInt(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Commit() +{ + status_t res = _OperationError(OpCommit); + if (res != B_OK) + return res; + + fReply->Stream().GetOpaque(NULL); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Create(uint64* before, uint64* after, bool& atomic) +{ + status_t res = _OperationError(OpCreate); + if (res != B_OK) + return res; + + atomic = fReply->Stream().GetBoolean(); + *before = fReply->Stream().GetUHyper(); + *after = fReply->Stream().GetUHyper(); + + uint32 count = fReply->Stream().GetUInt(); + for (uint32 i = 0; i < count; i++) + fReply->Stream().GetUInt(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +// Bit Twiddling Hacks +// http://graphics.stanford.edu/~seander/bithacks.html +static inline uint32 CountBits(uint32 v) +{ + v = v - ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; +} + + +status_t +ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count) +{ + status_t res = _OperationError(OpGetAttr); + if (res != B_OK) + return res; + + return _DecodeAttrs(fReply->Stream(), attrs, count); +} + + +status_t +ReplyInterpreter::GetFH(FileHandle* fh) +{ + status_t res = _OperationError(OpGetFH); + if (res != B_OK) + return res; + + uint32 size; + const void* ptr = fReply->Stream().GetOpaque(&size); + if (ptr == NULL || size > NFS4_FHSIZE) + return B_BAD_VALUE; + + if (fh != NULL) { + fh->fSize = size; + memcpy(fh->fData, ptr, size); + } + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Link(uint64* before, uint64* after, bool& atomic) +{ + status_t res = _OperationError(OpLink); + if (res != B_OK) + return res; + + atomic = fReply->Stream().GetBoolean(); + *before = fReply->Stream().GetUHyper(); + *after = fReply->Stream().GetUHyper(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Lock(LockInfo* linfo) +{ + status_t res = _OperationError(OpLock); + if (res != B_OK) + return res; + + linfo->fOwner->fStateSeq = fReply->Stream().GetUInt(); + linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt(); + linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt(); + linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::LockT(uint64* pos, uint64* len, LockType* type) +{ + status_t res = _OperationError(OpLockU); + if (res != B_WOULD_BLOCK || NFS4Error() != NFS4ERR_DENIED) + return res; + + *pos = fReply->Stream().GetUHyper(); + *len = fReply->Stream().GetUHyper(); + *type = static_cast(fReply->Stream().GetInt()); + + fReply->Stream().GetUHyper(); + fReply->Stream().GetOpaque(NULL); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::LockU(LockInfo* linfo) +{ + status_t res = _OperationError(OpLockU); + if (res != B_OK) + return res; + + linfo->fOwner->fStateSeq = fReply->Stream().GetUInt(); + linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt(); + linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt(); + linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Open(uint32* id, uint32* seq, bool* confirm, + OpenDelegationData* delegData, ChangeInfo* changeInfo) +{ + status_t res = _OperationError(OpOpen); + if (res != B_OK) + return res; + + *seq = fReply->Stream().GetUInt(); + id[0] = fReply->Stream().GetUInt(); + id[1] = fReply->Stream().GetUInt(); + id[2] = fReply->Stream().GetUInt(); + + // change info + bool atomic = fReply->Stream().GetBoolean(); + uint64 before = fReply->Stream().GetUHyper(); + uint64 after = fReply->Stream().GetUHyper(); + if (changeInfo != NULL) { + changeInfo->fAtomic = atomic; + changeInfo->fBefore = before; + changeInfo->fAfter = after; + } + + uint32 flags = fReply->Stream().GetUInt(); + *confirm = (flags & OPEN4_RESULT_CONFIRM) == OPEN4_RESULT_CONFIRM; + + // attrmask + uint32 bcount = fReply->Stream().GetUInt(); + for (uint32 i = 0; i < bcount; i++) + fReply->Stream().GetUInt(); + + // delegation info + uint32 delegation = fReply->Stream().GetUInt(); + OpenDelegationData data; + if (delegData == NULL) + delegData = &data; + + if (delegation == OPEN_DELEGATE_NONE) { + delegData->fType = OPEN_DELEGATE_NONE; + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; + } + + delegData->fStateSeq = fReply->Stream().GetUInt(); + delegData->fStateID[0] = fReply->Stream().GetUInt(); + delegData->fStateID[1] = fReply->Stream().GetUInt(); + delegData->fStateID[2] = fReply->Stream().GetUInt(); + + delegData->fRecall = fReply->Stream().GetBoolean(); + + switch (delegation) { + case OPEN_DELEGATE_READ: + delegData->fType = OPEN_DELEGATE_READ; + break; + case OPEN_DELEGATE_WRITE: + delegData->fType = OPEN_DELEGATE_WRITE; + + int32 limitBy = fReply->Stream().GetInt(); + if (limitBy == NFS_LIMIT_SIZE) + delegData->fSpaceLimit = fReply->Stream().GetUHyper(); + else if (limitBy == NFS_LIMIT_BLOCKS) { + uint32 numBlocks = fReply->Stream().GetUInt(); + delegData->fSpaceLimit = fReply->Stream().GetUInt() * numBlocks; + } + break; + } + + // ACE data + fReply->Stream().GetUInt(); + fReply->Stream().GetUInt(); + fReply->Stream().GetUInt(); + fReply->Stream().GetOpaque(NULL); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::OpenConfirm(uint32* stateSeq) +{ + status_t res = _OperationError(OpOpenConfirm); + if (res != B_OK) + return res; + + *stateSeq = fReply->Stream().GetUInt(); + fReply->Stream().GetUInt(); + fReply->Stream().GetUInt(); + fReply->Stream().GetUInt(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Read(void* buffer, uint32* size, bool* eof) +{ + status_t res = _OperationError(OpRead); + if (res != B_OK) + return res; + + *eof = fReply->Stream().GetBoolean(); + const void* ptr = fReply->Stream().GetOpaque(size); + memcpy(buffer, ptr, *size); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf, + DirEntry** dirents, uint32* _count, bool* eof) +{ + status_t res = _OperationError(OpReadDir); + if (res != B_OK) + return res; + + *cookieVerf = fReply->Stream().GetUHyper(); + + bool isNext; + uint32 count = 0; + + // TODO: using list instead of array would make this much more elegant + // and efficient + XDR::Stream::Position dataStart = fReply->Stream().Current(); + isNext = fReply->Stream().GetBoolean(); + while (isNext) { + fReply->Stream().GetUHyper(); + + free(fReply->Stream().GetString()); + AttrValue* values; + uint32 attrCount; + _DecodeAttrs(fReply->Stream(), &values, &attrCount); + delete[] values; + + count++; + + isNext = fReply->Stream().GetBoolean(); + } + + DirEntry* entries = new(std::nothrow) DirEntry[count]; + if (entries == NULL) + return B_NO_MEMORY; + + count = 0; + fReply->Stream().SetPosition(dataStart); + isNext = fReply->Stream().GetBoolean(); + while (isNext) { + *cookie = fReply->Stream().GetUHyper(); + + entries[count].fName = fReply->Stream().GetString(); + _DecodeAttrs(fReply->Stream(), &entries[count].fAttrs, + &entries[count].fAttrCount); + + count++; + + isNext = fReply->Stream().GetBoolean(); + } + *eof = fReply->Stream().GetBoolean(); + + *_count = count; + *dirents = entries; + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::ReadLink(void* buffer, uint32* size, uint32 maxSize) +{ + status_t res = _OperationError(OpReadLink); + if (res != B_OK) + return res; + + const void* ptr = fReply->Stream().GetOpaque(size); + memcpy(buffer, ptr, min_c(*size, maxSize)); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Remove(uint64* before, uint64* after, bool& atomic) +{ + status_t res = _OperationError(OpRemove); + if (res != B_OK) + return res; + + atomic = fReply->Stream().GetBoolean(); + *before = fReply->Stream().GetUHyper(); + *after = fReply->Stream().GetUHyper(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Rename(uint64* fromBefore, uint64* fromAfter, + bool& fromAtomic, uint64* toBefore, uint64* toAfter, bool& toAtomic) +{ + status_t res = _OperationError(OpRename); + if (res != B_OK) + return res; + + fromAtomic = fReply->Stream().GetBoolean(); + *fromBefore = fReply->Stream().GetUHyper(); + *fromAfter = fReply->Stream().GetUHyper(); + + toAtomic = fReply->Stream().GetBoolean(); + *toBefore = fReply->Stream().GetUHyper(); + *toAfter = fReply->Stream().GetUHyper(); + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::SetAttr() +{ + status_t res = _OperationError(OpSetAttr); + if (res != B_OK) + return res; + + uint32 bcount = fReply->Stream().GetUInt(); + for (uint32 i = 0; i < bcount; i++) + fReply->Stream().GetUInt(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier) +{ + status_t res = _OperationError(OpSetClientID); + if (res != B_OK) + return res; + + *clientid = fReply->Stream().GetUHyper(); + *verifier = fReply->Stream().GetUHyper(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::Write(uint32* size) +{ + status_t res = _OperationError(OpWrite); + if (res != B_OK) + return res; + + *size = fReply->Stream().GetUInt(); + fReply->Stream().GetInt(); + fReply->Stream().GetUHyper(); + + return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +static const char* +sFlattenPathname(XDR::ReadStream& str) +{ + uint32 count = str.GetUInt(); + char* pathname = NULL; + uint32 size = 0; + for (uint32 i = 0; i < count; i++) { + const char* path = str.GetString(); + size += strlen(path) + 1; + if (pathname == NULL) { + pathname = reinterpret_cast(malloc(strlen(path + 1))); + pathname[0] = '\0'; + } else { + *pathname++ = '/'; + pathname = reinterpret_cast(realloc(pathname, size)); + } + strcat(pathname, path); + free(const_cast(path)); + } + + return pathname; +} + + +status_t +ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs, + uint32* count) +{ + uint32 bcount = fReply->Stream().GetUInt(); + uint32* bitmap = new(std::nothrow) uint32[bcount]; + if (bitmap == NULL) + return B_NO_MEMORY; + + uint32 attr_count = 0; + for (uint32 i = 0; i < bcount; i++) { + bitmap[i] = str.GetUInt(); + attr_count += CountBits(bitmap[i]); + } + + if (attr_count == 0) { + *attrs = NULL; + *count = 0; + return B_OK; + } else if (attr_count > FATTR4_MAXIMUM_ATTR_ID) + return B_BAD_VALUE; + + uint32 size; + const void* ptr = str.GetOpaque(&size); + XDR::ReadStream stream(const_cast(ptr), size); + + AttrValue* values = new(std::nothrow) AttrValue[attr_count]; + if (values == NULL) { + delete[] bitmap; + return B_NO_MEMORY; + } + + uint32 current = 0; + + if (sIsAttrSet(FATTR4_SUPPORTED_ATTRS, bitmap, bcount)) { + values[current].fAttribute = FATTR4_SUPPORTED_ATTRS; + uint32 count = stream.GetInt(); + uint32 i; + // two uint32 are enough for NFS4, not for NFS4.1 + for (i = 0; i < min_c(count, 2); i++) + ((uint32*)&values[current].fData.fValue64)[i] = stream.GetUInt(); + for (; i < count; i++) + stream.GetUInt(); + current++; + } + + if (sIsAttrSet(FATTR4_TYPE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_TYPE; + values[current].fData.fValue32 = stream.GetInt(); + current++; + } + + if (sIsAttrSet(FATTR4_FH_EXPIRE_TYPE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_FH_EXPIRE_TYPE; + values[current].fData.fValue32 = stream.GetUInt(); + current++; + } + + if (sIsAttrSet(FATTR4_CHANGE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_CHANGE; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + + if (sIsAttrSet(FATTR4_SIZE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_SIZE; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + + if (sIsAttrSet(FATTR4_FSID, bitmap, bcount)) { + values[current].fAttribute = FATTR4_FSID; + values[current].fFreePointer = true; + + FileSystemId fsid; + fsid.fMajor = stream.GetUHyper(); + fsid.fMinor = stream.GetUHyper(); + + values[current].fData.fPointer = malloc(sizeof(fsid)); + memcpy(values[current].fData.fPointer, &fsid, sizeof(fsid)); + current++; + } + + if (sIsAttrSet(FATTR4_LEASE_TIME, bitmap, bcount)) { + values[current].fAttribute = FATTR4_LEASE_TIME; + values[current].fData.fValue32 = stream.GetUInt(); + current++; + } + + if (sIsAttrSet(FATTR4_FILEID, bitmap, bcount)) { + values[current].fAttribute = FATTR4_FILEID; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + + if (sIsAttrSet(FATTR4_FILES_FREE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_FILES_FREE; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + + if (sIsAttrSet(FATTR4_FILES_TOTAL, bitmap, bcount)) { + values[current].fAttribute = FATTR4_FILES_TOTAL; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + + if (sIsAttrSet(FATTR4_FS_LOCATIONS, bitmap, bcount)) { + values[current].fAttribute = FATTR4_FS_LOCATIONS; + + FSLocations* locs = new FSLocations; + locs->fRootPath = sFlattenPathname(stream); + locs->fCount = stream.GetUInt(); + locs->fLocations = new FSLocation[locs->fCount]; + for (uint32 i = 0; i < locs->fCount; i++) { + locs->fLocations[i].fRootPath = sFlattenPathname(stream); + locs->fLocations[i].fCount = stream.GetUInt(); + locs->fLocations[i].fLocations + = new const char*[locs->fLocations[i].fCount]; + for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) + locs->fLocations[i].fLocations[j] = stream.GetString(); + } + values[current].fData.fLocations = locs; + current++; + } + + if (sIsAttrSet(FATTR4_MAXREAD, bitmap, bcount)) { + values[current].fAttribute = FATTR4_MAXREAD; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + + if (sIsAttrSet(FATTR4_MAXWRITE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_MAXWRITE; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + + if (sIsAttrSet(FATTR4_MODE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_MODE; + values[current].fData.fValue32 = stream.GetUInt(); + current++; + } + + if (sIsAttrSet(FATTR4_NUMLINKS, bitmap, bcount)) { + values[current].fAttribute = FATTR4_NUMLINKS; + values[current].fData.fValue32 = stream.GetUInt(); + current++; + } + + if (sIsAttrSet(FATTR4_OWNER, bitmap, bcount)) { + values[current].fAttribute = FATTR4_OWNER; + values[current].fFreePointer = true; + values[current].fData.fPointer = stream.GetString(); + current++; + } + + if (sIsAttrSet(FATTR4_OWNER_GROUP, bitmap, bcount)) { + values[current].fAttribute = FATTR4_OWNER_GROUP; + values[current].fFreePointer = true; + values[current].fData.fPointer = stream.GetString(); + current++; + } + + if (sIsAttrSet(FATTR4_SPACE_FREE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_SPACE_FREE; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + + if (sIsAttrSet(FATTR4_SPACE_TOTAL, bitmap, bcount)) { + values[current].fAttribute = FATTR4_SPACE_TOTAL; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + + if (sIsAttrSet(FATTR4_TIME_ACCESS, bitmap, bcount)) { + values[current].fAttribute = FATTR4_TIME_ACCESS; + values[current].fFreePointer = true; + + struct timespec ts; + ts.tv_sec = static_cast(stream.GetHyper()); + ts.tv_nsec = static_cast(stream.GetUInt()); + + values[current].fData.fPointer = malloc(sizeof(ts)); + memcpy(values[current].fData.fPointer, &ts, sizeof(ts)); + current++; + } + + if (sIsAttrSet(FATTR4_TIME_CREATE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_TIME_CREATE; + values[current].fFreePointer = true; + + struct timespec ts; + ts.tv_sec = static_cast(stream.GetHyper()); + ts.tv_nsec = static_cast(stream.GetUInt()); + + values[current].fData.fPointer = malloc(sizeof(ts)); + memcpy(values[current].fData.fPointer, &ts, sizeof(ts)); + current++; + } + + if (sIsAttrSet(FATTR4_TIME_METADATA, bitmap, bcount)) { + values[current].fAttribute = FATTR4_TIME_METADATA; + values[current].fFreePointer = true; + + struct timespec ts; + ts.tv_sec = static_cast(stream.GetHyper()); + ts.tv_nsec = static_cast(stream.GetUInt()); + + values[current].fData.fPointer = malloc(sizeof(ts)); + memcpy(values[current].fData.fPointer, &ts, sizeof(ts)); + current++; + } + + if (sIsAttrSet(FATTR4_TIME_MODIFY, bitmap, bcount)) { + values[current].fAttribute = FATTR4_TIME_MODIFY; + values[current].fFreePointer = true; + + struct timespec ts; + ts.tv_sec = static_cast(stream.GetHyper()); + ts.tv_nsec = static_cast(stream.GetUInt()); + + values[current].fData.fPointer = malloc(sizeof(ts)); + memcpy(values[current].fData.fPointer, &ts, sizeof(ts)); + current++; + } + + delete[] bitmap; + + *count = attr_count; + *attrs = values; + return str.IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +ReplyInterpreter::_OperationError(Opcode op) +{ + if (fDecodeError) + return B_BAD_VALUE; + + if (fReply == NULL) + return B_NOT_INITIALIZED; + + if (fReply->Error() != B_OK || fReply->Stream().IsEOF()) { + fDecodeError = true; + return fReply->Error(); + } + + if (fReply->Stream().GetInt() != op) { + fDecodeError = true; + return B_BAD_VALUE; + } + + status_t result = _NFS4ErrorToHaiku(fReply->Stream().GetUInt()); + if (result != B_OK) + fDecodeError = true; + return result; +} + + +status_t +ReplyInterpreter::_NFS4ErrorToHaiku(uint32 x) +{ + switch (x) { + case NFS4_OK: return B_OK; + case NFS4ERR_PERM: return B_PERMISSION_DENIED; + case NFS4ERR_NOENT: return B_ENTRY_NOT_FOUND; + case NFS4ERR_IO: return B_IO_ERROR; + case NFS4ERR_NXIO: return B_DEVICE_NOT_FOUND; + case NFS4ERR_ACCESS: return B_NOT_ALLOWED; + case NFS4ERR_EXIST: return B_FILE_EXISTS; + case NFS4ERR_XDEV: return B_CROSS_DEVICE_LINK; + case NFS4ERR_NOTDIR: return B_NOT_A_DIRECTORY; + case NFS4ERR_ISDIR: return B_IS_A_DIRECTORY; + case NFS4ERR_INVAL: return B_BAD_VALUE; + case NFS4ERR_FBIG: return B_FILE_TOO_LARGE; + case NFS4ERR_NOTSUPP: return B_UNSUPPORTED; + case NFS4ERR_ROFS: return B_READ_ONLY_DEVICE; + case NFS4ERR_NAMETOOLONG: return B_NAME_TOO_LONG; + case NFS4ERR_NOTEMPTY: return B_DIRECTORY_NOT_EMPTY; + // ... + case NFS4ERR_DELAY: + case NFS4ERR_DENIED: + case NFS4ERR_LOCKED: + case NFS4ERR_GRACE: + return B_WOULD_BLOCK; + + case NFS4ERR_STALE: + case NFS4ERR_FHEXPIRED: + return B_FILE_NOT_FOUND; + // ... + default: return B_ERROR; + } +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h new file mode 100644 index 0000000000..b630b8ffb9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.h @@ -0,0 +1,240 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef REPLYINTERPRETER_H +#define REPLYINTERPRETER_H + + +#include + +#include "FileInfo.h" +#include "NFS4Defs.h" +#include "RPCReply.h" + + +struct FSLocation { + const char* fRootPath; + const char** fLocations; + uint32 fCount; + + ~FSLocation(); +}; + +struct FSLocations { + const char* fRootPath; + FSLocation* fLocations; + uint32 fCount; + + ~FSLocations(); +}; + +struct AttrValue { + AttrValue(); + ~AttrValue(); + + uint8 fAttribute; + bool fFreePointer; + union { + uint32 fValue32; + uint64 fValue64; + void* fPointer; + FSLocations* fLocations; + } fData; +}; + +struct DirEntry { + const char* fName; + AttrValue* fAttrs; + uint32 fAttrCount; + + DirEntry(); + ~DirEntry(); +}; + +class LockInfo; + +class ReplyInterpreter { +public: + ReplyInterpreter(RPC::Reply* reply = NULL); + ~ReplyInterpreter(); + + inline status_t SetTo(RPC::Reply* reply); + inline void Reset(); + + inline uint32 NFS4Error(); + + status_t Access(uint32* supported, uint32* allowed); + status_t Close(); + status_t Commit(); + status_t Create(uint64* before, uint64* after, bool& atomic); + inline status_t DelegReturn(); + status_t GetAttr(AttrValue** attrs, uint32* count); + status_t GetFH(FileHandle* fh); + status_t Link(uint64* before, uint64* after, bool& atomic); + status_t Lock(LockInfo* linfo); + status_t LockT(uint64* pos, uint64* len, LockType* type); + status_t LockU(LockInfo* linfo); + inline status_t LookUp(); + inline status_t LookUpUp(); + inline status_t Nverify(); + status_t Open(uint32* id, uint32* seq, bool* confirm, + OpenDelegationData* delegData, + ChangeInfo* changeInfo = NULL); + inline status_t OpenAttrDir(); + status_t OpenConfirm(uint32* stateSeq); + inline status_t PutFH(); + inline status_t PutRootFH(); + status_t Read(void* buffer, uint32* size, bool* eof); + status_t ReadDir(uint64* cookie, uint64* cookieVerf, + DirEntry** dirents, uint32* count, bool* eof); + status_t ReadLink(void* buffer, uint32* size, uint32 maxSize); + status_t Remove(uint64* before, uint64* after, bool& atomic); + status_t Rename(uint64* fromBefore, uint64* fromAfter, + bool& fromAtomic, uint64* toBefore, uint64* toAfter, + bool& toAtomic); + inline status_t Renew(); + inline status_t SaveFH(); + status_t SetAttr(); + status_t SetClientID(uint64* clientid, uint64* verifier); + inline status_t SetClientIDConfirm(); + inline status_t Verify(); + status_t Write(uint32* size); + inline status_t ReleaseLockOwner(); + +private: + void _ParseHeader(); + + status_t _DecodeAttrs(XDR::ReadStream& stream, AttrValue** attrs, + uint32* count); + status_t _OperationError(Opcode op); + + static status_t _NFS4ErrorToHaiku(uint32 x); + + uint32 fNFS4Error; + bool fDecodeError; + RPC::Reply* fReply; +}; + + +inline status_t +ReplyInterpreter::SetTo(RPC::Reply* _reply) +{ + if (fReply != NULL) + return B_DONT_DO_THAT; + + fDecodeError = false; + fReply = _reply; + + if (fReply != NULL) + _ParseHeader(); + + return B_OK; +} + + +inline void +ReplyInterpreter::Reset() +{ + delete fReply; + fReply = NULL; + fDecodeError = false; +} + + +inline uint32 +ReplyInterpreter::NFS4Error() +{ + return fNFS4Error; +} + + +inline status_t +ReplyInterpreter::DelegReturn() +{ + return _OperationError(OpDelegReturn); +} + + +inline status_t +ReplyInterpreter::LookUp() +{ + return _OperationError(OpLookUp); +} + + +inline status_t +ReplyInterpreter::LookUpUp() +{ + return _OperationError(OpLookUpUp); +} + + +inline status_t +ReplyInterpreter::OpenAttrDir() +{ + return _OperationError(OpOpenAttrDir); +} + + +inline status_t +ReplyInterpreter::Nverify() +{ + return _OperationError(OpNverify); +} + + +inline status_t +ReplyInterpreter::PutFH() +{ + return _OperationError(OpPutFH); +} + + +inline status_t +ReplyInterpreter::PutRootFH() +{ + return _OperationError(OpPutRootFH); +} + + +inline status_t +ReplyInterpreter::Renew() +{ + return _OperationError(OpRenew); +} + + +inline status_t +ReplyInterpreter::SaveFH() +{ + return _OperationError(OpSaveFH); +} + + +inline status_t +ReplyInterpreter::SetClientIDConfirm() +{ + return _OperationError(OpSetClientIDConfirm); +} + + +inline status_t +ReplyInterpreter::Verify() +{ + return _OperationError(OpVerify); +} + + +inline status_t +ReplyInterpreter::ReleaseLockOwner() +{ + return _OperationError(OpReleaseLockOwner); +} + + +#endif // REPLYINTERPRETER_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/Request.cpp b/src/add-ons/kernel/file_systems/nfs4/Request.cpp new file mode 100644 index 0000000000..b8e8f9663d --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Request.cpp @@ -0,0 +1,158 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "Request.h" + +#include "FileSystem.h" +#include "Inode.h" + + +status_t +Request::Send(Cookie* cookie) +{ + switch (fServer->ID().fProtocol) { + case IPPROTO_UDP: return _SendUDP(cookie); + case IPPROTO_TCP: return _SendTCP(cookie); + } + + return B_BAD_VALUE; +} + + +status_t +Request::_SendUDP(Cookie* cookie) +{ + RPC::Reply* rpl = NULL; + RPC::Request* rpc; + + status_t result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc); + if (result != B_OK) + return result; + + if (cookie != NULL) + cookie->RegisterRequest(rpc); + + int requestTimeout = sSecToBigTime(60); + int retryLimit = 0; + bool hard = true; + + if (fFileSystem != NULL) { + requestTimeout = fFileSystem->GetConfiguration().fRequestTimeout; + retryLimit = fFileSystem->GetConfiguration().fRetryLimit; + hard = fFileSystem->GetConfiguration().fHard; + } + + result = fServer->WaitCall(rpc, requestTimeout); + if (result != B_OK) { + int attempts = 1; + while (result != B_OK && (hard || attempts++ < retryLimit)) { + result = fServer->ResendCallAsync(fBuilder.Request(), rpc); + if (result != B_OK) { + if (cookie != NULL) + cookie->UnregisterRequest(rpc); + return result; + } + + result = fServer->WaitCall(rpc, requestTimeout); + } + + if (result != B_OK) { + if (cookie != NULL) + cookie->UnregisterRequest(rpc); + fServer->CancelCall(rpc); + delete rpc; + return result; + } + } + + if (cookie != NULL) + cookie->UnregisterRequest(rpc); + + if (rpc->fError != B_OK) { + delete rpl; + result = rpc->fError; + delete rpc; + return result; + } else { + fReply.SetTo(rpl); + delete rpc; + return B_OK; + } +} + + +status_t +Request::_SendTCP(Cookie* cookie) +{ + RPC::Reply* rpl = NULL; + RPC::Request* rpc; + + status_t result; + int attempts = 0; + + int requestTimeout = sSecToBigTime(60); + int retryLimit = 0; + bool hard = true; + + if (fFileSystem != NULL) { + requestTimeout = fFileSystem->GetConfiguration().fRequestTimeout; + retryLimit = fFileSystem->GetConfiguration().fRetryLimit; + hard = fFileSystem->GetConfiguration().fHard; + } + + do { + result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc); + if (result == B_NO_MEMORY) + return result; + else if (result != B_OK) { + fServer->Repair(); + continue; + } + + if (cookie != NULL) + cookie->RegisterRequest(rpc); + + result = fServer->WaitCall(rpc, requestTimeout); + if (result != B_OK) { + if (cookie != NULL) + cookie->UnregisterRequest(rpc); + + fServer->CancelCall(rpc); + delete rpc; + + fServer->Repair(); + } + } while (result != B_OK && (hard || attempts++ < retryLimit)); + + if (result != B_OK) + return result; + + if (cookie != NULL) + cookie->UnregisterRequest(rpc); + + if (rpc->fError != B_OK) { + delete rpl; + result = rpc->fError; + delete rpc; + return result; + } + + fReply.SetTo(rpl); + delete rpc; + return B_OK; +} + + +void +Request::Reset() +{ + fBuilder.Reset(); + fReply.Reset(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Request.h b/src/add-ons/kernel/file_systems/nfs4/Request.h new file mode 100644 index 0000000000..f740bb4619 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/Request.h @@ -0,0 +1,68 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef REQUEST_H +#define REQUEST_H + + +#include "ReplyInterpreter.h" +#include "RequestBuilder.h" +#include "RPCServer.h" + + +class Cookie; +class FileSystem; + +class Request { +public: + inline Request(RPC::Server* server, + FileSystem* fileSystem); + + inline RequestBuilder& Builder(); + inline ReplyInterpreter& Reply(); + + status_t Send(Cookie* cookie = NULL); + void Reset(); + +private: + status_t _SendUDP(Cookie* cookie); + status_t _SendTCP(Cookie* cookie); + + RPC::Server* fServer; + FileSystem* fFileSystem; + + RequestBuilder fBuilder; + ReplyInterpreter fReply; +}; + + +inline +Request::Request(RPC::Server* server, FileSystem* fileSystem) + : + fServer(server), + fFileSystem(fileSystem) +{ + ASSERT(server != NULL); +} + + +inline RequestBuilder& +Request::Builder() +{ + return fBuilder; +} + + +inline ReplyInterpreter& +Request::Reply() +{ + return fReply; +} + + +#endif // REQUEST_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp new file mode 100644 index 0000000000..83ff3b4cf8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.cpp @@ -0,0 +1,917 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RequestBuilder.h" + +#include +#include + +#include "Cookie.h" +#include "OpenState.h" +#include "RPCCallback.h" +#include "RPCCallbackServer.h" + + +RequestBuilder::RequestBuilder(Procedure proc) + : + fOpCount(0), + fProcedure(proc), + fRequest(NULL) +{ + _InitHeader(); +} + + +RequestBuilder::~RequestBuilder() +{ + delete fRequest; +} + + +void +RequestBuilder::_InitHeader() +{ + fRequest = RPC::Call::Create(fProcedure, RPC::Auth::CreateSys(), + RPC::Auth::CreateNone()); + + if (fRequest == NULL) + return; + + if (fProcedure == ProcCompound) { + fRequest->Stream().AddOpaque(NULL, 0); + fRequest->Stream().AddUInt(0); + + fOpCountPosition = fRequest->Stream().Current(); + fRequest->Stream().AddUInt(0); + } +} + + +status_t +RequestBuilder::Access() +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpAccess); + fRequest->Stream().AddUInt(ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY + | ACCESS4_EXTEND | ACCESS4_DELETE | ACCESS4_EXECUTE); + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Close(uint32 seq, const uint32* id, uint32 stateSeq) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpClose); + fRequest->Stream().AddUInt(seq); + fRequest->Stream().AddUInt(stateSeq); + fRequest->Stream().AddUInt(id[0]); + fRequest->Stream().AddUInt(id[1]); + fRequest->Stream().AddUInt(id[2]); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Commit(uint64 offset, uint32 count) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpCommit); + fRequest->Stream().AddUHyper(offset); + fRequest->Stream().AddUInt(count); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Create(FileType type, const char* name, AttrValue* attr, + uint32 count, const char* path) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + if (type == NF4LNK && path == NULL) + return B_BAD_VALUE; + if (name == NULL) + return B_BAD_VALUE; + if (type == NF4BLK || type == NF4CHR) + return B_BAD_VALUE; + + fRequest->Stream().AddUInt(OpCreate); + fRequest->Stream().AddUInt(type); + if (type == NF4LNK) + fRequest->Stream().AddString(path); + fRequest->Stream().AddString(name); + _EncodeAttrs(fRequest->Stream(), attr, count); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::DelegReturn(const uint32* id, uint32 seq) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpDelegReturn); + + fRequest->Stream().AddUInt(seq); + fRequest->Stream().AddUInt(id[0]); + fRequest->Stream().AddUInt(id[1]); + fRequest->Stream().AddUInt(id[2]); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::GetAttr(Attribute* attrs, uint32 count) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpGetAttr); + _AttrBitmap(fRequest->Stream(), attrs, count); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::GetFH() +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpGetFH); + fOpCount++; + + return B_OK; +} + + +void +RequestBuilder::_GenerateLockOwner(XDR::WriteStream& stream, + OpenState* state, LockOwner* owner) +{ + stream.AddUHyper(state->fClientID); + + uint64 lockOwner[2]; + lockOwner[0] = owner->fOwner; + lockOwner[1] = state->fInfo.fFileId; + stream.AddOpaque(lockOwner, sizeof(lockOwner)); +} + + +status_t +RequestBuilder::Lock(OpenState* state, LockInfo* lock, uint32* sequence, + bool reclaim) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpLock); + + fRequest->Stream().AddInt(lock->fType); + fRequest->Stream().AddBoolean(reclaim); + + fRequest->Stream().AddUHyper(lock->fStart); + fRequest->Stream().AddUHyper(lock->fLength); + + if (lock->fOwner->fStateId[0] == 0 && lock->fOwner->fStateId[1] == 0 + && lock->fOwner->fStateId[2] == 0) { + + fRequest->Stream().AddBoolean(true); // new lock owner + + // open seq stateid + fRequest->Stream().AddUInt(*sequence); + fRequest->Stream().AddUInt(state->fStateSeq); + fRequest->Stream().AddUInt(state->fStateID[0]); + fRequest->Stream().AddUInt(state->fStateID[1]); + fRequest->Stream().AddUInt(state->fStateID[2]); + + // lock seq owner + fRequest->Stream().AddUInt(lock->fOwner->fSequence++); + _GenerateLockOwner(fRequest->Stream(), state, lock->fOwner); + + } else { + fRequest->Stream().AddBoolean(false); // old lock owner + (*sequence)--; + + // lock stateid seq + fRequest->Stream().AddUInt(lock->fOwner->fStateSeq); + fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]); + fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]); + fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]); + + fRequest->Stream().AddUInt(lock->fOwner->fSequence++); + } + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::LockT(LockType type, uint64 pos, uint64 len, + OpenState* state) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpLockT); + + fRequest->Stream().AddInt(type); + + fRequest->Stream().AddUHyper(pos); + fRequest->Stream().AddUHyper(len); + + fRequest->Stream().AddUHyper(state->fClientID); + + uint32 owner = find_thread(NULL); + fRequest->Stream().AddOpaque(&owner, sizeof(owner)); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::LockU(LockInfo* lock) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpLockU); + + fRequest->Stream().AddInt(lock->fType); + + fRequest->Stream().AddUInt(lock->fOwner->fSequence++); + fRequest->Stream().AddUInt(lock->fOwner->fStateSeq); + fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]); + fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]); + fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]); + + fRequest->Stream().AddUHyper(lock->fStart); + fRequest->Stream().AddUHyper(lock->fLength); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Link(const char* name) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + if (name == NULL) + return B_BAD_VALUE; + + fRequest->Stream().AddUInt(OpLink); + fRequest->Stream().AddString(name); + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::LookUp(const char* name) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + if (name == NULL) + return B_BAD_VALUE; + + fRequest->Stream().AddUInt(OpLookUp); + fRequest->Stream().AddString(name, strlen(name)); + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::LookUpUp() +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpLookUpUp); + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Nverify(AttrValue* attr, uint32 count) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpNverify); + _EncodeAttrs(fRequest->Stream(), attr, count); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Open(OpenClaim claim, uint32 seq, uint32 access, uint64 id, + OpenCreate oc, uint64 ownerId, const char* name, AttrValue* attr, + uint32 count, bool excl, OpenDelegation delegationType) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpOpen); + fRequest->Stream().AddUInt(seq); + fRequest->Stream().AddUInt(access); + fRequest->Stream().AddUInt(0); // deny none + fRequest->Stream().AddUHyper(id); + + char owner[128]; + int pos = 0; + *(uint64*)(owner + pos) = ownerId; + pos += sizeof(uint64); + + fRequest->Stream().AddOpaque(owner, pos); + + fRequest->Stream().AddUInt(oc); + if (oc == OPEN4_CREATE) { + fRequest->Stream().AddInt(excl ? GUARDED4 : UNCHECKED4); + _EncodeAttrs(fRequest->Stream(), attr, count); + } + + fRequest->Stream().AddUInt(claim); + switch (claim) { + case CLAIM_NULL: + fRequest->Stream().AddString(name, strlen(name)); + break; + case CLAIM_PREVIOUS: + fRequest->Stream().AddUInt(delegationType); + break; + default: + return B_UNSUPPORTED; + } + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::OpenConfirm(uint32 seq, const uint32* id, uint32 stateSeq) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpOpenConfirm); + fRequest->Stream().AddUInt(stateSeq); + fRequest->Stream().AddUInt(id[0]); + fRequest->Stream().AddUInt(id[1]); + fRequest->Stream().AddUInt(id[2]); + fRequest->Stream().AddUInt(seq); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::OpenAttrDir(bool create) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpOpenAttrDir); + fRequest->Stream().AddBoolean(create); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::PutFH(const FileHandle& fh) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpPutFH); + fRequest->Stream().AddOpaque(fh.fData, fh.fSize); + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::PutRootFH() +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpPutRootFH); + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Read(const uint32* id, uint32 stateSeq, uint64 pos, uint32 len) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpRead); + fRequest->Stream().AddUInt(stateSeq); + fRequest->Stream().AddUInt(id[0]); + fRequest->Stream().AddUInt(id[1]); + fRequest->Stream().AddUInt(id[2]); + fRequest->Stream().AddUHyper(pos); + fRequest->Stream().AddUInt(len); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::ReadDir(uint32 count, uint64 cookie, uint64 cookieVerf, + Attribute* attrs, uint32 attrCount) +{ + (void)count; + + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpReadDir); + fRequest->Stream().AddUHyper(cookie); + fRequest->Stream().AddUHyper(cookieVerf); + + // consider predicting this values basing on count or buffer size + fRequest->Stream().AddUInt(0x2000); + fRequest->Stream().AddUInt(0x8000); + _AttrBitmap(fRequest->Stream(), attrs, attrCount); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::ReadLink() +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpReadLink); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Remove(const char* file) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpRemove); + fRequest->Stream().AddString(file); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Rename(const char* from, const char* to) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpRename); + fRequest->Stream().AddString(from); + fRequest->Stream().AddString(to); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Renew(uint64 clientId) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpRenew); + fRequest->Stream().AddUHyper(clientId); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::SaveFH() +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpSaveFH); + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::SetAttr(const uint32* id, uint32 stateSeq, AttrValue* attr, + uint32 count) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpSetAttr); + fRequest->Stream().AddUInt(stateSeq); + if (id != NULL) { + fRequest->Stream().AddUInt(id[0]); + fRequest->Stream().AddUInt(id[1]); + fRequest->Stream().AddUInt(id[2]); + } else { + fRequest->Stream().AddUInt(0); + fRequest->Stream().AddUInt(0); + fRequest->Stream().AddUInt(0); + } + _EncodeAttrs(fRequest->Stream(), attr, count); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::SetClientID(RPC::Server* server) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpSetClientID); + uint64 verifier = rand(); + verifier = verifier << 32 | rand(); + fRequest->Stream().AddUHyper(verifier); + + status_t result = _GenerateClientId(fRequest->Stream(), server); + if (result != B_OK) + return result; + + fRequest->Stream().AddUInt(0x40000000); + + if (server->GetCallback() != NULL) { + ASSERT(server->GetCallback()->CBServer() != NULL); + + uint32 id = server->GetCallback()->ID(); + + PeerAddress local = server->GetCallback()->CBServer()->LocalID(); + PeerAddress servAddr = server->LocalID(); + servAddr.SetPort(local.Port()); + + fRequest->Stream().AddString(local.ProtocolString()); + + char* uAddr = servAddr.UniversalAddress(); + if (uAddr == NULL) + return B_NO_MEMORY; + fRequest->Stream().AddString(uAddr); + free(uAddr); + + fRequest->Stream().AddUInt(id); + } else { + fRequest->Stream().AddString(""); + fRequest->Stream().AddString(""); + fRequest->Stream().AddUInt(0); + } + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::_GenerateClientId(XDR::WriteStream& stream, + const RPC::Server* server) +{ + char id[512] = "HAIKU:kernel:"; + int pos = strlen(id); + + PeerAddress local = server->LocalID(); + + memcpy(id + pos, server->ID().InAddr(), server->ID().InAddrSize()); + pos += sizeof(server->ID().InAddrSize()); + + memcpy(id + pos, local.InAddr(), local.InAddrSize()); + pos += sizeof(local.InAddrSize()); + + *(uint16*)(id + pos) = server->ID().Port(); + pos += sizeof(uint16); + + *(uint16*)(id + pos) = server->ID().fProtocol; + pos += sizeof(uint16); + + stream.AddOpaque(id, pos); + + return B_OK; +} + + +status_t +RequestBuilder::SetClientIDConfirm(uint64 id, uint64 ver) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpSetClientIDConfirm); + fRequest->Stream().AddUHyper(id); + fRequest->Stream().AddUHyper(ver); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Verify(AttrValue* attr, uint32 count) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpVerify); + _EncodeAttrs(fRequest->Stream(), attr, count); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::Write(const uint32* id, uint32 stateSeq, const void* buffer, + uint64 pos, uint32 len, bool stable) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpWrite); + fRequest->Stream().AddUInt(stateSeq); + fRequest->Stream().AddUInt(id[0]); + fRequest->Stream().AddUInt(id[1]); + fRequest->Stream().AddUInt(id[2]); + fRequest->Stream().AddUHyper(pos); + fRequest->Stream().AddInt(stable ? FILE_SYNC4 : UNSTABLE4); + fRequest->Stream().AddOpaque(buffer, len); + + fOpCount++; + + return B_OK; +} + + +status_t +RequestBuilder::ReleaseLockOwner(OpenState* state, LockOwner* owner) +{ + if (fProcedure != ProcCompound) + return B_BAD_VALUE; + if (fRequest == NULL) + return B_NO_MEMORY; + + fRequest->Stream().AddUInt(OpReleaseLockOwner); + _GenerateLockOwner(fRequest->Stream(), state, owner); + + fOpCount++; + + return B_OK; +} + + +RPC::Call* +RequestBuilder::Request() +{ + if (fProcedure == ProcCompound) + fRequest->Stream().InsertUInt(fOpCountPosition, fOpCount); + + if (fRequest == NULL || fRequest->Stream().Error() == B_OK) + return fRequest; + else + return NULL; +} + + +void +RequestBuilder::_AttrBitmap(XDR::WriteStream& stream, Attribute* attrs, + uint32 count) +{ + // 2 is safe in NFS4, not in NFS4.1 though + uint32 bitmap[2]; + memset(bitmap, 0, sizeof(bitmap)); + for (uint32 i = 0; i < count; i++) { + bitmap[attrs[i] / 32] |= 1 << attrs[i] % 32; + } + + uint32 bcount = bitmap[1] != 0 ? 2 : 1; + stream.AddUInt(bcount); + for (uint32 i = 0; i < bcount; i++) + stream.AddUInt(bitmap[i]); +} + + +void +RequestBuilder::_EncodeAttrs(XDR::WriteStream& stream, AttrValue* attr, + uint32 count) +{ + if (count == 0) { + stream.AddUInt(0); + stream.AddOpaque(NULL, 0); + return; + } + + Attribute* attrs + = reinterpret_cast(malloc(sizeof(Attribute) * count)); + for (uint32 i = 0; i < count; i++) + attrs[i] = static_cast(attr[i].fAttribute); + _AttrBitmap(stream, attrs, count); + free(attrs); + + uint32 i = 0; + XDR::WriteStream str; + if (i < count && attr[i].fAttribute == FATTR4_TYPE) { + str.AddUInt(attr[i].fData.fValue32); + i++; + } + + if (i < count && attr[i].fAttribute == FATTR4_SIZE) { + str.AddUHyper(attr[i].fData.fValue64); + i++; + } + + if (i < count && attr[i].fAttribute == FATTR4_FILEHANDLE) { + FileHandle* fh = reinterpret_cast(attr[i].fData.fPointer); + str.AddOpaque(fh->fData, fh->fSize); + i++; + } + + if (i < count && attr[i].fAttribute == FATTR4_FILEID) { + str.AddUHyper(attr[i].fData.fValue64); + i++; + } + + if (i < count && attr[i].fAttribute == FATTR4_MODE) { + str.AddUInt(attr[i].fData.fValue32); + i++; + } + + if (i < count && attr[i].fAttribute == FATTR4_OWNER) { + str.AddString(reinterpret_cast(attr[i].fData.fPointer)); + i++; + } + + if (i < count && attr[i].fAttribute == FATTR4_OWNER_GROUP) { + str.AddString(reinterpret_cast(attr[i].fData.fPointer)); + i++; + } + + if (i < count && attr[i].fAttribute == FATTR4_TIME_ACCESS_SET) { + str.AddInt(1); // SET_TO_CLIENT_TIME4 + + struct timespec* ts + = reinterpret_cast(attr[i].fData.fPointer); + str.AddHyper(ts->tv_sec); + str.AddUInt(ts->tv_nsec); + + i++; + } + + if (i < count && attr[i].fAttribute == FATTR4_TIME_MODIFY_SET) { + str.AddInt(1); // SET_TO_CLIENT_TIME4 + + struct timespec* ts + = reinterpret_cast(attr[i].fData.fPointer); + str.AddHyper(ts->tv_sec); + str.AddUInt(ts->tv_nsec); + + i++; + } + + stream.AddOpaque(str); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h new file mode 100644 index 0000000000..6bf75248f9 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RequestBuilder.h @@ -0,0 +1,122 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef REQUESTBUILDER_H +#define REQUESTBUILDER_H + + +#include + +#include "FileInfo.h" +#include "NFS4Defs.h" +#include "ReplyInterpreter.h" +#include "RPCCall.h" +#include "RPCServer.h" +#include "XDR.h" + + +class OpenState; +class LockInfo; +class LockOwner; + +class RequestBuilder { +public: + RequestBuilder(Procedure p = ProcCompound); + ~RequestBuilder(); + + inline void Reset(Procedure proc = ProcCompound); + + status_t Access(); + status_t Close(uint32 seq, const uint32* id, + uint32 stateSeq); + status_t Commit(uint64 offset, uint32 count); + status_t Create(FileType type, const char* name, + AttrValue* attr, uint32 count, + const char* path = NULL); + status_t DelegReturn(const uint32* id, uint32 seq); + status_t GetAttr(Attribute* attrs, uint32 count); + status_t GetFH(); + status_t Link(const char* name); + status_t Lock(OpenState* state, LockInfo* lock, + uint32* sequence, bool reclaim = false); + status_t LockT(LockType type, uint64 pos, + uint64 len, OpenState* state); + status_t LockU(LockInfo* lock); + status_t LookUp(const char* name); + status_t LookUpUp(); + status_t Nverify(AttrValue* attr, uint32 count); + status_t Open(OpenClaim claim, uint32 seq, + uint32 access, uint64 id, OpenCreate oc, + uint64 ownerId, const char* name, + AttrValue* attr = NULL, + uint32 count = 0, bool excl = false, + OpenDelegation delegType + = OPEN_DELEGATE_NONE); + status_t OpenAttrDir(bool create); + status_t OpenConfirm(uint32 seq, const uint32* id, + uint32 stateSeq); + status_t PutFH(const FileHandle& fh); + status_t PutRootFH(); + status_t Read(const uint32* id, uint32 stateSeq, + uint64 pos, uint32 len); + status_t ReadDir(uint32 count, uint64 cookie, + uint64 cookieVerf, Attribute* attrs, + uint32 attrCount); + status_t ReadLink(); + status_t Remove(const char* file); + status_t Rename(const char* from, const char* to); + status_t Renew(uint64 clientId); + status_t SaveFH(); + status_t SetAttr(const uint32* id, uint32 stateSeq, + AttrValue* attr, uint32 count); + status_t SetClientID(RPC::Server* server); + status_t SetClientIDConfirm(uint64 id, uint64 ver); + status_t Verify(AttrValue* attr, uint32 count); + status_t Write(const uint32* id, uint32 stateSeq, + const void* buffer, uint64 pos, + uint32 len, bool stable = false); + status_t ReleaseLockOwner(OpenState* state, + LockOwner* owner); + + RPC::Call* Request(); + +private: + void _InitHeader(); + + void _GenerateLockOwner(XDR::WriteStream& stream, + OpenState* state, LockOwner* owner); + status_t _GenerateClientId(XDR::WriteStream& stream, + const RPC::Server* server); + + void _EncodeAttrs(XDR::WriteStream& stream, + AttrValue* attr, uint32 count); + void _AttrBitmap(XDR::WriteStream& stream, + Attribute* attrs, uint32 count); + + uint32 fOpCount; + XDR::Stream::Position fOpCountPosition; + + Procedure fProcedure; + + RPC::Call* fRequest; +}; + + +inline void +RequestBuilder::Reset(Procedure proc) +{ + fRequest->Stream().Clear(); + fOpCount = 0; + fProcedure = proc; + delete fRequest; + + _InitHeader(); +} + + +#endif // REQUESTBUILDER_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.cpp b/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.cpp new file mode 100644 index 0000000000..11ca0a235b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RequestInterpreter.h" + +#include + +#include + + +RequestInterpreter::RequestInterpreter(RPC::CallbackRequest* request) + : + fRequest(request) +{ + fOperationCount = fRequest->Stream().GetUInt(); +} + + +RequestInterpreter::~RequestInterpreter() +{ + delete fRequest; +} + + +status_t +RequestInterpreter::GetAttr(FileHandle* handle, int* _mask) +{ + if (fLastOperation != OpCallbackGetAttr) + return B_BAD_VALUE; + + uint32 size; + const void* ptr = fRequest->Stream().GetOpaque(&size); + handle->fSize = size; + memcpy(handle->fData, ptr, size); + + uint32 count = fRequest->Stream().GetUInt(); + if (count < 1) { + *_mask = 0; + return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK; + } + + uint32 bitmap = fRequest->Stream().GetUInt(); + uint32 mask = 0; + + if ((bitmap & (1 << FATTR4_CHANGE)) != 0) + mask |= CallbackAttrChange; + if ((bitmap & (1 << FATTR4_SIZE)) != 0) + mask |= CallbackAttrSize; + + *_mask = mask; + + for (uint32 i = 1; i < count; i++) + fRequest->Stream().GetUInt(); + + return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + + +status_t +RequestInterpreter::Recall(FileHandle* handle, bool& truncate, uint32* stateSeq, + uint32* stateID) +{ + if (fLastOperation != OpCallbackRecall) + return B_BAD_VALUE; + + *stateSeq = fRequest->Stream().GetUInt(); + stateID[0] = fRequest->Stream().GetUInt(); + stateID[1] = fRequest->Stream().GetUInt(); + stateID[2] = fRequest->Stream().GetUInt(); + + truncate = fRequest->Stream().GetBoolean(); + + uint32 size; + const void* ptr = fRequest->Stream().GetOpaque(&size); + handle->fSize = size; + memcpy(handle->fData, ptr, size); + + return fRequest->Stream().IsEOF() ? B_BAD_VALUE : B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.h b/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.h new file mode 100644 index 0000000000..a17f2cebd7 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RequestInterpreter.h @@ -0,0 +1,55 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef REQUESTINTERPRETER_H +#define REQUESTINTERPRETER_H + + +#include + +#include "FileInfo.h" +#include "NFS4Defs.h" +#include "RPCCallbackRequest.h" + + +class RequestInterpreter { +public: + RequestInterpreter(RPC::CallbackRequest* request); + ~RequestInterpreter(); + + inline uint32 OperationCount(); + inline uint32 Operation(); + + status_t GetAttr(FileHandle* handle, int* mask); + status_t Recall(FileHandle* handle, bool& truncate, + uint32* stateSeq, uint32* stateID); + +private: + uint32 fOperationCount; + uint32 fLastOperation; + + RPC::CallbackRequest* fRequest; +}; + + +inline uint32 +RequestInterpreter::OperationCount() +{ + return fOperationCount; +} + + +inline uint32 +RequestInterpreter::Operation() +{ + fLastOperation = fRequest->Stream().GetUInt(); + return fLastOperation; +} + + +#endif // REQUESTINTERPRETER_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/RootInode.cpp b/src/add-ons/kernel/file_systems/nfs4/RootInode.cpp new file mode 100644 index 0000000000..a215d6bb7b --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RootInode.cpp @@ -0,0 +1,219 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "RootInode.h" + +#include + +#include "MetadataCache.h" +#include "Request.h" + + +RootInode::RootInode() + : + fInfoCacheExpire(0), + fName(NULL), + fIOSize(0) +{ + mutex_init(&fInfoCacheLock, NULL); +} + + +RootInode::~RootInode() +{ + free(const_cast(fName)); + mutex_destroy(&fInfoCacheLock); +} + + +status_t +RootInode::ReadInfo(struct fs_info* info) +{ + ASSERT(info != NULL); + + status_t result = _UpdateInfo(); + if (result != B_OK) + return result; + + memcpy(info, &fInfoCache, sizeof(struct fs_info)); + + return B_OK; +} + + +status_t +RootInode::_UpdateInfo(bool force) +{ + if (!force && fInfoCacheExpire > time(NULL)) + return B_OK; + + MutexLocker _(fInfoCacheLock); + + if (fInfoCacheExpire > time(NULL)) + return B_OK; + + do { + RPC::Server* server = fFileSystem->Server(); + Request request(server, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + Attribute attr[] = { FATTR4_FILES_FREE, FATTR4_FILES_TOTAL, + FATTR4_MAXREAD, FATTR4_MAXWRITE, FATTR4_SPACE_FREE, + FATTR4_SPACE_TOTAL }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), server)) + continue; + + reply.PutFH(); + + AttrValue* values; + uint32 count, next = 0; + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + if (count >= next && values[next].fAttribute == FATTR4_FILES_FREE) { + fInfoCache.free_nodes = values[next].fData.fValue64; + next++; + } + + if (count >= next && values[next].fAttribute == FATTR4_FILES_TOTAL) { + fInfoCache.total_nodes = values[next].fData.fValue64; + next++; + } + + uint64 ioSize = LONGLONG_MAX; + if (count >= next && values[next].fAttribute == FATTR4_MAXREAD) { + ioSize = min_c(ioSize, values[next].fData.fValue64); + next++; + } + + if (count >= next && values[next].fAttribute == FATTR4_MAXWRITE) { + ioSize = min_c(ioSize, values[next].fData.fValue64); + next++; + } + + if (ioSize == LONGLONG_MAX) + ioSize = 32768; + fInfoCache.io_size = ioSize; + fInfoCache.block_size = ioSize; + fIOSize = ioSize; + + if (count >= next && values[next].fAttribute == FATTR4_SPACE_FREE) { + fInfoCache.free_blocks = values[next].fData.fValue64 / ioSize; + next++; + } + + if (count >= next && values[next].fAttribute == FATTR4_SPACE_TOTAL) { + fInfoCache.total_blocks = values[next].fData.fValue64 / ioSize; + next++; + } + + delete[] values; + + break; + } while (true); + + fInfoCache.flags = B_FS_IS_PERSISTENT | B_FS_IS_SHARED + | B_FS_SUPPORTS_NODE_MONITORING; + + if (fFileSystem->NamedAttrs() + || fFileSystem->GetConfiguration().fEmulateNamedAttrs) + fInfoCache.flags |= B_FS_HAS_MIME | B_FS_HAS_ATTR; + + strncpy(fInfoCache.volume_name, fName, B_FILE_NAME_LENGTH); + + fInfoCacheExpire = time(NULL) + MetadataCache::kExpirationTime; + + return B_OK; +} + + +bool +RootInode::ProbeMigration() +{ + do { + RPC::Server* server = fFileSystem->Server(); + Request request(server, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + req.Access(); + + status_t result = request.Send(); + if (result != B_OK) + continue; + + ReplyInterpreter& reply = request.Reply(); + + if (reply.NFS4Error() == NFS4ERR_MOVED) + return true; + + if (HandleErrors(reply.NFS4Error(), server)) + continue; + + return false; + } while (true); +} + + +status_t +RootInode::GetLocations(AttrValue** attrv) +{ + ASSERT(attrv != NULL); + + do { + RPC::Server* server = fFileSystem->Server(); + Request request(server, fFileSystem); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + Attribute attr[] = { FATTR4_FS_LOCATIONS }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (HandleErrors(reply.NFS4Error(), server)) + continue; + + reply.PutFH(); + + uint32 count; + result = reply.GetAttr(attrv, &count); + if (result != B_OK) + return result; + if (count < 1) + return B_ERROR; + return B_OK; + + } while (true); + + return B_OK; +} + + +const char* +RootInode::Name() const +{ + ASSERT(fName != NULL); + return fName; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/RootInode.h b/src/add-ons/kernel/file_systems/nfs4/RootInode.h new file mode 100644 index 0000000000..8fff6adc1a --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/RootInode.h @@ -0,0 +1,74 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef ROOTINODE_H +#define ROOTINODE_H + + +#include + +#include "Inode.h" + + +class RootInode : public Inode { +public: + RootInode(); + ~RootInode(); + + virtual const char* Name() const; + inline void SetName(const char* name); + + status_t ReadInfo(struct fs_info* info); + inline void MakeInfoInvalid(); + + inline uint32 IOSize(); + + bool ProbeMigration(); + status_t GetLocations(AttrValue** attr); + +private: + struct fs_info fInfoCache; + mutex fInfoCacheLock; + time_t fInfoCacheExpire; + + const char* fName; + + uint32 fIOSize; + + status_t _UpdateInfo(bool force = false); + +}; + + +inline void +RootInode::MakeInfoInvalid() +{ + fInfoCacheExpire = 0; +} + + +inline uint32 +RootInode::IOSize() +{ + if (fIOSize == 0) + _UpdateInfo(true); + return fIOSize; +} + + +inline void +RootInode::SetName(const char* name) +{ + ASSERT(name != NULL); + + free(const_cast(fName)); + fName = strdup(name); +} + + +#endif // ROOTINODE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp new file mode 100644 index 0000000000..b77feae8f4 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "VnodeToInode.h" + + +Inode* +VnodeToInode::Get() +{ + if (fInode == NULL) { + status_t result = fFileSystem->GetInode(fID, &fInode); + if (result != B_OK) + fInode = NULL; + } + + return fInode; +} + + +void +VnodeToInode::Replace(Inode* newInode) +{ + WriteLocker _(fLock); + if (fInode != NULL && !IsRoot()) { + fInode->GetFileSystem()->InoIdMap()->MarkRemoved(fID); + delete fInode; + } + + fInode = newInode; + if (fInode != NULL) { + ASSERT(fFileSystem == fInode->GetFileSystem()); + fInode->GetFileSystem()->InoIdMap()->AddEntry(fInode->fInfo, fID); + } +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h new file mode 100644 index 0000000000..302457a721 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/VnodeToInode.h @@ -0,0 +1,134 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef VNODETOINODE_H +#define VNODETOINODE_H + +#include +#include +#include + +#include "Inode.h" +#include "InodeIdMap.h" +#include "RootInode.h" + +class VnodeToInode { +public: + inline VnodeToInode(ino_t id, FileSystem* fileSystem); + inline ~VnodeToInode(); + + inline void Lock(); + inline void Unlock(); + + inline Inode* GetPointer() const; + Inode* Get(); + void Replace(Inode* newInode); + + inline void Remove(); + inline void Clear(); + + inline ino_t ID() const; + + inline bool IsRoot() const; +private: + ino_t fID; + rw_lock fLock; + + Inode* fInode; + FileSystem* fFileSystem; +}; + +class VnodeToInodeLocking { +public: + inline bool Lock(VnodeToInode* vti) + { + vti->Lock(); + return true; + } + + inline void Unlock(VnodeToInode* vti) + { + vti->Unlock(); + } +}; + +typedef AutoLocker VnodeToInodeLocker; + +inline +VnodeToInode::VnodeToInode(ino_t id, FileSystem* fileSystem) + : + fID(id), + fInode(NULL), + fFileSystem(fileSystem) +{ + rw_lock_init(&fLock, NULL); +} + + +inline +VnodeToInode::~VnodeToInode() +{ + Remove(); + if (fFileSystem != NULL && !IsRoot()) + fFileSystem->InoIdMap()->RemoveEntry(fID); + rw_lock_destroy(&fLock); +} + + +inline void +VnodeToInode::Lock() +{ + rw_lock_read_lock(&fLock); +} + + +inline void +VnodeToInode::Unlock() +{ + rw_lock_read_unlock(&fLock); +} + + +inline void +VnodeToInode::Remove() +{ + Replace(NULL); +} + + +inline void +VnodeToInode::Clear() +{ + WriteLocker _(fLock); + if (!IsRoot()) + delete fInode; + fInode = NULL; +} + + +inline bool +VnodeToInode::IsRoot() const +{ + return fInode && fFileSystem && fInode->ID() == fFileSystem->Root()->ID(); +} + + +inline Inode* +VnodeToInode::GetPointer() const +{ + return fInode; +} + + +inline ino_t +VnodeToInode::ID() const +{ + return fID; +} + +#endif // VNODETOINODE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp new file mode 100644 index 0000000000..da435bfb43 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp @@ -0,0 +1,197 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "WorkQueue.h" + +#include + + +WorkQueue* gWorkQueue = NULL; + + +WorkQueue::WorkQueue() + : + fQueueSemaphore(create_sem(0, NULL)), + fThreadCancel(create_sem(0, NULL)) +{ + mutex_init(&fQueueLock, NULL); + + fThread = spawn_kernel_thread(&WorkQueue::LaunchWorkingThread, + "NFSv4 Work Queue", B_NORMAL_PRIORITY, this); + if (fThread < B_OK) { + fInitError = fThread; + return; + } + + status_t result = resume_thread(fThread); + if (result != B_OK) { + kill_thread(fThread); + fInitError = result; + return; + } + + fInitError = B_OK; +} + + +WorkQueue::~WorkQueue() +{ + release_sem(fThreadCancel); + + status_t result; + wait_for_thread(fThread, &result); + + mutex_destroy(&fQueueLock); + delete_sem(fThreadCancel); + delete_sem(fQueueSemaphore); +} + + +status_t +WorkQueue::EnqueueJob(JobType type, void* args) +{ + WorkQueueEntry* entry = new(std::nothrow) WorkQueueEntry; + if (entry == NULL) + return B_NO_MEMORY; + + entry->fType = type; + entry->fArguments = args; + if (type == IORequest) + reinterpret_cast(args)->fInode->BeginAIOOp(); + + MutexLocker locker(fQueueLock); + fQueue.InsertAfter(fQueue.Tail(), entry); + locker.Unlock(); + + release_sem(fQueueSemaphore); + return B_OK; +} + + +status_t +WorkQueue::LaunchWorkingThread(void* object) +{ + ASSERT(object != NULL); + + WorkQueue* queue = reinterpret_cast(object); + return queue->WorkingThread(); +} + + +status_t +WorkQueue::WorkingThread() +{ + while (true) { + object_wait_info object[2]; + object[0].object = fThreadCancel; + object[0].type = B_OBJECT_TYPE_SEMAPHORE; + object[0].events = B_EVENT_ACQUIRE_SEMAPHORE; + + object[1].object = fQueueSemaphore; + object[1].type = B_OBJECT_TYPE_SEMAPHORE; + object[1].events = B_EVENT_ACQUIRE_SEMAPHORE; + + status_t result = wait_for_objects(object, 2); + + if (result < B_OK + || (object[0].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) { + return result; + } else if ((object[1].events & B_EVENT_ACQUIRE_SEMAPHORE) == 0) + continue; + + acquire_sem(fQueueSemaphore); + + DequeueJob(); + } + + return B_OK; +} + + +void +WorkQueue::DequeueJob() +{ + MutexLocker locker(fQueueLock); + WorkQueueEntry* entry = fQueue.RemoveHead(); + locker.Unlock(); + ASSERT(entry != NULL); + + void* args = entry->fArguments; + switch (entry->fType) { + case DelegationRecall: + JobRecall(reinterpret_cast(args)); + break; + case IORequest: + JobIO(reinterpret_cast(args)); + break; + } + + delete entry; +} + + +void +WorkQueue::JobRecall(DelegationRecallArgs* args) +{ + ASSERT(args != NULL); + args->fDelegation->GetInode()->RecallDelegation(args->fTruncate); +} + + +void +WorkQueue::JobIO(IORequestArgs* args) +{ + ASSERT(args != NULL); + + uint64 offset = io_request_offset(args->fRequest); + uint64 length = io_request_length(args->fRequest); + + char* buffer = reinterpret_cast(malloc(length)); + if (buffer == NULL) { + notify_io_request(args->fRequest, B_NO_MEMORY); + args->fInode->EndAIOOp(); + return; + } + + bool eof = false; + uint64 size = 0; + status_t result; + if (io_request_is_write(args->fRequest)) { + if (offset + length > args->fInode->MaxFileSize()) + length = args->fInode->MaxFileSize() - offset; + + result = read_from_io_request(args->fRequest, buffer, length); + do { + size_t bytesWritten = length - size; + result = args->fInode->WriteDirect(NULL, offset + size, + buffer + size, &bytesWritten); + size += bytesWritten; + } while (size < length && result == B_OK); + } else { + do { + size_t bytesRead = length - size; + result = args->fInode->ReadDirect(NULL, offset + size, buffer, + &bytesRead, &eof); + if (result != B_OK) + break; + + result = write_to_io_request(args->fRequest, buffer, bytesRead); + if (result != B_OK) + break; + + size += bytesRead; + } while (size < length && result == B_OK && !eof); + + } + free(buffer); + + notify_io_request(args->fRequest, result); + args->fInode->EndAIOOp(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/WorkQueue.h b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.h new file mode 100644 index 0000000000..954d011388 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.h @@ -0,0 +1,82 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef WORKQUEUE_H +#define WORKQUEUE_H + + +#include +#include +#include +#include + +#include "Delegation.h" +#include "Inode.h" + + +enum JobType { + DelegationRecall, + IORequest +}; + +struct DelegationRecallArgs { + Delegation* fDelegation; + bool fTruncate; +}; + +struct IORequestArgs { + io_request* fRequest; + Inode* fInode; +}; + +struct WorkQueueEntry : public DoublyLinkedListLinkImpl { + JobType fType; + void* fArguments; +}; + +class WorkQueue { +public: + WorkQueue(); + ~WorkQueue(); + + inline status_t InitStatus(); + + status_t EnqueueJob(JobType type, void* args); + +protected: + static status_t LaunchWorkingThread(void* object); + status_t WorkingThread(); + + void DequeueJob(); + + void JobRecall(DelegationRecallArgs* args); + void JobIO(IORequestArgs* args); + +private: + status_t fInitError; + + sem_id fQueueSemaphore; + mutex fQueueLock; + DoublyLinkedList fQueue; + + sem_id fThreadCancel; + thread_id fThread; +}; + + +inline status_t +WorkQueue::InitStatus() +{ + return fInitError; +} + + +extern WorkQueue* gWorkQueue; + + +#endif // WORKQUEUE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/XDR.cpp b/src/add-ons/kernel/file_systems/nfs4/XDR.cpp new file mode 100644 index 0000000000..86a6462525 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/XDR.cpp @@ -0,0 +1,337 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "XDR.h" + +#include +#include + +#include + + +using namespace XDR; + +Stream::Stream(void* buffer, uint32 size) + : + fBuffer(reinterpret_cast(buffer)), + fSize(size), + fPosition(0) +{ +} + + +Stream::~Stream() +{ +} + + +uint32 +Stream::_PositionToSize() const +{ + return fPosition * sizeof(uint32); +} + + +uint32 +Stream::_RealSize(uint32 size) const +{ + uint32 real_size = size; + if (real_size % 4 != 0) + real_size = ((real_size >> 2) + 1) << 2; + return real_size; +} + + +ReadStream::ReadStream(void* buffer, uint32 size) + : + Stream(buffer, size), + fEOF(false) +{ +} + + +ReadStream::~ReadStream() +{ +} + + +int32 +ReadStream::GetInt() +{ + if (_PositionToSize() >= fSize) { + fEOF = true; + return 0; + } + + return B_BENDIAN_TO_HOST_INT32(fBuffer[fPosition++]); +} + + +uint32 +ReadStream::GetUInt() +{ + if (_PositionToSize() >= fSize) { + fEOF = true; + return 0; + } + + return B_BENDIAN_TO_HOST_INT32(fBuffer[fPosition++]); +} + + +int64 +ReadStream::GetHyper() +{ + if (_PositionToSize() + sizeof(int64) > fSize) { + fEOF = true; + return 0; + } + + int64* ptr = reinterpret_cast(fBuffer + fPosition); + fPosition += 2; + + return B_BENDIAN_TO_HOST_INT64(*ptr); +} + + +uint64 +ReadStream::GetUHyper() +{ + if (_PositionToSize() + sizeof(uint64) > fSize) { + fEOF = true; + return 0; + } + + uint64* ptr = reinterpret_cast(fBuffer + fPosition); + fPosition += 2; + + return B_BENDIAN_TO_HOST_INT64(*ptr); +} + + +char* +ReadStream::GetString() +{ + if (_PositionToSize() >= fSize) { + fEOF = true; + return NULL; + } + + uint32 size; + const void* ptr = GetOpaque(&size); + if (ptr == NULL) + return NULL; + + char* str = reinterpret_cast(malloc(size + 1)); + if (str == NULL) + return NULL; + + memcpy(str, ptr, size); + str[size] = 0; + + return str; +} + + +const void* +ReadStream::GetOpaque(uint32* size) +{ + if (_PositionToSize() >= fSize) { + fEOF = true; + return NULL; + } + + void* ptr = NULL; + uint32 s = GetUInt(); + if (s != 0) { + ptr = fBuffer + fPosition; + if (_PositionToSize() + s <= fSize) + fPosition += _RealSize(s) / sizeof(uint32); + else { + s = fSize - _PositionToSize(); + fPosition = fSize; + } + } + + if (size != NULL) + *size = s; + + return ptr; +} + + +WriteStream::WriteStream() + : + Stream(malloc(kInitialSize), kInitialSize), + fError(B_OK) +{ +} + + +WriteStream::WriteStream(const WriteStream& x) + : + Stream(malloc(x.fSize), x.fSize), + fError(x.fError) +{ + fPosition = x.fPosition; + memcpy(fBuffer, x.fBuffer, fSize); +} + + +WriteStream::~WriteStream() +{ + free(fBuffer); +} + + +void +WriteStream::Clear() +{ + free(fBuffer); + fSize = kInitialSize; + fBuffer = reinterpret_cast(malloc(fSize)); + fError = B_OK; + fPosition = 0; +} + + +status_t +WriteStream::InsertUInt(Stream::Position pos, uint32 x) +{ + if (pos * sizeof(uint32) >= fSize) { + fError = B_BAD_VALUE; + return B_BAD_VALUE; + } + + fBuffer[pos] = B_HOST_TO_BENDIAN_INT32(x); + return B_OK; +} + + +status_t +WriteStream::AddInt(int32 x) +{ + status_t err = _CheckResize(sizeof(int32)); + if (err != B_OK) + return err; + + fBuffer[fPosition++] = B_HOST_TO_BENDIAN_INT32(x); + return B_OK; +} + + +status_t +WriteStream::AddUInt(uint32 x) +{ + status_t err = _CheckResize(sizeof(uint32)); + if (err != B_OK) + return err; + + fBuffer[fPosition++] = B_HOST_TO_BENDIAN_INT32(x); + return B_OK; +} + + +status_t +WriteStream::AddHyper(int64 x) +{ + status_t err = _CheckResize(sizeof(int64)); + if (err != B_OK) + return err; + + int64* ptr = reinterpret_cast(fBuffer + fPosition); + *ptr = B_HOST_TO_BENDIAN_INT64(x); + fPosition += 2; + return B_OK; +} + + +status_t +WriteStream::AddUHyper(uint64 x) +{ + status_t err = _CheckResize(sizeof(uint64)); + if (err != B_OK) + return err; + + uint64* ptr = reinterpret_cast(fBuffer + fPosition); + *ptr = B_HOST_TO_BENDIAN_INT64(x); + fPosition += 2; + return B_OK; +} + + +status_t +WriteStream::AddString(const char* str, uint32 maxlen) +{ + uint32 len = strlen(str); + uint32 size = maxlen == 0 ? len : min_c(maxlen, len); + + return AddOpaque(str, size); +} + + +status_t +WriteStream::AddOpaque(const void* ptr, uint32 size) +{ + uint32 real_size = _RealSize(size); + status_t err = _CheckResize(real_size + sizeof(uint32)); + if (err != B_OK) + return err; + + AddUInt(size); + memset(fBuffer + fPosition, 0, real_size); + memcpy(fBuffer + fPosition, ptr, size); + fPosition += real_size / sizeof(int32); + + return B_OK; +} + + +status_t +WriteStream::AddOpaque(const WriteStream& stream) +{ + return AddOpaque(stream.Buffer(), stream.Size()); +} + + +status_t +WriteStream::Append(const WriteStream& stream) +{ + uint32 size = stream.Size(); + status_t err = _CheckResize(size); + if (err != B_OK) + return err; + + memcpy(fBuffer + fPosition, stream.Buffer(), size); + fPosition += size / sizeof(int32); + + return B_OK; +} + + +status_t +WriteStream::_CheckResize(uint32 size) +{ + if (_PositionToSize() + size <= fSize) + return B_OK; + + uint32 new_size = max_c(fSize * 2, fPosition * sizeof(uint32) + size); + + void* ptr = realloc(fBuffer, new_size); + if (ptr == NULL) { + fError = B_NO_MEMORY; + return B_NO_MEMORY; + } + + fBuffer = reinterpret_cast(ptr); + fSize = new_size; + + return B_OK; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/XDR.h b/src/add-ons/kernel/file_systems/nfs4/XDR.h new file mode 100644 index 0000000000..78cfbc6392 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/XDR.h @@ -0,0 +1,166 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef XDR_H +#define XDR_H + + +#include + + +namespace XDR { + +class Stream { +public: + typedef uint32 Position; + + virtual ~Stream(); + + inline const void* Buffer() const; + inline Position Current() const; + +protected: + Stream(void* buffer, uint32 size); + + inline uint32 _PositionToSize() const; + inline uint32 _RealSize(uint32 size) const; + + uint32* fBuffer; + uint32 fSize; + Position fPosition; +}; + +class ReadStream : public Stream { +public: + ReadStream(void* buffer, uint32 size); + virtual ~ReadStream(); + + inline void SetPosition(Position position); + + inline int Size() const; + + int32 GetInt(); + uint32 GetUInt(); + int64 GetHyper(); + uint64 GetUHyper(); + + inline bool GetBoolean(); + + char* GetString(); + + const void* GetOpaque(uint32* size); + + inline bool IsEOF() const; + +private: + bool fEOF; +}; + +class WriteStream : public Stream { +public: + WriteStream(); + WriteStream(const WriteStream& x); + virtual ~WriteStream(); + + inline int Size() const; + void Clear(); + + status_t InsertUInt(Stream::Position pos, uint32 x); + + status_t AddInt(int32 x); + status_t AddUInt(uint32 x); + status_t AddHyper(int64 x); + status_t AddUHyper(uint64 x); + + inline status_t AddBoolean(bool x); + + status_t AddString(const char* str, uint32 maxlen = 0); + + status_t AddOpaque(const void* ptr, uint32 size); + status_t AddOpaque(const WriteStream& stream); + + status_t Append(const WriteStream& stream); + + inline status_t Error() const; + +private: + status_t _CheckResize(uint32 size); + + status_t fError; + + static const uint32 kInitialSize = 64; +}; + + +inline const void* +Stream::Buffer() const +{ + return fBuffer; +} + + +inline Stream::Position +Stream::Current() const +{ + return fPosition; +} + + +inline void +ReadStream::SetPosition(Position position) +{ + fPosition = position; +} + + +inline int +ReadStream::Size() const +{ + return fSize; +} + + +inline bool +ReadStream::IsEOF() const +{ + return fEOF; +} + + +inline bool +ReadStream::GetBoolean() +{ + return GetInt() != 0; +} + + +inline int +WriteStream::Size() const +{ + return fPosition * sizeof(uint32); +} + + +inline status_t +WriteStream::AddBoolean(bool x) +{ + return AddInt(static_cast(x)); +} + + +inline status_t +WriteStream::Error() const +{ + return fError; +} + + +} // namespace XDR + + +#endif // XDR_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.cpp b/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.cpp new file mode 100644 index 0000000000..2622663a9f --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.cpp @@ -0,0 +1,280 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "IdMapper.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +port_id gRequestPort; +port_id gReplyPort; + +const char* kNobodyName = "nobody"; +uid_t gNobodyId; + +const char* kNogroupName = "nobody"; +uid_t gNogroupId; + +const char* gDomainName = "localdomain"; + + +status_t +SendError(status_t error) +{ + return write_port(gReplyPort, MsgError, &error, sizeof(error)); +} + + +status_t +MatchDomain(char* name) +{ + char* domain = strchr(name, '@'); + if (domain == NULL) + return B_MISMATCHED_VALUES; + + if (strcmp(domain + 1, gDomainName) != 0) + return B_BAD_VALUE; + + *domain = '\0'; + + return B_OK; +} + + +char* +AddDomain(const char* name) +{ + uint32 fullLength = strlen(name) + strlen(gDomainName) + 2; + char* fullName = reinterpret_cast(malloc(fullLength)); + if (fullName == NULL) + return NULL; + + strcpy(fullName, name); + strcat(fullName, "@"); + strcat(fullName, gDomainName); + + return fullName; +} + + +status_t +NameToUID(void* buffer) +{ + char* userName = reinterpret_cast(buffer); + + struct passwd* userInfo = NULL; + + if (MatchDomain(userName) == B_OK) + userInfo = getpwnam(userName); + + if (userInfo == NULL) + return write_port(gReplyPort, MsgReply, &gNobodyId, sizeof(gNobodyId)); + + return write_port(gReplyPort, MsgReply, &userInfo->pw_uid, sizeof(uid_t)); +} + + +status_t +UIDToName(void* buffer) +{ + uid_t userId = *reinterpret_cast(buffer); + + const char* name = NULL; + + struct passwd* userInfo = getpwuid(userId); + if (userInfo != NULL) { + name = userInfo->pw_name; + name = AddDomain(name); + } + + status_t result; + + if (name != NULL) { + result = write_port(gReplyPort, MsgReply, name, strlen(name) + 1); + free(const_cast(name)); + } else { + result = write_port(gReplyPort, MsgReply, kNobodyName, + strlen(kNobodyName) + 1); + } + + return result; +} + + +status_t +NameToGID(void* buffer) +{ + char* groupName = reinterpret_cast(buffer); + + struct group* groupInfo = NULL; + + if (MatchDomain(groupName) == B_OK) + groupInfo = getgrnam(groupName); + + if (groupInfo == NULL) { + return write_port(gReplyPort, MsgReply, &gNogroupId, + sizeof(gNogroupId)); + } + + return write_port(gReplyPort, MsgReply, &groupInfo->gr_gid, sizeof(gid_t)); +} + + +status_t +GIDToName(void* buffer) +{ + gid_t groupId = *reinterpret_cast(buffer); + + const char* name = NULL; + + struct group* groupInfo = getgrgid(groupId); + if (groupInfo != NULL) { + name = groupInfo->gr_name; + name = AddDomain(name); + } + + status_t result; + + if (name != NULL) { + result = write_port(gReplyPort, MsgReply, name, strlen(name) + 1); + free(const_cast(name)); + } else { + result = write_port(gReplyPort, MsgReply, kNogroupName, + strlen(kNogroupName) + 1); + } + + return result; +} + + +status_t +ParseRequest(int32 code, void* buffer) +{ + switch (code) { + case MsgNameToUID: + return NameToUID(buffer); + + case MsgUIDToName: + return UIDToName(buffer); + + case MsgNameToGID: + return NameToGID(buffer); + + case MsgGIDToName: + return GIDToName(buffer); + + default: + return SendError(B_BAD_VALUE); + } +} + + +status_t +MainLoop() +{ + do { + ssize_t size = port_buffer_size(gRequestPort); + if (size < B_OK) + return 0; + + void* buffer = malloc(size); + if (buffer == NULL) + return B_NO_MEMORY; + + int32 code; + size = read_port(gRequestPort, &code, buffer, size); + if (size < B_OK) { + free(buffer); + return 0; + } + + status_t result = ParseRequest(code, buffer); + free(buffer); + + if (result != B_OK) + return 0; + + } while (true); +} + + +status_t +ReadSettings() +{ + BPath path; + status_t result = find_directory(B_COMMON_SETTINGS_DIRECTORY, &path); + if (result != B_OK) + return result; + result = path.Append("nfs4_idmapper.conf"); + if (result != B_OK) + return result; + + BFile file(path.Path(), B_READ_ONLY); + if (file.InitCheck() != B_OK) + return file.InitCheck(); + + off_t size; + result = file.GetSize(&size); + if (result != B_OK) + return result; + + void* buffer = malloc(size); + if (buffer == NULL) + return B_NO_MEMORY; + + file.Read(buffer, size); + + gDomainName = reinterpret_cast(buffer); + + return B_OK; +} + + +int +main(int argc, char** argv) +{ + gRequestPort = find_port(kRequestPortName); + if (gRequestPort < B_OK) { + fprintf(stderr, "%s\n", strerror(gRequestPort)); + return gRequestPort; + } + + gReplyPort = find_port(kReplyPortName); + if (gReplyPort < B_OK) { + fprintf(stderr, "%s\n", strerror(gReplyPort)); + return gReplyPort; + } + + ReadSettings(); + + struct passwd* userInfo = getpwnam(kNobodyName); + if (userInfo != NULL) + gNobodyId = userInfo->pw_uid; + else + gNobodyId = 0; + + struct group* groupInfo = getgrnam(kNogroupName); + if (groupInfo != NULL) + gNogroupId = groupInfo->gr_gid; + else + gNogroupId = 0; + + return MainLoop(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.h b/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.h new file mode 100644 index 0000000000..bf0707ea1e --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/idmapper/IdMapper.h @@ -0,0 +1,26 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef IDMAPPER_H +#define IDMAPPER_H + + +enum MsgCode { + MsgError, + MsgReply, + MsgNameToUID, + MsgUIDToName, + MsgNameToGID, + MsgGIDToName +}; + +static const char* kRequestPortName = "nfs4idmap_request"; +static const char* kReplyPortName = "nfs4idmap_reply"; + + +#endif // IDMAPPER_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/idmapper/Jamfile b/src/add-ons/kernel/file_systems/nfs4/idmapper/Jamfile new file mode 100644 index 0000000000..f9f4bb2f20 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/idmapper/Jamfile @@ -0,0 +1,13 @@ +SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ; + +Application nfs4_idmapper_server + : + IdMapper.cpp + + : + be + $(TARGET_LIBSUPC++) + + : + nfs4_idmapper_server.rdef +; diff --git a/src/add-ons/kernel/file_systems/nfs4/idmapper/nfs4_idmapper_server.rdef b/src/add-ons/kernel/file_systems/nfs4/idmapper/nfs4_idmapper_server.rdef new file mode 100644 index 0000000000..9b601815d2 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/idmapper/nfs4_idmapper_server.rdef @@ -0,0 +1,20 @@ +/* + * nfs4_idmapper_server.rdef + */ + +resource app_signature "application/x-vnd.Haiku-nfs4_idmapper-server"; + +resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP; + +resource app_version { + major = 1, + middle = 0, + minor = 0, + + variety = B_APPV_ALPHA, + internal = 0, + + short_info = "nfs4_idmapper_server", + long_info = "nfs4_idmapper_server ©2012 Haiku, Inc" +}; + diff --git a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp new file mode 100644 index 0000000000..bb80315688 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp @@ -0,0 +1,1528 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include + +#include +#include +#include + +#include "Connection.h" +#include "FileSystem.h" +#include "IdMap.h" +#include "Inode.h" +#include "NFS4Defs.h" +#include "RequestBuilder.h" +#include "ReplyInterpreter.h" +#include "RootInode.h" +#include "RPCCallbackServer.h" +#include "RPCServer.h" +#include "VnodeToInode.h" +#include "WorkQueue.h" + +#ifdef DEBUG +#define TRACE_NFS4 +#endif + +#ifdef TRACE_NFS4 +static mutex gTraceLock = MUTEX_INITIALIZER(NULL); + +#define TRACE(x...) \ + { \ + mutex_lock(&gTraceLock); \ + dprintf("nfs4: %s(): ", __FUNCTION__); \ + dprintf(x); \ + dprintf("\n"); \ + mutex_unlock(&gTraceLock); \ + } +#else +#define TRACE(x...) (void)0 +#endif + +extern fs_volume_ops gNFSv4VolumeOps; +extern fs_vnode_ops gNFSv4VnodeOps; + + +RPC::ServerManager* gRPCServerManager; + + +RPC::ProgramData* +CreateNFS4Server(RPC::Server* serv) +{ + return new NFS4Server(serv); +} + + +// Format: ip{4,6}_address:path options +// Available options: +// hard - retry requests until success +// soft - retry requests no more than retrans times (default) +// timeo=X - request time limit before next retransmission (default: 60s) +// retrans=X - retry requests X times (default: 5) +// ac - use metadata cache (default) +// noac - do not use metadata cache +// xattr-emu - emulate named attributes +// noxattr-emu - do not emulate named attributes (default) +// port=X - connect to port X (default: 2049) +// proto=X - user transport protocol X (default: tcp) +static status_t +ParseArguments(const char* _args, AddressResolver** address, char** _path, + MountConfiguration* conf) +{ + if (_args == NULL) + return B_BAD_VALUE; + + char* args = strdup(_args); + if (args == NULL) + return B_NO_MEMORY; + MemoryDeleter argsDeleter(args); + + char* options = strchr(args, ' '); + if (options != NULL) + *options++ = '\0'; + + char* path = strrchr(args, ':'); + if (path == NULL) + return B_MISMATCHED_VALUES; + *path++ = '\0'; + + *address = new AddressResolver(args); + if (*address == NULL) + return B_NO_MEMORY; + + *_path = strdup(path); + if (*_path == NULL) { + delete *address; + return B_NO_MEMORY; + } + + conf->fHard = false; + conf->fRetryLimit = 5; + conf->fRequestTimeout = sSecToBigTime(60); + conf->fEmulateNamedAttrs = false; + conf->fCacheMetadata = true; + + char* optionsEnd = NULL; + if (options != NULL) + optionsEnd = strchr(options, ' '); + while (options != NULL && *options != '\0') { + if (optionsEnd != NULL) + *optionsEnd++ = '\0'; + + if (strcmp(options, "hard") == 0) + conf->fHard = true; + else if (strncmp(options, "retrans=", 8) == 0) { + options += strlen("retrans="); + conf->fRetryLimit = atoi(options); + } else if (strncmp(options, "timeo=", 6) == 0) { + options += strlen("timeo="); + conf->fRequestTimeout = atoi(options); + } else if (strcmp(options, "noac") == 0) + conf->fCacheMetadata = false; + else if (strcmp(options, "xattr-emu") == 0) + conf->fEmulateNamedAttrs = true; + else if (strncmp(options, "port=", 5) == 0) { + options += strlen("port="); + (*address)->ForcePort(atoi(options)); + } else if (strncmp(options, "proto=", 6) == 0) { + options += strlen("proto="); + (*address)->ForceProtocol(options); + } + + options = optionsEnd; + if (options != NULL) + optionsEnd = strchr(options, ' '); + } + + return B_OK; +} + + +static status_t +nfs4_mount(fs_volume* volume, const char* device, uint32 flags, + const char* args, ino_t* _rootVnodeID) +{ + TRACE("volume = %p, device = %s, flags = %" B_PRIu32 ", args = %s", volume, + device, flags, args); + + status_t result; + + /* prepare idmapper server */ + MutexLocker locker(gIdMapperLock); + gIdMapper = new(std::nothrow) IdMap; + if (gIdMapper == NULL) + return B_NO_MEMORY; + + result = gIdMapper->InitStatus(); + if (result != B_OK) { + delete gIdMapper; + gIdMapper = NULL; + return result; + } + locker.Unlock(); + + AddressResolver* resolver; + MountConfiguration config; + char* path; + result = ParseArguments(args, &resolver, &path, &config); + if (result != B_OK) + return result; + MemoryDeleter pathDeleter(path); + + RPC::Server* server; + result = gRPCServerManager->Acquire(&server, resolver, CreateNFS4Server); + delete resolver; + if (result != B_OK) + return result; + + FileSystem* fs; + result = FileSystem::Mount(&fs, server, path, volume->id, config); + if (result != B_OK) { + gRPCServerManager->Release(server); + return result; + } + + Inode* inode = fs->Root(); + if (inode == NULL) { + delete fs; + gRPCServerManager->Release(server); + + return B_IO_ERROR; + } + + volume->private_volume = fs; + volume->ops = &gNFSv4VolumeOps; + + VnodeToInode* vti = new VnodeToInode(inode->ID(), fs); + if (vti == NULL) { + delete fs; + gRPCServerManager->Release(server); + return B_NO_MEMORY; + } + + vti->Replace(inode); + result = publish_vnode(volume, inode->ID(), vti, &gNFSv4VnodeOps, + inode->Type(), 0); + if (result != B_OK) + return result; + + *_rootVnodeID = inode->ID(); + + TRACE("*_rootVnodeID = %" B_PRIi64, inode->ID()); + + return B_OK; +} + + +static status_t +nfs4_get_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode, int* _type, + uint32* _flags, bool reenter) +{ + FileSystem* fs = reinterpret_cast(volume->private_volume); + TRACE("volume = %p, id = %" B_PRIi64, volume, id); + + VnodeToInode* vnodeToInode = new VnodeToInode(id, fs); + if (vnodeToInode == NULL) + return B_NO_MEMORY; + + Inode* inode; + status_t result = fs->GetInode(id, &inode); + if (result != B_OK) + return result; + + vnodeToInode->Replace(inode); + vnode->ops = &gNFSv4VnodeOps; + vnode->private_node = vnodeToInode; + + *_type = inode->Type(); + *_flags = 0; + + return B_OK; +} + + +static status_t +nfs4_unmount(fs_volume* volume) +{ + TRACE("volume = %p", volume); + FileSystem* fs = reinterpret_cast(volume->private_volume); + RPC::Server* server = fs->Server(); + + delete fs; + gRPCServerManager->Release(server); + + return B_OK; +} + + +static status_t +nfs4_read_fs_info(fs_volume* volume, struct fs_info* info) +{ + TRACE("volume = %p", volume); + + FileSystem* fs = reinterpret_cast(volume->private_volume); + RootInode* inode = reinterpret_cast(fs->Root()); + return inode->ReadInfo(info); +} + + +static status_t +nfs4_lookup(fs_volume* volume, fs_vnode* dir, const char* name, ino_t* _id) +{ + VnodeToInode* vti = reinterpret_cast(dir->private_node); + + if (!strcmp(name, ".")) { + *_id = vti->ID(); + void* ptr; + return get_vnode(volume, *_id, &ptr); + } + + VnodeToInodeLocker locker(vti); + + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s", volume, vti->ID(), + name); + + status_t result = inode->LookUp(name, _id); + if (result != B_OK) + return result; + locker.Unlock(); + + TRACE("*_id = %" B_PRIi64, *_id); + + // If VTI holds an outdated Inode next operation performed on it will + // return either ERR_STALE or ERR_FHEXPIRED. Both of these error codes + // will cause FileInfo data to be updated (the former will also cause Inode + // object to be recreated). We are taking an optimistic (an lazy) approach + // here. The following code just ensures VTI won't be removed too soon. + void* ptr; + result = get_vnode(volume, *_id, &ptr); + if (result == B_OK) + unremove_vnode(volume, *_id); + + return result; +} + + +static status_t +nfs4_get_vnode_name(fs_volume* volume, fs_vnode* vnode, char* buffer, + size_t bufferSize) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + strncpy(buffer, inode->Name(), bufferSize); + return B_OK; +} + + +static status_t +nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID()); + + delete vti; + return B_OK; +} + + +static status_t +nfs4_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) +{ + FileSystem* fs = reinterpret_cast(volume->private_volume); + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID()); + + if (fs->Root() == vti->GetPointer()) + return B_OK; + + ASSERT(vti->GetPointer() == NULL); + delete vti; + + return B_OK; +} + + +static status_t +nfs4_read_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos, + const iovec* vecs, size_t count, size_t* _numBytes) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \ + ", count = %lu, numBytes = %lu", _volume, vti->ID(), _cookie, pos, + count, *_numBytes); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + status_t result; + size_t totalRead = 0; + bool eof = false; + for (size_t i = 0; i < count && !eof; i++) { + size_t bytesLeft = vecs[i].iov_len; + char* buffer = reinterpret_cast(vecs[i].iov_base); + + do { + size_t bytesRead = bytesLeft; + result = inode->ReadDirect(cookie, pos, buffer, &bytesRead, &eof); + if (result != B_OK) + return result; + + totalRead += bytesRead; + pos += bytesRead; + buffer += bytesRead; + bytesLeft -= bytesRead; + } while (bytesLeft > 0 && !eof); + } + + *_numBytes = totalRead; + + TRACE("*numBytes = %lu", totalRead); + + return B_OK; +} + + +static status_t +nfs4_write_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos, + const iovec* vecs, size_t count, size_t* _numBytes) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \ + ", count = %lu, numBytes = %lu", _volume, vti->ID(), _cookie, pos, + count, *_numBytes); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + status_t result; + for (size_t i = 0; i < count; i++) { + uint64 bytesLeft = vecs[i].iov_len; + if (pos + bytesLeft > inode->MaxFileSize()) + bytesLeft = inode->MaxFileSize() - pos; + + char* buffer = reinterpret_cast(vecs[i].iov_base); + + do { + size_t bytesWritten = bytesLeft; + + result = inode->WriteDirect(cookie, pos, buffer, &bytesWritten); + if (result != B_OK) + return result; + + bytesLeft -= bytesWritten; + pos += bytesWritten; + buffer += bytesWritten; + } while (bytesLeft > 0); + } + + return B_OK; +} + + +static status_t +nfs4_io(fs_volume* volume, fs_vnode* vnode, void* cookie, io_request* request) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(), + cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + IORequestArgs* args = new(std::nothrow) IORequestArgs; + if (args == NULL) { + notify_io_request(request, B_NO_MEMORY); + return B_NO_MEMORY; + } + args->fRequest = request; + args->fInode = inode; + + status_t result = gWorkQueue->EnqueueJob(IORequest, args); + if (result != B_OK) + notify_io_request(request, result); + + return result; +} + + +static status_t +nfs4_get_file_map(fs_volume* volume, fs_vnode* vnode, off_t _offset, + size_t size, struct file_io_vec* vecs, size_t* _count) +{ + return B_ERROR; +} + + +static status_t +nfs4_set_flags(fs_volume* volume, fs_vnode* vnode, void* _cookie, int flags) +{ + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, flags = %d", volume, + reinterpret_cast(vnode->private_node)->ID(), _cookie, + flags); + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + cookie->fMode = (cookie->fMode & ~(O_APPEND | O_NONBLOCK)) | flags; + return B_OK; +} + + +static status_t +nfs4_fsync(fs_volume* volume, fs_vnode* vnode) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->SyncAndCommit(); +} + + +static status_t +nfs4_read_symlink(fs_volume* volume, fs_vnode* link, char* buffer, + size_t* _bufferSize) +{ + VnodeToInode* vti = reinterpret_cast(link->private_node); + TRACE("volume = %p, link = %" B_PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->ReadLink(buffer, _bufferSize); +} + + +static status_t +nfs4_create_symlink(fs_volume* volume, fs_vnode* dir, const char* name, + const char* path, int mode) +{ + VnodeToInode* vti = reinterpret_cast(dir->private_node); + TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, path = %s, mode = %d", + volume, vti->ID(), name, path, mode); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + ino_t id; + status_t result = inode->CreateLink(name, path, mode, &id); + if (result != B_OK) + return result; + + result = get_vnode(volume, id, reinterpret_cast(&vti)); + if (result == B_OK) { + unremove_vnode(volume, id); + vti->Clear(); + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* vnode) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + VnodeToInode* dirVti = reinterpret_cast(dir->private_node); + TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, vnode = %" B_PRIi64, + volume, dirVti->ID(), name, vti->ID()); + + VnodeToInodeLocker _dir(dirVti); + Inode* dirInode = dirVti->Get(); + if (dirInode == NULL) + return B_ENTRY_NOT_FOUND; + + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Link(dirInode, name); +} + + +static status_t +nfs4_unlink(fs_volume* volume, fs_vnode* dir, const char* name) +{ + VnodeToInode* vti = reinterpret_cast(dir->private_node); + + VnodeToInodeLocker locker(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s", volume, vti->ID(), + name); + + ino_t id; + status_t result = inode->Remove(name, NF4REG, &id); + if (result != B_OK) + return result; + locker.Unlock(); + + result = acquire_vnode(volume, id); + if (result == B_OK) { + ASSERT(get_vnode(volume, id, reinterpret_cast(&vti)) == B_OK); + vti->Remove(); + put_vnode(volume, id); + remove_vnode(volume, id); + + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName, + fs_vnode* toDir, const char* toName) +{ + VnodeToInode* fromVti + = reinterpret_cast(fromDir->private_node); + VnodeToInode* toVti = reinterpret_cast(toDir->private_node); + TRACE("volume = %p, fromDir = %" B_PRIi64 ", toDir = %" B_PRIi64 "," \ + " fromName = %s, toName = %s", volume, fromVti->ID(), toVti->ID(), \ + fromName, toName); + + VnodeToInodeLocker _from(fromVti); + Inode* fromInode = fromVti->Get(); + if (fromInode == NULL) + return B_ENTRY_NOT_FOUND; + + + VnodeToInodeLocker _to(toVti); + Inode* toInode = toVti->Get(); + if (toInode == NULL) + return B_ENTRY_NOT_FOUND; + + ino_t id; + status_t result = Inode::Rename(fromInode, toInode, fromName, toName, false, + &id); + if (result != B_OK) + return result; + + VnodeToInode* vti; + result = get_vnode(volume, id, reinterpret_cast(&vti)); + if (result == B_OK) { + Inode* child = vti->Get(); + if (child == NULL) { + put_vnode(volume, id); + return B_ENTRY_NOT_FOUND; + } + + unremove_vnode(volume, id); + child->fInfo.fParent = toInode->fInfo.fHandle; + child->fInfo.CreateName(toInode->fInfo.fPath, toName); + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_access(fs_volume* volume, fs_vnode* vnode, int mode) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", mode = %d", volume, vti->ID(), + mode); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Access(mode); +} + + +static status_t +nfs4_read_stat(fs_volume* volume, fs_vnode* vnode, struct stat* stat) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Stat(stat); +} + + +static status_t +nfs4_write_stat(fs_volume* volume, fs_vnode* vnode, const struct stat* stat, + uint32 statMask) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", statMask = %" B_PRIu32, volume, + vti->ID(), statMask); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->WriteStat(stat, statMask); +} + + +static status_t +get_new_vnode(fs_volume* volume, ino_t id, VnodeToInode** _vti) +{ + FileSystem* fs = reinterpret_cast(volume->private_volume); + Inode* inode; + VnodeToInode* vti; + + status_t result = acquire_vnode(volume, id); + if (result == B_OK) { + ASSERT(get_vnode(volume, id, reinterpret_cast(_vti)) == B_OK); + unremove_vnode(volume, id); + + // Release after acquire + put_vnode(volume, id); + + vti = *_vti; + + if (vti->Get() == NULL) { + result = fs->GetInode(id, &inode); + if (result != B_OK) { + put_vnode(volume, id); + return result; + } + + vti->Replace(inode); + } + return B_OK; + } + + return get_vnode(volume, id, reinterpret_cast(_vti)); +} + + +static status_t +nfs4_create(fs_volume* volume, fs_vnode* dir, const char* name, int openMode, + int perms, void** _cookie, ino_t* _newVnodeID) +{ + OpenFileCookie* cookie = new OpenFileCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + VnodeToInode* vti = reinterpret_cast(dir->private_node); + TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, openMode = %d," \ + " perms = %d", volume, vti->ID(), name, openMode, perms); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + FileSystem* fs = reinterpret_cast(volume->private_volume); + MutexLocker createLocker(fs->CreateFileLock()); + + OpenDelegationData data; + status_t result = inode->Create(name, openMode, perms, cookie, &data, + _newVnodeID); + if (result != B_OK) { + delete cookie; + return result; + } + + result = get_new_vnode(volume, *_newVnodeID, &vti); + if (result != B_OK) { + delete cookie; + return result; + } + + VnodeToInodeLocker _child(vti); + Inode* child = vti->Get(); + if (child == NULL) { + delete cookie; + put_vnode(volume, *_newVnodeID); + return B_ENTRY_NOT_FOUND; + } + + child->SetOpenState(cookie->fOpenState); + + if (data.fType != OPEN_DELEGATE_NONE) { + Delegation* delegation + = new(std::nothrow) Delegation(data, child, + cookie->fOpenState->fClientID); + if (delegation != NULL) { + delegation->fInfo = cookie->fOpenState->fInfo; + delegation->fFileSystem = child->GetFileSystem(); + child->SetDelegation(delegation); + } + } + + TRACE("*cookie = %p, *newVnodeID = %" B_PRIi64, *_cookie, *_newVnodeID); + return result; +} + + +static status_t +nfs4_open(fs_volume* volume, fs_vnode* vnode, int openMode, void** _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", openMode = %d", volume, + vti->ID(), openMode); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) { + *_cookie = NULL; + return B_OK; + } + + OpenFileCookie* cookie = new OpenFileCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + status_t result = inode->Open(openMode, cookie); + if (result != B_OK) + delete cookie; + + TRACE("*cookie = %p", *_cookie); + + return result; +} + + +static status_t +nfs4_close(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(), + _cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + + if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) + return B_OK; + + Cookie* cookie = reinterpret_cast(_cookie); + return cookie->CancelAll(); +} + + +static status_t +nfs4_free_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(), + _cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) + return B_OK; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + inode->Close(cookie); + delete cookie; + + return B_OK; +} + + +static status_t +nfs4_read(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos, + void* buffer, size_t* length) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \ + ", length = %lu", volume, vti->ID(), _cookie, pos, *length); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR) + return B_IS_A_DIRECTORY; + + if (inode->Type() == S_IFLNK) + return B_BAD_VALUE; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + return inode->Read(cookie, pos, buffer, length);; +} + + +static status_t +nfs4_write(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos, + const void* _buffer, size_t* length) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \ + ", length = %lu", volume, vti->ID(), _cookie, pos, *length); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR) + return B_IS_A_DIRECTORY; + + if (inode->Type() == S_IFLNK) + return B_BAD_VALUE; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + return inode->Write(cookie, pos, _buffer, length); +} + + +static status_t +nfs4_create_dir(fs_volume* volume, fs_vnode* parent, const char* name, + int mode) +{ + VnodeToInode* vti = reinterpret_cast(parent->private_node); + TRACE("volume = %p, parent = %" B_PRIi64 ", mode = %d", volume, vti->ID(), + mode); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + ino_t id; + status_t result = inode->CreateDir(name, mode, &id); + if (result != B_OK) + return result; + + result = get_vnode(volume, id, reinterpret_cast(&vti)); + if (result == B_OK) { + unremove_vnode(volume, id); + vti->Clear(); + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name) +{ + VnodeToInode* vti = reinterpret_cast(parent->private_node); + TRACE("volume = %p, parent = %" B_PRIi64 ", name = %s", volume, vti->ID(), + name); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + ino_t id; + status_t result = inode->Remove(name, NF4DIR, &id); + if (result != B_OK) + return result; + + result = acquire_vnode(volume, id); + if (result == B_OK) { + ASSERT(get_vnode(volume, id, reinterpret_cast(&vti)) == B_OK); + vti->Remove(); + put_vnode(volume, id); + remove_vnode(volume, id); + + put_vnode(volume, id); + } + + return B_OK; +} + + +static status_t +nfs4_open_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie) +{ + OpenDirCookie* cookie = new(std::nothrow) OpenDirCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + status_t result = inode->OpenDir(cookie); + if (result != B_OK) + delete cookie; + + TRACE("*cookie = %p", *_cookie); + + return result; +} + + +static status_t +nfs4_close_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, + reinterpret_cast(vnode->private_node)->ID(), _cookie); + + Cookie* cookie = reinterpret_cast(_cookie); + return cookie->CancelAll(); +} + + +static status_t +nfs4_free_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie) +{ + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, + reinterpret_cast(vnode->private_node)->ID(), cookie); + + delete reinterpret_cast(cookie); + return B_OK; +} + + +static status_t +nfs4_read_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie, + struct dirent* buffer, size_t bufferSize, uint32* _num) +{ + OpenDirCookie* cookie = reinterpret_cast(_cookie); + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(), + _cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->ReadDir(buffer, bufferSize, _num, cookie); +} + + +static status_t +nfs4_rewind_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, + reinterpret_cast(vnode->private_node)->ID(), _cookie); + + OpenDirCookie* cookie = reinterpret_cast(_cookie); + cookie->fSpecial = 0; + if (cookie->fSnapshot != NULL) + cookie->fSnapshot->ReleaseReference(); + cookie->fSnapshot = NULL; + cookie->fCurrent = NULL; + cookie->fEOF = false; + + return B_OK; +} + + +static status_t +nfs4_open_attr_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie) +{ + OpenDirCookie* cookie = new(std::nothrow) OpenDirCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID()); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + status_t result = inode->OpenAttrDir(cookie); + if (result != B_OK) + delete cookie; + + return result; +} + + +static status_t +nfs4_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie) +{ + return nfs4_close_dir(volume, vnode, cookie); +} + + +static status_t +nfs4_free_attr_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie) +{ + return nfs4_free_dir_cookie(volume, vnode, cookie); +} + + +static status_t +nfs4_read_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie, + struct dirent* buffer, size_t bufferSize, uint32* _num) +{ + return nfs4_read_dir(volume, vnode, cookie, buffer, bufferSize, _num); +} + + +static status_t +nfs4_rewind_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie) +{ + return nfs4_rewind_dir(volume, vnode, cookie); +} + + +static status_t +nfs4_create_attr(fs_volume* volume, fs_vnode* vnode, const char* name, + uint32 type, int openMode, void** _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenAttrCookie* cookie = new OpenAttrCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + status_t result = inode->OpenAttr(name, openMode, cookie, true, type); + if (result != B_OK) + delete cookie; + + return result; +} + + +static status_t +nfs4_open_attr(fs_volume* volume, fs_vnode* vnode, const char* name, + int openMode, void** _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenAttrCookie* cookie = new OpenAttrCookie; + if (cookie == NULL) + return B_NO_MEMORY; + *_cookie = cookie; + + status_t result = inode->OpenAttr(name, openMode, cookie, false); + if (result != B_OK) + delete cookie; + + return result; +} + + +static status_t +nfs4_close_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + Cookie* cookie = reinterpret_cast(_cookie); + return cookie->CancelAll(); +} + + +static status_t +nfs4_free_attr_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + inode->CloseAttr(cookie); + delete cookie; + + return B_OK; +} + + +static status_t +nfs4_read_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos, + void* buffer, size_t* length) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + bool eof; + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->ReadDirect(cookie, pos, buffer, length, &eof); +} + + +static status_t +nfs4_write_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos, + const void* buffer, size_t* length) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->WriteDirect(cookie, pos, buffer, length); +} + + +static status_t +nfs4_read_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie, + struct stat* stat) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Stat(stat, cookie); +} + + +static status_t +nfs4_write_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie, + const struct stat* stat, int statMask) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenAttrCookie* cookie = reinterpret_cast(_cookie); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->WriteStat(stat, statMask, cookie); +} + + +static status_t +nfs4_rename_attr(fs_volume* volume, fs_vnode* fromVnode, const char* fromName, + fs_vnode* toVnode, const char* toName) +{ + VnodeToInode* vti = reinterpret_cast(toVnode->private_node); + VnodeToInodeLocker to(vti); + Inode* toInode = vti->Get(); + if (toInode == NULL) + return B_ENTRY_NOT_FOUND; + + vti = reinterpret_cast(fromVnode->private_node); + VnodeToInodeLocker from(vti); + Inode* fromInode = vti->Get(); + if (fromInode == NULL) + return B_ENTRY_NOT_FOUND; + + return Inode::Rename(fromInode, toInode, fromName, toName, true); +} + + +static status_t +nfs4_remove_attr(fs_volume* volume, fs_vnode* vnode, const char* name) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->Remove(name, NF4NAMEDATTR, NULL); +} + + +static status_t +nfs4_test_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie, + struct flock* lock) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenFileCookie* cookie = reinterpret_cast(_cookie); + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p", volume, + vti->ID(), _cookie, lock); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + return inode->TestLock(cookie, lock); +} + + +static status_t +nfs4_acquire_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie, + const struct flock* lock, bool wait) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + OpenFileCookie* cookie = reinterpret_cast(_cookie); + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p", volume, + vti->ID(), _cookie, lock); + + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + inode->RevalidateFileCache(); + return inode->AcquireLock(cookie, lock, wait); +} + + +static status_t +nfs4_release_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie, + const struct flock* lock) +{ + VnodeToInode* vti = reinterpret_cast(vnode->private_node); + TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p", volume, + vti->ID(), _cookie, lock); + + VnodeToInodeLocker _(vti); + Inode* inode = vti->Get(); + if (inode == NULL) + return B_ENTRY_NOT_FOUND; + + if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) + return B_OK; + + OpenFileCookie* cookie = reinterpret_cast(_cookie); + + if (lock != NULL) + return inode->ReleaseLock(cookie, lock); + else + return inode->ReleaseAllLocks(cookie); +} + + +status_t +nfs4_init() +{ + gRPCServerManager = new(std::nothrow) RPC::ServerManager; + if (gRPCServerManager == NULL) + return B_NO_MEMORY; + + mutex_init(&gIdMapperLock, "idmapper Init Lock"); + gIdMapper = NULL; + + gWorkQueue = new(std::nothrow) WorkQueue; + if (gWorkQueue == NULL || gWorkQueue->InitStatus() != B_OK) { + delete gWorkQueue; + mutex_destroy(&gIdMapperLock); + delete gRPCServerManager; + return B_NO_MEMORY; + } + + return B_OK; +} + + +status_t +nfs4_uninit() +{ + RPC::CallbackServer::ShutdownAll(); + + delete gIdMapper; + delete gWorkQueue; + delete gRPCServerManager; + + mutex_destroy(&gIdMapperLock); + + return B_OK; +} + + +static status_t +nfs4_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + return nfs4_init(); + case B_MODULE_UNINIT: + return nfs4_uninit(); + default: + return B_ERROR; + } +} + + +fs_volume_ops gNFSv4VolumeOps = { + nfs4_unmount, + nfs4_read_fs_info, + NULL, + NULL, + nfs4_get_vnode, +}; + +fs_vnode_ops gNFSv4VnodeOps = { + nfs4_lookup, + nfs4_get_vnode_name, + nfs4_put_vnode, + nfs4_remove_vnode, + + /* VM file access */ + NULL, // can_page() + nfs4_read_pages, + nfs4_write_pages, + + nfs4_io, + NULL, // cancel_io() + + nfs4_get_file_map, + + NULL, // ioctl() + nfs4_set_flags, + NULL, // fs_select() + NULL, // fs_deselect() + nfs4_fsync, + + nfs4_read_symlink, + nfs4_create_symlink, + + nfs4_link, + nfs4_unlink, + nfs4_rename, + + nfs4_access, + nfs4_read_stat, + nfs4_write_stat, + NULL, // fs_preallocate() + + /* file operations */ + nfs4_create, + nfs4_open, + nfs4_close, + nfs4_free_cookie, + nfs4_read, + nfs4_write, + + /* directory operations */ + nfs4_create_dir, + nfs4_remove_dir, + nfs4_open_dir, + nfs4_close_dir, + nfs4_free_dir_cookie, + nfs4_read_dir, + nfs4_rewind_dir, + + /* attribute directory operations */ + nfs4_open_attr_dir, + nfs4_close_attr_dir, + nfs4_free_attr_dir_cookie, + nfs4_read_attr_dir, + nfs4_rewind_attr_dir, + + /* attribute operations */ + nfs4_create_attr, + nfs4_open_attr, + nfs4_close_attr, + nfs4_free_attr_cookie, + nfs4_read_attr, + nfs4_write_attr, + + nfs4_read_attr_stat, + nfs4_write_attr_stat, + nfs4_rename_attr, + nfs4_remove_attr, + + /* support for node and FS layers */ + NULL, // create_special_node + NULL, // get_super_vnode + + /* lock operations */ + nfs4_test_lock, + nfs4_acquire_lock, + nfs4_release_lock, +}; + +static file_system_module_info sNFSv4ModuleInfo = { + { + "file_systems/nfs4" B_CURRENT_FS_API_VERSION, + 0, + nfs4_std_ops, + }, + + "nfs4", // short_name + "Network File System version 4", // pretty_name + + // DDM flags + 0, + + // scanning + NULL, // identify_partition() + NULL, // scan_partition() + NULL, // free_identify_partition_cookie() + NULL, // free_partition_content_cookie() + + nfs4_mount, +}; + +module_info* modules[] = { + (module_info*)&sNFSv4ModuleInfo, + NULL, +}; + diff --git a/src/add-ons/kernel/network/Jamfile b/src/add-ons/kernel/network/Jamfile index 66d4448e41..7667873344 100644 --- a/src/add-ons/kernel/network/Jamfile +++ b/src/add-ons/kernel/network/Jamfile @@ -2,6 +2,7 @@ SubDir HAIKU_TOP src add-ons kernel network ; SubInclude HAIKU_TOP src add-ons kernel network datalink_protocols ; SubInclude HAIKU_TOP src add-ons kernel network devices ; +SubInclude HAIKU_TOP src add-ons kernel network dns_resolver ; SubInclude HAIKU_TOP src add-ons kernel network notifications ; SubInclude HAIKU_TOP src add-ons kernel network protocols ; SubInclude HAIKU_TOP src add-ons kernel network stack ; diff --git a/src/add-ons/kernel/network/dns_resolver/Definitions.h b/src/add-ons/kernel/network/dns_resolver/Definitions.h new file mode 100644 index 0000000000..6aef5c09f8 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/Definitions.h @@ -0,0 +1,23 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef DEFINITIONS_H +#define DEFINITIONS_H + + +const char* kPortNameReq = "dns_resolver_req"; +const char* kPortNameRpl = "dns_resolver_rpl"; + +enum MsgCodes { + MsgReply, + MsgError, + MsgGetAddrInfo, +}; + + +#endif // DEFINITIONS_H + diff --git a/src/add-ons/kernel/network/dns_resolver/Jamfile b/src/add-ons/kernel/network/dns_resolver/Jamfile new file mode 100644 index 0000000000..8dcbbeb2ea --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/Jamfile @@ -0,0 +1,4 @@ +SubDir HAIKU_TOP src add-ons kernel network dns_resolver ; + +SubInclude HAIKU_TOP src add-ons kernel network dns_resolver kernel_add_on ; +SubInclude HAIKU_TOP src add-ons kernel network dns_resolver server ; diff --git a/src/add-ons/kernel/network/dns_resolver/kernel_add_on/Jamfile b/src/add-ons/kernel/network/dns_resolver/kernel_add_on/Jamfile new file mode 100644 index 0000000000..30bc8c5a89 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/kernel_add_on/Jamfile @@ -0,0 +1,9 @@ +SubDir HAIKU_TOP src add-ons kernel network dns_resolver kernel_add_on ; + +UsePrivateKernelHeaders ; + +SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ; + +KernelAddon dns_resolver : + dns_resolver.cpp +; diff --git a/src/add-ons/kernel/network/dns_resolver/kernel_add_on/dns_resolver.cpp b/src/add-ons/kernel/network/dns_resolver/kernel_add_on/dns_resolver.cpp new file mode 100644 index 0000000000..476b3948fd --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/kernel_add_on/dns_resolver.cpp @@ -0,0 +1,241 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include + +#include +#include +#include +#include +#include +#include + +#include "Definitions.h" + + +static mutex gPortLock; +static port_id gPortRequest = -1; +static port_id gPortReply = -1; + +static const int32 kQueueLength = 1; + + +static status_t +dns_resolver_repair() +{ + status_t result = B_OK; + + gPortRequest = create_port(kQueueLength, kPortNameReq); + if (gPortRequest < B_OK) + return gPortRequest; + + gPortReply = create_port(kQueueLength, kPortNameRpl); + if (gPortReply < B_OK) { + delete_port(gPortRequest); + return gPortReply; + } + + char path[256]; + if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, static_cast(-1), + false, path, sizeof(path)) != B_OK) { + delete_port(gPortReply); + delete_port(gPortRequest); + return B_NAME_NOT_FOUND; + } + strlcat(path, "/dns_resolver_server", sizeof(path)); + + const char* args[] = { path, NULL }; + thread_id thread = load_image_etc(1, args, NULL, B_NORMAL_PRIORITY, + B_SYSTEM_TEAM, 0); + if (thread < B_OK) { + delete_port(gPortReply); + delete_port(gPortRequest); + return thread; + } + + set_port_owner(gPortRequest, thread); + set_port_owner(gPortReply, thread); + + result = resume_thread(thread); + if (result != B_OK) { + kill_thread(thread); + delete_port(gPortReply); + delete_port(gPortRequest); + return result; + } + + return B_OK; +} + + +static status_t +dns_resolver_init() +{ + mutex_init(&gPortLock, NULL); + return dns_resolver_repair(); +} + + +static status_t +dns_resolver_uninit() +{ + delete_port(gPortRequest); + delete_port(gPortReply); + mutex_destroy(&gPortLock); + + return B_OK; +} + + +static void +RelocateEntries(struct addrinfo* addr) +{ + char* generalOffset = reinterpret_cast(addr); + + struct addrinfo* current = addr; + while (current != NULL) { + uint64 addrOffset = reinterpret_cast(current->ai_addr); + uint64 nameOffset = reinterpret_cast(current->ai_canonname); + uint64 nextOffset = reinterpret_cast(current->ai_next); + + if (current->ai_addr != NULL) { + current->ai_addr + = reinterpret_cast(generalOffset + addrOffset); + } + + if (current->ai_canonname != NULL) + current->ai_canonname = generalOffset + nameOffset; + + if (current->ai_next != NULL) { + current->ai_next + = reinterpret_cast(generalOffset + nextOffset); + } + + current = current->ai_next; + } +} + + +static status_t +GetAddrInfo(const char* node, const char* service, + const struct addrinfo* hints, struct addrinfo** res) +{ + uint32 nodeSize = node != NULL ? strlen(node) + 1 : 1; + uint32 serviceSize = service != NULL ? strlen(service) + 1 : 1; + uint32 size = nodeSize + serviceSize + sizeof(*hints); + char* buffer = reinterpret_cast(malloc(size)); + if (buffer == NULL) + return B_NO_MEMORY; + MemoryDeleter _(buffer); + + off_t off = 0; + if (node != NULL) + strcpy(buffer + off, node); + else + buffer[off] = '\0'; + off += nodeSize; + + if (service != NULL) + strcpy(buffer + off, service); + else + buffer[off] = '\0'; + off += serviceSize; + + if (hints != NULL) + memcpy(buffer + off, hints, sizeof(*hints)); + else { + struct addrinfo *nullHints + = reinterpret_cast(buffer + off); + memset(nullHints, 0, sizeof(*nullHints)); + nullHints->ai_family = AF_UNSPEC; + } + + MutexLocker locker(gPortLock); + do { + status_t result = write_port(gPortRequest, MsgGetAddrInfo, buffer, + size); + if (result != B_OK) { + result = dns_resolver_repair(); + if (result != B_OK) + return result; + continue; + } + + ssize_t replySize = port_buffer_size(gPortReply); + if (replySize < B_OK) { + result = dns_resolver_repair(); + if (result != B_OK) + return result; + continue; + } + + void* reply = malloc(replySize); + if (reply == NULL) + return B_NO_MEMORY; + + int32 code; + replySize = read_port(gPortReply, &code, reply, replySize); + if (replySize < B_OK) { + result = dns_resolver_repair(); + if (result != B_OK) { + free(reply); + return result; + } + continue; + } + + struct addrinfo* addr; + switch (code) { + case MsgReply: + addr = reinterpret_cast(reply); + RelocateEntries(addr); + *res = addr; + return B_OK; + case MsgError: + result = *reinterpret_cast(reply); + free(reply); + return result; + default: + free(reply); + return B_BAD_VALUE; + } + + } while (true); +} + + +static status_t +dns_resolver_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + return dns_resolver_init(); + case B_MODULE_UNINIT: + return dns_resolver_uninit(); + default: + return B_ERROR; + } +} + + +static dns_resolver_module sDNSResolverModule = { + { + DNS_RESOLVER_MODULE_NAME, + 0, + dns_resolver_std_ops, + }, + + GetAddrInfo, +}; + +module_info* modules[] = { + (module_info*)&sDNSResolverModule, + NULL +}; + diff --git a/src/add-ons/kernel/network/dns_resolver/server/Jamfile b/src/add-ons/kernel/network/dns_resolver/server/Jamfile new file mode 100644 index 0000000000..e2fbbaad61 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/server/Jamfile @@ -0,0 +1,16 @@ +SubDir HAIKU_TOP src add-ons kernel network dns_resolver server ; + +UsePrivateKernelHeaders ; + +SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ; + +Application dns_resolver_server + : + main.cpp + + : + be $(TARGET_NETWORK_LIBS) + + : + dns_resolver_server.rdef +; diff --git a/src/add-ons/kernel/network/dns_resolver/server/dns_resolver_server.rdef b/src/add-ons/kernel/network/dns_resolver/server/dns_resolver_server.rdef new file mode 100644 index 0000000000..e0a0df5a41 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/server/dns_resolver_server.rdef @@ -0,0 +1,20 @@ +/* + * dns_resolver_server.rdef + */ + +resource app_signature "application/x-vnd.Haiku-dns-resolver-server"; + +resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP; + +resource app_version { + major = 1, + middle = 0, + minor = 0, + + variety = B_APPV_ALPHA, + internal = 0, + + short_info = "dns_resolver_server", + long_info = "dns_resolver_server ©2012 Haiku, Inc" +}; + diff --git a/src/add-ons/kernel/network/dns_resolver/server/main.cpp b/src/add-ons/kernel/network/dns_resolver/server/main.cpp new file mode 100644 index 0000000000..f82d405427 --- /dev/null +++ b/src/add-ons/kernel/network/dns_resolver/server/main.cpp @@ -0,0 +1,178 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include +#include + +#include +#include + +#include +#include +#include + +#include "Definitions.h" + + +port_id gRequestPort; +port_id gReplyPort; + + +status_t +Serialize(char** _reply, uint32* _totalSize, const struct addrinfo* ai) +{ + uint32 addrsSize = ai == NULL ? 0 : sizeof(addrinfo); + uint32 namesSize = 0; + uint32 socksSize = 0; + + const struct addrinfo* current = ai; + while (current != NULL) { + if (current->ai_canonname != NULL) + namesSize += strlen(current->ai_canonname) + 1; + if (current->ai_addr != NULL) { + if (current->ai_family == AF_INET) + socksSize += sizeof(sockaddr_in); + else + socksSize += sizeof(sockaddr_in6); + } + if (current->ai_next != NULL) + addrsSize += sizeof(addrinfo); + current = current->ai_next; + } + + uint32 totalSize = addrsSize + namesSize + socksSize; + char* reply = reinterpret_cast(malloc(totalSize)); + if (reply == NULL) + return B_NO_MEMORY; + + uint32 addrPos = 0; + uint32 namePos = addrsSize; + uint32 sockPos = addrsSize + namesSize; + + struct addrinfo temp; + + current = ai; + while (current != NULL) { + memcpy(&temp, current, sizeof(addrinfo)); + + if (current->ai_canonname != NULL) { + strcpy(reply + namePos, current->ai_canonname); + uint32 nSize = strlen(current->ai_canonname) + 1; + temp.ai_canonname = reinterpret_cast(namePos); + namePos += nSize; + } + if (current->ai_addr != NULL) { + if (current->ai_family == AF_INET) { + memcpy(reply + sockPos, current->ai_addr, sizeof(sockaddr_in)); + temp.ai_addr = reinterpret_cast(sockPos); + sockPos += sizeof(sockaddr_in); + } else { + memcpy(reply + sockPos, current->ai_addr, sizeof(sockaddr_in6)); + temp.ai_addr = reinterpret_cast(sockPos); + sockPos += sizeof(sockaddr_in6); + } + } + + addrinfo* next = current->ai_next; + if (next != NULL) + temp.ai_next = reinterpret_cast(addrPos) + 1; + else + temp.ai_next = NULL; + + memcpy(reply + addrPos, &temp, sizeof(addrinfo)); + addrPos += sizeof(addrinfo); + + current = next; + } + + *_reply = reply; + *_totalSize = totalSize; + return B_OK; +} + + +status_t +GetAddrInfo(const char* buffer) +{ + const char* node = buffer[0] == '\0' ? NULL : buffer; + uint32 nodeSize = node != NULL ? strlen(node) + 1 : 1; + + const char* service = buffer[nodeSize] == '\0' ? NULL : buffer + nodeSize; + uint32 serviceSize = service != NULL ? strlen(service) + 1 : 1; + + const struct addrinfo* hints + = reinterpret_cast(buffer + nodeSize + serviceSize); + + struct addrinfo* ai; + status_t result = getaddrinfo(node, service, hints, &ai); + if (result != B_OK) + return write_port(gReplyPort, MsgError, &result, sizeof(result)); + + uint32 totalSize; + char* reply; + result = Serialize(&reply, &totalSize, ai); + freeaddrinfo(ai); + if (result != B_OK) + return write_port(gReplyPort, MsgError, &result, sizeof(result)); + return write_port(gReplyPort, MsgReply, reply, totalSize); +} + + +status_t +MainLoop() +{ + do { + ssize_t size = port_buffer_size(gRequestPort); + if (size < B_OK) + return 0; + + void* buffer = malloc(size); + if (buffer == NULL) + return B_NO_MEMORY; + MemoryDeleter _(buffer); + + int32 code; + size = read_port(gRequestPort, &code, buffer, size); + if (size < B_OK) + return 0; + + status_t result; + switch (code) { + case MsgGetAddrInfo: + result = GetAddrInfo(reinterpret_cast(buffer)); + default: + result = B_BAD_VALUE; + write_port(gReplyPort, MsgError, &result, sizeof(result)); + result = B_OK; + } + + if (result != B_OK) + return 0; + } while (true); +} + + +int +main(int argc, char** argv) +{ + gRequestPort = find_port(kPortNameReq); + if (gRequestPort < B_OK) { + fprintf(stderr, "%s\n", strerror(gRequestPort)); + return gRequestPort; + } + + gReplyPort = find_port(kPortNameRpl); + if (gReplyPort < B_OK) { + fprintf(stderr, "%s\n", strerror(gReplyPort)); + return gReplyPort; + } + + return MainLoop(); +} + diff --git a/src/add-ons/translators/gif/GIFLoad.cpp b/src/add-ons/translators/gif/GIFLoad.cpp index 02a44f3e2d..64e73fef5e 100644 --- a/src/add-ons/translators/gif/GIFLoad.cpp +++ b/src/add-ons/translators/gif/GIFLoad.cpp @@ -339,7 +339,7 @@ GIFLoad::ReadGIFImageData() goto bad_end; //memcpy(newEntry, fOldCode, fOldCodeLength); - for (int x = 0; x < fOldCodeLength; x++) { + for (unsigned int x = 0; x < fOldCodeLength; x++) { newEntry[x] = fOldCode[x]; } @@ -347,7 +347,7 @@ GIFLoad::ReadGIFImageData() newEntry[fOldCodeLength] = *fTable[fNewCode]; } else { // Does not exist in table //memcpy(newEntry, fOldCode, fOldCodeLength); - for (int x = 0; x < fOldCodeLength; x++) { + for (unsigned int x = 0; x < fOldCodeLength; x++) { newEntry[x] = fOldCode[x]; } @@ -360,7 +360,7 @@ GIFLoad::ReadGIFImageData() fTable[fNextCode] = MemblockAllocate(fOldCodeLength + 1); //memcpy(fTable[fNextCode], newEntry, fOldCodeLength + 1); - for (int x = 0; x < fOldCodeLength + 1; x++) { + for (unsigned int x = 0; x < fOldCodeLength + 1; x++) { fTable[fNextCode][x] = newEntry[x]; } diff --git a/src/add-ons/translators/gif/GIFLoad.h b/src/add-ons/translators/gif/GIFLoad.h index 885364d835..a981b3f5b3 100644 --- a/src/add-ons/translators/gif/GIFLoad.h +++ b/src/add-ons/translators/gif/GIFLoad.h @@ -86,7 +86,7 @@ class GIFLoad { int fPass, fRow, fWidth, fHeight; unsigned char fOldCode[4096]; - int fOldCodeLength; + unsigned int fOldCodeLength; short fNewCode; int fBits, fMaxCode, fCodeSize; short fClearCode, fEndCode, fNextCode; diff --git a/src/apps/mail/MailWindow.cpp b/src/apps/mail/MailWindow.cpp index 8a81cba786..f9879d6825 100644 --- a/src/apps/mail/MailWindow.cpp +++ b/src/apps/mail/MailWindow.cpp @@ -470,20 +470,20 @@ TMailWindow::TMailWindow(BRect rect, const char* title, TMailApp* app, // Button Bar + BuildButtonBar(); + float bbwidth = 0, bbheight = 0; bool showButtonBar = fApp->ShowButtonBar(); if (showButtonBar) { - BuildButtonBar(); fButtonBar->ShowLabels(showButtonBar); fButtonBar->Arrange(true); fButtonBar->GetPreferredSize(&bbwidth, &bbheight); fButtonBar->ResizeTo(Bounds().right, bbheight); fButtonBar->MoveTo(0, height); fButtonBar->Show(); - } else - fButtonBar = NULL; + } r.top = r.bottom = height + bbheight + 1; fHeaderView = new THeaderView (r, rect, fIncoming, resending, @@ -2169,7 +2169,7 @@ TMailWindow::Reply(entry_ref *ref, TMailWindow *window, uint32 type) BString date(mail->Date()); if (date.Length() <= 0) date = B_TRANSLATE("(Date unavailable)"); - + preamble.ReplaceAll("%n", name); preamble.ReplaceAll("%e", address); preamble.ReplaceAll("%d", date); @@ -2332,7 +2332,7 @@ TMailWindow::Send(bool now) B_WARNING_ALERT); alert->SetShortcut(1, B_ESCAPE); userAnswer = alert->Go(); - + if (userAnswer == 1) { // Cancel was picked. return -1; @@ -2469,7 +2469,7 @@ TMailWindow::Send(bool now) B_TRANSLATE("Start now"), B_TRANSLATE("OK")); alert->SetShortcut(1, B_ESCAPE); int32 start = alert->Go(); - + if (start == 0) { result = be_roster->Launch("application/x-vnd.Be-POST"); if (result == B_OK) { @@ -2559,7 +2559,7 @@ TMailWindow::SaveAsDraft() else strlcpy(fileName, fHeaderView->fSubject->Text(), sizeof(fileName)); - + uint32 originalLength = strlen(fileName); // convert /, \ and : to - @@ -2579,7 +2579,7 @@ TMailWindow::SaveAsDraft() break; char appendix[B_FILE_NAME_LENGTH]; sprintf(appendix, " %ld", i++); - int32 pos = min_c(sizeof(fileName) - strlen(appendix), + int32 pos = min_c(sizeof(fileName) - strlen(appendix), originalLength); sprintf(fileName + pos, "%s", appendix); } while (status == B_FILE_EXISTS); @@ -2612,12 +2612,12 @@ TMailWindow::SaveAsDraft() WriteAttrString(&draft, B_MAIL_ATTR_CC, fHeaderView->fCc->Text()); if (fHeaderView->fBcc != NULL) WriteAttrString(&draft, B_MAIL_ATTR_BCC, fHeaderView->fBcc->Text()); - + // Add account BMenuItem* menuItem = fHeaderView->fAccountMenu->FindMarked(); if (menuItem != NULL) WriteAttrString(&draft, B_MAIL_ATTR_ACCOUNT, menuItem->Label()); - + // Add encoding menuItem = fHeaderView->fEncodingMenu->FindMarked(); if (menuItem != NULL) @@ -2862,21 +2862,21 @@ TMailWindow::OpenMessage(const entry_ref *ref, uint32 characterSetForDecoding) fHeaderView->fCc->SetText(string.String()); if (node.ReadAttrString(B_MAIL_ATTR_BCC, &string) == B_OK) fHeaderView->fBcc->SetText(string.String()); - + // Restore account if (node.ReadAttrString(B_MAIL_ATTR_ACCOUNT, &string) == B_OK) { BMenuItem* accountItem = fHeaderView->fAccountMenu->FindItem(string.String()); if (accountItem != NULL) accountItem->SetMarked(true); } - + // Restore encoding if (node.ReadAttrString("MAIL:encoding", &string) == B_OK) { BMenuItem* encodingItem = fHeaderView->fEncodingMenu->FindItem(string.String()); if (encodingItem != NULL) encodingItem->SetMarked(true); } - + // Restore attachments if (node.ReadAttrString("MAIL:attachments", &string) == B_OK) { BMessage msg(REFS_RECEIVED); @@ -2893,7 +2893,7 @@ TMailWindow::OpenMessage(const entry_ref *ref, uint32 characterSetForDecoding) } AddEnclosure(&msg); } - + // restore the reading position if available PostMessage(M_READ_POS); diff --git a/src/bin/Jamfile b/src/bin/Jamfile index a06268af96..ee517b1777 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -6,7 +6,7 @@ SetSubDirSupportedPlatformsBeOSCompatible ; AddSubDirSupportedPlatforms libbe_test ; -UsePrivateHeaders app interface shared storage support usb ; +UsePrivateHeaders app interface shared storage support tracker usb ; UsePrivateSystemHeaders ; SubDirHdrs $(HAIKU_TOP) src add-ons kernel file_cache ; UseLibraryHeaders ncurses ; diff --git a/src/bin/mountvolume.cpp b/src/bin/mountvolume.cpp index b9bd217749..43c4c5aebc 100644 --- a/src/bin/mountvolume.cpp +++ b/src/bin/mountvolume.cpp @@ -1,6 +1,6 @@ /* * Copyright 2005-2007 Ingo Weinhold, bonefish@users.sf.net - * Copyright 2005-2009 Axel Dörfler, axeld@pinc-software.de + * Copyright 2005-2013 Axel Dörfler, axeld@pinc-software.de * Copyright 2009 Jonas Sundström, jonas@kirilla.se * * All rights reserved. Distributed under the terms of the MIT License. @@ -27,6 +27,9 @@ #include #include +#include + + using std::set; using std::string; @@ -56,6 +59,7 @@ static const char* kUsage = " -alldos - mount all mountable DOS volumes\n" " -ro, -readonly - mount volumes read-only\n" " -u, -unmount - unmount the volume with the name \n" + " -open - opens the mounted volumes in Tracker\n" "\n" "[info]\n" " -p, -l - list all mounted and mountable volumes\n" @@ -116,6 +120,25 @@ size_string(int64 size) } +static status_t +open_in_tracker(BPartition* partition) +{ + BPath mountPoint; + status_t status = partition->GetMountPoint(&mountPoint); + if (status != B_OK) + return status; + + entry_ref ref; + status = get_ref_for_path(mountPoint.Path(), &ref); + if (status != B_OK) + return status; + + BMessage refs(B_REFS_RECEIVED); + refs.AddRef("refs", &ref); + return BMessenger(kTrackerSignature).SendMessage(&refs); +} + + // #pragma mark - @@ -127,7 +150,8 @@ struct MountVisitor : public BDiskDeviceVisitor { mountBFS(false), mountHFS(false), mountDOS(false), - readOnly(false) + readOnly(false), + openInTracker(false) { } @@ -184,7 +208,7 @@ struct MountVisitor : public BDiskDeviceVisitor { // mount/unmount if (mount) { status_t error = partition->Mount(NULL, - (readOnly ? B_MOUNT_READ_ONLY : 0)); + readOnly ? B_MOUNT_READ_ONLY : 0); if (!silent) { if (error >= B_OK) { BPath mountPoint; @@ -196,6 +220,8 @@ struct MountVisitor : public BDiskDeviceVisitor { name, strerror(error)); } } + if (openInTracker && error == B_OK) + open_in_tracker(partition); } else if (unmount) { status_t error = partition->Unmount(); if (!silent) { @@ -219,9 +245,10 @@ struct MountVisitor : public BDiskDeviceVisitor { bool mountHFS; bool mountDOS; bool readOnly; + bool openInTracker; }; -// PrintPartitionsVisitor + struct PrintPartitionsVisitor : public BDiskDeviceVisitor { PrintPartitionsVisitor() : listMountablePartitions(false), @@ -330,9 +357,10 @@ MountVolume::RefsReceived(BMessage* message) entry_ref ref; BPath path; - int32 argc = refCount + 1; + int32 argc = refCount + 2; char** argv = new char*[argc + 1]; argv[0] = strdup(kAppName); + argv[1] = strdup("-open"); for (int32 i = 0; i < refCount; i++) { message->FindRef("refs", i, &ref); @@ -341,7 +369,7 @@ MountVolume::RefsReceived(BMessage* message) fprintf(stderr, "Failed to get a path (%s) from entry (%s): %s\n", path.Path(), ref.name, strerror(status)); } - argv[1 + i] = strdup(path.Path()); + argv[2 + i] = strdup(path.Path()); } argv[argc] = NULL; @@ -385,6 +413,8 @@ MountVolume::ArgvReceived(int32 argc, char** argv) if (argi >= argc) print_usage_and_exit(true); mountVisitor.toUnmount.insert(argv[argi]); + } else if (strcmp(arg, "-open") == 0) { + mountVisitor.openInTracker = true; } else if (strcmp(arg, "-p") == 0 || strcmp(arg, "-l") == 0) { printPartitionsVisitor.listMountablePartitions = true; } else if (strcmp(arg, "-lh") == 0) { @@ -461,6 +491,9 @@ MountVolume::ArgvReceived(int32 argc, char** argv) } } if (status >= B_OK) { + if (mountVisitor.openInTracker) + open_in_tracker(partition); + // remove from list mountVisitor.toMount.erase(name); } else if (id >= 0) diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index ebca444cb1..27613f88f0 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -5484,7 +5484,10 @@ file_close(struct file_descriptor* descriptor) if (status == B_OK) { // remove all outstanding locks for this team - release_advisory_lock(vnode, NULL); + if (HAS_FS_CALL(vnode, release_lock)) + status = FS_CALL(vnode, release_lock, descriptor->cookie, NULL); + else + status = release_advisory_lock(vnode, NULL); } return status; } @@ -6015,7 +6018,11 @@ common_fcntl(int fd, int op, size_t argument, bool kernel) case F_GETLK: if (vnode != NULL) { - status = get_advisory_lock(vnode, &flock); + if (HAS_FS_CALL(vnode, test_lock)) { + status = FS_CALL(vnode, test_lock, descriptor->cookie, + &flock); + } else + status = get_advisory_lock(vnode, &flock); if (status == B_OK) { // copy back flock structure status = user_memcpy((struct flock*)argument, &flock, @@ -6034,7 +6041,11 @@ common_fcntl(int fd, int op, size_t argument, bool kernel) if (vnode == NULL) { status = B_BAD_VALUE; } else if (flock.l_type == F_UNLCK) { - status = release_advisory_lock(vnode, &flock); + if (HAS_FS_CALL(vnode, release_lock)) { + status = FS_CALL(vnode, release_lock, descriptor->cookie, + &flock); + } else + status = release_advisory_lock(vnode, &flock); } else { // the open mode must match the lock type if (((descriptor->open_mode & O_RWMASK) == O_RDONLY @@ -6043,8 +6054,13 @@ common_fcntl(int fd, int op, size_t argument, bool kernel) && flock.l_type == F_RDLCK)) status = B_FILE_ERROR; else { - status = acquire_advisory_lock(vnode, -1, - &flock, op == F_SETLKW); + if (HAS_FS_CALL(vnode, acquire_lock)) { + status = FS_CALL(vnode, acquire_lock, + descriptor->cookie, &flock, op == F_SETLKW); + } else { + status = acquire_advisory_lock(vnode, -1, + &flock, op == F_SETLKW); + } } } break; diff --git a/src/system/kernel/util/Jamfile b/src/system/kernel/util/Jamfile index 59ff30c7c6..02ce7ce690 100644 --- a/src/system/kernel/util/Jamfile +++ b/src/system/kernel/util/Jamfile @@ -4,6 +4,9 @@ UsePrivateHeaders [ FDirName kernel util ] ; KernelMergeObject kernel_util.o : AVLTreeBase.cpp + hostname.cpp + inet_addr.c + inet_ntop.c kernel_cpp.cpp KernelReferenceable.cpp khash.cpp diff --git a/src/system/kernel/util/hostname.cpp b/src/system/kernel/util/hostname.cpp new file mode 100644 index 0000000000..676f16990e --- /dev/null +++ b/src/system/kernel/util/hostname.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include +#include + +#include +#include + +#include + + +static status_t +get_path(char *path, bool create) +{ + status_t status = find_directory(B_COMMON_SETTINGS_DIRECTORY, -1, create, + path, B_PATH_NAME_LENGTH); + if (status != B_OK) + return status; + + strlcat(path, "/network", B_PATH_NAME_LENGTH); + if (create) + mkdir(path, 0755); + strlcat(path, "/hostname", B_PATH_NAME_LENGTH); + return B_OK; +} + + +extern "C" int +sethostname(const char *hostName, size_t nameSize) +{ + char path[B_PATH_NAME_LENGTH]; + if (get_path(path, false) != B_OK) { + __set_errno(B_ERROR); + return -1; + } + + int file = open(path, O_WRONLY | O_CREAT, 0644); + if (file < 0) + return -1; + + nameSize = min_c(nameSize, MAXHOSTNAMELEN); + + if (write(file, hostName, nameSize) != (ssize_t)nameSize + || write(file, "\n", 1) != 1) { + close(file); + return -1; + } + + close(file); + return 0; +} + + +extern "C" int +gethostname(char *hostName, size_t nameSize) +{ + // look up hostname from network settings hostname file + + char path[B_PATH_NAME_LENGTH]; + if (get_path(path, false) != B_OK) { + __set_errno(B_ERROR); + return -1; + } + + int file = open(path, O_RDONLY); + if (file < 0) + return -1; + + nameSize = min_c(nameSize, MAXHOSTNAMELEN); + + int length = read(file, hostName, nameSize - 1); + close(file); + + if (length < 0) + return -1; + + hostName[length] = '\0'; + + char *end = strpbrk(hostName, "\r\n\t"); + if (end != NULL) + end[0] = '\0'; + + return 0; +} + diff --git a/src/system/kernel/util/inet_addr.c b/src/system/kernel/util/inet_addr.c new file mode 100644 index 0000000000..dca5d40cd1 --- /dev/null +++ b/src/system/kernel/util/inet_addr.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include + +#include + +/*% + * Ascii internet address interpretation routine. + * The value returned is in network order. + */ +in_addr_t +inet_addr(const char *cp) { + struct in_addr val; + + if (inet_aton(cp, &val)) + return (val.s_addr); + return (INADDR_NONE); +} + +/*% + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(const char *cp, struct in_addr *addr) { + u_long val; + int base, n; + char c; + u_int8_t parts[4]; + u_int8_t *pp = parts; + int digit; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!isdigit((unsigned char)c)) + return (0); + val = 0; base = 10; digit = 0; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else { + base = 8; + digit = 1 ; + } + } + for (;;) { + if (isascii(c) && isdigit((unsigned char)c)) { + if (base == 8 && (c == '8' || c == '9')) + return (0); + val = (val * base) + (c - '0'); + c = *++cp; + digit = 1; + } else if (base == 16 && isascii(c) && + isxdigit((unsigned char)c)) { + val = (val << 4) | + (c + 10 - (islower((unsigned char)c) ? 'a' : 'A')); + c = *++cp; + digit = 1; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3 || val > 0xffU) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && (!isascii(c) || !isspace((unsigned char)c))) + return (0); + /* + * Did we get a valid digit? + */ + if (!digit) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + case 1: /*%< a -- 32 bits */ + break; + + case 2: /*%< a.b -- 8.24 bits */ + if (val > 0xffffffU) + return (0); + val |= parts[0] << 24; + break; + + case 3: /*%< a.b.c -- 8.8.16 bits */ + if (val > 0xffffU) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /*%< a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xffU) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr != NULL) + addr->s_addr = htonl(val); + return (1); +} + diff --git a/src/system/kernel/util/inet_ntop.c b/src/system/kernel/util/inet_ntop.c new file mode 100644 index 0000000000..ba3605e760 --- /dev/null +++ b/src/system/kernel/util/inet_ntop.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "$Id: inet_ntop.c,v 1.5 2005/11/03 22:59:52 marka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/*% + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static const char *inet_ntop4 __P((const u_char *src, char *dst, size_t size)); +static const char *inet_ntop6 __P((const u_char *src, char *dst, size_t size)); + +/* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * author: + * Paul Vixie, 1996. + */ +const char * +inet_ntop(af, src, dst, size) + int af; + const void *src; + char *dst; + socklen_t size; +{ + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, size)); + case AF_INET6: + return (inet_ntop6(src, dst, size)); + default: + errno = EAFNOSUPPORT; + return (NULL); + } + /* NOTREACHED */ +} + +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a u_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4(src, dst, size) + const u_char *src; + char *dst; + size_t size; +{ + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + + if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) >= size) { + errno = ENOSPC; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} + +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop6(src, dst, size) + const u_char *src; + char *dst; + size_t size; +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { int base, len; } best, cur; + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += SPRINTF((tp, "%x", words[i])); + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} +