(CommandList
  children: [
    (Assignment
      keyword: Assign_None
      pairs: [(assign_pair lhs:(LhsName name:tid) op:Equal rhs:{(DQ ('sftp permissions'))} spids:[7])]
      spids: [7]
    )
    (Assignment
      keyword: Assign_None
      pairs: [
        (assign_pair
          lhs: (LhsName name:SERVER_LOG)
          op: Equal
          rhs: {(${ VSub_Name OBJ) (/sftp-server.log)}
          spids: [13]
        )
      ]
      spids: [13]
    )
    (Assignment
      keyword: Assign_None
      pairs: [
        (assign_pair
          lhs: (LhsName name:CLIENT_LOG)
          op: Equal
          rhs: {(${ VSub_Name OBJ) (/sftp.log)}
          spids: [19]
        )
      ]
      spids: [19]
    )
    (Assignment
      keyword: Assign_None
      pairs: [
        (assign_pair
          lhs: (LhsName name:TEST_SFTP_SERVER)
          op: Equal
          rhs: {(${ VSub_Name OBJ) (/sftp-server.sh)}
          spids: [25]
        )
      ]
      spids: [25]
    )
    (FuncDef
      name: prepare_server
      body: 
        (BraceGroup
          children: [
            (SimpleCommand
              words: [
                {(printf)}
                {
                  (DQ ('#!/bin/sh') (EscapedLiteralPart token:<Lit_EscapedChar '\\n'>) ('exec ') 
                    ($ VSub_Name '$SFTPSERVER') (' -el debug3 ') ($ VSub_Star '$*') (' 2>') ($ VSub_Name '$SERVER_LOG') 
                    (EscapedLiteralPart token:<Lit_EscapedChar '\\n'>)
                  )
                }
              ]
              redirects: [
                (Redir
                  op_id: Redir_Great
                  fd: -1
                  arg_word: {($ VSub_Name '$TEST_SFTP_SERVER')}
                  spids: [55]
                )
              ]
            )
            (C {(chmod)} {(a) (Lit_Other '+') (x)} {($ VSub_Name '$TEST_SFTP_SERVER')})
          ]
          spids: [36]
        )
      spids: [32 35]
    )
    (FuncDef
      name: run_client
      body: 
        (BraceGroup
          children: [
            (Pipeline
              children: [
                (C {(echo)} {(DQ ($ VSub_At '$@'))})
                (SimpleCommand
                  words: [{(${ VSub_Name SFTP)} {(-D)} {(${ VSub_Name TEST_SFTP_SERVER)} {(-vvvb)} {(-)}]
                  redirects: [
                    (Redir
                      op_id: Redir_Great
                      fd: -1
                      arg_word: {($ VSub_Name '$CLIENT_LOG')}
                      spids: [100]
                    )
                    (Redir op_id:Redir_GreatAnd fd:2 arg_word:{(1)} spids:[103])
                  ]
                )
              ]
              negated: False
            )
          ]
          spids: [75]
        )
      spids: [71 74]
    )
    (FuncDef
      name: prepare_files
      body: 
        (BraceGroup
          children: [
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_prep)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$1'))}
                  spids: [116]
                )
              ]
              spids: [116]
            )
            (C {(rm)} {(-f)} {(${ VSub_Name COPY)} {(${ VSub_Name COPY) (.1)})
            (AndOr
              children: [
                (C {(test)} {(-d)} {(${ VSub_Name COPY) (.dd)})
                (BraceGroup
                  children: [
                    (Sentence
                      child: 
                        (AndOr
                          children: [
                            (C {(rmdir)} {(${ VSub_Name COPY) (.dd)})
                            (C {(fatal)} {(DQ ('rmdir ') (${ VSub_Name COPY) (.dd))})
                          ]
                          op_id: Op_DPipe
                        )
                      terminator: <Op_Semi ';'>
                    )
                  ]
                  spids: [147]
                )
              ]
              op_id: Op_DAmp
            )
            (AndOr
              children: [
                (C {(test)} {(-z)} {(DQ ($ VSub_Name '$_prep'))})
                (ControlFlow token:<ControlFlow_Return return>)
              ]
              op_id: Op_DAmp
            )
            (AndOr
              children: [
                (C {(sh)} {(-c)} {(DQ ($ VSub_Name '$_prep'))})
                (C {(fail)} 
                  {
                    (DQ ('preparation failed: ') (EscapedLiteralPart token:<Lit_EscapedChar '\\"'>) 
                      ($ VSub_Name '$_prep') (EscapedLiteralPart token:<Lit_EscapedChar '\\"'>)
                    )
                  }
                )
              ]
              op_id: Op_DPipe
            )
          ]
          spids: [113]
        )
      spids: [109 112]
    )
    (FuncDef
      name: postcondition
      body: 
        (BraceGroup
          children: [
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_title)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$1'))}
                  spids: [214]
                )
              ]
              spids: [214]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_check)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$2'))}
                  spids: [220]
                )
              ]
              spids: [220]
            )
            (AndOr
              children: [
                (C {(test)} {(-z)} {(DQ ($ VSub_Name '$_check'))})
                (ControlFlow token:<ControlFlow_Return return>)
              ]
              op_id: Op_DAmp
            )
            (AndOr
              children: [
                (C {(${ VSub_Name TEST_SHELL)} {(-c)} {(DQ ($ VSub_Name '$_check'))})
                (C {(fail)} {(DQ ('postcondition check failed: ') ($ VSub_Name '$_title'))})
              ]
              op_id: Op_DPipe
            )
          ]
          spids: [211]
        )
      spids: [207 210]
    )
    (FuncDef
      name: ro_test
      body: 
        (BraceGroup
          children: [
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_desc)
                  op: Equal
                  rhs: {($ VSub_Number '$1')}
                  spids: [268]
                )
              ]
              spids: [268]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_cmd)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$2'))}
                  spids: [272]
                )
              ]
              spids: [272]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_prep)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$3'))}
                  spids: [278]
                )
              ]
              spids: [278]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_expect_success_post)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$4'))}
                  spids: [284]
                )
              ]
              spids: [284]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_expect_fail_post)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$5'))}
                  spids: [290]
                )
              ]
              spids: [290]
            )
            (C {(verbose)} {(DQ ($ VSub_Name '$tid') (': read-only ') ($ VSub_Name '$_desc'))})
            (C {(prepare_files)} {(DQ ($ VSub_Name '$_prep'))})
            (C {(prepare_server)})
            (AndOr
              children: [
                (C {(run_client)} {(DQ ($ VSub_Name '$_cmd'))})
                (C {(fail)} {(DQ ('plain ') ($ VSub_Name '$_desc') (' failed'))})
              ]
              op_id: Op_DPipe
            )
            (C {(postcondition)} {(DQ ($ VSub_Name '$_desc') (' no-readonly'))} 
              {(DQ ($ VSub_Name '$_expect_success_post'))}
            )
            (C {(prepare_files)} {(DQ ($ VSub_Name '$_prep'))})
            (C {(prepare_server)} {(-R)})
            (AndOr
              children: [
                (C {(run_client)} {(DQ ($ VSub_Name '$_cmd'))})
                (C {(fail)} {(DQ ('read-only ') ($ VSub_Name '$_desc') (' succeeded'))})
              ]
              op_id: Op_DAmp
            )
            (C {(postcondition)} {(DQ ($ VSub_Name '$_desc') (' readonly'))} 
              {(DQ ($ VSub_Name '$_expect_fail_post'))}
            )
          ]
          spids: [265]
        )
      spids: [261 264]
    )
    (FuncDef
      name: perm_test
      body: 
        (BraceGroup
          children: [
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_op)
                  op: Equal
                  rhs: {($ VSub_Number '$1')}
                  spids: [402]
                )
              ]
              spids: [402]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_whitelist_ops)
                  op: Equal
                  rhs: {($ VSub_Number '$2')}
                  spids: [406]
                )
              ]
              spids: [406]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_cmd)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$3'))}
                  spids: [410]
                )
              ]
              spids: [410]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_prep)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$4'))}
                  spids: [416]
                )
              ]
              spids: [416]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_expect_success_post)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$5'))}
                  spids: [422]
                )
              ]
              spids: [422]
            )
            (Assignment
              keyword: Assign_None
              pairs: [
                (assign_pair
                  lhs: (LhsName name:_expect_fail_post)
                  op: Equal
                  rhs: {(DQ ($ VSub_Number '$6'))}
                  spids: [428]
                )
              ]
              spids: [428]
            )
            (C {(verbose)} {(DQ ($ VSub_Name '$tid') (': explicit ') ($ VSub_Name '$_op'))})
            (C {(prepare_files)} {(DQ ($ VSub_Name '$_prep'))})
            (C {(prepare_server)})
            (AndOr
              children: [
                (C {(run_client)} {(DQ ($ VSub_Name '$_cmd'))})
                (C {(fail)} {(DQ ('plain ') ($ VSub_Name '$_op') (' failed'))})
              ]
              op_id: Op_DPipe
            )
            (C {(postcondition)} {(DQ ($ VSub_Name '$_op') (' no white/blacklists'))} 
              {(DQ ($ VSub_Name '$_expect_success_post'))}
            )
            (C {(prepare_files)} {(DQ ($ VSub_Name '$_prep'))})
            (C {(prepare_server)} {(-p)} 
              {($ VSub_Name '$_op') (Lit_Comma ',') ($ VSub_Name '$_whitelist_ops')}
            )
            (AndOr
              children: [
                (C {(run_client)} {(DQ ($ VSub_Name '$_cmd'))})
                (C {(fail)} {(DQ ('whitelisted ') ($ VSub_Name '$_op') (' failed'))})
              ]
              op_id: Op_DPipe
            )
            (C {(postcondition)} {(DQ ($ VSub_Name '$_op') (' whitelisted'))} 
              {(DQ ($ VSub_Name '$_expect_success_post'))}
            )
            (C {(prepare_files)} {(DQ ($ VSub_Name '$_prep'))})
            (C {(prepare_server)} {(-P)} {($ VSub_Name '$_op')})
            (AndOr
              children: [
                (C {(run_client)} {(DQ ($ VSub_Name '$_cmd'))})
                (C {(fail)} {(DQ ('blacklisted ') ($ VSub_Name '$_op') (' succeeded'))})
              ]
              op_id: Op_DAmp
            )
            (C {(postcondition)} {(DQ ($ VSub_Name '$_op') (' blacklisted'))} 
              {(DQ ($ VSub_Name '$_expect_fail_post'))}
            )
            (C {(prepare_files)} {(DQ ($ VSub_Name '$_prep'))})
            (C {(prepare_server)} {(-p)} {($ VSub_Name '$_whitelist_ops')})
            (AndOr
              children: [
                (C {(run_client)} {(DQ ($ VSub_Name '$_cmd'))})
                (C {(fail)} {(DQ ('no whitelist ') ($ VSub_Name '$_op') (' succeeded'))})
              ]
              op_id: Op_DAmp
            )
            (C {(postcondition)} {(DQ ($ VSub_Name '$_op') (' not in whitelist'))} 
              {(DQ ($ VSub_Name '$_expect_fail_post'))}
            )
          ]
          spids: [399]
        )
      spids: [395 398]
    )
    (C {(ro_test)} {(DQ (upload))} {(DQ ('put ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} {(DQ )} 
      {(DQ ('cmp ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} {(DQ ('test ! -f ') ($ VSub_Name '$COPY'))}
    )
    (C {(ro_test)} {(DQ (setstat))} {(DQ ('chmod 0700 ') ($ VSub_Name '$COPY'))} 
      {(DQ ('touch ') ($ VSub_Name '$COPY') ('; chmod 0400 ') ($ VSub_Name '$COPY'))} {(DQ ('test -x ') ($ VSub_Name '$COPY'))} {(DQ ('test ! -x ') ($ VSub_Name '$COPY'))}
    )
    (C {(ro_test)} {(DQ (rm))} {(DQ ('rm ') ($ VSub_Name '$COPY'))} 
      {(DQ ('touch ') ($ VSub_Name '$COPY'))} {(DQ ('test ! -f ') ($ VSub_Name '$COPY'))} {(DQ ('test -f ') ($ VSub_Name '$COPY'))}
    )
    (C {(ro_test)} {(DQ (mkdir))} {(DQ ('mkdir ') (${ VSub_Name COPY) (.dd))} {(DQ )} 
      {(DQ ('test -d ') (${ VSub_Name COPY) (.dd))} {(DQ ('test ! -d ') (${ VSub_Name COPY) (.dd))}
    )
    (C {(ro_test)} {(DQ (rmdir))} {(DQ ('rmdir ') (${ VSub_Name COPY) (.dd))} 
      {(DQ ('mkdir ') (${ VSub_Name COPY) (.dd))} {(DQ ('test ! -d ') (${ VSub_Name COPY) (.dd))} {(DQ ('test -d ') (${ VSub_Name COPY) (.dd))}
    )
    (C {(ro_test)} {(DQ (posix-rename))} 
      {(DQ ('rename ') ($ VSub_Name '$COPY') (' ') (${ VSub_Name COPY) (.1))} {(DQ ('touch ') ($ VSub_Name '$COPY'))} 
      {(DQ ('test -f ') (${ VSub_Name COPY) ('.1 -a ! -f ') ($ VSub_Name '$COPY'))} {(DQ ('test -f ') ($ VSub_Name '$COPY') (' -a ! -f ') (${ VSub_Name COPY) (.1))}
    )
    (C {(ro_test)} {(DQ (oldrename))} 
      {(DQ ('rename -l ') ($ VSub_Name '$COPY') (' ') (${ VSub_Name COPY) (.1))} {(DQ ('touch ') ($ VSub_Name '$COPY'))} 
      {(DQ ('test -f ') (${ VSub_Name COPY) ('.1 -a ! -f ') ($ VSub_Name '$COPY'))} {(DQ ('test -f ') ($ VSub_Name '$COPY') (' -a ! -f ') (${ VSub_Name COPY) (.1))}
    )
    (C {(ro_test)} {(DQ (symlink))} 
      {(DQ ('ln -s ') ($ VSub_Name '$COPY') (' ') (${ VSub_Name COPY) (.1))} {(DQ ('touch ') ($ VSub_Name '$COPY'))} {(DQ ('test -h ') (${ VSub_Name COPY) (.1))} 
      {(DQ ('test ! -h ') (${ VSub_Name COPY) (.1))}
    )
    (C {(ro_test)} {(DQ (hardlink))} {(DQ ('ln ') ($ VSub_Name '$COPY') (' ') (${ VSub_Name COPY) (.1))} 
      {(DQ ('touch ') ($ VSub_Name '$COPY'))} {(DQ ('test -f ') (${ VSub_Name COPY) (.1))} {(DQ ('test ! -f ') (${ VSub_Name COPY) (.1))}
    )
    (C {(perm_test)} {(DQ (open))} {(DQ ('realpath,stat,lstat,read,close'))} 
      {(DQ ('get ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} {(DQ )} {(DQ ('cmp ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} 
      {(DQ ('! cmp ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY') (' 2>/dev/null'))}
    )
    (C {(perm_test)} {(DQ (read))} {(DQ ('realpath,stat,lstat,open,close'))} 
      {(DQ ('get ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} {(DQ )} {(DQ ('cmp ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} 
      {(DQ ('! cmp ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY') (' 2>/dev/null'))}
    )
    (C {(perm_test)} {(DQ (write))} {(DQ ('realpath,stat,lstat,open,close'))} 
      {(DQ ('put ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} {(DQ )} {(DQ ('cmp ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} 
      {(DQ ('! cmp ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY') (' 2>/dev/null'))}
    )
    (C {(perm_test)} {(DQ (lstat))} {(DQ ('realpath,stat,open,read,close'))} 
      {(DQ ('get ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} {(DQ )} {(DQ ('cmp ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY'))} 
      {(DQ ('! cmp ') ($ VSub_Name '$DATA') (' ') ($ VSub_Name '$COPY') (' 2>/dev/null'))}
    )
    (C {(perm_test)} {(DQ (opendir))} {(DQ ('realpath,readdir,stat,lstat'))} 
      {(DQ ('ls -ln ') ($ VSub_Name '$OBJ'))}
    )
    (C {(perm_test)} {(DQ (readdir))} {(DQ ('realpath,opendir,stat,lstat'))} 
      {(DQ ('ls -ln ') ($ VSub_Name '$OBJ'))}
    )
    (C {(perm_test)} {(DQ (setstat))} {(DQ ('realpath,stat,lstat'))} 
      {(DQ ('chmod 0700 ') ($ VSub_Name '$COPY'))} {(DQ ('touch ') ($ VSub_Name '$COPY') ('; chmod 0400 ') ($ VSub_Name '$COPY'))} 
      {(DQ ('test -x ') ($ VSub_Name '$COPY'))} {(DQ ('test ! -x ') ($ VSub_Name '$COPY'))}
    )
    (C {(perm_test)} {(DQ (remove))} {(DQ ('realpath,stat,lstat'))} {(DQ ('rm ') ($ VSub_Name '$COPY'))} 
      {(DQ ('touch ') ($ VSub_Name '$COPY'))} {(DQ ('test ! -f ') ($ VSub_Name '$COPY'))} {(DQ ('test -f ') ($ VSub_Name '$COPY'))}
    )
    (C {(perm_test)} {(DQ (mkdir))} {(DQ ('realpath,stat,lstat'))} 
      {(DQ ('mkdir ') (${ VSub_Name COPY) (.dd))} {(DQ )} {(DQ ('test -d ') (${ VSub_Name COPY) (.dd))} {(DQ ('test ! -d ') (${ VSub_Name COPY) (.dd))}
    )
    (C {(perm_test)} {(DQ (rmdir))} {(DQ ('realpath,stat,lstat'))} 
      {(DQ ('rmdir ') (${ VSub_Name COPY) (.dd))} {(DQ ('mkdir ') (${ VSub_Name COPY) (.dd))} {(DQ ('test ! -d ') (${ VSub_Name COPY) (.dd))} 
      {(DQ ('test -d ') (${ VSub_Name COPY) (.dd))}
    )
    (C {(perm_test)} {(DQ (posix-rename))} {(DQ ('realpath,stat,lstat'))} 
      {(DQ ('rename ') ($ VSub_Name '$COPY') (' ') (${ VSub_Name COPY) (.1))} {(DQ ('touch ') ($ VSub_Name '$COPY'))} 
      {(DQ ('test -f ') (${ VSub_Name COPY) ('.1 -a ! -f ') ($ VSub_Name '$COPY'))} {(DQ ('test -f ') ($ VSub_Name '$COPY') (' -a ! -f ') (${ VSub_Name COPY) (.1))}
    )
    (C {(perm_test)} {(DQ (rename))} {(DQ ('realpath,stat,lstat'))} 
      {(DQ ('rename -l ') ($ VSub_Name '$COPY') (' ') (${ VSub_Name COPY) (.1))} {(DQ ('touch ') ($ VSub_Name '$COPY'))} 
      {(DQ ('test -f ') (${ VSub_Name COPY) ('.1 -a ! -f ') ($ VSub_Name '$COPY'))} {(DQ ('test -f ') ($ VSub_Name '$COPY') (' -a ! -f ') (${ VSub_Name COPY) (.1))}
    )
    (C {(perm_test)} {(DQ (symlink))} {(DQ ('realpath,stat,lstat'))} 
      {(DQ ('ln -s ') ($ VSub_Name '$COPY') (' ') (${ VSub_Name COPY) (.1))} {(DQ ('touch ') ($ VSub_Name '$COPY'))} {(DQ ('test -h ') (${ VSub_Name COPY) (.1))} 
      {(DQ ('test ! -h ') (${ VSub_Name COPY) (.1))}
    )
    (C {(perm_test)} {(DQ (hardlink))} {(DQ ('realpath,stat,lstat'))} 
      {(DQ ('ln ') ($ VSub_Name '$COPY') (' ') (${ VSub_Name COPY) (.1))} {(DQ ('touch ') ($ VSub_Name '$COPY'))} {(DQ ('test -f ') (${ VSub_Name COPY) (.1))} 
      {(DQ ('test ! -f ') (${ VSub_Name COPY) (.1))}
    )
    (C {(perm_test)} {(DQ (statvfs))} {(DQ ('realpath,stat,lstat'))} {(DQ ('df /'))})
    (C {(rm)} {(-rf)} {(${ VSub_Name COPY)} {(${ VSub_Name COPY) (.1)} {(${ VSub_Name COPY) (.dd)})
  ]
)