##### SUMMARY
When using a module attempts to set the mode of a symlink, and the symlink target points to a read-only filesystem, then it may crash with an `OSError: [Errno 30] Read-only file system`.

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
`file` module, due to `ansible/module_utils/basic.py` line 1117, in a `module.set_mode_if_different(…)` call.

https://github.com/ansible/ansible/blob/35dcd231be4f0afe2e1ee7a53769b3de1cc5222f/lib/ansible/module_utils/basic.py#L1101-L1122

##### ANSIBLE VERSION
```
ansible 2.8.1
  python version = 3.7.3 (default, Mar 27 2019, 09:23:15) [Clang 10.0.1 (clang-1001.0.46.3)]
```

##### OS / ENVIRONMENT
OS on target host: Linux (or any platform where [`os.lchmod()` does not exist](https://mail.python.org/pipermail/docs/2011-February/003119.html)).

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- hosts: all
  tasks:
    - copy:
        dest: /tmp/fs.img
        content: "{{ '_' * 1048576 }}"

    - filesystem:
        type: vfat
        dev: /tmp/fs.img

    - mount:
        state: mounted
        path: /mnt/readonly_fs
        src: /tmp/fs.img
        fstype: vfat
        opts: ro

    - name: This succeeds
      file:
        state: link
        path: /tmp/some_symlink
        src: /mnt/readonly_fs/nonexistent
        follow: false
        force: true
        mode: 0757

    - name: This crashes
      file:
        state: link
        path: /tmp/some_other_symlink
        src: /mnt/readonly_fs
        follow: false
        force: true
        mode: 0757
```

##### EXPECTED RESULTS

The play should not fail, since `follow: false` should mean that the `chmod` only operates on `/tmp/some_symlink`, and never should never care about where it points.

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes -->
```paste below
PLAY [all] ******************************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [target.example.org]

TASK [copy] *****************************************************************************************
changed: [target.example.org]

TASK [filesystem] ***********************************************************************************
changed: [target.example.org]

TASK [mount] ****************************************************************************************
changed: [target.example.org]

TASK [This succeeds] ********************************************************************************
changed: [target.example.org]

TASK [This crashes] *********************************************************************************
fatal: [target.example.org]: FAILED! => {
    "changed": false,
    "module_stderr": "Shared connection to target.example.org closed.\r\n",
    "module_stdout": "Traceback (most recent call last):\r\n  File \"/root/.ansible/tmp/ansible-tmp-1561877445.915763-155915524570611/AnsiballZ_file.py\", line 114, in <module>\r\n    _ansiballz_main()\r\n  File \"/root/.ansible/tmp/ansible-tmp-1561877445.915763-155915524570611/AnsiballZ_file.py\", line 106, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/root/.ansible/tmp/ansible-tmp-1561877445.915763-155915524570611/AnsiballZ_file.py\", line 49, in invoke_module\r\n    imp.load_module('__main__', mod, module, MOD_DESC)\r\n  File \"/usr/lib/python3.6/imp.py\", line 235, in load_module\r\n    return load_source(name, filename, file)\r\n  File \"/usr/lib/python3.6/imp.py\", line 170, in load_source\r\n    module = _exec(spec, sys.modules[name])\r\n  File \"<frozen importlib._bootstrap>\", line 618, in _exec\r\n  File \"<frozen importlib._bootstrap_external>\", line 678, in exec_module\r\n  File \"<frozen importlib._bootstrap>\", line 219, in _call_with_frames_removed\r\n  File \"/tmp/ansible_file_payload_r219es0e/__main__.py\", line 921, in <module>\r\n  File \"/tmp/ansible_file_payload_r219es0e/__main__.py\", line 909, in main\r\n  File \"/tmp/ansible_file_payload_r219es0e/__main__.py\", line 742, in ensure_symlink\r\n  File \"/tmp/ansible_file_payload_r219es0e/ansible_file_payload.zip/ansible/module_utils/basic.py\", line 1339, in set_fs_attributes_if_different\r\n  File \"/tmp/ansible_file_payload_r219es0e/ansible_file_payload.zip/ansible/module_utils/basic.py\", line 1112, in set_mode_if_different\r\nOSError: [Errno 30] Read-only file system: b'/tmp/some_other_symlink'\r\n",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
}

PLAY RECAP ******************************************************************************************
target.example.org : ok=5    changed=4    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```

:kw binary_data: If False, append a newline to the data.  Default False
        :kw path_prefix: If given, additional path to find the command in.
            This adds to the PATH environment variable so helper commands in
            the same directory can also be found
        :kw cwd: If given, working directory to run the command inside
        :kw use_unsafe_shell: See `args` parameter.  Default False
        :kw prompt_regex: Regex string (not a compiled regex) which can be
            used to detect prompts in the stdout which would otherwise cause
            the execution to hang (especially if no input data is specified)
        :kw environ_update: dictionary to *update* os.environ with
        :kw umask: Umask to be used when running the command. Default None
        :kw encoding: Since we return native strings, on python3 we need to
            know the encoding to use to transform from bytes to text.  If you
            want to always get bytes back, use encoding=None.  The default is
            "utf-8".  This does not affect transformation of strings given as
            args.
        :kw errors: Since we return native strings, on python3 we need to
            transform stdout and stderr from bytes to text.  If the bytes are
            undecodable in the ``encoding`` specified, then use this error
            handler to deal with them.  The default is ``surrogate_or_strict``
            which means that the bytes will be decoded using the
            surrogateescape error handler if available (available on all
            python3 versions we support) otherwise a UnicodeError traceback
            will be raised.  This does not affect transformations of strings
            given as args.
        :kw expand_user_and_vars: When ``use_unsafe_shell=False`` this argument
            dictates whether ``~`` is expanded in paths and environment variables
            are expanded before running the command. When ``True`` a string such as
            ``$SHELL`` will be expanded regardless of escaping. When ``False`` and
            ``use_unsafe_shell=False`` no path or variable expansion will be done.