mirror of
https://github.com/open62541/open62541.git
synced 2026-10-16 20:33:18 +08:00
feat(driver): open files with the Session of the caller (Part 20 4.2.2)
The backend open gets the caller as a context: the Session of Open, CreateFile and the copies of MoveOrCopy, NULL for direct calls. Its result reaches the client.
This commit is contained in:
committed by
Andreas Ebner
parent
3350689aed
commit
0503c8fd97
@@ -458,8 +458,8 @@ buildLocalPath(const LocalFileSystemContext *ctx, const UA_String relPath,
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
localFsOpen(UA_FileTransferFileBackend *b, const UA_String path,
|
||||
UA_Byte mode, UA_UInt32 *handle) {
|
||||
localFsOpen(UA_FileTransferFileBackend *b, const UA_String path, UA_Byte mode,
|
||||
const UA_FileTransferOpenContext *context, UA_UInt32 *handle) {
|
||||
LocalFileSystemContext *ctx = (LocalFileSystemContext*)b->context;
|
||||
char localPath[FT_PATH_MAX];
|
||||
UA_StatusCode res = buildLocalPath(ctx, path, localPath);
|
||||
|
||||
@@ -249,17 +249,26 @@ deleteBackendTree(UA_FileTransferBackend *b, const UA_String path,
|
||||
|
||||
/* Copy a single file. Source and target may live in different backends. Uses
|
||||
* the backend fast-path only within one backend, otherwise streams the content
|
||||
* through read/write loops. */
|
||||
* through read/write loops. The files are opened for the caller of the copy. */
|
||||
static UA_StatusCode
|
||||
copyBackendFile(UA_FileTransferBackend *srcB, const UA_String fromPath,
|
||||
UA_FileTransferBackend *dstB, const UA_String toPath) {
|
||||
UA_FileTransferBackend *dstB, const UA_String toPath,
|
||||
const FTTreeAccess *access) {
|
||||
if(srcB == dstB && dstB->copy)
|
||||
return dstB->copy(dstB, fromPath, toPath);
|
||||
|
||||
UA_FileTransferOpenContext caller;
|
||||
const UA_FileTransferOpenContext *context = NULL;
|
||||
if(access) {
|
||||
caller.server = access->server;
|
||||
caller.sessionId = access->sessionId;
|
||||
caller.sessionContext = access->sessionContext;
|
||||
context = &caller;
|
||||
}
|
||||
UA_FileTransferFileBackend *srcF = &srcB->file;
|
||||
UA_FileTransferFileBackend *dstF = &dstB->file;
|
||||
UA_UInt32 src = 0;
|
||||
UA_StatusCode res = srcF->open(srcF, fromPath, UA_OPENFILEMODE_READ, &src);
|
||||
UA_StatusCode res = srcF->open(srcF, fromPath, UA_OPENFILEMODE_READ, context, &src);
|
||||
if(res != UA_STATUSCODE_GOOD)
|
||||
return res;
|
||||
res = createBackendEntry(dstB, toPath, false);
|
||||
@@ -268,7 +277,7 @@ copyBackendFile(UA_FileTransferBackend *srcB, const UA_String fromPath,
|
||||
return res;
|
||||
}
|
||||
UA_UInt32 dst = 0;
|
||||
res = dstF->open(dstF, toPath, UA_OPENFILEMODE_WRITE, &dst);
|
||||
res = dstF->open(dstF, toPath, UA_OPENFILEMODE_WRITE, context, &dst);
|
||||
if(res != UA_STATUSCODE_GOOD) {
|
||||
/* Remove the empty file just created so a failed open does not leave
|
||||
* a stale entry behind in the destination backend. */
|
||||
@@ -311,7 +320,7 @@ copyBackendTree(UA_FileTransferBackend *srcB, const UA_String fromPath,
|
||||
if(res != UA_STATUSCODE_GOOD)
|
||||
return res;
|
||||
if(!isDir)
|
||||
return copyBackendFile(srcB, fromPath, dstB, toPath);
|
||||
return copyBackendFile(srcB, fromPath, dstB, toPath, access);
|
||||
|
||||
res = createBackendEntry(dstB, toPath, true);
|
||||
if(res != UA_STATUSCODE_GOOD)
|
||||
@@ -679,11 +688,10 @@ createFileMethodCallback(UA_Server *server, const UA_NodeId *sessionId,
|
||||
|
||||
/* Optionally open the new file for reading and writing. A failed open
|
||||
* removes the file again. */
|
||||
UA_UInt32 handle = 0;
|
||||
FTHandle *h = NULL;
|
||||
if(requestFileOpen) {
|
||||
res = openFileHandle(server, newNode, sessionId, sessionContext,
|
||||
UA_OPENFILEMODE_READ | UA_OPENFILEMODE_WRITE,
|
||||
&handle);
|
||||
UA_OPENFILEMODE_READ | UA_OPENFILEMODE_WRITE, &h);
|
||||
if(res != UA_STATUSCODE_GOOD) {
|
||||
ftd->backend.remove(&ftd->backend, newNode->path);
|
||||
removeSubtree(server, newNode);
|
||||
@@ -692,16 +700,14 @@ createFileMethodCallback(UA_Server *server, const UA_NodeId *sessionId,
|
||||
}
|
||||
|
||||
/* The client cannot close a handle it does not receive */
|
||||
UA_UInt32 handle = h ? h->handle : 0;
|
||||
res = UA_Variant_setScalarCopy(&output[0], &newNode->nodeId,
|
||||
&UA_TYPES[UA_TYPES_NODEID]);
|
||||
if(res == UA_STATUSCODE_GOOD)
|
||||
res = UA_Variant_setScalarCopy(&output[1], &handle,
|
||||
&UA_TYPES[UA_TYPES_UINT32]);
|
||||
if(res != UA_STATUSCODE_GOOD && requestFileOpen) {
|
||||
FTHandle *h = findFTHandle(ftd, sessionId, handle);
|
||||
if(h)
|
||||
closeFTHandle(server, h);
|
||||
}
|
||||
if(res != UA_STATUSCODE_GOOD && h)
|
||||
closeFTHandle(server, h);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ resolveHandle(FileTransferDriver *ftd, const UA_NodeId *sessionId,
|
||||
UA_StatusCode
|
||||
openFileHandle(UA_Server *server, FTEntry *node,
|
||||
const UA_NodeId *sessionId, void *sessionContext, UA_Byte mode,
|
||||
UA_UInt32 *outHandle) {
|
||||
FTHandle **outHandle) {
|
||||
FileTransferDriver *ftd = node->driver;
|
||||
/* Validate the mode bit combination */
|
||||
if((mode & ~UA_FILETRANSFER_OPENMODE_ALLBITS) ||
|
||||
@@ -287,8 +287,9 @@ openFileHandle(UA_Server *server, FTEntry *node,
|
||||
return UA_STATUSCODE_BADRESOURCEUNAVAILABLE;
|
||||
|
||||
UA_FileTransferFileBackend *b = &node->driver->backend.file;
|
||||
UA_FileTransferOpenContext context = {server, sessionId, sessionContext};
|
||||
UA_UInt32 backendHandle = 0;
|
||||
res = b->open(b, node->path, mode, &backendHandle);
|
||||
res = b->open(b, node->path, mode, &context, &backendHandle);
|
||||
if(res != UA_STATUSCODE_GOOD)
|
||||
return res;
|
||||
|
||||
@@ -316,7 +317,7 @@ openFileHandle(UA_Server *server, FTEntry *node,
|
||||
if(writeBit)
|
||||
node->openForWrite = true;
|
||||
|
||||
*outHandle = h->handle;
|
||||
*outHandle = h;
|
||||
return UA_STATUSCODE_GOOD;
|
||||
}
|
||||
|
||||
@@ -329,7 +330,6 @@ openMethodCallback(UA_Server *server, const UA_NodeId *sessionId,
|
||||
FTEntry *node = resolveFTEntry(server, objectId, objectContext);
|
||||
if(!node)
|
||||
return UA_STATUSCODE_BADNOTSUPPORTED;
|
||||
FileTransferDriver *ftd = node->driver;
|
||||
|
||||
if(node->isDirectory || node->zombie)
|
||||
return UA_STATUSCODE_BADNOTFOUND;
|
||||
@@ -341,19 +341,16 @@ openMethodCallback(UA_Server *server, const UA_NodeId *sessionId,
|
||||
/* Open takes a Byte bit mask, not the generated 32-bit UA_OpenFileMode. */
|
||||
UA_Byte mode = *(UA_Byte*)input[0].data;
|
||||
|
||||
UA_UInt32 handle = 0;
|
||||
res = openFileHandle(server, node, sessionId, sessionContext, mode, &handle);
|
||||
FTHandle *h = NULL;
|
||||
res = openFileHandle(server, node, sessionId, sessionContext, mode, &h);
|
||||
if(res != UA_STATUSCODE_GOOD)
|
||||
return res;
|
||||
|
||||
/* The client cannot close a handle it does not receive */
|
||||
res = UA_Variant_setScalarCopy(&output[0], &handle,
|
||||
res = UA_Variant_setScalarCopy(&output[0], &h->handle,
|
||||
&UA_TYPES[UA_TYPES_UINT32]);
|
||||
if(res != UA_STATUSCODE_GOOD) {
|
||||
FTHandle *h = findFTHandle(ftd, sessionId, handle);
|
||||
if(h)
|
||||
closeFTHandle(server, h);
|
||||
}
|
||||
if(res != UA_STATUSCODE_GOOD)
|
||||
closeFTHandle(server, h);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ FTHandle *findFTHandle(FileTransferDriver *ftd, const UA_NodeId *sessionId,
|
||||
/* mode combines UA_OPENFILEMODE_* flags in the protocol's Byte representation. */
|
||||
UA_StatusCode openFileHandle(UA_Server *server, FTEntry *node,
|
||||
const UA_NodeId *sessionId, void *sessionContext,
|
||||
UA_Byte mode, UA_UInt32 *outHandle);
|
||||
UA_Byte mode, FTHandle **outHandle);
|
||||
UA_StatusCode closeFTHandle(UA_Server *server, FTHandle *handle);
|
||||
void closeDriverHandles(UA_Server *server, FileTransferDriver *ftd);
|
||||
void closeSessionHandles(UA_Server *server, FileTransferDriver *ftd,
|
||||
|
||||
@@ -53,7 +53,7 @@ memFsHandle(UA_FileTransferFileBackend *b, UA_UInt32 handle) {
|
||||
|
||||
static UA_StatusCode
|
||||
memFsOpen(UA_FileTransferFileBackend *b, const UA_String path, UA_Byte mode,
|
||||
UA_UInt32 *handle) {
|
||||
const UA_FileTransferOpenContext *context, UA_UInt32 *handle) {
|
||||
MemFs *fs = (MemFs*)b->context;
|
||||
MemFile *file = memFsFind(fs, path);
|
||||
if(!file)
|
||||
|
||||
@@ -78,6 +78,15 @@ typedef struct {
|
||||
* leaves an existing one unchanged. */
|
||||
} UA_FileTransferFileInfo;
|
||||
|
||||
/* The caller of an open. The driver passes the Session of the Method call for
|
||||
* Open, CreateFile and the copies of MoveOrCopy. Direct calls of the
|
||||
* application pass NULL. */
|
||||
typedef struct {
|
||||
UA_Server *server;
|
||||
const UA_NodeId *sessionId;
|
||||
void *sessionContext;
|
||||
} UA_FileTransferOpenContext;
|
||||
|
||||
typedef struct UA_FileTransferFileBackend UA_FileTransferFileBackend;
|
||||
struct UA_FileTransferFileBackend {
|
||||
/* Backend-owned storage and handle state */
|
||||
@@ -85,9 +94,14 @@ struct UA_FileTransferFileBackend {
|
||||
|
||||
/* Open a file. mode is a UA_Byte bit mask of UA_OPENFILEMODE_* flags,
|
||||
* validated by the driver. Append starts at EOF; otherwise start at zero.
|
||||
* EraseExisting truncates the file. Return an independent handle. */
|
||||
* EraseExisting truncates the file. Return an independent handle.
|
||||
* context is the caller or NULL. For a Method call, the driver has checked
|
||||
* the mode, the locking (Part 20, 4.2.2), the access rights and the handle
|
||||
* limits before, and the result is returned to the client, e.g.
|
||||
* Bad_NotSupported for a mode the file does not offer. */
|
||||
UA_StatusCode (*open)(UA_FileTransferFileBackend *b, const UA_String path,
|
||||
UA_Byte mode, UA_UInt32 *handle);
|
||||
UA_Byte mode, const UA_FileTransferOpenContext *context,
|
||||
UA_UInt32 *handle);
|
||||
|
||||
/* Close the handle and flush buffered writes. Report flush errors here;
|
||||
* a destination close error also fails a driver-managed copy. */
|
||||
|
||||
@@ -53,7 +53,7 @@ sfPosition(UA_UInt32 handle) {
|
||||
|
||||
static UA_StatusCode
|
||||
sfOpen(UA_FileTransferFileBackend *b, const UA_String path, UA_Byte mode,
|
||||
UA_UInt32 *handle) {
|
||||
const UA_FileTransferOpenContext *context, UA_UInt32 *handle) {
|
||||
for(size_t i = 0; i < SF_MAXOPEN; i++) {
|
||||
if(sfFiles[i].used)
|
||||
continue;
|
||||
|
||||
@@ -329,8 +329,8 @@ memHandle(UA_FileTransferFileBackend *b, UA_UInt32 handle) {
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
memOpen(UA_FileTransferFileBackend *b, const UA_String path,
|
||||
UA_Byte mode, UA_UInt32 *handle) {
|
||||
memOpen(UA_FileTransferFileBackend *b, const UA_String path, UA_Byte mode,
|
||||
const UA_FileTransferOpenContext *context, UA_UInt32 *handle) {
|
||||
MemBackendContext *ctx = (MemBackendContext*)b->context;
|
||||
MemEntry *e = memFind(ctx, path);
|
||||
if(!e || e->isDir)
|
||||
@@ -683,7 +683,7 @@ runBackendContract(UA_FileTransferBackend *b) {
|
||||
UA_STATUSCODE_BADBROWSENAMEDUPLICATED);
|
||||
|
||||
/* Write content */
|
||||
res = f->open(f, UA_STRING("hello.txt"), UA_OPENFILEMODE_WRITE, &fc);
|
||||
res = f->open(f, UA_STRING("hello.txt"), UA_OPENFILEMODE_WRITE, NULL, &fc);
|
||||
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(f->write(f, fc, UA_BYTESTRING("Hello World")),
|
||||
UA_STATUSCODE_GOOD);
|
||||
@@ -704,7 +704,7 @@ runBackendContract(UA_FileTransferBackend *b) {
|
||||
UA_STATUSCODE_BADNOTFOUND);
|
||||
|
||||
/* Read in chunks until the end of the file */
|
||||
res = f->open(f, UA_STRING("hello.txt"), UA_OPENFILEMODE_READ, &fc);
|
||||
res = f->open(f, UA_STRING("hello.txt"), UA_OPENFILEMODE_READ, NULL, &fc);
|
||||
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(f->read(f, fc, 5, &out), UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(out.length, 5);
|
||||
@@ -733,7 +733,7 @@ runBackendContract(UA_FileTransferBackend *b) {
|
||||
|
||||
/* EraseExisting truncates */
|
||||
res = f->open(f, UA_STRING("hello.txt"),
|
||||
UA_OPENFILEMODE_WRITE | UA_OPENFILEMODE_ERASEEXISTING, &fc);
|
||||
UA_OPENFILEMODE_WRITE | UA_OPENFILEMODE_ERASEEXISTING, NULL, &fc);
|
||||
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(f->write(f, fc, UA_BYTESTRING("Hi")), UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(f->close(f, fc), UA_STATUSCODE_GOOD);
|
||||
@@ -743,7 +743,7 @@ runBackendContract(UA_FileTransferBackend *b) {
|
||||
|
||||
/* Append positions at the end */
|
||||
res = f->open(f, UA_STRING("hello.txt"),
|
||||
UA_OPENFILEMODE_WRITE | UA_OPENFILEMODE_APPEND, &fc);
|
||||
UA_OPENFILEMODE_WRITE | UA_OPENFILEMODE_APPEND, NULL, &fc);
|
||||
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(f->getPosition(f, fc, &pos), UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(pos, 2);
|
||||
@@ -754,19 +754,19 @@ runBackendContract(UA_FileTransferBackend *b) {
|
||||
ck_assert_uint_eq(info.size, 3);
|
||||
|
||||
/* Empty write is a no-op */
|
||||
res = f->open(f, UA_STRING("hello.txt"), UA_OPENFILEMODE_WRITE, &fc);
|
||||
res = f->open(f, UA_STRING("hello.txt"), UA_OPENFILEMODE_WRITE, NULL, &fc);
|
||||
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(f->write(f, fc, UA_BYTESTRING_NULL), UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(f->close(f, fc), UA_STATUSCODE_GOOD);
|
||||
|
||||
/* Invalid open modes and missing files */
|
||||
ck_assert_uint_eq(f->open(f, UA_STRING("hello.txt"), 0, &fc),
|
||||
ck_assert_uint_eq(f->open(f, UA_STRING("hello.txt"), 0, NULL, &fc),
|
||||
UA_STATUSCODE_BADINVALIDARGUMENT);
|
||||
ck_assert_uint_eq(f->open(f, UA_STRING("missing"),
|
||||
UA_OPENFILEMODE_READ, &fc),
|
||||
UA_OPENFILEMODE_READ, NULL, &fc),
|
||||
UA_STATUSCODE_BADNOTFOUND);
|
||||
ck_assert_uint_eq(f->open(f, UA_STRING("missing"),
|
||||
UA_OPENFILEMODE_WRITE, &fc),
|
||||
UA_OPENFILEMODE_WRITE, NULL, &fc),
|
||||
UA_STATUSCODE_BADNOTFOUND);
|
||||
|
||||
/* Listing */
|
||||
@@ -971,7 +971,7 @@ START_TEST(localDirectoryBackendSandbox) {
|
||||
UA_UInt32 fc = 0;
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
UA_String p = UA_STRING((char*)(uintptr_t)badPaths[i]);
|
||||
ck_assert_uint_eq(b.file.open(&b.file, p, UA_OPENFILEMODE_READ, &fc),
|
||||
ck_assert_uint_eq(b.file.open(&b.file, p, UA_OPENFILEMODE_READ, NULL, &fc),
|
||||
UA_STATUSCODE_BADINVALIDARGUMENT);
|
||||
ck_assert_uint_eq(createEntry(&b, p, false), UA_STATUSCODE_BADINVALIDARGUMENT);
|
||||
ck_assert_uint_eq(createEntry(&b, p, true),
|
||||
@@ -1015,7 +1015,7 @@ START_TEST(localDirectoryUtf8Names) {
|
||||
UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(createEntry(&b, path, false), UA_STATUSCODE_GOOD);
|
||||
UA_UInt32 fc = 0;
|
||||
ck_assert_uint_eq(b.file.open(&b.file, path, UA_OPENFILEMODE_WRITE, &fc),
|
||||
ck_assert_uint_eq(b.file.open(&b.file, path, UA_OPENFILEMODE_WRITE, NULL, &fc),
|
||||
UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(b.file.write(&b.file, fc, UA_BYTESTRING("utf8")),
|
||||
UA_STATUSCODE_GOOD);
|
||||
@@ -1060,7 +1060,7 @@ START_TEST(localDirectoryUtf8Names) {
|
||||
ck_assert_str_eq(info.name, name);
|
||||
ck_assert_str_eq(info.mimeType, "text/plain");
|
||||
ck_assert_uint_eq(info.size, 4);
|
||||
ck_assert_uint_eq(file.open(&file, UA_STRING_NULL, UA_OPENFILEMODE_READ, &fc),
|
||||
ck_assert_uint_eq(file.open(&file, UA_STRING_NULL, UA_OPENFILEMODE_READ, NULL, &fc),
|
||||
UA_STATUSCODE_GOOD);
|
||||
UA_ByteString content;
|
||||
ck_assert_uint_eq(file.read(&file, fc, 10, &content), UA_STATUSCODE_GOOD);
|
||||
@@ -1181,7 +1181,7 @@ memBackendWithFile(const char *name, const char *content) {
|
||||
ck_assert_uint_eq(createEntry(&b, path, false), UA_STATUSCODE_GOOD);
|
||||
if(content && strlen(content) > 0) {
|
||||
UA_UInt32 fc = 0;
|
||||
ck_assert_uint_eq(b.file.open(&b.file, path, UA_OPENFILEMODE_WRITE, &fc),
|
||||
ck_assert_uint_eq(b.file.open(&b.file, path, UA_OPENFILEMODE_WRITE, NULL, &fc),
|
||||
UA_STATUSCODE_GOOD);
|
||||
UA_ByteString data = UA_BYTESTRING((char*)(uintptr_t)content);
|
||||
ck_assert_uint_eq(b.file.write(&b.file, fc, data), UA_STATUSCODE_GOOD);
|
||||
@@ -1750,7 +1750,7 @@ memBackendWithTree(void) {
|
||||
ck_assert_uint_eq(createEntry(&b, UA_STRING("docs/sub/b.txt"), false), UA_STATUSCODE_GOOD);
|
||||
UA_UInt32 fc = 0;
|
||||
ck_assert_uint_eq(b.file.open(&b.file, UA_STRING("docs/a.txt"),
|
||||
UA_OPENFILEMODE_WRITE, &fc),
|
||||
UA_OPENFILEMODE_WRITE, NULL, &fc),
|
||||
UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(b.file.write(&b.file, fc, UA_BYTESTRING("content-a")),
|
||||
UA_STATUSCODE_GOOD);
|
||||
@@ -2088,7 +2088,7 @@ writeMemFile(UA_FileTransferBackend *b, const char *name, const char *content) {
|
||||
UA_String path = UA_STRING((char*)(uintptr_t)name);
|
||||
ck_assert_uint_eq(createEntry(b, path, false), UA_STATUSCODE_GOOD);
|
||||
UA_UInt32 fc = 0;
|
||||
ck_assert_uint_eq(b->file.open(&b->file, path, UA_OPENFILEMODE_WRITE, &fc),
|
||||
ck_assert_uint_eq(b->file.open(&b->file, path, UA_OPENFILEMODE_WRITE, NULL, &fc),
|
||||
UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(b->file.write(&b->file, fc,
|
||||
UA_BYTESTRING((char*)(uintptr_t)content)),
|
||||
@@ -2446,7 +2446,7 @@ START_TEST(localFileMount) {
|
||||
UA_FileTransferFileInfo info;
|
||||
for(size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
|
||||
UA_String path = UA_STRING((char*)(uintptr_t)paths[i]);
|
||||
ck_assert_uint_eq(backend.open(&backend, path, UA_OPENFILEMODE_READ, &handle),
|
||||
ck_assert_uint_eq(backend.open(&backend, path, UA_OPENFILEMODE_READ, NULL, &handle),
|
||||
UA_STATUSCODE_BADINVALIDARGUMENT);
|
||||
ck_assert_uint_eq(backend.getInfo(&backend, path, &info), UA_STATUSCODE_BADINVALIDARGUMENT);
|
||||
}
|
||||
@@ -2702,8 +2702,8 @@ static struct {
|
||||
} minimalFiles[MINIMAL_MAXOPEN];
|
||||
|
||||
static UA_StatusCode
|
||||
minimalOpen(UA_FileTransferFileBackend *b, const UA_String path,
|
||||
UA_Byte mode, UA_UInt32 *handle) {
|
||||
minimalOpen(UA_FileTransferFileBackend *b, const UA_String path, UA_Byte mode,
|
||||
const UA_FileTransferOpenContext *context, UA_UInt32 *handle) {
|
||||
if(mode & UA_OPENFILEMODE_WRITE)
|
||||
return UA_STATUSCODE_BADNOTWRITABLE;
|
||||
for(size_t i = 0; i < MINIMAL_MAXOPEN; i++) {
|
||||
@@ -3112,6 +3112,7 @@ static UA_StatusCode (*memCreateFn)(UA_FileTransferBackend *b, const UA_String p
|
||||
const UA_FileTransferFileInfo *info);
|
||||
static UA_StatusCode (*memOpenFn)(UA_FileTransferFileBackend *b,
|
||||
const UA_String path, UA_Byte mode,
|
||||
const UA_FileTransferOpenContext *context,
|
||||
UA_UInt32 *handle);
|
||||
|
||||
static UA_Boolean
|
||||
@@ -3135,9 +3136,9 @@ failingCreate(UA_FileTransferBackend *b, const UA_String path,
|
||||
|
||||
static UA_StatusCode
|
||||
failingOpen(UA_FileTransferFileBackend *b, const UA_String path, UA_Byte mode,
|
||||
UA_UInt32 *handle) {
|
||||
const UA_FileTransferOpenContext *context, UA_UInt32 *handle) {
|
||||
return isFailingPath(path) ? UA_STATUSCODE_BADUSERACCESSDENIED :
|
||||
memOpenFn(b, path, mode, handle);
|
||||
memOpenFn(b, path, mode, context, handle);
|
||||
}
|
||||
|
||||
/* A move to another mount that cannot delete the source keeps the data. A
|
||||
@@ -3649,15 +3650,15 @@ START_TEST(localWriteOnlyFile) {
|
||||
ck_assert_uint_eq(info.accessRights, UA_FILEACCESS_WRITE);
|
||||
|
||||
UA_UInt32 h = 0;
|
||||
ck_assert_uint_eq(b.file.open(&b.file, name, UA_OPENFILEMODE_READ, &h),
|
||||
ck_assert_uint_eq(b.file.open(&b.file, name, UA_OPENFILEMODE_READ, NULL, &h),
|
||||
UA_STATUSCODE_BADUSERACCESSDENIED);
|
||||
ck_assert_uint_eq(b.file.open(&b.file, name, UA_OPENFILEMODE_WRITE, &h),
|
||||
ck_assert_uint_eq(b.file.open(&b.file, name, UA_OPENFILEMODE_WRITE, NULL, &h),
|
||||
UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(b.file.write(&b.file, h, UA_BYTESTRING("XY")), UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(b.file.close(&b.file, h), UA_STATUSCODE_GOOD);
|
||||
/* Append starts at the end; SetPosition still moves the next write */
|
||||
ck_assert_uint_eq(b.file.open(&b.file, name, UA_OPENFILEMODE_WRITE |
|
||||
UA_OPENFILEMODE_APPEND, &h), UA_STATUSCODE_GOOD);
|
||||
UA_OPENFILEMODE_APPEND, NULL, &h), UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(b.file.write(&b.file, h, UA_BYTESTRING("Z")), UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(b.file.setPosition(&b.file, h, 0), UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(b.file.write(&b.file, h, UA_BYTESTRING("Q")), UA_STATUSCODE_GOOD);
|
||||
@@ -3666,7 +3667,7 @@ START_TEST(localWriteOnlyFile) {
|
||||
checkLocalFile(path, "QYcZ");
|
||||
ck_assert_int_eq(fchmod(fileno(permissionsFile), 0200), 0);
|
||||
ck_assert_uint_eq(b.file.open(&b.file, name, UA_OPENFILEMODE_WRITE |
|
||||
UA_OPENFILEMODE_ERASEEXISTING, &h), UA_STATUSCODE_GOOD);
|
||||
UA_OPENFILEMODE_ERASEEXISTING, NULL, &h), UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(b.file.close(&b.file, h), UA_STATUSCODE_GOOD);
|
||||
ck_assert_int_eq(fchmod(fileno(permissionsFile), 0600), 0);
|
||||
checkLocalFile(path, "");
|
||||
@@ -4110,6 +4111,129 @@ accessBackend(UA_FileTransferBackend backend, AccessPolicy *policy) {
|
||||
return backend;
|
||||
}
|
||||
|
||||
/* Records the opens of the backend and forwards them to the memory backend
|
||||
* unless a result is preset */
|
||||
static struct {
|
||||
size_t calls;
|
||||
UA_Boolean hasContext;
|
||||
UA_NodeId sessionId;
|
||||
void *sessionContext;
|
||||
UA_String path;
|
||||
UA_Byte mode;
|
||||
UA_StatusCode result;
|
||||
} recordedOpen;
|
||||
|
||||
static UA_StatusCode
|
||||
recordingOpen(UA_FileTransferFileBackend *b, const UA_String path, UA_Byte mode,
|
||||
const UA_FileTransferOpenContext *context, UA_UInt32 *handle) {
|
||||
recordedOpen.calls++;
|
||||
recordedOpen.hasContext = (context != NULL);
|
||||
UA_NodeId_clear(&recordedOpen.sessionId);
|
||||
if(context) {
|
||||
ck_assert_ptr_eq(context->server, server_ft);
|
||||
ck_assert_uint_eq(UA_NodeId_copy(context->sessionId, &recordedOpen.sessionId),
|
||||
UA_STATUSCODE_GOOD);
|
||||
recordedOpen.sessionContext = context->sessionContext;
|
||||
}
|
||||
UA_String_clear(&recordedOpen.path);
|
||||
ck_assert_uint_eq(UA_String_copy(&path, &recordedOpen.path), UA_STATUSCODE_GOOD);
|
||||
recordedOpen.mode = mode;
|
||||
if(recordedOpen.result != UA_STATUSCODE_GOOD)
|
||||
return recordedOpen.result;
|
||||
return memOpen(b, path, mode, context, handle);
|
||||
}
|
||||
|
||||
static void
|
||||
resetRecordedOpen(void) {
|
||||
UA_NodeId_clear(&recordedOpen.sessionId);
|
||||
UA_String_clear(&recordedOpen.path);
|
||||
memset(&recordedOpen, 0, sizeof(recordedOpen));
|
||||
}
|
||||
|
||||
START_TEST(openReceivesCaller) {
|
||||
resetRecordedOpen();
|
||||
UA_FileTransferBackend backend = memBackendWithFile("f.bin", "content");
|
||||
backend.file.open = recordingOpen;
|
||||
server_ft->adminSession.context = &accessSessionContext;
|
||||
UA_NodeId fileId = addTestFileBackend(backend, "SessionOpen", NULL);
|
||||
|
||||
UA_UInt32 h = callOpen(fileId, UA_OPENFILEMODE_READ, UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(recordedOpen.calls, 1);
|
||||
ck_assert(recordedOpen.hasContext);
|
||||
ck_assert(UA_NodeId_equal(&recordedOpen.sessionId,
|
||||
&server_ft->adminSession.sessionId));
|
||||
ck_assert_ptr_eq(recordedOpen.sessionContext, &accessSessionContext);
|
||||
UA_String path = UA_STRING("f.bin");
|
||||
ck_assert(UA_String_equal(&recordedOpen.path, &path));
|
||||
ck_assert_uint_eq(recordedOpen.mode, UA_OPENFILEMODE_READ);
|
||||
UA_ByteString data = callRead(fileId, h, 100, UA_STATUSCODE_GOOD);
|
||||
UA_ByteString expected = UA_BYTESTRING("content");
|
||||
ck_assert(UA_ByteString_equal(&data, &expected));
|
||||
UA_ByteString_clear(&data);
|
||||
callClose(fileId, h, UA_STATUSCODE_GOOD);
|
||||
|
||||
/* The result of the backend reaches the client */
|
||||
recordedOpen.result = UA_STATUSCODE_BADTRANSACTIONPENDING;
|
||||
callOpen(fileId, UA_OPENFILEMODE_WRITE | UA_OPENFILEMODE_ERASEEXISTING,
|
||||
UA_STATUSCODE_BADTRANSACTIONPENDING);
|
||||
ck_assert_uint_eq(readOpenCount(fileId), 0);
|
||||
|
||||
/* The mode and the locking are checked before */
|
||||
recordedOpen.result = UA_STATUSCODE_GOOD;
|
||||
size_t calls = recordedOpen.calls;
|
||||
callOpen(fileId, UA_OPENFILEMODE_ERASEEXISTING, UA_STATUSCODE_BADINVALIDARGUMENT);
|
||||
h = callOpen(fileId, UA_OPENFILEMODE_WRITE, UA_STATUSCODE_GOOD);
|
||||
callOpen(fileId, UA_OPENFILEMODE_READ, UA_STATUSCODE_BADNOTREADABLE);
|
||||
ck_assert_uint_eq(recordedOpen.calls, calls + 1);
|
||||
callClose(fileId, h, UA_STATUSCODE_GOOD);
|
||||
resetRecordedOpen();
|
||||
} END_TEST
|
||||
|
||||
/* CreateFile and the copies of MoveOrCopy open for the caller, direct calls
|
||||
* of the application pass no context */
|
||||
START_TEST(openInDirectories) {
|
||||
resetRecordedOpen();
|
||||
UA_FileTransferBackend backend = memBackendWithTree();
|
||||
backend.file.open = recordingOpen;
|
||||
UA_NodeId fsId = UA_NODEID_NULL;
|
||||
ck_assert_uint_eq(testAddDirectory(UA_NODEID_NULL, UA_NS0ID(OBJECTSFOLDER),
|
||||
UA_QUALIFIEDNAME(0, "FileSystem"), backendArg(backend),
|
||||
NULL, &fsId), UA_STATUSCODE_GOOD);
|
||||
UA_UInt32 h = 0;
|
||||
UA_NodeId fileId = callCreateFile(fsId, "new.bin", true, &h, UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(recordedOpen.calls, 1);
|
||||
ck_assert(recordedOpen.hasContext);
|
||||
ck_assert(UA_NodeId_equal(&recordedOpen.sessionId,
|
||||
&server_ft->adminSession.sessionId));
|
||||
ck_assert_uint_eq(recordedOpen.mode, UA_OPENFILEMODE_READ | UA_OPENFILEMODE_WRITE);
|
||||
UA_String path = UA_STRING("new.bin");
|
||||
ck_assert(UA_String_equal(&recordedOpen.path, &path));
|
||||
callClose(fileId, h, UA_STATUSCODE_GOOD);
|
||||
UA_NodeId_clear(&fileId);
|
||||
|
||||
UA_NodeId docsId = resolveChild(server_ft, fsId, "docs");
|
||||
UA_NodeId aId = resolveChild(server_ft, docsId, "a.txt");
|
||||
UA_NodeId copyId = callMoveOrCopy(docsId, aId, fsId, true, "copy.txt",
|
||||
UA_STATUSCODE_GOOD);
|
||||
ck_assert_uint_eq(recordedOpen.calls, 3);
|
||||
ck_assert(recordedOpen.hasContext);
|
||||
ck_assert(UA_NodeId_equal(&recordedOpen.sessionId,
|
||||
&server_ft->adminSession.sessionId));
|
||||
ck_assert_uint_eq(recordedOpen.mode, UA_OPENFILEMODE_WRITE);
|
||||
path = UA_STRING("copy.txt");
|
||||
ck_assert(UA_String_equal(&recordedOpen.path, &path));
|
||||
|
||||
UA_UInt32 fc = 0;
|
||||
ck_assert_uint_eq(backend.file.open(&backend.file, path, UA_OPENFILEMODE_READ,
|
||||
NULL, &fc), UA_STATUSCODE_GOOD);
|
||||
ck_assert(!recordedOpen.hasContext);
|
||||
ck_assert_uint_eq(backend.file.close(&backend.file, fc), UA_STATUSCODE_GOOD);
|
||||
UA_NodeId_clear(©Id);
|
||||
UA_NodeId_clear(&aId);
|
||||
UA_NodeId_clear(&docsId);
|
||||
resetRecordedOpen();
|
||||
} END_TEST
|
||||
|
||||
START_TEST(fileAccessRightsEnforced) {
|
||||
AccessPolicy policy;
|
||||
UA_FileTransferBackend backend =
|
||||
@@ -5953,13 +6077,16 @@ START_TEST(indexedDirectoryAndSessionCleanup) {
|
||||
lockServer(server_ft);
|
||||
for(size_t s = 0; s < 2; s++) {
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
FTHandle *opened = NULL;
|
||||
ck_assert_uint_eq(openFileHandle(server_ft, file, &sessions[s], NULL,
|
||||
UA_OPENFILEMODE_READ, &handles[s][i]), UA_STATUSCODE_GOOD);
|
||||
UA_OPENFILEMODE_READ, &opened), UA_STATUSCODE_GOOD);
|
||||
handles[s][i] = opened->handle;
|
||||
ck_assert_ptr_null(findFTHandle(ftd, &sessions[1 - s], handles[s][i]));
|
||||
}
|
||||
UA_UInt32 rejected;
|
||||
FTHandle *rejected = NULL;
|
||||
ck_assert_uint_eq(openFileHandle(server_ft, file, &sessions[s], NULL,
|
||||
UA_OPENFILEMODE_READ, &rejected), UA_STATUSCODE_BADRESOURCEUNAVAILABLE);
|
||||
ck_assert_ptr_null(rejected);
|
||||
}
|
||||
unlockServer(server_ft);
|
||||
ck_assert_uint_eq(readOpenCount(files[0]), 16);
|
||||
@@ -6090,6 +6217,7 @@ int main(void) {
|
||||
tcase_add_test(tc_file, readOnlyBackendNeedsNoWriteCallbacks);
|
||||
tcase_add_test(tc_file, userWritableFollowsStorage);
|
||||
tcase_add_test(tc_file, fileAccessRightsEnforced);
|
||||
tcase_add_test(tc_file, openReceivesCaller);
|
||||
#ifndef _WIN32
|
||||
tcase_add_test(tc_file, localDirectoryAccessRightsFollowPermissions);
|
||||
#endif
|
||||
@@ -6152,6 +6280,7 @@ int main(void) {
|
||||
tcase_add_test(tc_dir, localDirectoryListingErrors);
|
||||
#endif
|
||||
tcase_add_checked_fixture(tc_dir, setup, teardown);
|
||||
tcase_add_test(tc_dir, openInDirectories);
|
||||
suite_add_tcase(s, tc_dir);
|
||||
|
||||
TCase *tc_backend = tcase_create("Storage Backends");
|
||||
|
||||
Reference in New Issue
Block a user