@@ -154,7 +154,14 @@ class SFTP extends EventEmitter {
154
154
this . _pktData = undefined ;
155
155
this . _writeReqid = - 1 ;
156
156
this . _requests = { } ;
157
- this . _maxPktLen = ( this . _isOpenSSH ? OPENSSH_MAX_PKT_LEN : 34000 ) ;
157
+ this . _maxInPktLen = OPENSSH_MAX_PKT_LEN ;
158
+ this . _maxOutPktLen = 34000 ;
159
+ this . _maxReadLen =
160
+ ( this . _isOpenSSH ? OPENSSH_MAX_PKT_LEN : 34000 ) - PKT_RW_OVERHEAD ;
161
+ this . _maxWriteLen =
162
+ ( this . _isOpenSSH ? OPENSSH_MAX_PKT_LEN : 34000 ) - PKT_RW_OVERHEAD ;
163
+
164
+ this . maxOpenHandles = undefined ;
158
165
159
166
// Channel compatibility
160
167
this . _client = client ;
@@ -208,8 +215,8 @@ class SFTP extends EventEmitter {
208
215
return ;
209
216
if ( this . _pktLen === 0 )
210
217
return doFatalSFTPError ( this , 'Invalid packet length' ) ;
211
- if ( this . _pktLen > this . _maxPktLen ) {
212
- const max = this . _maxPktLen ;
218
+ if ( this . _pktLen > this . _maxInPktLen ) {
219
+ const max = this . _maxInPktLen ;
213
220
return doFatalSFTPError (
214
221
this ,
215
222
`Packet length ${ this . _pktLen } exceeds max length of ${ max } `
@@ -432,7 +439,7 @@ class SFTP extends EventEmitter {
432
439
return ;
433
440
}
434
441
435
- const maxDataLen = this . _maxPktLen - PKT_RW_OVERHEAD ;
442
+ const maxDataLen = this . _maxWriteLen ;
436
443
const overflow = Math . max ( len - maxDataLen , 0 ) ;
437
444
const origPosition = position ;
438
445
@@ -1421,7 +1428,7 @@ class SFTP extends EventEmitter {
1421
1428
throw new Error ( 'Client-only method called in server mode' ) ;
1422
1429
1423
1430
const ext = this . _extensions [ 'hardlink@openssh.com' ] ;
1424
- if ( ! ext || ext . indexOf ( '1' ) === - 1 )
1431
+ if ( ext !== '1' )
1425
1432
throw new Error ( 'Server does not support this extended request' ) ;
1426
1433
1427
1434
/*
@@ -1461,7 +1468,7 @@ class SFTP extends EventEmitter {
1461
1468
throw new Error ( 'Client-only method called in server mode' ) ;
1462
1469
1463
1470
const ext = this . _extensions [ 'fsync@openssh.com' ] ;
1464
- if ( ! ext || ext . indexOf ( '1' ) === - 1 )
1471
+ if ( ext !== '1' )
1465
1472
throw new Error ( 'Server does not support this extended request' ) ;
1466
1473
if ( ! Buffer . isBuffer ( handle ) )
1467
1474
throw new Error ( 'handle is not a Buffer' ) ;
@@ -1492,6 +1499,103 @@ class SFTP extends EventEmitter {
1492
1499
`SFTP: Outbound: ${ isBuffered ? 'Buffered' : 'Sending' } fsync@openssh.com`
1493
1500
) ;
1494
1501
}
1502
+ ext_openssh_lsetstat ( path , attrs , cb ) {
1503
+ if ( this . server )
1504
+ throw new Error ( 'Client-only method called in server mode' ) ;
1505
+
1506
+ const ext = this . _extensions [ 'lsetstat@openssh.com' ] ;
1507
+ if ( ext !== '1' )
1508
+ throw new Error ( 'Server does not support this extended request' ) ;
1509
+
1510
+ let flags = 0 ;
1511
+ let attrsLen = 0 ;
1512
+
1513
+ if ( typeof attrs === 'object' && attrs !== null ) {
1514
+ attrs = attrsToBytes ( attrs ) ;
1515
+ flags = attrs . flags ;
1516
+ attrsLen = attrs . nb ;
1517
+ } else if ( typeof attrs === 'function' ) {
1518
+ cb = attrs ;
1519
+ }
1520
+
1521
+ /*
1522
+ uint32 id
1523
+ string "lsetstat@openssh.com"
1524
+ string path
1525
+ ATTRS attrs
1526
+ */
1527
+ const pathLen = Buffer . byteLength ( path ) ;
1528
+ let p = 9 ;
1529
+ const buf =
1530
+ Buffer . allocUnsafe ( 4 + 1 + 4 + 4 + 20 + 4 + pathLen + 4 + attrsLen ) ;
1531
+
1532
+ writeUInt32BE ( buf , buf . length - 4 , 0 ) ;
1533
+ buf [ 4 ] = REQUEST . EXTENDED ;
1534
+ const reqid = this . _writeReqid = ( this . _writeReqid + 1 ) & MAX_REQID ;
1535
+ writeUInt32BE ( buf , reqid , 5 ) ;
1536
+
1537
+ writeUInt32BE ( buf , 20 , p ) ;
1538
+ buf . utf8Write ( 'lsetstat@openssh.com' , p += 4 , 20 ) ;
1539
+
1540
+ writeUInt32BE ( buf , pathLen , p += 20 ) ;
1541
+ buf . utf8Write ( path , p += 4 , pathLen ) ;
1542
+
1543
+ writeUInt32BE ( buf , flags , p += pathLen ) ;
1544
+ if ( attrsLen ) {
1545
+ p += 4 ;
1546
+
1547
+ if ( attrsLen === ATTRS_BUF . length )
1548
+ buf . set ( ATTRS_BUF , p ) ;
1549
+ else
1550
+ bufferCopy ( ATTRS_BUF , buf , 0 , attrsLen , p ) ;
1551
+
1552
+ p += attrsLen ;
1553
+ }
1554
+
1555
+ this . _requests [ reqid ] = { cb } ;
1556
+
1557
+ const isBuffered = sendOrBuffer ( this , buf ) ;
1558
+ if ( this . _debug ) {
1559
+ const status = ( isBuffered ? 'Buffered' : 'Sending' ) ;
1560
+ this . _debug ( `SFTP: Outbound: ${ status } lsetstat@openssh.com` ) ;
1561
+ }
1562
+ }
1563
+ ext_openssh_expandPath ( path , cb ) {
1564
+ if ( this . server )
1565
+ throw new Error ( 'Client-only method called in server mode' ) ;
1566
+
1567
+ const ext = this . _extensions [ 'expand-path@openssh.com' ] ;
1568
+ if ( ext !== '1' )
1569
+ throw new Error ( 'Server does not support this extended request' ) ;
1570
+
1571
+ /*
1572
+ uint32 id
1573
+ string "expand-path@openssh.com"
1574
+ string path
1575
+ */
1576
+ const pathLen = Buffer . byteLength ( path ) ;
1577
+ let p = 9 ;
1578
+ const buf = Buffer . allocUnsafe ( 4 + 1 + 4 + 4 + 23 + 4 + pathLen ) ;
1579
+
1580
+ writeUInt32BE ( buf , buf . length - 4 , 0 ) ;
1581
+ buf [ 4 ] = REQUEST . EXTENDED ;
1582
+ const reqid = this . _writeReqid = ( this . _writeReqid + 1 ) & MAX_REQID ;
1583
+ writeUInt32BE ( buf , reqid , 5 ) ;
1584
+
1585
+ writeUInt32BE ( buf , 23 , p ) ;
1586
+ buf . utf8Write ( 'expand-path@openssh.com' , p += 4 , 23 ) ;
1587
+
1588
+ writeUInt32BE ( buf , pathLen , p += 20 ) ;
1589
+ buf . utf8Write ( path , p += 4 , pathLen ) ;
1590
+
1591
+ this . _requests [ reqid ] = { cb } ;
1592
+
1593
+ const isBuffered = sendOrBuffer ( this , buf ) ;
1594
+ if ( this . _debug ) {
1595
+ const status = ( isBuffered ? 'Buffered' : 'Sending' ) ;
1596
+ this . _debug ( `SFTP: Outbound: ${ status } expand-path@openssh.com` ) ;
1597
+ }
1598
+ }
1495
1599
// ===========================================================================
1496
1600
// Server-specific ===========================================================
1497
1601
// ===========================================================================
@@ -1760,7 +1864,7 @@ function tryCreateBuffer(size) {
1760
1864
}
1761
1865
1762
1866
function read_ ( self , handle , buf , off , len , position , cb , req_ ) {
1763
- const maxDataLen = self . _maxPktLen - PKT_RW_OVERHEAD ;
1867
+ const maxDataLen = self . _maxReadLen ;
1764
1868
const overflow = Math . max ( len - maxDataLen , 0 ) ;
1765
1869
1766
1870
if ( overflow )
@@ -2394,6 +2498,31 @@ function cleanupRequests(sftp) {
2394
2498
}
2395
2499
}
2396
2500
2501
+ function requestLimits ( sftp , cb ) {
2502
+ /*
2503
+ uint32 id
2504
+ string "limits@openssh.com"
2505
+ */
2506
+ let p = 9 ;
2507
+ const buf = Buffer . allocUnsafe ( 4 + 1 + 4 + 4 + 18 ) ;
2508
+
2509
+ writeUInt32BE ( buf , buf . length - 4 , 0 ) ;
2510
+ buf [ 4 ] = REQUEST . EXTENDED ;
2511
+ const reqid = sftp . _writeReqid = ( sftp . _writeReqid + 1 ) & MAX_REQID ;
2512
+ writeUInt32BE ( buf , reqid , 5 ) ;
2513
+
2514
+ writeUInt32BE ( buf , 18 , p ) ;
2515
+ buf . utf8Write ( 'limits@openssh.com' , p += 4 , 18 ) ;
2516
+
2517
+ sftp . _requests [ reqid ] = { extended : 'limits@openssh.com' , cb } ;
2518
+
2519
+ const isBuffered = sendOrBuffer ( sftp , buf ) ;
2520
+ if ( sftp . _debug ) {
2521
+ const which = ( isBuffered ? 'Buffered' : 'Sending' ) ;
2522
+ sftp . _debug ( `SFTP: Outbound: ${ which } limits@openssh.com` ) ;
2523
+ }
2524
+ }
2525
+
2397
2526
const CLIENT_HANDLERS = {
2398
2527
[ RESPONSE . VERSION ] : ( sftp , payload ) => {
2399
2528
if ( sftp . _version !== - 1 )
@@ -2434,6 +2563,24 @@ const CLIENT_HANDLERS = {
2434
2563
2435
2564
sftp . _version = version ;
2436
2565
sftp . _extensions = extensions ;
2566
+
2567
+ if ( extensions [ 'limits@openssh.com' ] === '1' ) {
2568
+ return requestLimits ( sftp , ( err , limits ) => {
2569
+ if ( ! err ) {
2570
+ if ( limits . maxPktLen > 0 )
2571
+ sftp . _maxOutPktLen = limits . maxPktLen ;
2572
+ if ( limits . maxReadLen > 0 )
2573
+ sftp . _maxReadLen = limits . maxReadLen ;
2574
+ if ( limits . maxWriteLen > 0 )
2575
+ sftp . _maxWriteLen = limits . maxWriteLen ;
2576
+ sftp . maxOpenHandles = (
2577
+ limits . maxOpenHandles > 0 ? limits . maxOpenHandles : Infinity
2578
+ ) ;
2579
+ }
2580
+ sftp . emit ( 'ready' ) ;
2581
+ } ) ;
2582
+ }
2583
+
2437
2584
sftp . emit ( 'ready' ) ;
2438
2585
} ,
2439
2586
[ RESPONSE . STATUS ] : ( sftp , payload ) => {
@@ -2669,6 +2816,32 @@ const CLIENT_HANDLERS = {
2669
2816
req . cb ( undefined , stats ) ;
2670
2817
return ;
2671
2818
}
2819
+ case 'limits@openssh.com' : {
2820
+ /*
2821
+ uint64 max-packet-length
2822
+ uint64 max-read-length
2823
+ uint64 max-write-length
2824
+ uint64 max-open-handles
2825
+ */
2826
+ const limits = {
2827
+ maxPktLen : bufferParser . readUInt64BE ( ) ,
2828
+ maxReadLen : bufferParser . readUInt64BE ( ) ,
2829
+ maxWriteLen : bufferParser . readUInt64BE ( ) ,
2830
+ maxOpenHandles : bufferParser . readUInt64BE ( ) ,
2831
+ } ;
2832
+ if ( limits . maxOpenHandles === undefined )
2833
+ break ;
2834
+ if ( sftp . _debug ) {
2835
+ sftp . _debug (
2836
+ 'SFTP: Inbound: Received EXTENDED_REPLY '
2837
+ + `(id:${ reqID } , ${ req . extended } )`
2838
+ ) ;
2839
+ }
2840
+ bufferParser . clear ( ) ;
2841
+ if ( typeof req . cb === 'function' )
2842
+ req . cb ( undefined , limits ) ;
2843
+ return ;
2844
+ }
2672
2845
default :
2673
2846
// Unknown extended request
2674
2847
sftp . _debug && sftp . _debug (
0 commit comments