Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to read files using that are being used by some other users' proccesses on remote server (High-level API) #282

Open
paf91 opened this issue Jul 2, 2024 · 8 comments

Comments

@paf91
Copy link

paf91 commented Jul 2, 2024

When trying to read bytes from file using share access flag

with smb_client.open_file(r"\\server\share\file.txt", mode='rb', share_access='r') as fd:
    file_bytes = fd.read()

or without flag

with open_file(r"\\server\share\file.txt", mode="rb") as fd:
    file_bytes = fd.read()

or simply copy file to read it afterwards using smbutils

from smbclient.shutil import copy
copy(r"\\server\share\file.txt", "file.txt")

I'm getting error:

The process cannot access the file because it is being used by another process

Meanwhile I could

  • open that file using microsoft excel on a windows machine
  • open that file after mounting directory (mount -t cifs ...) in linux

But can't access in python programm in linux with the above exception
It's nearly impossible to read files that are constantly used and shared by many people

@paf91 paf91 changed the title Unable to read files that are being used by some other users' proccesses Unable to read files that are being used by some other users' proccesses on remote server Jul 2, 2024
@adiroiban
Copy link
Contributor

adiroiban commented Jul 3, 2024

I think that the default behaviour in Python is to open a file in exclusive mode... without shared access.

As far as I know, when opening a file using the open() API there is no high level Python API to define the Windows shared access model.

I think that you will need to use the low-level smbprotocol API to open the file in shared mode.

The file open request can look like this.
The important part is share_access=ShareAccess.FILE_SHARE_READ

    handler = Open(tree, share_path)
    handler.create(
        impersonation_level=ImpersonationLevel.Impersonation,
        desired_access=(
            FilePipePrinterAccessMask.FILE_READ_DATA
            | FilePipePrinterAccessMask.FILE_READ_ATTRIBUTES
            | FilePipePrinterAccessMask.FILE_READ_EA
            ),
        file_attributes=SMBFileAttributes.FILE_ATTRIBUTE_NORMAL,
        share_access=ShareAccess.FILE_SHARE_READ,
        # Fail if file doesn't exist.
        create_disposition=CreateDisposition.FILE_OPEN,
        create_options=(
            CreateOptions.FILE_NON_DIRECTORY_FILE
            | CreateOptions.FILE_OPEN_REPARSE_POINT
            ),
        )

@paf91
Copy link
Author

paf91 commented Jul 3, 2024

@adiroiban I'm having issues with low-level API since shared path contains spaces (or maybe UTF-8 symbols). Meanwhile high-level API works fine.

e.g. share could be like "\server\share dir\test П"

image

@adiroiban
Copy link
Contributor

With all the obfuscation and just a screenshot, it's hard to troubleshoot this.

Please provide a short but complete example which demonstrates the problem.

@paf91 paf91 changed the title Unable to read files that are being used by some other users' proccesses on remote server Unable to read files using High-level API that are being used by some other users' proccesses on remote server Jul 3, 2024
@paf91
Copy link
Author

paf91 commented Jul 3, 2024

if you mean the full example, then this would be easiest way:
To reproduce error, you need to have a share name with spaces and UTF-8 symbols, e.g.
\\server\share dir\ПП 123\Д 5\

import logging
import uuid

from smbprotocol.connection import Connection
from smbprotocol.session import Session
from smbprotocol.tree import TreeConnect

log = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)

server = "testsever"
port = 445
username = "smbuser"
password = "smbpassword"
share = rf"\\{server}\share dir\ПП 123\Д 5\"

connection = Connection(uuid.uuid4(), server, port)
connection.connect()

try:
    session = Session(connection, username, password)
    session.connect()
    tree = TreeConnect(session, share)
    tree.connect()
except Exception as e:
   logger.error(msg=f"Can't connect to SMB share: {e}")
finally:
    connection.disconnect(True)

This step
tree = TreeConnect(session, share)
gives error:
The specified share name cannot be found on the remote server

Meanwhile you could easily do access files/dirs in this path using high-level api

@paf91 paf91 changed the title Unable to read files using High-level API that are being used by some other users' proccesses on remote server Unable to read files using that are being used by some other users' proccesses on remote server (High-level API) Jul 3, 2024
@jborean93
Copy link
Owner

Sorry I didn't reply earlier as I was away on leave. The SMB protocol is typically modelled after the Windows sharing mechanism used on Windows. This means that if a file is opened by another process (locally or also through SMB) then the existing and new handle needs to be opened with the required file share flags. In this case you can use the low level API to open the file handle but luckily the high level API also provides a way to open with custom share flags. The open_file method has a share_access kwarg which is documented at .

:param share_access: String that specifies the type of access that is allowed when a handle to this file is opened
by another process. The default is 'None' which exclusively locks the file until the file is closed. The
available access values are:
'r': Allow other handles to be opened with read access.
'w': Allow other handles to be opened with write access.
'd': Allow other handles to be opened with delete access.
A combination of values can be set to allow multiple access types together.

In this case if you want to open it with read file share then you can do share_access='r'. The combination you need to use depends on the access you are requesting, what access you will allow other processes to open it as, and what existing processes with an open handle will allow.

Meanwhile you could easily do access files/dirs in this path using high-level api

While you don't need the low level API as open_file has the share_access kwarg, the two APIs support the same types of paths. At a basic level SMB uses UTF-16-LE encoded strings and in Python a normal string should always support encoding to this format. In your case it's probably failing because the TreeConnect is just meant to be \\{server}\{share} whereas you are giving the full path including the directories and file path inside the share.

@paf91
Copy link
Author

paf91 commented Jul 8, 2024

@jborean93
As stated in issue, shared flag doesn't help

with smb_client.open_file(r"\\server\share\file.txt", mode='rb', share_access='r') as fd:
    file_bytes = fd.read()

I believe it's a bug.
Meanwhile in library pysmb there is no such issue when i copy file using

        with open('text.txt', 'wb') as fp:
            conn.retrieveFile('sharename', '/somefolder/text.txt', fp)

@jborean93
Copy link
Owner

My apologies I missed that you tried the share_access flag. I will have to look into pysmb and see how it opens the file using your example that works. If pysmb can do it then so should this library (unless it’s some special SMB1 things).

@jborean93
Copy link
Owner

Looks like it also allows write share access so try adding w to the share_access kwarg https://github.com/miketeo/pysmb/blob/a527d5a57dc2c16ee26056c88bace39a7623065b/python3/smb/base.py#L930. Keep in mind there is nothing to stop another process from editing the file as you are copying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants