scp_server_file.c
Go to the documentation of this file.
1 /**
2  * @file scp_server_file.c
3  * @brief File operations
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneSSH Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SCP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "scp/scp_server.h"
37 #include "scp/scp_server_file.h"
38 #include "scp/scp_server_misc.h"
39 #include "path.h"
40 #include "debug.h"
41 
42 //Check SSH stack configuration
43 #if (SCP_SERVER_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Open a file for writing
48  * @param[in] session Handle referencing an SCP session
49  * @param[in] filename NULL-terminating string that contains the filename
50  * @param[in] mode File access rights
51  * @param[in] size Size of the file, in bytes
52  * @return Error code
53  **/
54 
56  const char_t *filename, uint32_t mode, uint64_t size)
57 {
58  error_t error;
59  uint_t perm;
60  ScpServerContext *context;
61 
62  //Point to the SCP server context
63  context = session->context;
64 
65  //The SCP command line can specify either a file or a directory
66  if(fsDirExists(session->path) || session->recursive)
67  {
68  //Retrieve the full pathname
69  pathCopy(context->path, session->path, SCP_SERVER_MAX_PATH_LEN);
71  pathCanonicalize(context->path);
72  }
73  else
74  {
75  //Copy the full pathname
76  pathCopy(context->path, session->path, SCP_SERVER_MAX_PATH_LEN);
77  }
78 
79  //Retrieve permissions for the specified file
80  perm = scpServerGetFilePermissions(session, context->path);
81 
82  //Check access rights
83  if((perm & SCP_FILE_PERM_WRITE) != 0)
84  {
85  //Open the file for writing
86  session->file = fsOpenFile(context->path, FS_FILE_MODE_WRITE |
88 
89  //Valid file pointer?
90  if(session->file != NULL)
91  {
92  //Save the size of the file
93  session->fileSize = size;
94  session->fileOffset = 0;
95 
96  //Successful processing
97  error = NO_ERROR;
98  }
99  else
100  {
101  //Failed to open the file
102  error = ERROR_FILE_NOT_FOUND;
103  }
104  }
105  else
106  {
107  //Insufficient access rights
108  error = ERROR_ACCESS_DENIED;
109  }
110 
111  //Return status code
112  return error;
113 }
114 
115 
116 /**
117  * @brief Open a file for reading
118  * @param[in] session Handle referencing an SCP session
119  * @return Error code
120  **/
121 
123 {
124  error_t error;
125  uint_t perm;
126  FsFileStat fileStat;
127 
128  //Retrieve the attributes of the specified file
129  error = fsGetFileStat(session->path, &fileStat);
130 
131  //Check status code
132  if(!error)
133  {
134  //Check file type
135  if((fileStat.attributes & FS_FILE_ATTR_DIRECTORY) == 0)
136  {
137  //Retrieve permissions for the specified file
138  perm = scpServerGetFilePermissions(session, session->path);
139 
140  //Check access rights
141  if((perm & SCP_FILE_PERM_READ) != 0)
142  {
143  //Open the file for reading
144  session->file = fsOpenFile(session->path, FS_FILE_MODE_READ);
145 
146  //Valid file pointer?
147  if(session->file != NULL)
148  {
149  //Save the size of the file
150  session->fileSize = fileStat.size;
151  session->fileOffset = 0;
152 
153  //The mode bits determine what actions the owner of the file
154  //can perform on the file
155  if((fileStat.attributes & FS_FILE_ATTR_READ_ONLY) != 0)
156  {
157  session->fileMode = SCP_MODE_IRUSR | SCP_MODE_IRGRP |
159  }
160  else
161  {
162  session->fileMode = SCP_MODE_IRUSR | SCP_MODE_IWUSR |
165  }
166 
167  //Successful processing
168  error = NO_ERROR;
169  }
170  else
171  {
172  //Failed to open the file
173  error = ERROR_FILE_NOT_FOUND;
174  }
175  }
176  else
177  {
178  //Insufficient access rights
179  error = ERROR_ACCESS_DENIED;
180  }
181  }
182  else
183  {
184  //The path name does not reference a file but a directory
185  error = ERROR_FILE_NOT_FOUND;
186  }
187  }
188  else
189  {
190  //The specified file does not exist
191  error = ERROR_FILE_NOT_FOUND;
192  }
193 
194  //Return status code
195  return error;
196 }
197 
198 
199 /**
200  * @brief Write data to the specified file
201  * @param[in] session Handle referencing an SCP session
202  * @return Error code
203  **/
204 
206 {
207  error_t error;
208  size_t n;
209 
210  //Initialize status code
211  error = NO_ERROR;
212 
213  //Receive file content from the SCP client
214  if(session->bufferPos < session->bufferLen)
215  {
216  //Receive more data
217  error = sshReadChannel(session->channel,
218  session->buffer + session->bufferPos,
219  session->bufferLen - session->bufferPos, &n, 0);
220 
221  //Check status code
222  if(!error)
223  {
224  //Advance data pointer
225  session->bufferPos += n;
226  }
227  }
228  else if(session->fileOffset < session->fileSize)
229  {
230  //Any data pending in the buffer?
231  if(session->bufferLen > 0)
232  {
233  //Check the status of the write operation
234  if(session->statusCode == NO_ERROR)
235  {
236  //Write data to the specified file
237  session->statusCode = fsWriteFile(session->file, session->buffer,
238  session->bufferLen);
239  }
240 
241  //Increment file offset
242  session->fileOffset += session->bufferLen;
243  }
244 
245  //Limit the number of bytes to copy at a time
246  if((session->fileSize - session->fileOffset) < SCP_SERVER_BUFFER_SIZE)
247  {
248  n = (size_t) (session->fileSize - session->fileOffset);
249  }
250  else
251  {
253  }
254 
255  //Set up next data transfer
256  session->bufferLen = n;
257  session->bufferPos = 0;
258  }
259  else
260  {
261  //Close file
262  fsCloseFile(session->file);
263  session->file = NULL;
264 
265  //Flush receive buffer
266  session->bufferLen = 0;
267  session->bufferPos = 0;
268 
269  //Update SCP session state
270  if(session->statusCode == NO_ERROR)
271  {
272  session->state = SCP_SERVER_SESSION_STATE_WRITE_STATUS;
273  }
274  else
275  {
276  session->state = SCP_SERVER_SESSION_STATE_ERROR;
277  }
278  }
279 
280  //Return status code
281  return error;
282 }
283 
284 
285 /**
286  * @brief Read data from the specified file
287  * @param[in] session Handle referencing an SCP session
288  * @return error
289  **/
290 
292 {
293  error_t error;
294  size_t n;
295  size_t length;
296 
297  //Initialize status code
298  error = NO_ERROR;
299 
300  //Send file content to the SCP client
301  if(session->bufferPos < session->bufferLen)
302  {
303  //Send more data
304  error = sshWriteChannel(session->channel,
305  session->buffer + session->bufferPos,
306  session->bufferLen - session->bufferPos, &n, 0);
307 
308  //Check status code
309  if(error == NO_ERROR || error == ERROR_TIMEOUT)
310  {
311  //Advance data pointer
312  session->bufferPos += n;
313  }
314  }
315  else if(session->fileOffset < session->fileSize)
316  {
317  //Limit the number of bytes to copy at a time
318  if((session->fileSize - session->fileOffset) < SCP_SERVER_BUFFER_SIZE)
319  {
320  length = (size_t) (session->fileSize - session->fileOffset);
321  }
322  else
323  {
325  }
326 
327  //Read data from the specified file
328  error = fsReadFile(session->file, session->buffer, length, &n);
329 
330  //Check status code
331  if(!error)
332  {
333  //Sanity check
334  if(n == length)
335  {
336  //Increment file offset
337  session->fileOffset += n;
338 
339  //Set up next data transfer
340  session->bufferLen = n;
341  session->bufferPos = 0;
342  }
343  else
344  {
345  //Report an error
346  error = ERROR_READ_FAILED;
347  }
348  }
349  }
350  else
351  {
352  //Close file
353  fsCloseFile(session->file);
354  session->file = NULL;
355 
356  //Recursive copy?
357  if(session->recursive)
358  {
359  //Remove the file name from the path
360  pathRemoveFilename(session->path);
361  pathRemoveSlash(session->path);
362  }
363 
364  //Flush transmit buffer
365  session->bufferLen = 0;
366  session->bufferPos = 0;
367 
368  //Update SCP session state
369  session->state = SCP_SERVER_SESSION_STATE_READ_STATUS;
370  }
371 
372  //Return status code
373  return error;
374 }
375 
376 #endif
Path manipulation helper functions.
error_t scpServerOpenFileForWriting(ScpServerSession *session, const char_t *filename, uint32_t mode, uint64_t size)
Open a file for writing.
@ SCP_SERVER_SESSION_STATE_WRITE_STATUS
Definition: scp_server.h:155
Helper functions for SCP server.
#define SCP_SERVER_BUFFER_SIZE
Definition: scp_server.h:74
error_t scpServerOpenFileForReading(ScpServerSession *session)
Open a file for reading.
#define ScpServerSession
Definition: scp_server.h:113
@ FS_FILE_MODE_WRITE
Definition: fs_port.h:73
@ SCP_FILE_PERM_READ
Definition: scp_server.h:139
#define SCP_SERVER_MAX_PATH_LEN
Definition: scp_server.h:95
error_t sshReadChannel(SshChannel *channel, void *data, size_t size, size_t *received, uint_t flags)
Receive data from the specified channel.
Definition: ssh.c:2180
error_t fsReadFile(FsFile *file, void *data, size_t size, size_t *length)
Read data from the specified file.
@ FS_FILE_ATTR_DIRECTORY
Definition: fs_port.h:61
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:158
error_t scpServerWriteData(ScpServerSession *session)
Write data to the specified file.
error_t
Error codes.
Definition: error.h:43
@ SCP_SERVER_SESSION_STATE_READ_STATUS
Definition: scp_server.h:161
@ ERROR_FILE_NOT_FOUND
Definition: error.h:156
@ SCP_SERVER_SESSION_STATE_ERROR
Definition: scp_server.h:163
void fsCloseFile(FsFile *file)
Close a file.
#define SCP_MODE_IWUSR
Definition: scp_common.h:47
#define SCP_MODE_IRGRP
Definition: scp_common.h:44
error_t scpServerReadData(ScpServerSession *session)
Read data from the specified file.
FsFile * fsOpenFile(const char_t *path, uint_t mode)
Open the specified file for reading or writing.
uint32_t attributes
Definition: fs_port.h:97
char_t filename[]
Definition: tftp_common.h:93
error_t sshWriteChannel(SshChannel *channel, const void *data, size_t length, size_t *written, uint_t flags)
Write data to the specified channel.
Definition: ssh.c:2051
File status.
Definition: fs_port.h:96
uint32_t size
Definition: fs_port.h:98
#define ScpServerContext
Definition: scp_server.h:109
@ ERROR_ACCESS_DENIED
Definition: error.h:148
uint8_t length
Definition: tcp.h:368
#define SCP_MODE_IRUSR
Definition: scp_common.h:48
#define SCP_MODE_IWOTH
Definition: scp_common.h:39
SCP server.
@ SCP_FILE_PERM_WRITE
Definition: scp_server.h:140
File operations.
#define SCP_MODE_IWGRP
Definition: scp_common.h:43
@ ERROR_TIMEOUT
Definition: error.h:95
char char_t
Definition: compiler_port.h:48
uint_t scpServerGetFilePermissions(ScpServerSession *session, const char_t *path)
Get permissions for the specified file or directory.
uint8_t n
@ ERROR_READ_FAILED
Definition: error.h:223
bool_t fsDirExists(const char_t *path)
Check whether a directory exists.
@ FS_FILE_MODE_TRUNC
Definition: fs_port.h:75
error_t fsWriteFile(FsFile *file, void *data, size_t length)
Write data to the specified file.
error_t fsGetFileStat(const char_t *path, FsFileStat *fileStat)
Retrieve the attributes of the specified file.
void pathRemoveSlash(char_t *path)
Remove the trailing slash from a given path.
Definition: path.c:360
@ FS_FILE_MODE_READ
Definition: fs_port.h:72
#define SCP_MODE_IROTH
Definition: scp_common.h:40
unsigned int uint_t
Definition: compiler_port.h:50
Secure Shell (SSH)
@ FS_FILE_ATTR_READ_ONLY
Definition: fs_port.h:57
void pathRemoveFilename(char_t *path)
Remove the trailing file name from the supplied path.
Definition: path.c:120
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
@ FS_FILE_MODE_CREATE
Definition: fs_port.h:74
void pathCopy(char_t *dest, const char_t *src, size_t maxLen)
Copy a path.
Definition: path.c:137
void pathCombine(char_t *path, const char_t *more, size_t maxLen)
Concatenate two paths.
Definition: path.c:394