@@ -154,7 +154,14 @@ class SFTP extends EventEmitter {
154154 this . _pktData = undefined ;
155155 this . _writeReqid = - 1 ;
156156 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 ;
158165
159166 // Channel compatibility
160167 this . _client = client ;
@@ -208,8 +215,8 @@ class SFTP extends EventEmitter {
208215 return ;
209216 if ( this . _pktLen === 0 )
210217 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 ;
213220 return doFatalSFTPError (
214221 this ,
215222 `Packet length ${ this . _pktLen } exceeds max length of ${ max } `
@@ -432,7 +439,7 @@ class SFTP extends EventEmitter {
432439 return ;
433440 }
434441
435- const maxDataLen = this . _maxPktLen - PKT_RW_OVERHEAD ;
442+ const maxDataLen = this . _maxWriteLen ;
436443 const overflow = Math . max ( len - maxDataLen , 0 ) ;
437444 const origPosition = position ;
438445
@@ -1421,7 +1428,7 @@ class SFTP extends EventEmitter {
14211428 throw new Error ( 'Client-only method called in server mode' ) ;
14221429
14231430 const ext = this . _extensions [ '[email protected] ' ] ; 1424- if ( ! ext || ext . indexOf ( '1' ) === - 1 )
1431+ if ( ext !== '1' )
14251432 throw new Error ( 'Server does not support this extended request' ) ;
14261433
14271434 /*
@@ -1461,7 +1468,7 @@ class SFTP extends EventEmitter {
14611468 throw new Error ( 'Client-only method called in server mode' ) ;
14621469
14631470 const ext = this . _extensions [ '[email protected] ' ] ; 1464- if ( ! ext || ext . indexOf ( '1' ) === - 1 )
1471+ if ( ext !== '1' )
14651472 throw new Error ( 'Server does not support this extended request' ) ;
14661473 if ( ! Buffer . isBuffer ( handle ) )
14671474 throw new Error ( 'handle is not a Buffer' ) ;
@@ -1492,6 +1499,103 @@ class SFTP extends EventEmitter {
14921499 `SFTP: Outbound: ${ isBuffered ? 'Buffered' : 'Sending' } [email protected] ` 14931500 ) ;
14941501 }
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 [ '[email protected] ' ] ; 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+ 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 ( '[email protected] ' , 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 } [email protected] ` ) ; 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 [ '[email protected] ' ] ; 1568+ if ( ext !== '1' )
1569+ throw new Error ( 'Server does not support this extended request' ) ;
1570+
1571+ /*
1572+ uint32 id
1573+ 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 ( '[email protected] ' , 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 } [email protected] ` ) ; 1597+ }
1598+ }
14951599 // ===========================================================================
14961600 // Server-specific ===========================================================
14971601 // ===========================================================================
@@ -1760,7 +1864,7 @@ function tryCreateBuffer(size) {
17601864}
17611865
17621866function read_ ( self , handle , buf , off , len , position , cb , req_ ) {
1763- const maxDataLen = self . _maxPktLen - PKT_RW_OVERHEAD ;
1867+ const maxDataLen = self . _maxReadLen ;
17641868 const overflow = Math . max ( len - maxDataLen , 0 ) ;
17651869
17661870 if ( overflow )
@@ -2394,6 +2498,31 @@ function cleanupRequests(sftp) {
23942498 }
23952499}
23962500
2501+ function requestLimits ( sftp , cb ) {
2502+ /*
2503+ uint32 id
2504+ 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 ( '[email protected] ' , p += 4 , 18 ) ; 2516+
2517+ sftp . _requests [ reqid ] = { extended :
'[email protected] ' , cb
} ; 2518+
2519+ const isBuffered = sendOrBuffer ( sftp , buf ) ;
2520+ if ( sftp . _debug ) {
2521+ const which = ( isBuffered ? 'Buffered' : 'Sending' ) ;
2522+ sftp . _debug ( `SFTP: Outbound: ${ which } [email protected] ` ) ; 2523+ }
2524+ }
2525+
23972526const CLIENT_HANDLERS = {
23982527 [ RESPONSE . VERSION ] : ( sftp , payload ) => {
23992528 if ( sftp . _version !== - 1 )
@@ -2434,6 +2563,24 @@ const CLIENT_HANDLERS = {
24342563
24352564 sftp . _version = version ;
24362565 sftp . _extensions = extensions ;
2566+
2567+ if ( extensions [ '[email protected] ' ] === '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+
24372584 sftp . emit ( 'ready' ) ;
24382585 } ,
24392586 [ RESPONSE . STATUS ] : ( sftp , payload ) => {
@@ -2669,6 +2816,32 @@ const CLIENT_HANDLERS = {
26692816 req . cb ( undefined , stats ) ;
26702817 return ;
26712818 }
2819+ 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+ }
26722845 default :
26732846 // Unknown extended request
26742847 sftp . _debug && sftp . _debug (
0 commit comments