Problem/Motivation
With sites that use s3fs for public file takeover, the file upload archive functionality does not work. Currently it results in a Drupal FormState error, which is a separate issue.
When digging through logs, these are the subsequent errors and stack traces for the route of the problem:
User warning: Mode not supported: w+. Use one 'r', 'w', 'a', or 'x'. in Aws\S3\StreamWrapper->triggerError() (line 757 of /app/vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php) #0 /app/web/core/includes/bootstrap.inc(347): _drupal_error_handler_real(512, 'Mode not suppor...', '/app/vendor/aws...', 757)
#1 [internal function]: _drupal_error_handler(512, 'Mode not suppor...', '/app/vendor/aws...', 757, Array)
#2 /app/vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php(757): trigger_error('Mode not suppor...', 512)
#3 /app/vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php(150): Aws\S3\StreamWrapper->triggerError(Array)
#4 /app/web/modules/contrib/s3fs/src/StreamWrapper/S3fsStream.php(702): Aws\S3\StreamWrapper->stream_open('s3://my-bucket...', 'w+b', 0, NULL)
#5 [internal function]: Drupal\s3fs\StreamWrapper\S3fsStream->stream_open('public://01a_pr...', 'w+b', 0, NULL)
#6 /app/web/core/lib/Drupal/Core/Archiver/Zip.php(62): ZipArchive->extractTo('public://01a_pr...')
#7 /app/web/modules/contrib/file_entity/src/Form/FileAddArchiveForm.php(130): Drupal\Core\Archiver\Zip->extract('public://01a_pr...')
#8 [internal function]: Drupal\file_entity\Form\FileAddArchiveForm->submitForm(Array, Object(Drupal\Core\Form\FormState))
#9 /app/web/core/lib/Drupal/Core/Form/FormSubmitter.php(114): call_user_func_array(Array, Array)
#10 /app/web/core/lib/Drupal/Core/Form/FormSubmitter.php(52): Drupal\Core\Form\FormSubmitter->executeSubmitHandlers(Array, Object(Drupal\Core\Form\FormState))
#11 /app/web/core/lib/Drupal/Core/Form/FormBuilder.php(592): Drupal\Core\Form\FormSubmitter->doSubmitForm(Array, Object(Drupal\Core\Form\FormState))
#12 /app/web/core/lib/Drupal/Core/Form/FormBuilder.php(320): Drupal\Core\Form\FormBuilder->processForm('file_add_archiv...', Array, Object(Drupal\Core\Form\FormState))
#13 /app/web/core/lib/Drupal/Core/Controller/FormController.php(73): Drupal\Core\Form\FormBuilder->buildForm(Object(Drupal\file_entity\Form\FileAddArchiveForm), Object(Drupal\Core\Form\FormState))
#14 [internal function]: Drupal\Core\Controller\FormController->getContentResult(Object(Symfony\Component\HttpFoundation\Request), Object(Drupal\Core\Routing\RouteMatch))
#15 /app/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array(Array, Array)
#16 /app/web/core/lib/Drupal/Core/Render/Renderer.php(564): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#17 /app/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#18 /app/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)
#19 /app/vendor/symfony/http-kernel/HttpKernel.php(158): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#20 /app/vendor/symfony/http-kernel/HttpKernel.php(80): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#21 /app/web/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#22 /app/web/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#23 /app/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#24 /app/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#25 /app/web/modules/contrib/purge_queuer_url/src/StackMiddleware/UrlRegistrar.php(175): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#26 /app/web/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\purge_queuer_url\StackMiddleware\UrlRegistrar->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#27 /app/web/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#28 /app/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#29 /app/web/core/lib/Drupal/Core/DrupalKernel.php(709): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#30 /app/web/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#31 {main}.Warning: ZipArchive::extractTo(public://01a_primary_logo_jpg3_0/01. Primary Logo_Colored.jpg): failed to open stream: "Drupal\s3fs\StreamWrapper\PublicS3fsStream::stream_open" call failed in Drupal\Core\Archiver\Zip->extract() (line 62 of /app/web/core/lib/Drupal/Core/Archiver/Zip.php) #0 /app/web/core/includes/bootstrap.inc(347): _drupal_error_handler_real(2, 'ZipArchive::ext...', '/app/web/core/l...', 62)
#1 [internal function]: _drupal_error_handler(2, 'ZipArchive::ext...', '/app/web/core/l...', 62, Array)
#2 /app/web/core/lib/Drupal/Core/Archiver/Zip.php(62): ZipArchive->extractTo('public://01a_pr...')
#3 /app/web/modules/contrib/file_entity/src/Form/FileAddArchiveForm.php(130): Drupal\Core\Archiver\Zip->extract('public://01a_pr...')
#4 [internal function]: Drupal\file_entity\Form\FileAddArchiveForm->submitForm(Array, Object(Drupal\Core\Form\FormState))
#5 /app/web/core/lib/Drupal/Core/Form/FormSubmitter.php(114): call_user_func_array(Array, Array)
#6 /app/web/core/lib/Drupal/Core/Form/FormSubmitter.php(52): Drupal\Core\Form\FormSubmitter->executeSubmitHandlers(Array, Object(Drupal\Core\Form\FormState))
#7 /app/web/core/lib/Drupal/Core/Form/FormBuilder.php(592): Drupal\Core\Form\FormSubmitter->doSubmitForm(Array, Object(Drupal\Core\Form\FormState))
#8 /app/web/core/lib/Drupal/Core/Form/FormBuilder.php(320): Drupal\Core\Form\FormBuilder->processForm('file_add_archiv...', Array, Object(Drupal\Core\Form\FormState))
#9 /app/web/core/lib/Drupal/Core/Controller/FormController.php(73): Drupal\Core\Form\FormBuilder->buildForm(Object(Drupal\file_entity\Form\FileAddArchiveForm), Object(Drupal\Core\Form\FormState))
#10 [internal function]: Drupal\Core\Controller\FormController->getContentResult(Object(Symfony\Component\HttpFoundation\Request), Object(Drupal\Core\Routing\RouteMatch))
#11 /app/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array(Array, Array)
#12 /app/web/core/lib/Drupal/Core/Render/Renderer.php(564): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#13 /app/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#14 /app/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)
#15 /app/vendor/symfony/http-kernel/HttpKernel.php(158): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#16 /app/vendor/symfony/http-kernel/HttpKernel.php(80): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#17 /app/web/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#18 /app/web/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#19 /app/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#20 /app/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#21 /app/web/modules/contrib/purge_queuer_url/src/StackMiddleware/UrlRegistrar.php(175): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#22 /app/web/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\purge_queuer_url\StackMiddleware\UrlRegistrar->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#23 /app/web/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#24 /app/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#25 /app/web/core/lib/Drupal/Core/DrupalKernel.php(709): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#26 /app/web/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#27 {main}.The issue seems to be that somewhere the "w+b" mode is being used in an fopen or similar function, but I can't find where that is in the aws-sdk-php library or Drupal ArchiveManager service.
What I did find from the S3 docs was that simultaneous streams with r+ and w+ are indeed no longer supported:
You can upload larger files by streaming data using fopen() and a “w”, “x”, or “a” stream access mode. The Amazon S3 stream wrapper does not support simultaneous read and write streams (e.g. “r+”, “w+”, etc). This is because the HTTP protocol doesn’t allow simultaneous reading and writing.
Steps to reproduce
1. Have a site with S3FS enabled with public file takeover enabled so that all public files are to be stored in an S3 bucket.
2. Have a zip archive with several files (ex used had 2 images and 1 pdf)
3. Use the file upload archive form to extract the archive in an attempt to save each file in the archive as individual file entities.
4. You will get the error "Form errors cannot be set after form validation has finished."
5. Comment out the setErrorByName() call on line 155 of FileAddArchiveForm.php and re-run steps 1-3
6. In PHP logs you will see the errors listed above.
Comments