sftp_server_directory.c
Go to the documentation of this file.
1 /**
2  * @file sftp_server_directory.c
3  * @brief Directory 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 SFTP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "sftp/sftp_server.h"
38 #include "sftp/sftp_server_misc.h"
39 #include "path.h"
40 #include "debug.h"
41 
42 //Check SSH stack configuration
43 #if (SFTP_SERVER_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Create a directory
48  * @param[in] session Handle referencing an SFTP session
49  * @param[in] path Directory path
50  * @param[in] attributes Modifications to be made to its attributes
51  * @return Error code
52  **/
53 
55  const SshString *path, const SftpFileAttrs *attributes)
56 {
57  error_t error;
58  uint_t perm;
59  SftpServerContext *context;
60 
61  //Point to the SFTP server context
62  context = session->context;
63 
64  //Retrieve the full pathname
65  error = sftpServerGetPath(session, path, context->path,
67  //Any error to report?
68  if(error)
69  return error;
70 
71  //Retrieve permissions for the specified directory
72  perm = sftpServerGetFilePermissions(session, context->path);
73  //Insufficient access rights?
74  if((perm & SFTP_FILE_PERM_WRITE) == 0)
75  return ERROR_ACCESS_DENIED;
76 
77  //Create the specified directory
78  error = fsCreateDir(context->path);
79 
80  //Return status code
81  return error;
82 }
83 
84 
85 /**
86  * @brief Remove a directory
87  * @param[in] session Handle referencing an SFTP session
88  * @param[in] path Directory path
89  * @return Error code
90  **/
91 
93  const SshString *path)
94 {
95  error_t error;
96  uint_t perm;
97  SftpServerContext *context;
98 
99  //Point to the SFTP server context
100  context = session->context;
101 
102  //Retrieve the full pathname
103  error = sftpServerGetPath(session, path, context->path,
105  //Any error to report?
106  if(error)
107  return error;
108 
109  //Retrieve permissions for the specified directory
110  perm = sftpServerGetFilePermissions(session, context->path);
111  //Insufficient access rights?
112  if((perm & SFTP_FILE_PERM_WRITE) == 0)
113  return ERROR_ACCESS_DENIED;
114 
115  //Remove the specified directory
116  error = fsRemoveDir(context->path);
117 
118  //Return status code
119  return error;
120 }
121 
122 
123 /**
124  * @brief Open a directory
125  * @param[in] session Handle referencing an SFTP session
126  * @param[in] path Path name of the directory to be listed
127  * @param[out] handle Opaque value that identifies the directory
128  * @return Error code
129  **/
130 
132  const SshString *path, uint32_t *handle)
133 {
134  error_t error;
135  uint_t i;
136  uint_t perm;
137  SftpServerContext *context;
138  SftpFileObject *fileObject;
139 
140  //Point to the SFTP server context
141  context = session->context;
142 
143  //Retrieve the full pathname
144  error = sftpServerGetPath(session, path, context->path,
146  //Any error to report?
147  if(error)
148  return error;
149 
150  //Retrieve permissions for the specified directory
151  perm = sftpServerGetFilePermissions(session, context->path);
152  //Insufficient access rights?
153  if((perm & SFTP_FILE_PERM_READ) == 0)
154  return ERROR_ACCESS_DENIED;
155 
156  //Loop through file objects
157  for(i = 0; i < context->numFileObjects; i++)
158  {
159  //Point to the current file object
160  fileObject = &context->fileObjects[i];
161 
162  //Unused file object?
163  if(fileObject->type == SSH_FILEXFER_TYPE_INVALID)
164  {
165  break;
166  }
167  }
168 
169  //Any file object available for use?
170  if(i < context->numFileObjects)
171  {
172  //Open the specified directory
173  fileObject->dir = fsOpenDir(context->path);
174 
175  //Valid handle?
176  if(fileObject->dir != NULL)
177  {
178  //Initialize file object
179  fileObject->type = SSH_FILEXFER_TYPE_DIRECTORY;
180  fileObject->session = session;
181  fileObject->size = 0;
182  fileObject->offset = 0;
183  fileObject->file = NULL;
184 
185  //Save path name
186  osStrcpy(fileObject->path, context->path);
187 
188  //Generate a unique handle
189  fileObject->handle = sftpServerGenerateHandle(session);
190 
191  //The SSH_FXP_OPENDIR request returns a handle which may be used to
192  //access the directory later
193  *handle = fileObject->handle;
194 
195  //The directory was successfully opened
196  error = NO_ERROR;
197  }
198  else
199  {
200  //The specified path name does not exist
201  error = ERROR_OPEN_FAILED;
202  }
203  }
204  else
205  {
206  //The file object table runs out of space
207  error = ERROR_OUT_OF_RESOURCES;
208  }
209 
210  //Return status code
211  return error;
212 }
213 
214 
215 /**
216  * @brief Read an entry from the specified directory
217  * @param[in] session Handle referencing an SFTP session
218  * @param[in] handle Opaque value that identifies the directory
219  * @param[out] name File name being returned
220  * @return Error code
221  **/
222 
224  const SshBinaryString *handle, SftpName *name)
225 {
226  error_t error;
227  uint_t perm;
228  FsDirEntry dirEntry;
229  SftpServerContext *context;
230  SftpFileObject *fileObject;
231 
232  //Initialize status code
233  error = NO_ERROR;
234 
235  //Point to the SFTP server context
236  context = session->context;
237 
238  //Clear name structure
239  osMemset(name, 0, sizeof(SftpName));
240 
241  //The SSH_FXP_OPENDIR request returns a handle which may be used to
242  //access the directory later
243  fileObject = sftpServerFindDir(session, handle);
244  //Invalid handle?
245  if(fileObject == NULL)
246  return ERROR_INVALID_HANDLE;
247 
248  //Loop through the directory
249  while(!error)
250  {
251  //Read a new entry from the directory
252  error = fsReadDir(fileObject->dir, &dirEntry);
253 
254  //Check status code
255  if(!error)
256  {
257  //Retrieve the full pathname
258  pathCopy(context->path, fileObject->path, SFTP_SERVER_MAX_PATH_LEN);
259  pathCombine(context->path, dirEntry.name, SFTP_SERVER_MAX_PATH_LEN);
260  pathCanonicalize(context->path);
261 
262  //Retrieve permissions for the specified file
263  perm = sftpServerGetFilePermissions(session, context->path);
264 
265  //Check access rights?
266  if((perm & SFTP_FILE_PERM_READ) != 0)
267  {
268  //Copy the file name
269  osStrcpy(context->path, dirEntry.name);
270 
271  //File name
272  name->filename.value = context->path;
273  name->filename.length = osStrlen(context->path);
274 
275  //File type
276  if((dirEntry.attributes & FS_FILE_ATTR_DIRECTORY) != 0)
277  {
278  name->attributes.type = SSH_FILEXFER_TYPE_DIRECTORY;
279  }
280  else
281  {
282  name->attributes.type = SSH_FILEXFER_TYPE_REGULAR;
283  }
284 
285  //Size of the file
286  name->attributes.size = dirEntry.size;
287 
288  //File permissions
289  if((dirEntry.attributes & FS_FILE_ATTR_READ_ONLY) != 0)
290  {
291  name->attributes.permissions = SFTP_MODE_IRUSR;
292  }
293  else
294  {
295  name->attributes.permissions = SFTP_MODE_IRUSR | SFTP_MODE_IWUSR;
296  }
297 
298  //Modification time
299  name->attributes.mtime = dirEntry.modified;
300  name->attributes.atime = dirEntry.modified;
301 
302  //Attribute bits
303  if((dirEntry.attributes & FS_FILE_ATTR_READ_ONLY) != 0)
304  {
305  name->attributes.bits |= SSH_FILEXFER_ATTR_FLAGS_READONLY;
306  }
307 
308  if((dirEntry.attributes & FS_FILE_ATTR_SYSTEM) != 0)
309  {
310  name->attributes.bits |= SSH_FILEXFER_ATTR_FLAGS_SYSTEM;
311  }
312 
313  if((dirEntry.attributes & FS_FILE_ATTR_HIDDEN) != 0)
314  {
315  name->attributes.bits |= SSH_FILEXFER_ATTR_FLAGS_HIDDEN;
316  }
317 
318  if((dirEntry.attributes & FS_FILE_ATTR_ARCHIVE) != 0)
319  {
320  name->attributes.bits |= SSH_FILEXFER_ATTR_FLAGS_ARCHIVE;
321  }
322 
323  //Specify which of the attribute fields are present
324  name->attributes.flags = SSH_FILEXFER_ATTR_SIZE |
326 
327  //Successful processing
328  break;
329  }
330  }
331  }
332 
333  //Return status code
334  return error;
335 }
336 
337 
338 /**
339  * @brief Close a directory
340  * @param[in] session Handle referencing an SFTP session
341  * @param[in] handle Opaque value that identifies the directory
342  * @return Error code
343  **/
344 
346  const SshBinaryString *handle)
347 {
348  error_t error;
349  SftpFileObject *fileObject;
350 
351  //The SSH_FXP_OPENDIR request returns a handle which may be used to
352  //access the directory later
353  fileObject = sftpServerFindDir(session, handle);
354 
355  //Any matching directory?
356  if(fileObject != NULL)
357  {
358  //Close directory
359  fsCloseDir(fileObject->dir);
360  fileObject->dir = NULL;
361 
362  //Mark the entry as free
363  fileObject->type = SSH_FILEXFER_TYPE_INVALID;
364 
365  //Successful processing
366  error = NO_ERROR;
367  }
368  else
369  {
370  //The supplied handle is not valid
371  error = ERROR_INVALID_HANDLE;
372  }
373 
374  //Return status code
375  return error;
376 }
377 
378 
379 /**
380  * @brief Find the directory object that matches a given handle
381  * @param[in] session Handle referencing an SFTP session
382  * @param[in] handle Opaque variable-length string
383  * @return Pointer to the matching directory object
384  **/
385 
387  const SshBinaryString *handle)
388 {
389  uint_t i;
390  SftpServerContext *context;
391  SftpFileObject *fileObject;
392 
393  //Point to the SFTP server context
394  context = session->context;
395 
396  //Valid handle?
397  if(handle->length == sizeof(uint32_t))
398  {
399  //Loop through file objects
400  for(i = 0; i < context->numFileObjects; i++)
401  {
402  //Point to the current file object
403  fileObject = &context->fileObjects[i];
404 
405  //The handle can identify a file or a directory
406  if(fileObject->type == SSH_FILEXFER_TYPE_DIRECTORY &&
407  fileObject->session == session &&
408  fileObject->handle == LOAD32BE(handle->value))
409  {
410  //The handle matches the current directory object
411  return fileObject;
412  }
413  }
414  }
415 
416  //The handle does not match any active directory object
417  return NULL;
418 }
419 
420 #endif
error_t sftpServerOpenDir(SftpServerSession *session, const SshString *path, uint32_t *handle)
Open a directory.
@ FS_FILE_ATTR_SYSTEM
Definition: fs_port.h:59
Path manipulation helper functions.
error_t sftpServerCloseDir(SftpServerSession *session, const SshBinaryString *handle)
Close a directory.
#define LOAD32BE(p)
Definition: cpu_endian.h:210
Binary string.
Definition: ssh_types.h:67
FsFile * file
File pointer.
Definition: sftp_server.h:193
error_t sftpServerReadDir(SftpServerSession *session, const SshBinaryString *handle, SftpName *name)
Read an entry from the specified directory.
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
#define SSH_FILEXFER_ATTR_FLAGS_ARCHIVE
Definition: sftp_common.h:73
FsDir * dir
Directory pointer.
Definition: sftp_server.h:194
error_t sftpServerRemoveDir(SftpServerSession *session, const SshString *path)
Remove a directory.
SftpServerSession * session
Pointer to the SFTP session.
Definition: sftp_server.h:188
uint8_t attributes[]
Definition: radius.h:88
char_t name[]
#define osStrlen(s)
Definition: os_port.h:165
error_t fsRemoveDir(const char_t *path)
Remove a directory.
@ ERROR_INVALID_HANDLE
Definition: error.h:281
error_t fsCreateDir(const char_t *path)
Create a directory.
SFTP server.
@ FS_FILE_ATTR_HIDDEN
Definition: fs_port.h:58
FsDir * fsOpenDir(const char_t *path)
Open a directory stream.
@ ERROR_OPEN_FAILED
Definition: error.h:75
size_t length
Definition: ssh_types.h:69
@ SFTP_FILE_PERM_READ
Definition: sftp_server.h:146
@ FS_FILE_ATTR_DIRECTORY
Definition: fs_port.h:61
Directory operations.
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:158
#define SSH_FILEXFER_ATTR_SIZE
Definition: sftp_common.h:50
@ SSH_FILEXFER_TYPE_REGULAR
Definition: sftp_common.h:190
#define SSH_FILEXFER_ATTR_FLAGS_SYSTEM
Definition: sftp_common.h:70
error_t
Error codes.
Definition: error.h:43
SftpFileType type
File type.
Definition: sftp_server.h:187
#define SFTP_SERVER_MAX_PATH_LEN
Definition: sftp_server.h:109
#define SFTP_MODE_IWUSR
Definition: sftp_common.h:92
uint32_t sftpServerGenerateHandle(SftpServerSession *session)
Generate a unique handle.
uint64_t offset
Offset within the file.
Definition: sftp_server.h:192
SftpFileObject * sftpServerFindDir(SftpServerSession *session, const SshBinaryString *handle)
Find the directory object that matches a given handle.
error_t fsReadDir(FsDir *dir, FsDirEntry *dirEntry)
Read an entry from the specified directory stream.
@ ERROR_ACCESS_DENIED
Definition: error.h:148
String.
Definition: ssh_types.h:56
File or directory object.
Definition: sftp_server.h:186
const uint8_t * value
Definition: ssh_types.h:68
#define SSH_FILEXFER_ATTR_ACMODTIME
Definition: sftp_common.h:53
char_t name[FS_MAX_NAME_LEN+1]
Definition: fs_port.h:112
Name structure.
Definition: sftp_common.h:265
@ FS_FILE_ATTR_ARCHIVE
Definition: fs_port.h:62
DateTime modified
Definition: fs_port.h:111
uint64_t size
Size of the file.
Definition: sftp_server.h:191
Directory entry.
Definition: fs_port.h:108
uint32_t attributes
Definition: fs_port.h:109
uint32_t size
Definition: fs_port.h:110
Helper functions for SFTP server.
#define SSH_FILEXFER_ATTR_FLAGS_READONLY
Definition: sftp_common.h:69
char_t path[SFTP_SERVER_MAX_PATH_LEN+1]
Path name.
Definition: sftp_server.h:189
@ SSH_FILEXFER_TYPE_INVALID
Definition: sftp_common.h:189
#define SFTP_MODE_IRUSR
Definition: sftp_common.h:93
#define SSH_FILEXFER_ATTR_PERMISSIONS
Definition: sftp_common.h:52
#define SSH_FILEXFER_ATTR_FLAGS_HIDDEN
Definition: sftp_common.h:71
@ SSH_FILEXFER_TYPE_DIRECTORY
Definition: sftp_common.h:191
uint_t sftpServerGetFilePermissions(SftpServerSession *session, const char_t *path)
Get permissions for the specified file or directory.
@ SFTP_FILE_PERM_WRITE
Definition: sftp_server.h:147
void fsCloseDir(FsDir *dir)
Close a directory stream.
#define SftpServerSession
Definition: sftp_server.h:120
unsigned int uint_t
Definition: compiler_port.h:50
#define osMemset(p, value, length)
Definition: os_port.h:135
File attributes.
Definition: sftp_common.h:247
Secure Shell (SSH)
#define SftpServerContext
Definition: sftp_server.h:116
uint32_t handle
Opaque value that identifies the file.
Definition: sftp_server.h:190
@ FS_FILE_ATTR_READ_ONLY
Definition: fs_port.h:57
error_t sftpServerGetPath(SftpServerSession *session, const SshString *path, char_t *fullPath, size_t maxLen)
Retrieve the full pathname.
error_t sftpServerCreateDir(SftpServerSession *session, const SshString *path, const SftpFileAttrs *attributes)
Create a directory.
#define osStrcpy(s1, s2)
Definition: os_port.h:207
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
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