1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2005-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% 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, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22 23%%% Description: SFTP functions 24 25-module(ssh_xfer). 26 27-export([open/6, opendir/3, readdir/3, close/3, read/5, write/5, 28 rename/5, remove/3, mkdir/4, rmdir/3, realpath/3, extended/4, 29 stat/4, fstat/4, lstat/4, setstat/4, 30 readlink/3, fsetstat/4, symlink/4, 31 protocol_version_request/2, 32 xf_reply/2, 33 xf_send_reply/3, xf_send_names/3, xf_send_name/4, 34 xf_send_status/3, xf_send_status/4, xf_send_status/5, 35 xf_send_handle/3, xf_send_attr/3, xf_send_data/3, 36 encode_erlang_status/1, 37 decode_open_flags/2, encode_open_flags/1, 38 decode_ace_mask/1, decode_ext/1, 39 decode_ATTR/2, encode_ATTR/2]). 40 41-include("ssh.hrl"). 42-include("ssh_xfer.hrl"). 43 44-import(lists, [foldl/3, reverse/1]). 45 46-define(is_set(F, Bits), 47 ((F) band (Bits)) == (F)). 48 49protocol_version_request(XF, Version) -> 50 xf_request(XF, ?SSH_FXP_INIT, <<?UINT32(Version)>>). 51 52open(XF, ReqID, FileName, Access, Flags, Attrs) -> 53 Vsn = XF#ssh_xfer.vsn, 54 MBits = if Vsn >= 5 -> 55 M = encode_ace_mask(Access), 56 ?uint32(M); 57 true -> 58 (<<>>) 59 end, 60 F = encode_open_flags(Flags), 61 xf_request(XF,?SSH_FXP_OPEN, 62 [?uint32(ReqID), 63 ?string_utf8(FileName), 64 MBits, 65 ?uint32(F), 66 encode_ATTR(Vsn,Attrs)]). 67 68opendir(XF, ReqID, DirName) -> 69 xf_request(XF, ?SSH_FXP_OPENDIR, 70 [?uint32(ReqID), 71 ?string_utf8(DirName)]). 72 73 74close(XF, ReqID, Handle) -> 75 xf_request(XF, ?SSH_FXP_CLOSE, 76 [?uint32(ReqID), 77 ?binary(Handle)]). 78 79read(XF, ReqID, Handle, Offset, Length) -> 80 xf_request(XF, ?SSH_FXP_READ, 81 [?uint32(ReqID), 82 ?binary(Handle), 83 ?uint64(Offset), 84 ?uint32(Length)]). 85 86readdir(XF, ReqID, Handle) -> 87 xf_request(XF, ?SSH_FXP_READDIR, 88 [?uint32(ReqID), 89 ?binary(Handle)]). 90 91write(XF,ReqID, Handle, Offset, Data) -> 92 Data1 = if 93 is_binary(Data) -> 94 Data; 95 is_list(Data) -> 96 ?to_binary(Data) 97 end, 98 xf_request(XF,?SSH_FXP_WRITE, 99 [?uint32(ReqID), 100 ?binary(Handle), 101 ?uint64(Offset), 102 ?binary(Data1)]). 103 104%% Remove a file 105remove(XF, ReqID, File) -> 106 xf_request(XF, ?SSH_FXP_REMOVE, 107 [?uint32(ReqID), 108 ?string_utf8(File)]). 109 110%% Rename a file/directory 111rename(XF, ReqID, OldPath, NewPath, Flags) -> 112 Vsn = XF#ssh_xfer.vsn, 113 FlagBits 114 = if Vsn >= 5 -> 115 F0 = encode_rename_flags(Flags), 116 ?uint32(F0); 117 true -> 118 (<<>>) 119 end, 120 xf_request(XF, ?SSH_FXP_RENAME, 121 [?uint32(ReqID), 122 ?string_utf8(OldPath), 123 ?string_utf8(NewPath), 124 FlagBits]). 125 126 127 128%% Create directory 129mkdir(XF, ReqID, Path, Attrs) -> 130 xf_request(XF, ?SSH_FXP_MKDIR, 131 [?uint32(ReqID), 132 ?string_utf8(Path), 133 encode_ATTR(XF#ssh_xfer.vsn, Attrs)]). 134 135%% Remove a directory 136rmdir(XF, ReqID, Dir) -> 137 xf_request(XF, ?SSH_FXP_RMDIR, 138 [?uint32(ReqID), 139 ?string_utf8(Dir)]). 140 141%% Stat file 142stat(XF, ReqID, Path, Flags) -> 143 Vsn = XF#ssh_xfer.vsn, 144 AttrFlags = if Vsn >= 5 -> 145 F = encode_attr_flags(Vsn, Flags), 146 ?uint32(F); 147 true -> 148 [] 149 end, 150 xf_request(XF, ?SSH_FXP_STAT, 151 [?uint32(ReqID), 152 ?string_utf8(Path), 153 AttrFlags]). 154 155 156%% Stat file - follow symbolic links 157lstat(XF, ReqID, Path, Flags) -> 158 Vsn = XF#ssh_xfer.vsn, 159 AttrFlags = if Vsn >= 5 -> 160 F = encode_attr_flags(Vsn, Flags), 161 ?uint32(F); 162 true -> 163 [] 164 end, 165 xf_request(XF, ?SSH_FXP_LSTAT, 166 [?uint32(ReqID), 167 ?string_utf8(Path), 168 AttrFlags]). 169 170%% Stat open file 171fstat(XF, ReqID, Handle, Flags) -> 172 Vsn = XF#ssh_xfer.vsn, 173 AttrFlags = if Vsn >= 5 -> 174 F = encode_attr_flags(Vsn, Flags), 175 ?uint32(F); 176 true -> 177 [] 178 end, 179 xf_request(XF, ?SSH_FXP_FSTAT, 180 [?uint32(ReqID), 181 ?binary(Handle), 182 AttrFlags]). 183 184%% Modify file attributes 185setstat(XF, ReqID, Path, Attrs) -> 186 xf_request(XF, ?SSH_FXP_SETSTAT, 187 [?uint32(ReqID), 188 ?string_utf8(Path), 189 encode_ATTR(XF#ssh_xfer.vsn, Attrs)]). 190 191 192%% Modify file attributes 193fsetstat(XF, ReqID, Handle, Attrs) -> 194 xf_request(XF, ?SSH_FXP_FSETSTAT, 195 [?uint32(ReqID), 196 ?binary(Handle), 197 encode_ATTR(XF#ssh_xfer.vsn, Attrs)]). 198 199%% Read a symbolic link 200readlink(XF, ReqID, Path) -> 201 xf_request(XF, ?SSH_FXP_READLINK, 202 [?uint32(ReqID), 203 ?string_utf8(Path)]). 204 205 206%% Create a symbolic link 207symlink(XF, ReqID, LinkPath, TargetPath) -> 208 LinkPath1 = unicode:characters_to_binary(LinkPath), 209 TargetPath1 = unicode:characters_to_binary(TargetPath), 210 xf_request(XF, ?SSH_FXP_SYMLINK, 211 [?uint32(ReqID), 212 ?binary(LinkPath1), 213 ?binary(TargetPath1)]). 214 215%% Convert a path into a 'canonical' form 216realpath(XF, ReqID, Path) -> 217 xf_request(XF, ?SSH_FXP_REALPATH, 218 [?uint32(ReqID), 219 ?string_utf8(Path)]). 220 221extended(XF, ReqID, Request, Data) -> 222 xf_request(XF, ?SSH_FXP_EXTENDED, 223 [?uint32(ReqID), 224 ?string(Request), 225 ?binary(Data)]). 226 227 228%% Send xfer request to connection manager 229xf_request(XF, Op, Arg) -> 230 CM = XF#ssh_xfer.cm, 231 Channel = XF#ssh_xfer.channel, 232 Data = if 233 is_binary(Arg) -> 234 Arg; 235 is_list(Arg) -> 236 ?to_binary(Arg) 237 end, 238 Size = 1+size(Data), 239 ssh_connection:send(CM, Channel, [<<?UINT32(Size), Op, Data/binary>>]). 240 241xf_send_reply(#ssh_xfer{cm = CM, channel = Channel}, Op, Arg) -> 242 Data = if 243 is_binary(Arg) -> 244 Arg; 245 is_list(Arg) -> 246 ?to_binary(Arg) 247 end, 248 Size = 1 + size(Data), 249 ssh_connection:send(CM, Channel, [<<?UINT32(Size), Op, Data/binary>>]). 250 251xf_send_name(XF, ReqId, Name, Attr) -> 252 xf_send_names(XF, ReqId, [{Name, Attr}]). 253 254 255xf_send_handle(#ssh_xfer{cm = CM, channel = Channel}, 256 ReqId, Handle) -> 257 HLen = length(Handle), 258 Size = 1 + 4 + 4+HLen, 259 ToSend = [<<?UINT32(Size), ?SSH_FXP_HANDLE, ?UINT32(ReqId), ?UINT32(HLen)>>, 260 Handle], 261 ssh_connection:send(CM, Channel, ToSend). 262 263xf_send_names(#ssh_xfer{cm = CM, channel = Channel, vsn = Vsn}, 264 ReqId, NamesAndAttrs) -> 265 Count = length(NamesAndAttrs), 266 {Data, Len} = encode_names(Vsn, NamesAndAttrs), 267 Size = 1 + 4 + 4 + Len, 268 ToSend = [<<?UINT32(Size), 269 ?SSH_FXP_NAME, 270 ?UINT32(ReqId), 271 ?UINT32(Count)>>, 272 Data], 273 ssh_connection:send(CM, Channel, ToSend). 274 275xf_send_status(XF, ReqId, ErrorCode) -> 276 xf_send_status(XF, ReqId, ErrorCode, ""). 277 278xf_send_status(XF, ReqId, ErrorCode, ErrorMsg) -> 279 xf_send_status(XF, ReqId, ErrorCode, ErrorMsg, <<>>). 280 281xf_send_status(#ssh_xfer{cm = CM, channel = Channel}, 282 ReqId, ErrorCode, ErrorMsg, Data) -> 283 LangTag = "en", 284 ELen = length(ErrorMsg), 285 TLen = 2, %% length(LangTag), 286 Size = 1 + 4 + 4 + 4+ELen + 4+TLen + size(Data), 287 ToSend = [<<?UINT32(Size), ?SSH_FXP_STATUS, ?UINT32(ReqId), 288 ?UINT32(ErrorCode)>>, 289 <<?UINT32(ELen)>>, ErrorMsg, 290 <<?UINT32(TLen)>>, LangTag, 291 Data], 292 ssh_connection:send(CM, Channel, ToSend). 293 294xf_send_attr(#ssh_xfer{cm = CM, channel = Channel, vsn = Vsn}, ReqId, Attr) -> 295 EncAttr = encode_ATTR(Vsn, Attr), 296 ALen = size(EncAttr), 297 Size = 1 + 4 + ALen, 298 ToSend = [<<?UINT32(Size), ?SSH_FXP_ATTRS, ?UINT32(ReqId)>>, EncAttr], 299 ssh_connection:send(CM, Channel, ToSend). 300 301xf_send_data(#ssh_xfer{cm = CM, channel = Channel}, ReqId, Data) -> 302 DLen = size(Data), 303 Size = 1 + 4 + 4+DLen, 304 ToSend = [<<?UINT32(Size), ?SSH_FXP_DATA, ?UINT32(ReqId), ?UINT32(DLen)>>, 305 Data], 306 ssh_connection:send(CM, Channel, ToSend). 307 308xf_reply(_XF, << ?SSH_FXP_STATUS, ?UINT32(ReqID), ?UINT32(Status), 309 ?UINT32(ELen), Err:ELen/binary, 310 ?UINT32(LLen), Lang:LLen/binary, 311 Reply/binary >> ) -> 312 Stat = decode_status(Status), 313 {status, ReqID, {Stat,binary_to_list(Err),binary_to_list(Lang), 314 Reply}}; 315xf_reply(_XF, << ?SSH_FXP_STATUS, ?UINT32(ReqID), ?UINT32(Status)>> ) -> 316 Stat = decode_status(Status), 317 {status, ReqID, {Stat,"","",<<>>}}; 318xf_reply(_XF, <<?SSH_FXP_HANDLE, ?UINT32(ReqID), 319 ?UINT32(HLen), Handle:HLen/binary>>) -> 320 {handle, ReqID, Handle}; 321xf_reply(_XF, <<?SSH_FXP_DATA, ?UINT32(ReqID), 322 ?UINT32(DLen), Data:DLen/binary>>) -> 323 {data, ReqID, Data}; 324xf_reply(XF, <<?SSH_FXP_NAME, ?UINT32(ReqID), 325 ?UINT32(Count), AData/binary>>) -> 326 {name, ReqID, decode_names(XF#ssh_xfer.vsn, Count, AData)}; 327xf_reply(XF, <<?SSH_FXP_ATTRS, ?UINT32(ReqID), 328 AData/binary>>) -> 329 {A, _} = decode_ATTR(XF#ssh_xfer.vsn, AData), 330 {attrs, ReqID, A}; 331xf_reply(_XF, <<?SSH_FXP_EXTENDED_REPLY, ?UINT32(ReqID), 332 RData>>) -> 333 {extended_reply, ReqID, RData}. 334 335 336 337decode_status(Status) -> 338 case Status of 339 ?SSH_FX_OK -> ok; 340 ?SSH_FX_EOF -> eof; 341 ?SSH_FX_NO_SUCH_FILE -> no_such_file; 342 ?SSH_FX_PERMISSION_DENIED -> permission_denied; 343 ?SSH_FX_FAILURE -> failure; 344 ?SSH_FX_BAD_MESSAGE -> bad_message; 345 ?SSH_FX_NO_CONNECTION -> no_connection; 346 ?SSH_FX_CONNECTION_LOST -> connection_lost; 347 ?SSH_FX_OP_UNSUPPORTED -> op_unsupported; 348 ?SSH_FX_INVALID_HANDLE -> invalid_handle; 349 ?SSH_FX_NO_SUCH_PATH -> no_such_path; 350 ?SSH_FX_FILE_ALREADY_EXISTS -> file_already_exists; 351 ?SSH_FX_WRITE_PROTECT -> write_protect; 352 ?SSH_FX_NO_MEDIA -> no_media; 353 ?SSH_FX_NO_SPACE_ON_FILESYSTEM -> no_space_on_filesystem; 354 ?SSH_FX_QUOTA_EXCEEDED -> quota_exceeded; 355 ?SSH_FX_UNKNOWN_PRINCIPLE -> unknown_principle; 356 ?SSH_FX_LOCK_CONFlICT -> lock_conflict; 357 ?SSH_FX_NOT_A_DIRECTORY -> not_a_directory; 358 ?SSH_FX_FILE_IS_A_DIRECTORY -> file_is_a_directory; 359 ?SSH_FX_CANNOT_DELETE -> cannot_delete; 360 _ -> {error,Status} 361 end. 362 363encode_erlang_status(Status) -> 364 case Status of 365 ok -> ?SSH_FX_OK; 366 eof -> ?SSH_FX_EOF; 367 enoent -> ?SSH_FX_NO_SUCH_FILE; 368 eacces -> ?SSH_FX_PERMISSION_DENIED; 369 eisdir -> ?SSH_FX_FILE_IS_A_DIRECTORY; 370 eperm -> ?SSH_FX_CANNOT_DELETE; 371 eexist -> ?SSH_FX_FILE_ALREADY_EXISTS; 372 _ -> ?SSH_FX_FAILURE 373 end. 374 375decode_ext(<<?UINT32(NameLen), Name:NameLen/binary, 376 ?UINT32(DataLen), Data:DataLen/binary, 377 Tail/binary>>) -> 378 [{binary_to_list(Name), binary_to_list(Data)} 379 | decode_ext(Tail)]; 380decode_ext(<<>>) -> 381 []. 382 383%% 384%% Encode rename flags 385%% 386encode_rename_flags(Flags) -> 387 encode_bits( 388 fun(overwrite) -> ?SSH_FXP_RENAME_OVERWRITE; 389 (atomic) -> ?SSH_FXP_RENAME_ATOMIC; 390 (native) -> ?SSH_FXP_RENAME_NATIVE 391 end, Flags). 392 393%% decode_rename_flags(F) -> 394%% decode_bits(F, 395%% [{?SSH_FXP_RENAME_OVERWRITE, overwrite}, 396%% {?SSH_FXP_RENAME_ATOMIC, atomic}, 397%% {?SSH_FXP_RENAME_NATIVE, native}]). 398 399 400encode_open_flags(Flags) -> 401 encode_bits( 402 fun (read) -> ?SSH_FXF_READ; 403 (write) -> ?SSH_FXF_WRITE; 404 (append) -> ?SSH_FXF_APPEND; 405 (creat) -> ?SSH_FXF_CREAT; 406 (trunc) -> ?SSH_FXF_TRUNC; 407 (excl) -> ?SSH_FXF_EXCL; 408 (create_new) -> ?SSH_FXF_CREATE_NEW; 409 (create_truncate) -> ?SSH_FXF_CREATE_TRUNCATE; 410 (open_existing) -> ?SSH_FXF_OPEN_EXISTING; 411 (open_or_create) -> ?SSH_FXF_OPEN_OR_CREATE; 412 (truncate_existing) -> ?SSH_FXF_TRUNCATE_EXISTING; 413 (append_data) -> ?SSH_FXF_ACCESS_APPEND_DATA; 414 (append_data_atomic) -> ?SSH_FXF_ACCESS_APPEND_DATA_ATOMIC; 415 (text_mode) -> ?SSH_FXF_ACCESS_TEXT_MODE; 416 (read_lock) -> ?SSH_FXF_ACCESS_READ_LOCK; 417 (write_lock) -> ?SSH_FXF_ACCESS_WRITE_LOCK; 418 (delete_lock) -> ?SSH_FXF_ACCESS_DELETE_LOCK 419 end, Flags). 420 421encode_ace_mask(Access) -> 422 encode_bits( 423 fun(read_data) -> ?ACE4_READ_DATA; 424 (list_directory) -> ?ACE4_LIST_DIRECTORY; 425 (write_data) -> ?ACE4_WRITE_DATA; 426 (add_file) -> ?ACE4_ADD_FILE; 427 (append_data) -> ?ACE4_APPEND_DATA; 428 (add_subdirectory) -> ?ACE4_ADD_SUBDIRECTORY; 429 (read_named_attrs) -> ?ACE4_READ_NAMED_ATTRS; 430 (write_named_attrs) -> ?ACE4_WRITE_NAMED_ATTRS; 431 (execute) -> ?ACE4_EXECUTE; 432 (delete_child) -> ?ACE4_DELETE_CHILD; 433 (read_attributes) -> ?ACE4_READ_ATTRIBUTES; 434 (write_attributes) -> ?ACE4_WRITE_ATTRIBUTES; 435 (delete) -> ?ACE4_DELETE; 436 (read_acl) -> ?ACE4_READ_ACL; 437 (write_acl) -> ?ACE4_WRITE_ACL; 438 (write_owner) -> ?ACE4_WRITE_OWNER; 439 (synchronize) -> ?ACE4_SYNCHRONIZE 440 end, Access). 441 442decode_ace_mask(F) -> 443 decode_bits(F, 444 [ 445 {?ACE4_READ_DATA, read_data}, 446 {?ACE4_LIST_DIRECTORY, list_directory}, 447 {?ACE4_WRITE_DATA, write_data}, 448 {?ACE4_ADD_FILE, add_file}, 449 {?ACE4_APPEND_DATA, append_data}, 450 {?ACE4_ADD_SUBDIRECTORY, add_subdirectory}, 451 {?ACE4_READ_NAMED_ATTRS, read_named_attrs}, 452 {?ACE4_WRITE_NAMED_ATTRS, write_named_attrs}, 453 {?ACE4_EXECUTE, execute}, 454 {?ACE4_DELETE_CHILD, delete_child}, 455 {?ACE4_READ_ATTRIBUTES, read_attributes}, 456 {?ACE4_WRITE_ATTRIBUTES, write_attributes}, 457 {?ACE4_DELETE, delete}, 458 {?ACE4_READ_ACL, read_acl}, 459 {?ACE4_WRITE_ACL, write_acl}, 460 {?ACE4_WRITE_OWNER, write_owner}, 461 {?ACE4_SYNCHRONIZE, synchronize} 462 ]). 463 464decode_open_flags(Vsn, F) when Vsn =< 3 -> 465 decode_bits(F, 466 [ 467 {?SSH_FXF_READ, read}, 468 {?SSH_FXF_WRITE, write}, 469 {?SSH_FXF_APPEND, append}, 470 {?SSH_FXF_CREAT, creat}, 471 {?SSH_FXF_TRUNC, trunc}, 472 {?SSH_FXF_EXCL, excl} 473 ]); 474decode_open_flags(Vsn, F) when Vsn >= 4 -> 475 R = decode_bits(F, 476 [ 477 {?SSH_FXF_ACCESS_APPEND_DATA, append_data}, 478 {?SSH_FXF_ACCESS_APPEND_DATA_ATOMIC, append_data_atomic}, 479 {?SSH_FXF_ACCESS_TEXT_MODE, text_mode}, 480 {?SSH_FXF_ACCESS_READ_LOCK, read_lock}, 481 {?SSH_FXF_ACCESS_WRITE_LOCK, write_lock}, 482 {?SSH_FXF_ACCESS_DELETE_LOCK, delete_lock} 483 ]), 484 AD = case F band ?SSH_FXF_ACCESS_DISPOSITION of 485 ?SSH_FXF_CREATE_NEW -> create_new; 486 ?SSH_FXF_CREATE_TRUNCATE -> create_truncate; 487 ?SSH_FXF_OPEN_EXISTING -> open_existing; 488 ?SSH_FXF_OPEN_OR_CREATE -> open_or_create; 489 ?SSH_FXF_TRUNCATE_EXISTING -> truncate_existing 490 end, 491 [AD | R]. 492 493encode_ace_type(Type) -> 494 case Type of 495 access_allowed -> ?ACE4_ACCESS_ALLOWED_ACE_TYPE; 496 access_denied -> ?ACE4_ACCESS_DENIED_ACE_TYPE; 497 system_audit -> ?ACE4_SYSTEM_AUDIT_ACE_TYPE; 498 system_alarm -> ?ACE4_SYSTEM_ALARM_ACE_TYPE 499 end. 500 501decode_ace_type(F) -> 502 case F of 503 ?ACE4_ACCESS_ALLOWED_ACE_TYPE -> access_allowed; 504 ?ACE4_ACCESS_DENIED_ACE_TYPE -> access_denied; 505 ?ACE4_SYSTEM_AUDIT_ACE_TYPE -> system_audit; 506 ?ACE4_SYSTEM_ALARM_ACE_TYPE -> system_alarm 507 end. 508 509encode_ace_flag(Flag) -> 510 encode_bits( 511 fun(file_inherit) -> ?ACE4_FILE_INHERIT_ACE; 512 (directory_inherit) -> ?ACE4_DIRECTORY_INHERIT_ACE; 513 (no_propagte_inherit) -> ?ACE4_NO_PROPAGATE_INHERIT_ACE; 514 (inherit_only) -> ?ACE4_INHERIT_ONLY_ACE; 515 (successful_access) -> ?ACE4_SUCCESSFUL_ACCESS_ACE_FLAG; 516 (failed_access) -> ?ACE4_FAILED_ACCESS_ACE_FLAG; 517 (identifier_group) -> ?ACE4_IDENTIFIER_GROUP 518 end, Flag). 519 520decode_ace_flag(F) -> 521 decode_bits(F, 522 [ 523 {?ACE4_FILE_INHERIT_ACE, file_inherit}, 524 {?ACE4_DIRECTORY_INHERIT_ACE, directory_inherit}, 525 {?ACE4_NO_PROPAGATE_INHERIT_ACE, no_propagte_inherit}, 526 {?ACE4_INHERIT_ONLY_ACE, inherit_only}, 527 {?ACE4_SUCCESSFUL_ACCESS_ACE_FLAG, successful_access}, 528 {?ACE4_FAILED_ACCESS_ACE_FLAG, failed_access}, 529 {?ACE4_IDENTIFIER_GROUP, identifier_group} 530 ]). 531 532encode_attr_flags(Vsn, all) -> 533 encode_attr_flags(Vsn, 534 [size, uidgid, permissions, 535 acmodtime, accesstime, createtime, 536 modifytime, acl, ownergroup, subsecond_times, 537 bits, extended]); 538encode_attr_flags(Vsn, Flags) -> 539 encode_bits( 540 fun(size) -> ?SSH_FILEXFER_ATTR_SIZE; 541 (uidgid) when Vsn =<3 -> ?SSH_FILEXFER_ATTR_UIDGID; 542 (permissions) -> ?SSH_FILEXFER_ATTR_PERMISSIONS; 543 (acmodtime) when Vsn =< 3 -> ?SSH_FILEXFER_ATTR_ACMODTIME; 544 (accesstime) when Vsn >= 5 -> ?SSH_FILEXFER_ATTR_ACCESSTIME; 545 (createtime) when Vsn >= 5 -> ?SSH_FILEXFER_ATTR_CREATETIME; 546 (modifytime) when Vsn >= 5 -> ?SSH_FILEXFER_ATTR_MODIFYTIME; 547 (acl) when Vsn >= 5 -> ?SSH_FILEXFER_ATTR_ACL; 548 (ownergroup) when Vsn >= 5 -> ?SSH_FILEXFER_ATTR_OWNERGROUP; 549 (subsecond_times) when Vsn >= 5 -> ?SSH_FILEXFER_ATTR_SUBSECOND_TIMES; 550 (bits) when Vsn >= 5 -> ?SSH_FILEXFER_ATTR_BITS; 551 (extended) when Vsn >= 5 -> ?SSH_FILEXFER_ATTR_EXTENDED; 552 (_) -> 0 553 end, Flags). 554 555encode_file_type(Type) -> 556 case Type of 557 regular -> ?SSH_FILEXFER_TYPE_REGULAR; 558 directory -> ?SSH_FILEXFER_TYPE_DIRECTORY; 559 symlink -> ?SSH_FILEXFER_TYPE_SYMLINK; 560 special -> ?SSH_FILEXFER_TYPE_SPECIAL; 561 unknown -> ?SSH_FILEXFER_TYPE_UNKNOWN; 562 other -> ?SSH_FILEXFER_TYPE_UNKNOWN; 563 socket -> ?SSH_FILEXFER_TYPE_SOCKET; 564 char_device -> ?SSH_FILEXFER_TYPE_CHAR_DEVICE; 565 block_device -> ?SSH_FILEXFER_TYPE_BLOCK_DEVICE; 566 fifo -> ?SSH_FILEXFER_TYPE_FIFO; 567 undefined -> ?SSH_FILEXFER_TYPE_UNKNOWN 568 end. 569 570decode_file_type(Type) -> 571 case Type of 572 ?SSH_FILEXFER_TYPE_REGULAR -> regular; 573 ?SSH_FILEXFER_TYPE_DIRECTORY -> directory; 574 ?SSH_FILEXFER_TYPE_SYMLINK -> symlink; 575 ?SSH_FILEXFER_TYPE_SPECIAL -> special; 576 ?SSH_FILEXFER_TYPE_UNKNOWN -> other; % unknown 577 ?SSH_FILEXFER_TYPE_SOCKET -> socket; 578 ?SSH_FILEXFER_TYPE_CHAR_DEVICE -> char_device; 579 ?SSH_FILEXFER_TYPE_BLOCK_DEVICE -> block_device; 580 ?SSH_FILEXFER_TYPE_FIFO -> fifo 581 end. 582 583encode_attrib_bits(Bits) -> 584 encode_bits( 585 fun(readonly) -> ?SSH_FILEXFER_ATTR_FLAGS_READONLY; 586 (system) -> ?SSH_FILEXFER_ATTR_FLAGS_SYSTEM; 587 (hidden) -> ?SSH_FILEXFER_ATTR_FLAGS_HIDDEN; 588 (case_insensitive) -> ?SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE; 589 (arcive) -> ?SSH_FILEXFER_ATTR_FLAGS_ARCHIVE; 590 (encrypted) -> ?SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED; 591 (compressed) -> ?SSH_FILEXFER_ATTR_FLAGS_COMPRESSED; 592 (sparse) -> ?SSH_FILEXFER_ATTR_FLAGS_SPARSE; 593 (append_only) -> ?SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY; 594 (immutable) -> ?SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE; 595 (sync) -> ?SSH_FILEXFER_ATTR_FLAGS_SYNC 596 end, Bits). 597 598decode_attrib_bits(F) -> 599 decode_bits(F, 600 [{?SSH_FILEXFER_ATTR_FLAGS_READONLY, readonly}, 601 {?SSH_FILEXFER_ATTR_FLAGS_SYSTEM, system}, 602 {?SSH_FILEXFER_ATTR_FLAGS_HIDDEN, hidden}, 603 {?SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE, case_insensitive}, 604 {?SSH_FILEXFER_ATTR_FLAGS_ARCHIVE, arcive}, 605 {?SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED, encrypted}, 606 {?SSH_FILEXFER_ATTR_FLAGS_COMPRESSED, compressed}, 607 {?SSH_FILEXFER_ATTR_FLAGS_SPARSE, sparse}, 608 {?SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY, append_only}, 609 {?SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE, immutable}, 610 {?SSH_FILEXFER_ATTR_FLAGS_SYNC, sync}]). 611 612 613%% 614%% Encode file attributes 615%% 616encode_ATTR(Vsn, A) -> 617 {Flags,As} = 618 encode_As(Vsn, 619 [{size, A#ssh_xfer_attr.size}, 620 {ownergroup, A#ssh_xfer_attr.owner}, 621 {ownergroup, A#ssh_xfer_attr.group}, 622 {permissions, A#ssh_xfer_attr.permissions}, 623 {acmodtime, A#ssh_xfer_attr.atime}, 624 {acmodtime, A#ssh_xfer_attr.mtime}, 625 {accesstime, A#ssh_xfer_attr.atime}, 626 {subsecond_times, A#ssh_xfer_attr.atime_nseconds}, 627 {createtime, A#ssh_xfer_attr.createtime}, 628 {subsecond_times, A#ssh_xfer_attr.createtime_nseconds}, 629 {modifytime, A#ssh_xfer_attr.mtime}, 630 {subsecond_times, A#ssh_xfer_attr.mtime_nseconds}, 631 {acl, A#ssh_xfer_attr.acl}, 632 {bits, A#ssh_xfer_attr.attrib_bits}, 633 {extended, A#ssh_xfer_attr.extensions}], 634 0, []), 635 Type = encode_file_type(A#ssh_xfer_attr.type), 636 Result = list_to_binary([?uint32(Flags), 637 if Vsn >= 5 -> 638 ?byte(Type); 639 true -> 640 (<<>>) 641 end, As]), 642 Result. 643 644 645encode_As(Vsn, [{_AName, undefined}|As], Flags, Acc) -> 646 encode_As(Vsn, As, Flags, Acc); 647encode_As(Vsn, [{AName, X}|As], Flags, Acc) -> 648 case AName of 649 size -> 650 encode_As(Vsn, As,Flags bor ?SSH_FILEXFER_ATTR_SIZE, 651 [?uint64(X) | Acc]); 652 ownergroup when Vsn=<4 -> 653 encode_As(Vsn, As,Flags bor ?SSH_FILEXFER_ATTR_UIDGID, 654 [?uint32(X) | Acc]); 655 ownergroup when Vsn>=5 -> 656 X1 = list_to_binary(integer_to_list(X)), % TODO: check owner and group 657 encode_As(Vsn, As,Flags bor ?SSH_FILEXFER_ATTR_OWNERGROUP, 658 [?binary(X1) | Acc]); 659 permissions -> 660 encode_As(Vsn, As,Flags bor ?SSH_FILEXFER_ATTR_PERMISSIONS, 661 [?uint32(X) | Acc]); 662 acmodtime when Vsn=<3 -> 663 encode_As(Vsn, As,Flags bor ?SSH_FILEXFER_ATTR_ACMODTIME, 664 [?uint32(X) | Acc]); 665 accesstime when Vsn>=5 -> 666 encode_As(Vsn, As, Flags bor ?SSH_FILEXFER_ATTR_ACCESSTIME, 667 [?uint64(X) | Acc]); 668 createtime when Vsn>=5-> 669 encode_As(Vsn, As, Flags bor ?SSH_FILEXFER_ATTR_CREATETIME, 670 [?uint64(X) | Acc]); 671 modifytime when Vsn>=5 -> 672 encode_As(Vsn, As, Flags bor ?SSH_FILEXFER_ATTR_MODIFYTIME, 673 [?uint64(X) | Acc]); 674 subsecond_times when Vsn>=5 -> 675 encode_As(Vsn, As, Flags bor ?SSH_FILEXFER_ATTR_SUBSECOND_TIMES, 676 [?uint64(X) | Acc]); 677 acl when Vsn >=5 -> 678 encode_As(Vsn, As, Flags bor ?SSH_FILEXFER_ATTR_ACL, 679 [encode_acl(X) | Acc]); 680 bits when Vsn>=5 -> 681 F = encode_attrib_bits(X), 682 encode_As(Vsn, As, Flags bor ?SSH_FILEXFER_ATTR_BITS, 683 [?uint32(F) | Acc]); 684 extended -> 685 encode_As(Vsn, As, Flags bor ?SSH_FILEXFER_ATTR_EXTENDED, 686 [encode_extensions(X) | Acc]); 687 _ -> 688 encode_As(Vsn, As, Flags, Acc) 689 end; 690encode_As(_Vsn, [], Flags, Acc) -> 691 {Flags, reverse(Acc)}. 692 693 694decode_ATTR(Vsn, <<?UINT32(Flags), Tail/binary>>) -> 695 {Type,Tail2} = 696 if Vsn =< 3 -> 697 {?SSH_FILEXFER_TYPE_UNKNOWN, Tail}; 698 true -> 699 <<?BYTE(T), TL/binary>> = Tail, 700 {T, TL} 701 end, 702 decode_As(Vsn, 703 [{size, #ssh_xfer_attr.size}, 704 {ownergroup, #ssh_xfer_attr.owner}, 705 {ownergroup, #ssh_xfer_attr.group}, 706 {permissions, #ssh_xfer_attr.permissions}, 707 {acmodtime, #ssh_xfer_attr.atime}, 708 {acmodtime, #ssh_xfer_attr.mtime}, 709 {accesstime, #ssh_xfer_attr.atime}, 710 {subsecond_times, #ssh_xfer_attr.atime_nseconds}, 711 {createtime, #ssh_xfer_attr.createtime}, 712 {subsecond_times, #ssh_xfer_attr.createtime_nseconds}, 713 {modifytime, #ssh_xfer_attr.mtime}, 714 {subsecond_times, #ssh_xfer_attr.mtime_nseconds}, 715 {acl, #ssh_xfer_attr.acl}, 716 {bits, #ssh_xfer_attr.attrib_bits}, 717 {extended, #ssh_xfer_attr.extensions}], 718 #ssh_xfer_attr { type = decode_file_type(Type) }, 719 Flags, 720 Tail2). 721 722decode_As(Vsn, [{AName, AField}|As], R, Flags, Tail) -> 723 case AName of 724 size when ?is_set(?SSH_FILEXFER_ATTR_SIZE, Flags) -> 725 <<?UINT64(X), Tail2/binary>> = Tail, 726 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 727 ownergroup when ?is_set(?SSH_FILEXFER_ATTR_UIDGID, Flags),Vsn=<3 -> 728 <<?UINT32(X), Tail2/binary>> = Tail, 729 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 730 ownergroup when ?is_set(?SSH_FILEXFER_ATTR_OWNERGROUP, Flags),Vsn>=5 -> 731 <<?UINT32(Len), Bin:Len/binary, Tail2/binary>> = Tail, 732 X = binary_to_list(Bin), 733 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 734 735 permissions when ?is_set(?SSH_FILEXFER_ATTR_PERMISSIONS,Flags),Vsn>=5-> 736 <<?UINT32(X), Tail2/binary>> = Tail, 737 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 738 739 permissions when ?is_set(?SSH_FILEXFER_ATTR_PERMISSIONS,Flags),Vsn=<3-> 740 <<?UINT32(X), Tail2/binary>> = Tail, 741 R1 = setelement(AField, R, X), 742 Type = case X band ?S_IFMT of 743 ?S_IFDIR -> directory; 744 ?S_IFCHR -> char_device; 745 ?S_IFBLK -> block_device; 746 ?S_IFIFO -> fifi; 747 ?S_IFREG -> regular; 748 ?S_IFSOCK -> socket; 749 ?S_IFLNK -> symlink; 750 _ -> unknown 751 end, 752 decode_As(Vsn, As, R1#ssh_xfer_attr { type=Type}, Flags, Tail2); 753 754 acmodtime when ?is_set(?SSH_FILEXFER_ATTR_ACMODTIME,Flags),Vsn=<3 -> 755 <<?UINT32(X), Tail2/binary>> = Tail, 756 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 757 accesstime when ?is_set(?SSH_FILEXFER_ATTR_ACCESSTIME,Flags),Vsn>=5 -> 758 <<?UINT64(X), Tail2/binary>> = Tail, 759 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 760 modifytime when ?is_set(?SSH_FILEXFER_ATTR_MODIFYTIME,Flags),Vsn>=5 -> 761 <<?UINT64(X), Tail2/binary>> = Tail, 762 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 763 createtime when ?is_set(?SSH_FILEXFER_ATTR_CREATETIME,Flags),Vsn>=5 -> 764 <<?UINT64(X), Tail2/binary>> = Tail, 765 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 766 subsecond_times when ?is_set(?SSH_FILEXFER_ATTR_SUBSECOND_TIMES,Flags),Vsn>=5 -> 767 <<?UINT32(X), Tail2/binary>> = Tail, 768 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 769 acl when ?is_set(?SSH_FILEXFER_ATTR_ACL, Flags), Vsn>=5 -> 770 {X,Tail2} = decode_acl(Tail), 771 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 772 bits when ?is_set(?SSH_FILEXFER_ATTR_BITS, Flags), Vsn >=5 -> 773 <<?UINT32(Y), Tail2/binary>> = Tail, 774 X = decode_attrib_bits(Y), 775 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 776 extended when ?is_set(?SSH_FILEXFER_ATTR_EXTENDED, Flags) -> 777 {X,Tail2} = decode_extended(Tail), 778 decode_As(Vsn, As, setelement(AField, R, X), Flags, Tail2); 779 _ -> 780 decode_As(Vsn, As, R, Flags, Tail) 781 end; 782decode_As(_Vsn, [], R, _, Tail) -> 783 {R, Tail}. 784 785 786 787 788decode_names(_Vsn, 0, _Data) -> 789 []; 790decode_names(Vsn, I, <<?UINT32(Len), FileName:Len/binary, 791 ?UINT32(LLen), _LongName:LLen/binary, 792 Tail/binary>>) when Vsn =< 3 -> 793 Name = unicode:characters_to_list(FileName), 794 {A, Tail2} = decode_ATTR(Vsn, Tail), 795 [{Name, A} | decode_names(Vsn, I-1, Tail2)]; 796decode_names(Vsn, I, <<?UINT32(Len), FileName:Len/binary, 797 Tail/binary>>) when Vsn >= 4 -> 798 Name = unicode:characters_to_list(FileName), 799 {A, Tail2} = decode_ATTR(Vsn, Tail), 800 [{Name, A} | decode_names(Vsn, I-1, Tail2)]. 801 802encode_names(Vsn, NamesAndAttrs) -> 803 lists:mapfoldl(fun(N, L) -> encode_name(Vsn, N, L) end, 0, NamesAndAttrs). 804 805encode_name(Vsn, {NameUC,Attr}, Len) when Vsn =< 3 -> 806 Name = binary_to_list(unicode:characters_to_binary(NameUC)), 807 NLen = length(Name), 808 EncAttr = encode_ATTR(Vsn, Attr), 809 ALen = size(EncAttr), 810 NewLen = Len + NLen*2 + 4 + 4 + ALen, 811 {[<<?UINT32(NLen)>>, Name, <<?UINT32(NLen)>>, Name, EncAttr], NewLen}; 812encode_name(Vsn, {NameUC,Attr}, Len) when Vsn >= 4 -> 813 Name = binary_to_list(unicode:characters_to_binary(NameUC)), 814 NLen = length(Name), 815 EncAttr = encode_ATTR(Vsn, Attr), 816 ALen = size(EncAttr), 817 {[<<?UINT32(NLen)>>, Name, EncAttr], 818 Len + 4 + NLen + ALen}. 819 820encode_acl(ACLList) -> 821 Count = length(ACLList), 822 [?uint32(Count) | encode_acl_items(ACLList)]. 823 824encode_acl_items([ACE|As]) -> 825 Type = encode_ace_type(ACE#ssh_xfer_ace.type), 826 Flag = encode_ace_flag(ACE#ssh_xfer_ace.flag), 827 Mask = encode_ace_mask(ACE#ssh_xfer_ace.mask), 828 Who = ACE#ssh_xfer_ace.who, 829 [?uint32(Type), ?uint32(Flag), ?uint32(Mask), 830 ?string_utf8(Who) | encode_acl_items(As)]; 831encode_acl_items([]) -> 832 []. 833 834 835decode_acl(<<?UINT32(Count), Tail/binary>>) -> 836 decode_acl_items(Count, Tail, []). 837 838decode_acl_items(0, Tail, Acc) -> 839 {reverse(Acc), Tail}; 840decode_acl_items(I, <<?UINT32(Type), 841 ?UINT32(Flag), 842 ?UINT32(Mask), 843 ?UINT32(WLen), BWho:WLen/binary, 844 Tail/binary>>, Acc) -> 845 decode_acl_items(I-1, Tail, 846 [#ssh_xfer_ace { type = decode_ace_type(Type), 847 flag = decode_ace_flag(Flag), 848 mask = decode_ace_mask(Mask), 849 who = unicode:characters_to_list(BWho)} | Acc]). 850 851encode_extensions(Exts) -> 852 Count = length(Exts), 853 [?uint32(Count) | encode_ext(Exts)]. 854 855encode_ext([{Type, Data} | Exts]) -> 856 [?string(Type), ?string(Data) | encode_ext(Exts)]; 857encode_ext([]) -> 858 []. 859 860 861decode_extended(<<?UINT32(Count), Tail/binary>>) -> 862 decode_ext(Count, Tail, []). 863 864decode_ext(0, Tail, Acc) -> 865 {reverse(Acc), Tail}; 866decode_ext(I, <<?UINT32(TLen), Type:TLen/binary, 867 ?UINT32(DLen), Data:DLen/binary, 868 Tail/binary>>, Acc) -> 869 decode_ext(I-1, Tail, [{binary_to_list(Type), Data}|Acc]). 870 871 872 873%% Encode bit encoded flags 874encode_bits(Fun, BitNames) -> 875 encode_bits(Fun, 0, BitNames). 876 877encode_bits(Fun, F, [Bit|BitNames]) -> 878 encode_bits(Fun, Fun(Bit) bor F, BitNames); 879encode_bits(_Fun, F, []) -> 880 F. 881 882%% Decode bit encoded flags 883decode_bits(F, [{Bit,BitName}|Bits]) -> 884 if F band Bit == Bit -> 885 [BitName | decode_bits(F, Bits)]; 886 true -> 887 decode_bits(F, Bits) 888 end; 889decode_bits(_F, []) -> 890 []. 891