Skip to content

Commit e9f35ae

Browse files
committed
Fix long path handling, listdir and delete
1 parent bd081a0 commit e9f35ae

File tree

3 files changed

+118
-58
lines changed

3 files changed

+118
-58
lines changed

adafruit_ble_file_transfer.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ class FileTransferService(Service):
103103

104104
ERR_NO_FILE = 0xb0
105105

106+
DIRECTORY = 0x01
107+
106108

107109
class FileTransferClient:
108110
def __init__(self, service):
@@ -122,14 +124,14 @@ def _readinto(self, buffer):
122124
while read == 0:
123125
try:
124126
read = self._service.raw.readinto(buffer)
125-
except ValueError:
127+
except ValueError as e:
128+
print(e)
126129
long_buffer = bytearray(512)
127130
read = self._service.raw.readinto(long_buffer)
128131
print("long packet", long_buffer[:read])
129132
return read
130133

131134
def read(self, path):
132-
print("read", path)
133135
path = path.encode("utf-8")
134136
chunk_size = 10
135137
encoded = struct.pack(">BIH", FileTransferService.READ, chunk_size, len(path)) + path
@@ -141,68 +143,68 @@ def read(self, path):
141143
while content_length is None or contents_read < content_length:
142144
read = self._readinto(b)
143145
cmd, status, content_length, chunk_length = struct.unpack_from(">BBII", b)
144-
print("got reply", cmd, status, content_length, chunk_length)
145146
if status != FileTransferService.OK:
146147
raise ValueError("Missing file")
147148
if buf is None:
148149
buf = bytearray(content_length)
149150
header_size = struct.calcsize(">BBII")
150-
print(read, header_size)
151151
buf[contents_read:contents_read + (read - header_size)] = b[header_size:read]
152152
contents_read += read - header_size
153-
print("total read", contents_read, buf)
154153
chunk_size = min(10, content_length - contents_read)
155-
print("replying requesting", chunk_size)
156154
encoded = struct.pack(">BBI", FileTransferService.READ, FileTransferService.OK, chunk_size)
157155
r = self._service.raw.write(encoded)
158-
print("return", buf)
159156
return buf
160157

161158
def write(self, path, contents):
162-
print("write", path, contents)
163159
path = path.encode("utf-8")
164160
encoded = struct.pack(">BIH", FileTransferService.WRITE, len(contents), len(path)) + path
165161
r = self._write(encoded)
166162
b = bytearray(struct.calcsize(">BBI"))
167-
print("write", r, encoded)
168163
written = 0
169164
while written < len(contents):
170165
read = self._readinto(b)
171166
cmd, status, free_space = struct.unpack(">BBI", b)
172-
print(cmd, status, free_space)
173167
self._service.raw.write(contents[written:written+free_space])
174168
written += free_space
175169

176170
def mkdir(self, path):
177-
print("mkdir", path)
178171
path = path.encode("utf-8")
179172
encoded = struct.pack(">BH", FileTransferService.MKDIR, len(path)) + path
180173
r = self._write(encoded)
181174

182175
b = bytearray(struct.calcsize(">BB"))
183176
read = self._readinto(b)
184177
cmd, status = struct.unpack(">BB", b)
185-
print(cmd, status)
186178

187179
def listdir(self, path):
188-
print("listdir", path)
180+
paths = []
189181
path = path.encode("utf-8")
190182
chunk_size = 10
191183
encoded = struct.pack(">BH", FileTransferService.LISTDIR, len(path)) + path
192184
r = self._write(encoded)
193-
b = bytearray(struct.calcsize(">BBIIBIH"))
185+
b = bytearray(self._service.raw.incoming_packet_length)
194186
i = 0
195-
total = 10
187+
total = 10 # starting value that will be replaced by the first response
188+
header_size = struct.calcsize(">BBIIBIH")
196189
while i < total:
197190
read = self._readinto(b)
198-
cmd, status, i, total, flags, file_size, path_length = struct.unpack(">BBIIBIH", b)
199-
if i >= total:
200-
break
201-
if len(b) < path_length:
202-
b = bytearray(path_length)
203-
read = self._readinto(b)
204-
path = str(b[:read], "utf-8")
205-
print(i, total, flags, file_size, path)
191+
offset = 0
192+
while offset < read:
193+
cmd, status, i, total, flags, file_size, path_length = struct.unpack_from(">BBIIBIH", b, offset=offset)
194+
if i >= total:
195+
break
196+
path = str(b[offset + header_size:offset+header_size + path_length], "utf-8")
197+
paths.append((path, file_size, flags))
198+
offset += header_size + path_length
199+
return paths
206200

207201
def delete(self, path):
208-
print("delete", path)
202+
path = path.encode("utf-8")
203+
encoded = struct.pack(">BH", FileTransferService.DELETE, len(path)) + path
204+
r = self._write(encoded)
205+
206+
b = bytearray(struct.calcsize(">BB"))
207+
read = self._readinto(b)
208+
cmd, status = struct.unpack(">BB", b)
209+
if status != FileTransferService.OK:
210+
raise ValueError("Missing file")

examples/ble_file_transfer_simpletest.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,21 @@
3131
print(c)
3232
client.mkdir("/world/")
3333
print(client.listdir("/world/"))
34-
client.write("/world/hello.txt", "Hi world".encode("utf-8"))
34+
client.write("/world/hi.txt", "Hi world".encode("utf-8"))
35+
client.write("/world/hello.txt", "Hello world".encode("utf-8"))
36+
c = client.read("/world/hello.txt")
37+
print(c)
38+
print(client.listdir("/world/"))
39+
client.delete("/world/hello.txt")
40+
try:
41+
client.delete("/world/") # should raise an exception
42+
except ValueError:
43+
print("exception correctly raised")
3544
print(client.listdir("/world/"))
36-
time.sleep(5)
45+
client.delete("/world/hi.txt")
46+
client.delete("/world/")
47+
print(client.listdir("/"))
48+
time.sleep(20)
3749
except ConnectionError:
3850
pass
3951

examples/ble_file_transfer_stub_server.py

Lines changed: 78 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,29 @@ def find_dir(path):
3636
i += 1
3737
return parent
3838

39+
def read_packets(buf, *, target_size=None):
40+
if not target_size:
41+
target_size = len(buf)
42+
total_read = 0
43+
buf = memoryview(buf)
44+
while total_read < target_size:
45+
read = service.raw.readinto(buf[total_read:])
46+
total_read += read
47+
48+
return read
49+
50+
def write_packets(buf):
51+
service.raw.write(buf)
52+
53+
def read_complete_path(starting_path, total_length):
54+
path = bytearray(total_length)
55+
current_path_length = len(starting_path)
56+
remaining_path = total_length - current_path_length
57+
path[:current_path_length] = starting_path
58+
if remaining_path > 0:
59+
read_packets(memoryview(path)[current_path_length:], target_size=remaining_path)
60+
return str(path, "utf-8")
61+
3962
packet_buffer = bytearray(256)
4063
out_buffer = bytearray(256)
4164
while True:
@@ -55,58 +78,47 @@ def find_dir(path):
5578
if command == FileTransferService.WRITE:
5679
content_length, path_length = struct.unpack_from(">IH", p, offset=1)
5780
path_start = struct.calcsize(">BIH")
58-
path = str(p[path_start:path_start+path_length], "utf-8")
81+
path = read_complete_path(p[path_start:], path_length)
5982
contents_read = 0
6083
contents = bytearray(content_length)
61-
print("write", content_length, path)
6284
d = find_dir(path)
6385
filename = path.split("/")[-1]
64-
print(filename)
6586
while contents_read < content_length:
6687
next_amount = min(10, content_length - contents_read)
67-
#print(FileTransferService.WRITE, FileTransferService.OK)
68-
service.raw.write(struct.pack(">BBI", FileTransferService.WRITE, FileTransferService.OK, next_amount))
69-
read = service.raw.readinto(packet_buffer)
70-
while read == 0:
71-
read = service.raw.readinto(packet_buffer)
88+
header = struct.pack(">BBI", FileTransferService.WRITE, FileTransferService.OK, next_amount)
89+
write_packets(header)
90+
read = read_packets(packet_buffer, target_size=next_amount)
7291
contents[contents_read:contents_read+read] = packet_buffer[:read]
7392
contents_read += read
74-
print(read, packet_buffer[:read])
7593
d[filename] = contents
7694

7795
elif command == adafruit_ble_file_transfer.FileTransferService.READ:
7896
free_space, path_length = struct.unpack_from(">IH", p, offset=1)
7997
path_start = struct.calcsize(">BIH")
80-
path = str(p[path_start:path_start+path_length], "utf-8")
81-
print("read", path)
98+
path = read_complete_path(p[path_start:], path_length)
8299
d = find_dir(path)
83100
filename = path.split("/")[-1]
84101
if filename not in d:
85102
print("missing path")
86-
service.raw.write(struct.pack(">BBII", FileTransferService.READ, FileTransferService.ERR, 0, 0))
103+
error_response = struct.pack(">BBII", FileTransferService.READ, FileTransferService.ERR, 0, 0)
104+
write_packets(error_response)
87105
continue
88106

89107
contents_sent = 0
90-
contents = stored_data[filename]
108+
contents = d[filename]
91109
while contents_sent < len(contents):
92110
remaining = len(contents) - contents_sent
93111
next_amount = min(remaining, free_space)
94-
print("sending", next_amount)
95112
header = struct.pack(">BBII", FileTransferService.READ, FileTransferService.OK, len(contents), next_amount)
96-
service.raw.write(header + contents[contents_sent:contents_sent + next_amount])
113+
write_packets(header + contents[contents_sent:contents_sent + next_amount])
97114
contents_sent += next_amount
98115

99-
# Wait for the next free amount even if we've sent everything. A 0 free reply can
100-
# confirm everything worked.
101-
read = service.raw.readinto(packet_buffer)
102-
while read == 0:
103-
read = service.raw.readinto(packet_buffer)
116+
read = read_packets(packet_buffer, target_size=struct.calcsize(">BBI"))
104117
confirm, status, free_space = struct.unpack_from(">BBI", p)
105118
elif command == adafruit_ble_file_transfer.FileTransferService.MKDIR:
106119
path_length = struct.unpack_from(">H", p, offset=1)[0]
107120
path_start = struct.calcsize(">BH")
108-
path = str(p[path_start:path_start+path_length], "utf-8")
109-
print("mkdir", path)
121+
path = read_complete_path(p[path_start:], path_length)
110122
pieces = path.split("/")
111123
parent = stored_data
112124
i = 1
@@ -124,30 +136,64 @@ def find_dir(path):
124136
header = struct.pack(">BB", FileTransferService.WRITE, FileTransferService.OK)
125137
else:
126138
header = struct.pack(">BB", FileTransferService.WRITE, FileTransferService.ERR)
127-
service.raw.write(header)
139+
write_packets(header)
128140
elif command == adafruit_ble_file_transfer.FileTransferService.LISTDIR:
129141
path_length = struct.unpack_from(">H", p, offset=1)[0]
130142
path_start = struct.calcsize(">BH")
131-
path = str(p[path_start:path_start+path_length], "utf-8")
132-
print("listdir", path)
143+
path = read_complete_path(p[path_start:], path_length)
133144

134145
# cmd, status, i, total, flags, file_size, path_length = struct.unpack(">BBIIBIH", b)
135146

136147
d = find_dir(path)
137148
if d is None:
138149
error = struct.pack(">BBIIBIH", FileTransferService.WRITE, FileTransferService.ERR, 0, 0, 0, 0, 0)
139-
service.raw.write(error)
150+
write_packets(error)
140151

141-
print(d, type(d))
142152
filenames = sorted(d.keys())
143153
total_files = len(filenames)
144154
for i, filename in enumerate(filenames):
145-
print(i, filename)
146-
header = struct.pack(">BBIIBIH", FileTransferService.WRITE, FileTransferService.OK, i, total_files, 0, 0, 0)
147-
service.raw.write(header + filename.encode("utf-8"))
148-
print("send last entry")
155+
encoded_filename = filename.encode("utf-8")
156+
flags = 0
157+
contents = d[filename]
158+
if isinstance(contents, dict):
159+
flags = FileTransferService.DIRECTORY
160+
else:
161+
content_length = len(contents)
162+
header = struct.pack(">BBIIBIH", FileTransferService.WRITE, FileTransferService.OK, i, total_files, flags, content_length, len(encoded_filename))
163+
packet = header + encoded_filename
164+
write_packets(packet)
165+
149166
header = struct.pack(">BBIIBIH", FileTransferService.WRITE, FileTransferService.OK, total_files, total_files, 0, 0, 0)
150-
service.raw.write(header)
167+
write_packets(header)
168+
elif command == adafruit_ble_file_transfer.FileTransferService.DELETE:
169+
path_length = struct.unpack_from(">H", p, offset=1)[0]
170+
path_start = struct.calcsize(">BH")
171+
path = read_complete_path(p[path_start:], path_length)
172+
d = find_dir(path)
173+
filename = path.split("/")[-1]
174+
if not filename and d:
175+
print("trying to delete directory with contents")
176+
error_response = struct.pack(">BB", FileTransferService.WRITE, FileTransferService.ERR)
177+
write_packets(error_response)
178+
continue
179+
180+
# We're a directory.
181+
if not filename:
182+
path = path[:-1]
183+
filename = path.split("/")[-1]
184+
d = find_dir(path)
185+
186+
if filename not in d:
187+
print("missing path")
188+
error_response = struct.pack(">BB", FileTransferService.WRITE, FileTransferService.ERR)
189+
write_packets(error_response)
190+
continue
191+
ok = True
192+
193+
del d[filename]
194+
195+
header = struct.pack(">BB", FileTransferService.WRITE, FileTransferService.OK)
196+
write_packets(header)
151197
else:
152198
print("unknown command", command)
153199
print("disconnected")

0 commit comments

Comments
 (0)