We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
LocalFileSystemToGCSOperator give false positive while copying file from src to dest, even when src has no file
### Apache Airflow Provider(s)

google

### Versions of Apache Airflow Providers

apache-airflow-providers-google==6.4.0

### Apache Airflow version

2.1.4

### Operating System

Debian GNU/Linux 10 (buster)

### Deployment

Docker-Compose

### Deployment details

_No response_

### What happened

When you run LocalFilesSystemToGCSOperator with the params for src and dest, the operator reports a false positive when there are no files present under the specified src directory. I expected it to fail stating the specified directory doesn't have any file.

[2022-03-15 14:26:15,475] {taskinstance.py:1107} INFO - Executing <Task(LocalFilesystemToGCSOperator): upload_files_to_GCS> on 2022-03-15T14:25:59.554459+00:00
[2022-03-15 14:26:15,484] {standard_task_runner.py:52} INFO - Started process 709 to run task
[2022-03-15 14:26:15,492] {standard_task_runner.py:76} INFO - Running: ['***', 'tasks', 'run', 'dag', 'upload_files_to_GCS', '2022-03-15T14:25:59.554459+00:00', '--job-id', '1562', '--pool', 'default_pool', '--raw', '--subdir', 'DAGS_FOLDER/dag.py', '--cfg-path', '/tmp/tmp_e9t7pl9', '--error-file', '/tmp/tmpyij6m4er']
[2022-03-15 14:26:15,493] {standard_task_runner.py:77} INFO - Job 1562: Subtask upload_files_to_GCS
[2022-03-15 14:26:15,590] {logging_mixin.py:104} INFO - Running <TaskInstance: dag.upload_files_to_GCS 2022-03-15T14:25:59.554459+00:00 [running]> on host 653e566fd372
[2022-03-15 14:26:15,752] {taskinstance.py:1300} INFO - Exporting the following env vars:
AIRFLOW_CTX_DAG_OWNER=jet2
AIRFLOW_CTX_DAG_ID=dag
AIRFLOW_CTX_TASK_ID=upload_files_to_GCS
AIRFLOW_CTX_EXECUTION_DATE=2022-03-15T14:25:59.554459+00:00
AIRFLOW_CTX_DAG_RUN_ID=manual__2022-03-15T14:25:59.554459+00:00
[2022-03-15 14:26:19,357] {taskinstance.py:1204} INFO - Marking task as SUCCESS. gag, task_id=upload_files_to_GCS, execution_date=20220315T142559, start_date=20220315T142615, end_date=20220315T142619
[2022-03-15 14:26:19,422] {taskinstance.py:1265} INFO - 1 downstream tasks scheduled from follow-on schedule check
[2022-03-15 14:26:19,458] {local_task_job.py:149} INFO - Task exited with return code 0

### What you think should happen instead

The operator should at least info that no files were copied than just making it successful. 

### How to reproduce

- create a Dag with LocalFilesSystemToGCSOperator 
- specify an empty directory as src and a gcp bucket as bucket_name, dest param(can be blank). 
- run the dag

### Anything else

No

### Are you willing to submit PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `airflow/providers/google/cloud/transfers/local_to_gcs.py`
Content:
```
1 #
2 # Licensed to the Apache Software Foundation (ASF) under one
3 # or more contributor license agreements.  See the NOTICE file
4 # distributed with this work for additional information
5 # regarding copyright ownership.  The ASF licenses this file
6 # to you under the Apache License, Version 2.0 (the
7 # "License"); you may not use this file except in compliance
8 # with the License.  You may obtain a copy of the License at
9 #
10 #   http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing,
13 # software distributed under the License is distributed on an
14 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 # KIND, either express or implied.  See the License for the
16 # specific language governing permissions and limitations
17 # under the License.
18 """This module contains operator for uploading local file(s) to GCS."""
19 import os
20 import warnings
21 from glob import glob
22 from typing import TYPE_CHECKING, Optional, Sequence, Union
23 
24 from airflow.models import BaseOperator
25 from airflow.providers.google.cloud.hooks.gcs import GCSHook
26 
27 if TYPE_CHECKING:
28     from airflow.utils.context import Context
29 
30 
31 class LocalFilesystemToGCSOperator(BaseOperator):
32     """
33     Uploads a file or list of files to Google Cloud Storage.
34     Optionally can compress the file for upload.
35 
36     .. seealso::
37         For more information on how to use this operator, take a look at the guide:
38         :ref:`howto/operator:LocalFilesystemToGCSOperator`
39 
40     :param src: Path to the local file, or list of local files. Path can be either absolute
41         (e.g. /path/to/file.ext) or relative (e.g. ../../foo/*/*.csv). (templated)
42     :param dst: Destination path within the specified bucket on GCS (e.g. /path/to/file.ext).
43         If multiple files are being uploaded, specify object prefix with trailing backslash
44         (e.g. /path/to/directory/) (templated)
45     :param bucket: The bucket to upload to. (templated)
46     :param gcp_conn_id: (Optional) The connection ID used to connect to Google Cloud.
47     :param google_cloud_storage_conn_id: (Deprecated) The connection ID used to connect to Google Cloud.
48         This parameter has been deprecated. You should pass the gcp_conn_id parameter instead.
49     :param mime_type: The mime-type string
50     :param delegate_to: The account to impersonate, if any
51     :param gzip: Allows for file to be compressed and uploaded as gzip
52     :param impersonation_chain: Optional service account to impersonate using short-term
53         credentials, or chained list of accounts required to get the access_token
54         of the last account in the list, which will be impersonated in the request.
55         If set as a string, the account must grant the originating account
56         the Service Account Token Creator IAM role.
57         If set as a sequence, the identities from the list must grant
58         Service Account Token Creator IAM role to the directly preceding identity, with first
59         account from the list granting this role to the originating account (templated).
60     """
61 
62     template_fields: Sequence[str] = (
63         'src',
64         'dst',
65         'bucket',
66         'impersonation_chain',
67     )
68 
69     def __init__(
70         self,
71         *,
72         src,
73         dst,
74         bucket,
75         gcp_conn_id='google_cloud_default',
76         google_cloud_storage_conn_id=None,
77         mime_type='application/octet-stream',
78         delegate_to=None,
79         gzip=False,
80         impersonation_chain: Optional[Union[str, Sequence[str]]] = None,
81         **kwargs,
82     ):
83         super().__init__(**kwargs)
84 
85         if google_cloud_storage_conn_id:
86             warnings.warn(
87                 "The google_cloud_storage_conn_id parameter has been deprecated. You should pass "
88                 "the gcp_conn_id parameter.",
89                 DeprecationWarning,
90                 stacklevel=3,
91             )
92             gcp_conn_id = google_cloud_storage_conn_id
93 
94         self.src = src
95         self.dst = dst
96         self.bucket = bucket
97         self.gcp_conn_id = gcp_conn_id
98         self.mime_type = mime_type
99         self.delegate_to = delegate_to
100         self.gzip = gzip
101         self.impersonation_chain = impersonation_chain
102 
103     def execute(self, context: 'Context'):
104         """Uploads a file or list of files to Google Cloud Storage"""
105         hook = GCSHook(
106             gcp_conn_id=self.gcp_conn_id,
107             delegate_to=self.delegate_to,
108             impersonation_chain=self.impersonation_chain,
109         )
110 
111         filepaths = self.src if isinstance(self.src, list) else glob(self.src)
112         if os.path.basename(self.dst):  # path to a file
113             if len(filepaths) > 1:  # multiple file upload
114                 raise ValueError(
115                     "'dst' parameter references filepath. Please specify "
116                     "directory (with trailing backslash) to upload multiple "
117                     "files. e.g. /path/to/directory/"
118                 )
119             object_paths = [self.dst]
120         else:  # directory is provided
121             object_paths = [os.path.join(self.dst, os.path.basename(filepath)) for filepath in filepaths]
122 
123         for filepath, object_path in zip(filepaths, object_paths):
124             hook.upload(
125                 bucket_name=self.bucket,
126                 object_name=object_path,
127                 mime_type=self.mime_type,
128                 filename=filepath,
129                 gzip=self.gzip,
130             )
131
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/airflow/providers/google/cloud/transfers/local_to_gcs.py b/airflow/providers/google/cloud/transfers/local_to_gcs.py
--- a/airflow/providers/google/cloud/transfers/local_to_gcs.py
+++ b/airflow/providers/google/cloud/transfers/local_to_gcs.py
@@ -109,6 +109,8 @@
         )
 
         filepaths = self.src if isinstance(self.src, list) else glob(self.src)
+        if not filepaths:
+            raise FileNotFoundError(self.src)
         if os.path.basename(self.dst):  # path to a file
             if len(filepaths) > 1:  # multiple file upload
                 raise ValueError(

{"golden_diff": "diff --git a/airflow/providers/google/cloud/transfers/local_to_gcs.py b/airflow/providers/google/cloud/transfers/local_to_gcs.py\n--- a/airflow/providers/google/cloud/transfers/local_to_gcs.py\n+++ b/airflow/providers/google/cloud/transfers/local_to_gcs.py\n@@ -109,6 +109,8 @@\n         )\n \n         filepaths = self.src if isinstance(self.src, list) else glob(self.src)\n+        if not filepaths:\n+            raise FileNotFoundError(self.src)\n         if os.path.basename(self.dst):  # path to a file\n             if len(filepaths) > 1:  # multiple file upload\n                 raise ValueError(\n", "issue": "LocalFileSystemToGCSOperator give false positive while copying file from src to dest, even when src has no file\n### Apache Airflow Provider(s)\n\ngoogle\n\n### Versions of Apache Airflow Providers\n\napache-airflow-providers-google==6.4.0\n\n### Apache Airflow version\n\n2.1.4\n\n### Operating System\n\nDebian GNU/Linux 10 (buster)\n\n### Deployment\n\nDocker-Compose\n\n### Deployment details\n\n_No response_\n\n### What happened\n\nWhen you run LocalFilesSystemToGCSOperator with the params for src and dest, the operator reports a false positive when there are no files present under the specified src directory. I expected it to fail stating the specified directory doesn't have any file.\r\n\r\n[2022-03-15 14:26:15,475] {taskinstance.py:1107} INFO - Executing <Task(LocalFilesystemToGCSOperator): upload_files_to_GCS> on 2022-03-15T14:25:59.554459+00:00\r\n[2022-03-15 14:26:15,484] {standard_task_runner.py:52} INFO - Started process 709 to run task\r\n[2022-03-15 14:26:15,492] {standard_task_runner.py:76} INFO - Running: ['***', 'tasks', 'run', 'dag', 'upload_files_to_GCS', '2022-03-15T14:25:59.554459+00:00', '--job-id', '1562', '--pool', 'default_pool', '--raw', '--subdir', 'DAGS_FOLDER/dag.py', '--cfg-path', '/tmp/tmp_e9t7pl9', '--error-file', '/tmp/tmpyij6m4er']\r\n[2022-03-15 14:26:15,493] {standard_task_runner.py:77} INFO - Job 1562: Subtask upload_files_to_GCS\r\n[2022-03-15 14:26:15,590] {logging_mixin.py:104} INFO - Running <TaskInstance: dag.upload_files_to_GCS 2022-03-15T14:25:59.554459+00:00 [running]> on host 653e566fd372\r\n[2022-03-15 14:26:15,752] {taskinstance.py:1300} INFO - Exporting the following env vars:\r\nAIRFLOW_CTX_DAG_OWNER=jet2\r\nAIRFLOW_CTX_DAG_ID=dag\r\nAIRFLOW_CTX_TASK_ID=upload_files_to_GCS\r\nAIRFLOW_CTX_EXECUTION_DATE=2022-03-15T14:25:59.554459+00:00\r\nAIRFLOW_CTX_DAG_RUN_ID=manual__2022-03-15T14:25:59.554459+00:00\r\n[2022-03-15 14:26:19,357] {taskinstance.py:1204} INFO - Marking task as SUCCESS. gag, task_id=upload_files_to_GCS, execution_date=20220315T142559, start_date=20220315T142615, end_date=20220315T142619\r\n[2022-03-15 14:26:19,422] {taskinstance.py:1265} INFO - 1 downstream tasks scheduled from follow-on schedule check\r\n[2022-03-15 14:26:19,458] {local_task_job.py:149} INFO - Task exited with return code 0\n\n### What you think should happen instead\n\nThe operator should at least info that no files were copied than just making it successful. \n\n### How to reproduce\n\n- create a Dag with LocalFilesSystemToGCSOperator \r\n- specify an empty directory as src and a gcp bucket as bucket_name, dest param(can be blank). \r\n- run the dag\n\n### Anything else\n\nNo\n\n### Are you willing to submit PR?\n\n- [ ] Yes I am willing to submit a PR!\n\n### Code of Conduct\n\n- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)\n\n", "before_files": [{"content": "#\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\"\"\"This module contains operator for uploading local file(s) to GCS.\"\"\"\nimport os\nimport warnings\nfrom glob import glob\nfrom typing import TYPE_CHECKING, Optional, Sequence, Union\n\nfrom airflow.models import BaseOperator\nfrom airflow.providers.google.cloud.hooks.gcs import GCSHook\n\nif TYPE_CHECKING:\n    from airflow.utils.context import Context\n\n\nclass LocalFilesystemToGCSOperator(BaseOperator):\n    \"\"\"\n    Uploads a file or list of files to Google Cloud Storage.\n    Optionally can compress the file for upload.\n\n    .. seealso::\n        For more information on how to use this operator, take a look at the guide:\n        :ref:`howto/operator:LocalFilesystemToGCSOperator`\n\n    :param src: Path to the local file, or list of local files. Path can be either absolute\n        (e.g. /path/to/file.ext) or relative (e.g. ../../foo/*/*.csv). (templated)\n    :param dst: Destination path within the specified bucket on GCS (e.g. /path/to/file.ext).\n        If multiple files are being uploaded, specify object prefix with trailing backslash\n        (e.g. /path/to/directory/) (templated)\n    :param bucket: The bucket to upload to. (templated)\n    :param gcp_conn_id: (Optional) The connection ID used to connect to Google Cloud.\n    :param google_cloud_storage_conn_id: (Deprecated) The connection ID used to connect to Google Cloud.\n        This parameter has been deprecated. You should pass the gcp_conn_id parameter instead.\n    :param mime_type: The mime-type string\n    :param delegate_to: The account to impersonate, if any\n    :param gzip: Allows for file to be compressed and uploaded as gzip\n    :param impersonation_chain: Optional service account to impersonate using short-term\n        credentials, or chained list of accounts required to get the access_token\n        of the last account in the list, which will be impersonated in the request.\n        If set as a string, the account must grant the originating account\n        the Service Account Token Creator IAM role.\n        If set as a sequence, the identities from the list must grant\n        Service Account Token Creator IAM role to the directly preceding identity, with first\n        account from the list granting this role to the originating account (templated).\n    \"\"\"\n\n    template_fields: Sequence[str] = (\n        'src',\n        'dst',\n        'bucket',\n        'impersonation_chain',\n    )\n\n    def __init__(\n        self,\n        *,\n        src,\n        dst,\n        bucket,\n        gcp_conn_id='google_cloud_default',\n        google_cloud_storage_conn_id=None,\n        mime_type='application/octet-stream',\n        delegate_to=None,\n        gzip=False,\n        impersonation_chain: Optional[Union[str, Sequence[str]]] = None,\n        **kwargs,\n    ):\n        super().__init__(**kwargs)\n\n        if google_cloud_storage_conn_id:\n            warnings.warn(\n                \"The google_cloud_storage_conn_id parameter has been deprecated. You should pass \"\n                \"the gcp_conn_id parameter.\",\n                DeprecationWarning,\n                stacklevel=3,\n            )\n            gcp_conn_id = google_cloud_storage_conn_id\n\n        self.src = src\n        self.dst = dst\n        self.bucket = bucket\n        self.gcp_conn_id = gcp_conn_id\n        self.mime_type = mime_type\n        self.delegate_to = delegate_to\n        self.gzip = gzip\n        self.impersonation_chain = impersonation_chain\n\n    def execute(self, context: 'Context'):\n        \"\"\"Uploads a file or list of files to Google Cloud Storage\"\"\"\n        hook = GCSHook(\n            gcp_conn_id=self.gcp_conn_id,\n            delegate_to=self.delegate_to,\n            impersonation_chain=self.impersonation_chain,\n        )\n\n        filepaths = self.src if isinstance(self.src, list) else glob(self.src)\n        if os.path.basename(self.dst):  # path to a file\n            if len(filepaths) > 1:  # multiple file upload\n                raise ValueError(\n                    \"'dst' parameter references filepath. Please specify \"\n                    \"directory (with trailing backslash) to upload multiple \"\n                    \"files. e.g. /path/to/directory/\"\n                )\n            object_paths = [self.dst]\n        else:  # directory is provided\n            object_paths = [os.path.join(self.dst, os.path.basename(filepath)) for filepath in filepaths]\n\n        for filepath, object_path in zip(filepaths, object_paths):\n            hook.upload(\n                bucket_name=self.bucket,\n                object_name=object_path,\n                mime_type=self.mime_type,\n                filename=filepath,\n                gzip=self.gzip,\n            )\n", "path": "airflow/providers/google/cloud/transfers/local_to_gcs.py"}], "after_files": [{"content": "#\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\"\"\"This module contains operator for uploading local file(s) to GCS.\"\"\"\nimport os\nimport warnings\nfrom glob import glob\nfrom typing import TYPE_CHECKING, Optional, Sequence, Union\n\nfrom airflow.models import BaseOperator\nfrom airflow.providers.google.cloud.hooks.gcs import GCSHook\n\nif TYPE_CHECKING:\n    from airflow.utils.context import Context\n\n\nclass LocalFilesystemToGCSOperator(BaseOperator):\n    \"\"\"\n    Uploads a file or list of files to Google Cloud Storage.\n    Optionally can compress the file for upload.\n\n    .. seealso::\n        For more information on how to use this operator, take a look at the guide:\n        :ref:`howto/operator:LocalFilesystemToGCSOperator`\n\n    :param src: Path to the local file, or list of local files. Path can be either absolute\n        (e.g. /path/to/file.ext) or relative (e.g. ../../foo/*/*.csv). (templated)\n    :param dst: Destination path within the specified bucket on GCS (e.g. /path/to/file.ext).\n        If multiple files are being uploaded, specify object prefix with trailing backslash\n        (e.g. /path/to/directory/) (templated)\n    :param bucket: The bucket to upload to. (templated)\n    :param gcp_conn_id: (Optional) The connection ID used to connect to Google Cloud.\n    :param google_cloud_storage_conn_id: (Deprecated) The connection ID used to connect to Google Cloud.\n        This parameter has been deprecated. You should pass the gcp_conn_id parameter instead.\n    :param mime_type: The mime-type string\n    :param delegate_to: The account to impersonate, if any\n    :param gzip: Allows for file to be compressed and uploaded as gzip\n    :param impersonation_chain: Optional service account to impersonate using short-term\n        credentials, or chained list of accounts required to get the access_token\n        of the last account in the list, which will be impersonated in the request.\n        If set as a string, the account must grant the originating account\n        the Service Account Token Creator IAM role.\n        If set as a sequence, the identities from the list must grant\n        Service Account Token Creator IAM role to the directly preceding identity, with first\n        account from the list granting this role to the originating account (templated).\n    \"\"\"\n\n    template_fields: Sequence[str] = (\n        'src',\n        'dst',\n        'bucket',\n        'impersonation_chain',\n    )\n\n    def __init__(\n        self,\n        *,\n        src,\n        dst,\n        bucket,\n        gcp_conn_id='google_cloud_default',\n        google_cloud_storage_conn_id=None,\n        mime_type='application/octet-stream',\n        delegate_to=None,\n        gzip=False,\n        impersonation_chain: Optional[Union[str, Sequence[str]]] = None,\n        **kwargs,\n    ):\n        super().__init__(**kwargs)\n\n        if google_cloud_storage_conn_id:\n            warnings.warn(\n                \"The google_cloud_storage_conn_id parameter has been deprecated. You should pass \"\n                \"the gcp_conn_id parameter.\",\n                DeprecationWarning,\n                stacklevel=3,\n            )\n            gcp_conn_id = google_cloud_storage_conn_id\n\n        self.src = src\n        self.dst = dst\n        self.bucket = bucket\n        self.gcp_conn_id = gcp_conn_id\n        self.mime_type = mime_type\n        self.delegate_to = delegate_to\n        self.gzip = gzip\n        self.impersonation_chain = impersonation_chain\n\n    def execute(self, context: 'Context'):\n        \"\"\"Uploads a file or list of files to Google Cloud Storage\"\"\"\n        hook = GCSHook(\n            gcp_conn_id=self.gcp_conn_id,\n            delegate_to=self.delegate_to,\n            impersonation_chain=self.impersonation_chain,\n        )\n\n        filepaths = self.src if isinstance(self.src, list) else glob(self.src)\n        if not filepaths:\n            raise FileNotFoundError(self.src)\n        if os.path.basename(self.dst):  # path to a file\n            if len(filepaths) > 1:  # multiple file upload\n                raise ValueError(\n                    \"'dst' parameter references filepath. Please specify \"\n                    \"directory (with trailing backslash) to upload multiple \"\n                    \"files. e.g. /path/to/directory/\"\n                )\n            object_paths = [self.dst]\n        else:  # directory is provided\n            object_paths = [os.path.join(self.dst, os.path.basename(filepath)) for filepath in filepaths]\n\n        for filepath, object_path in zip(filepaths, object_paths):\n            hook.upload(\n                bucket_name=self.bucket,\n                object_name=object_path,\n                mime_type=self.mime_type,\n                filename=filepath,\n                gzip=self.gzip,\n            )\n", "path": "airflow/providers/google/cloud/transfers/local_to_gcs.py"}]}