delegation_client.c revision 2e9e9eca83c3dee85b7815573a8cf1e6d1780741
1/**
2 * Copyright(c) 2011 Trusted Logic.   All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 *  * Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in
12 *    the documentation and/or other materials provided with the
13 *    distribution.
14 *  * Neither the name Trusted Logic nor the names of its
15 *    contributors may be used to endorse or promote products derived
16 *    from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#if defined(__ANDROID32__)
32#include <stddef.h>
33#endif
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38/*
39 * When porting to a new OS, insert here the appropriate include files
40 */
41#include <sys/stat.h>
42#include <errno.h>
43#include <sys/types.h>
44#include <fcntl.h>
45
46#if defined(LINUX) || defined(__ANDROID32__)
47#include <unistd.h>
48#include <sys/resource.h>
49
50
51#if defined(__ANDROID32__)
52/* fdatasync does not exist on Android */
53#define fdatasync fsync
54#else
55/*
56 * http://linux.die.net/man/2/fsync
57 * The function fdatasync seems to be absent of the header file
58 * in some distributions
59 */
60int fdatasync(int fd);
61#endif /* __ANDROID32__ */
62#include <syslog.h>
63#include <sys/types.h>
64#include <sys/stat.h>
65#include <pthread.h>
66#include <semaphore.h>
67#define PATH_SEPARATOR '/'
68#endif /* LINUX || __ANDROID32__ */
69
70#ifdef WIN32
71#include <windows.h>
72#include <io.h>
73#define PATH_SEPARATOR '\\'
74#endif
75
76#ifdef __SYMBIAN32__
77#include <unistd.h>
78#include "os_symbian.h"
79#define PATH_SEPARATOR '\\'
80#endif
81
82#include <stdarg.h>
83#include <assert.h>
84
85#include "service_delegation_protocol.h"
86
87#include "s_version.h"
88#include "s_error.h"
89#include "tee_client_api.h"
90
91/* You can define the preprocessor constant SUPPORT_DELEGATION_EXTENSION
92   if you want to pass extended options in a configuration file (option '-c').
93   It is up to you to define the format of this configuration file and the
94   extended option in the source file delegation_client_extension.c. You can
95   use extended options, e.g., to control the name of each partition file. */
96#ifdef SUPPORT_DELEGATION_EXTENSION
97#include "delegation_client_extension.h"
98#endif
99
100/*----------------------------------------------------------------------------
101 * Design notes
102 * ============
103 *
104 * This implementation of the delegation daemon supports the protocol
105 * specified in the Product Reference Manual ("Built-in Services Protocols Specification")
106 *
107 *----------------------------------------------------------------------------*/
108
109/*----------------------------------------------------------------------------
110 * Defines and structures
111 *----------------------------------------------------------------------------*/
112#define ECHANGE_BUFFER_INSTRUCTIONS_NB 100
113
114#define DEFAULT_WORKSPACE_SIZE (128*1024)
115
116/* A single shared memory block is used to contain the administrative data, the
117   instruction buffer and the workspace. The size of the instruction buffer is
118   fixed, but the size of workspace can be configured using the "-workspaceSize"
119   command-line option. */
120typedef struct
121{
122   DELEGATION_ADMINISTRATIVE_DATA sAdministrativeData;
123   uint32_t                       sInstructions[ECHANGE_BUFFER_INSTRUCTIONS_NB];
124   uint8_t                        sWorkspace[1/*g_nWorkspaceSize*/];
125} DELEGATION_EXCHANGE_BUFFER;
126
127#define MD_VAR_NOT_USED(variable)  do{(void)(variable);}while(0);
128
129#define MD_INLINE __inline
130
131/* ----------------------------------------------
132   Traces and logs
133
134   On Linux, traces and logs go either to the console (stderr) or to the syslog.
135   When the daemon is started, the logs go to the console. Once and if the daemon
136   is detached, the logs go to syslog.
137
138   On other systems, traces and logs go systematically to stderr
139
140   The difference between traces and logs is that traces are compiled out
141   in release builds whereas logs are visible to the customer.
142
143   -----------------------------------------------*/
144#if defined(LINUX) || (defined __ANDROID32__)
145
146static bool bDetached = false;
147
148static MD_INLINE void LogError(const char* format, ...)
149{
150   va_list ap;
151   va_start(ap, format);
152   if (bDetached)
153   {
154      vsyslog(LOG_ERR, format, ap);
155   }
156   else
157   {
158      fprintf(stderr, "ERROR: ");
159      vfprintf(stderr, format, ap);
160      fprintf(stderr, "\n");
161   }
162   va_end(ap);
163}
164
165static MD_INLINE void LogWarning(const char* format, ...)
166{
167   va_list ap;
168   va_start(ap, format);
169   if (bDetached)
170   {
171      vsyslog(LOG_WARNING, format, ap);
172   }
173   else
174   {
175      fprintf(stderr, "WARNING: ");
176      vfprintf(stderr, format, ap);
177      fprintf(stderr, "\n");
178   }
179   va_end(ap);
180}
181static MD_INLINE void LogInfo(const char* format, ...)
182{
183   va_list ap;
184   va_start(ap, format);
185   if (bDetached)
186   {
187      vsyslog(LOG_INFO, format, ap);
188   }
189   else
190   {
191      vfprintf(stderr, format, ap);
192      fprintf(stderr, "\n");
193   }
194   va_end(ap);
195}
196
197static MD_INLINE void TRACE_ERROR(const char* format, ...)
198{
199#ifndef NDEBUG
200   va_list ap;
201   va_start(ap, format);
202   if (bDetached)
203   {
204      vsyslog(LOG_ERR, format, ap);
205   }
206   else
207   {
208      fprintf(stderr, "TRACE: ERROR: ");
209      vfprintf(stderr, format, ap);
210      fprintf(stderr, "\n");
211   }
212   va_end(ap);
213#else
214   MD_VAR_NOT_USED(format);
215#endif /* NDEBUG */
216}
217
218static MD_INLINE void TRACE_WARNING(const char* format, ...)
219{
220#ifndef NDEBUG
221   va_list ap;
222   va_start(ap, format);
223   if (bDetached)
224   {
225      vsyslog(LOG_WARNING, format, ap);
226   }
227   else
228   {
229      fprintf(stderr, "TRACE: WARNING: ");
230      vfprintf(stderr, format, ap);
231      fprintf(stderr, "\n");
232   }
233   va_end(ap);
234#else
235   MD_VAR_NOT_USED(format);
236#endif /* NDEBUG */
237}
238
239static MD_INLINE void TRACE_INFO(const char* format, ...)
240{
241#ifndef NDEBUG
242   va_list ap;
243   va_start(ap, format);
244   if (bDetached)
245   {
246      vsyslog(LOG_DEBUG, format, ap);
247   }
248   else
249   {
250      fprintf(stderr, "TRACE: ");
251      vfprintf(stderr, format, ap);
252      fprintf(stderr, "\n");
253   }
254   va_end(ap);
255#else
256   MD_VAR_NOT_USED(format);
257#endif /* NDEBUG */
258}
259#elif defined __SYMBIAN32__
260/* defined in os_symbian.h */
261
262#elif defined NO_LOG_NO_TRACE
263static MD_INLINE void LogError(const char* format, ...)
264{
265   MD_VAR_NOT_USED(format);
266}
267static MD_INLINE void LogWarning(const char* format, ...)
268{
269   MD_VAR_NOT_USED(format);
270}
271static MD_INLINE void LogInfo(const char* format, ...)
272{
273   MD_VAR_NOT_USED(format);
274}
275
276static MD_INLINE void TRACE_ERROR(const char* format, ...)
277{
278   MD_VAR_NOT_USED(format);
279}
280
281static MD_INLINE void TRACE_WARNING(const char* format, ...)
282{
283   MD_VAR_NOT_USED(format);
284}
285
286static MD_INLINE void TRACE_INFO(const char* format, ...)
287{
288   MD_VAR_NOT_USED(format);
289}
290
291#else
292/* !defined(LINUX) || !defined(__ANDROID32__) */
293
294static MD_INLINE void LogError(const char* format, ...)
295{
296   va_list ap;
297   va_start(ap, format);
298   fprintf(stderr, "ERROR: ");
299   vfprintf(stderr, format, ap);
300   fprintf(stderr, "\n");
301   va_end(ap);
302}
303static MD_INLINE void LogWarning(const char* format, ...)
304{
305   va_list ap;
306   va_start(ap, format);
307   fprintf(stderr, "WARNING: ");
308   vfprintf(stderr, format, ap);
309   fprintf(stderr, "\n");
310   va_end(ap);
311}
312static MD_INLINE void LogInfo(const char* format, ...)
313{
314   va_list ap;
315   va_start(ap, format);
316   vfprintf(stderr, format, ap);
317   fprintf(stderr, "\n");
318   va_end(ap);
319}
320
321static MD_INLINE void TRACE_ERROR(const char* format, ...)
322{
323#ifndef NDEBUG
324   va_list ap;
325   va_start(ap, format);
326   fprintf(stderr, "TRACE: ERROR: ");
327   vfprintf(stderr, format, ap);
328   fprintf(stderr, "\n");
329   va_end(ap);
330#else
331   MD_VAR_NOT_USED(format);
332#endif /* NDEBUG */
333}
334
335static MD_INLINE void TRACE_WARNING(const char* format, ...)
336{
337#ifndef NDEBUG
338   va_list ap;
339   va_start(ap, format);
340   fprintf(stderr, "TRACE: WARNING: ");
341   vfprintf(stderr, format, ap);
342   fprintf(stderr, "\n");
343   va_end(ap);
344#else
345   MD_VAR_NOT_USED(format);
346#endif /* NDEBUG */
347}
348
349static MD_INLINE void TRACE_INFO(const char* format, ...)
350{
351#ifndef NDEBUG
352   va_list ap;
353   va_start(ap, format);
354   fprintf(stderr, "TRACE: ");
355   vfprintf(stderr, format, ap);
356   fprintf(stderr, "\n");
357   va_end(ap);
358#else
359   MD_VAR_NOT_USED(format);
360#endif /* NDEBUG */
361}
362#endif /* defined(LINUX) || defined(__ANDROID32__) */
363
364/*----------------------------------------------------------------------------
365 * Globals
366 *----------------------------------------------------------------------------*/
367/* The sector size */
368static uint32_t g_nSectorSize;
369
370/* The workspace size */
371static uint32_t g_nWorkspaceSize = DEFAULT_WORKSPACE_SIZE;
372
373/* UUID of the delegation service */
374static const TEEC_UUID g_sServiceId = SERVICE_DELEGATION_UUID;
375
376/* pWorkspaceBuffer points to the workspace buffer shared with the secure
377   world to transfer the sectors in the READ and WRITE instructions  */
378static uint8_t* g_pWorkspaceBuffer;
379static DELEGATION_EXCHANGE_BUFFER * g_pExchangeBuffer;
380TEEC_SharedMemory sExchangeSharedMem;
381/*
382   The absolute path name for each of the 16 possible partitions.
383 */
384static char* g_pPartitionNames[16];
385
386/* The file context for each of the 16 possible partitions. An entry
387   in this array is NULL if the corresponding partition is currently not opened
388 */
389static FILE* g_pPartitionFiles[16];
390
391/*----------------------------------------------------------------------------
392 * Utilities functions
393 *----------------------------------------------------------------------------*/
394static void printUsage(void)
395{
396   LogInfo("usage : tf_daemon [options]");
397   LogInfo("where [options] are:");
398   LogInfo("-h --help  Display help.");
399#ifdef SUPPORT_DELEGATION_EXTENSION
400   LogInfo("-c <conf>  Configuration file path.");
401#else
402   /* If the compilation parameter SUPPORT_DELEGATION_EXTENSION is not set, each
403      partition is stored as a file within the base dir */
404   LogInfo("-storageDir <baseDir>  Set the directory where the data will be stored; this directory");
405   LogInfo("           must be writable and executable (this parameter is mandatory)");
406#endif
407   LogInfo("-d         Turns on debug mode.  If not specified, the daemon will fork itself");
408   LogInfo("           and get detached from the console.");
409#ifndef SUPPORT_DELEGATION_EXTENSION
410   LogInfo("-workspaceSize <integer>  Set the size in bytes of the workspace. Must be greater or equal to 8 sectors.");
411   LogInfo("           (default is 128KB)");
412#endif
413}
414
415static TEEC_Result errno2serror(void)
416{
417   switch (errno)
418   {
419   case EINVAL:
420      return S_ERROR_BAD_PARAMETERS;
421   case EMFILE:
422      return S_ERROR_NO_MORE_HANDLES;
423   case ENOENT:
424      return S_ERROR_ITEM_NOT_FOUND;
425   case EEXIST:
426      return S_ERROR_ITEM_EXISTS;
427   case ENOSPC:
428      return S_ERROR_STORAGE_NO_SPACE;
429   case ENOMEM:
430      return S_ERROR_OUT_OF_MEMORY;
431   case EBADF:
432   case EACCES:
433   default:
434      return S_ERROR_STORAGE_UNREACHABLE;
435   }
436}
437
438/*
439 * Check if the directory in parameter exists with Read/Write access
440 * Return 0 in case of success and 1 otherwise.
441 */
442int static_checkStorageDirAndAccessRights(char * directoryName)
443{
444#ifdef __SYMBIAN32__
445   /* it looks like stat is not working properly on Symbian
446      Create and remove dummy file to check access rights */
447   FILE *stream;
448   char *checkAccess = NULL;
449
450   if (directoryName == NULL)
451   {
452      LogError("Directory Name is NULL");
453      return 1;
454   }
455
456   checkAccess = malloc(strlen(directoryName)+1/* \ */ +1 /* a */ + 1 /* 0 */);
457   if (!checkAccess)
458   {
459      LogError("storageDir '%s' allocation error", directoryName);
460      return 1;
461   }
462   sprintf(checkAccess,"%s\\a",directoryName);
463   stream = fopen(checkAccess, "w+b");
464   if (!stream)
465   {
466      LogError("storageDir '%s' is incorrect or cannot be reached", directoryName);
467      return 1;
468   }
469   fclose(stream);
470   unlink(checkAccess);
471#else
472   /* Non-Symbian OS: use stat */
473   struct stat buf;
474   int result = 0;
475
476   if (directoryName == NULL)
477   {
478      LogError("Directory Name is NULL");
479      return 1;
480   }
481
482   result = stat(directoryName, &buf);
483   if (result == 0)
484   {
485      /* Storage dir exists. Check access rights */
486#if defined(LINUX) || (defined __ANDROID32__)
487      if ((buf.st_mode & (S_IXUSR | S_IWUSR)) != (S_IXUSR | S_IWUSR))
488      {
489         LogError("storageDir '%s' does not have read-write access", directoryName);
490         return 1;
491      }
492#endif
493   }
494   else if (errno == ENOENT)
495   {
496      LogError("storageDir '%s' does not exist", directoryName);
497      return 1;
498   }
499   else
500   {
501      /* Another error */
502      LogError("storageDir '%s' is incorrect or cannot be reached", directoryName);
503      return 1;
504   }
505#endif
506   return 0;
507}
508
509
510
511/*----------------------------------------------------------------------------
512 * Instructions
513 *----------------------------------------------------------------------------*/
514
515/**
516 * This function executes the DESTROY_PARTITION instruction
517 *
518 * @param nPartitionID: the partition identifier
519 **/
520static TEEC_Result partitionDestroy(uint32_t nPartitionID)
521{
522   TEEC_Result  nError = S_SUCCESS;
523
524   if (g_pPartitionFiles[nPartitionID] != NULL)
525   {
526      /* The partition must not be currently opened */
527      LogError("g_pPartitionFiles not NULL");
528      return S_ERROR_BAD_STATE;
529   }
530
531   /* Try to erase the file */
532#if defined(LINUX) || (defined __ANDROID32__) || defined (__SYMBIAN32__)
533   if (unlink(g_pPartitionNames[nPartitionID]) != 0)
534#endif
535#ifdef WIN32
536   if (_unlink(g_pPartitionNames[nPartitionID]) != 0)
537#endif
538   {
539      /* File in use or OS didn't allow the operation */
540      nError = errno2serror();
541   }
542
543   return nError;
544}
545
546/**
547 * This function executes the CREATE_PARTITION instruction. When successful,
548 * it fills the g_pPartitionFiles[nPartitionID] slot.
549 *
550 * @param nPartitionID: the partition identifier
551 **/
552static TEEC_Result partitionCreate(uint32_t nPartitionID)
553{
554   uint32_t nError = S_SUCCESS;
555
556   if (g_pPartitionFiles[nPartitionID] != NULL)
557   {
558      /* The partition is already opened */
559      LogError("g_pPartitionFiles not NULL");
560      return S_ERROR_BAD_STATE;
561   }
562
563   /* Create the file unconditionnally */
564   LogInfo("Create storage file \"%s\"", g_pPartitionNames[nPartitionID]);
565   g_pPartitionFiles[nPartitionID] = fopen(g_pPartitionNames[nPartitionID], "w+b");
566
567   if (g_pPartitionFiles[nPartitionID] == NULL)
568   {
569      LogError("Cannot create storage file \"%s\"", g_pPartitionNames[nPartitionID]);
570      nError = errno2serror();
571      return nError;
572   }
573
574   return nError;
575}
576
577/**
578 * This function executes the OPEN_PARTITION instruction. When successful,
579 * it fills the g_pPartitionFiles[nPartitionID] slot and writes the partition
580 * size in hResultEncoder
581 *
582 * @param nPartitionID: the partition identifier
583 * @param pnPartitionSize: filled with the number of sectors in the partition
584 **/
585static TEEC_Result partitionOpen(uint32_t nPartitionID, uint32_t* pnPartitionSize)
586{
587   uint32_t nError = S_SUCCESS;
588
589   if (g_pPartitionFiles[nPartitionID] != NULL)
590   {
591      /* No partition must be currently opened in the session */
592      LogError("g_pPartitionFiles not NULL");
593      return S_ERROR_BAD_STATE;
594   }
595
596   /* Open the file */
597   g_pPartitionFiles[nPartitionID] = fopen(g_pPartitionNames[nPartitionID], "r+b");
598   if (g_pPartitionFiles[nPartitionID] == NULL)
599   {
600      if (errno == ENOENT)
601      {
602         /* File does not exist */
603         LogError("Storage file \"%s\" does not exist", g_pPartitionNames[nPartitionID]);
604         nError = S_ERROR_ITEM_NOT_FOUND;
605         return nError;
606      }
607      else
608      {
609         LogError("cannot open storage file \"%s\"", g_pPartitionNames[nPartitionID]);
610         nError = errno2serror();
611         return nError;
612      }
613   }
614   /* Determine the current number of sectors */
615   fseek(g_pPartitionFiles[nPartitionID], 0L, SEEK_END);
616   *pnPartitionSize = ftell(g_pPartitionFiles[nPartitionID]) / g_nSectorSize;
617
618   LogInfo("storage file \"%s\" successfully opened (size = %d KB (%d bytes))",
619      g_pPartitionNames[nPartitionID],
620      ((*pnPartitionSize) * g_nSectorSize) / 1024,
621      ((*pnPartitionSize) * g_nSectorSize));
622
623   return nError;
624}
625
626
627/**
628 * This function executes the CLOSE_PARTITION instruction.
629 * It closes the partition file.
630 *
631 * @param nPartitionID: the partition identifier
632 **/
633static TEEC_Result partitionClose(uint32_t nPartitionID)
634{
635   if (g_pPartitionFiles[nPartitionID] == NULL)
636   {
637      /* The partition is currently not opened */
638      return S_ERROR_BAD_STATE;
639   }
640   fclose(g_pPartitionFiles[nPartitionID]);
641   g_pPartitionFiles[nPartitionID] = NULL;
642   return S_SUCCESS;
643}
644
645/**
646 * This function executes the READ instruction.
647 *
648 * @param nPartitionID: the partition identifier
649 * @param nSectorIndex: the index of the sector to read
650 * @param nWorkspaceOffset: the offset in the workspace where the sector must be written
651 **/
652static TEEC_Result partitionRead(uint32_t nPartitionID, uint32_t nSectorIndex, uint32_t nWorkspaceOffset)
653{
654   FILE* pFile;
655
656   TRACE_INFO(">Partition %1X: read sector 0x%08X into workspace at offset 0x%08X",
657      nPartitionID, nSectorIndex, nWorkspaceOffset);
658
659   pFile = g_pPartitionFiles[nPartitionID];
660
661   if (pFile == NULL)
662   {
663      /* The partition is not opened */
664      return S_ERROR_BAD_STATE;
665   }
666
667   if (fseek(pFile, nSectorIndex*g_nSectorSize, SEEK_SET) != 0)
668   {
669      LogError("fseek error: %s", strerror(errno));
670      return errno2serror();
671   }
672
673   if (fread(g_pWorkspaceBuffer + nWorkspaceOffset,
674             g_nSectorSize, 1,
675             pFile) != 1)
676   {
677      if (feof(pFile))
678      {
679         LogError("fread error: End-Of-File detected");
680         return S_ERROR_ITEM_NOT_FOUND;
681      }
682      LogError("fread error: %s", strerror(errno));
683      return errno2serror();
684   }
685
686   return S_SUCCESS;
687}
688
689/**
690 * This function executes the WRITE instruction.
691 *
692 * @param nPartitionID: the partition identifier
693 * @param nSectorIndex: the index of the sector to read
694 * @param nWorkspaceOffset: the offset in the workspace where the sector must be read
695 **/
696static TEEC_Result partitionWrite(uint32_t nPartitionID, uint32_t nSectorIndex, uint32_t nWorkspaceOffset)
697{
698   FILE* pFile;
699
700   TRACE_INFO(">Partition %1X: write sector 0x%X from workspace at offset 0x%X",
701      nPartitionID, nSectorIndex, nWorkspaceOffset);
702
703   pFile = g_pPartitionFiles[nPartitionID];
704
705   if (pFile == NULL)
706   {
707      /* The partition is not opened */
708      return S_ERROR_BAD_STATE;
709   }
710
711   if (fseek(pFile, nSectorIndex*g_nSectorSize, SEEK_SET) != 0)
712   {
713      LogError("fseek error: %s", strerror(errno));
714      return errno2serror();
715   }
716
717   if (fwrite(g_pWorkspaceBuffer + nWorkspaceOffset,
718              g_nSectorSize, 1,
719              pFile) != 1)
720   {
721      LogError("fread error: %s", strerror(errno));
722      return errno2serror();
723   }
724   return S_SUCCESS;
725}
726
727
728/**
729 * This function executes the SET_SIZE instruction.
730 *
731 * @param nPartitionID: the partition identifier
732 * @param nNewSectorCount: the new sector count
733 **/
734static TEEC_Result partitionSetSize(uint32_t nPartitionID, uint32_t nNewSectorCount)
735{
736   FILE* pFile;
737   uint32_t nCurrentSectorCount;
738
739   pFile = g_pPartitionFiles[nPartitionID];
740
741   if (pFile==NULL)
742   {
743      /* The partition is not opened */
744      return S_ERROR_BAD_STATE;
745   }
746
747   /* Determine the current size of the partition */
748   if (fseek(pFile, 0, SEEK_END) != 0)
749   {
750      LogError("fseek error: %s", strerror(errno));
751      return errno2serror();
752   }
753   nCurrentSectorCount = ftell(pFile) / g_nSectorSize;
754
755   if (nNewSectorCount > nCurrentSectorCount)
756   {
757      uint32_t nAddedBytesCount;
758      /* Enlarge the partition file. Make sure we actually write
759         some non-zero data into the new sectors. Otherwise, some file-system
760         might not really reserve the storage space but use a
761         sparse representation. In this case, a subsequent write instruction
762         could fail due to out-of-space, which we want to avoid. */
763      nAddedBytesCount = (nNewSectorCount-nCurrentSectorCount)*g_nSectorSize;
764      while (nAddedBytesCount)
765      {
766         if (fputc(0xA5, pFile)!=0xA5)
767         {
768            return errno2serror();
769         }
770         nAddedBytesCount--;
771      }
772   }
773   else if (nNewSectorCount < nCurrentSectorCount)
774   {
775      int result = 0;
776      /* Truncate the partition file */
777#if defined(LINUX) || (defined __ANDROID32__)
778      result = ftruncate(fileno(pFile),nNewSectorCount * g_nSectorSize);
779#endif
780#if defined (__SYMBIAN32__)
781	  LogError("No truncate available in Symbian C API");
782#endif
783#ifdef WIN32
784      result = _chsize(_fileno(pFile),nNewSectorCount * g_nSectorSize);
785#endif
786      if (result)
787      {
788         return errno2serror();
789      }
790   }
791   return S_SUCCESS;
792}
793
794/**
795 * This function executes the SYNC instruction.
796 *
797 * @param pPartitionID: the partition identifier
798 **/
799static TEEC_Result partitionSync(uint32_t nPartitionID)
800{
801   TEEC_Result nError = S_SUCCESS;
802   int result;
803
804   FILE* pFile = g_pPartitionFiles[nPartitionID];
805
806   if (pFile == NULL)
807   {
808      /* The partition is not currently opened */
809      return S_ERROR_BAD_STATE;
810   }
811
812   /* First make sure that the data in the stdio buffers
813      is flushed to the file descriptor */
814   result=fflush(pFile);
815   if (result)
816   {
817      nError=errno2serror();
818      goto end;
819   }
820   /* Then synchronize the file descriptor with the file-system */
821
822#if defined(LINUX) || (defined __ANDROID32__)
823   result=fdatasync(fileno(pFile));
824#endif
825#if defined (__SYMBIAN32__)
826   result=fsync(fileno(pFile));
827#endif
828#ifdef WIN32
829   result=_commit(_fileno(pFile));
830#endif
831   if (result)
832   {
833      nError=errno2serror();
834   }
835
836end:
837   return nError;
838}
839
840/**
841 * This function executes the NOTIFY instruction.
842 *
843 * @param pMessage the message string
844 * @param nMessageType the type of messages
845 **/
846static void notify(const wchar_t* pMessage, uint32_t nMessageType)
847{
848   switch (nMessageType)
849   {
850   case DELEGATION_NOTIFY_TYPE_ERROR:
851      LogError("%ls", pMessage);
852      break;
853   case DELEGATION_NOTIFY_TYPE_WARNING:
854      LogWarning("%ls", pMessage);
855      break;
856   case DELEGATION_NOTIFY_TYPE_DEBUG:
857      LogInfo("DEBUG: %ls", pMessage);
858      break;
859   case DELEGATION_NOTIFY_TYPE_INFO:
860   default:
861      LogInfo("%ls", pMessage);
862      break;
863   }
864}
865
866/*----------------------------------------------------------------------------
867 * Session main function
868 *----------------------------------------------------------------------------*/
869
870/*
871 * This function runs a session opened on the delegation service. It fetches
872 * instructions and execute them in a loop. It never returns, but may call
873 * exit when instructed to shutdown by the service
874 */
875static int runSession(TEEC_Context* pContext, TEEC_Session* pSession, TEEC_Operation* pOperation)
876{
877   memset(&g_pExchangeBuffer->sAdministrativeData, 0x00, sizeof(g_pExchangeBuffer->sAdministrativeData));
878
879   while (true)
880   {
881      S_RESULT    nError;
882      TEEC_Result                      nTeeError;
883      uint32_t                         nInstructionsIndex;
884      uint32_t                         nInstructionsBufferSize = sizeof(g_pExchangeBuffer->sInstructions);
885
886      pOperation->paramTypes = TEEC_PARAM_TYPES(
887         TEEC_MEMREF_PARTIAL_INPUT,
888         TEEC_MEMREF_PARTIAL_OUTPUT,
889         TEEC_MEMREF_PARTIAL_INOUT,
890         TEEC_NONE);
891      pOperation->params[0].memref.parent = &sExchangeSharedMem;
892      pOperation->params[0].memref.offset = offsetof(DELEGATION_EXCHANGE_BUFFER, sAdministrativeData);
893      pOperation->params[0].memref.size   = sizeof(g_pExchangeBuffer->sAdministrativeData);
894
895      pOperation->params[1].memref.parent = &sExchangeSharedMem;
896      pOperation->params[1].memref.offset = offsetof(DELEGATION_EXCHANGE_BUFFER, sInstructions);
897      pOperation->params[1].memref.size   = sizeof(g_pExchangeBuffer->sInstructions);
898
899      pOperation->params[2].memref.parent = &sExchangeSharedMem;
900      pOperation->params[2].memref.offset = offsetof(DELEGATION_EXCHANGE_BUFFER, sWorkspace);
901      pOperation->params[2].memref.size   = g_nWorkspaceSize;
902
903      nTeeError = TEEC_InvokeCommand(pSession,
904                                  SERVICE_DELEGATION_GET_INSTRUCTIONS,   /* commandID */
905                                  pOperation,     /* IN OUT operation */
906                                  NULL             /* OUT errorOrigin, optional */
907                                 );
908
909      if (nTeeError != TEEC_SUCCESS)
910      {
911         LogError("TEEC_InvokeCommand error: 0x%08X", nTeeError);
912         LogError("Daemon exits");
913         exit(2);
914      }
915
916      if (pOperation->params[1].tmpref.size >  nInstructionsBufferSize)
917      {
918         /* Should not happen, probably an error from the service */
919         pOperation->params[1].tmpref.size = 0;
920      }
921
922      /* Reset the operation results */
923      nError = TEEC_SUCCESS;
924      g_pExchangeBuffer->sAdministrativeData.nSyncExecuted = 0;
925      memset(g_pExchangeBuffer->sAdministrativeData.nPartitionErrorStates, 0x00, sizeof(g_pExchangeBuffer->sAdministrativeData.nPartitionErrorStates));
926      memset(g_pExchangeBuffer->sAdministrativeData.nPartitionOpenSizes, 0x00, sizeof(g_pExchangeBuffer->sAdministrativeData.nPartitionOpenSizes));
927
928      /* Execute the instructions */
929      nInstructionsIndex = 0;
930      nInstructionsBufferSize = pOperation->params[1].tmpref.size;
931      while (true)
932      {
933         DELEGATION_INSTRUCTION * pInstruction;
934         uint32_t nInstructionID;
935         pInstruction = (DELEGATION_INSTRUCTION *)(&g_pExchangeBuffer->sInstructions[nInstructionsIndex/4]);
936         if (nInstructionsIndex + 4 <= nInstructionsBufferSize)
937         {
938            nInstructionID = pInstruction->sGeneric.nInstructionID;
939            nInstructionsIndex+=4;
940         }
941         else
942         {
943            goto instruction_parse_end;
944         }
945         if ((nInstructionID & 0x0F) == 0)
946         {
947            /* Partition-independent instruction */
948            switch (nInstructionID)
949            {
950            case DELEGATION_INSTRUCTION_SHUTDOWN:
951               {
952                  exit(0);
953                  /* The implementation of the TF Client API will automatically
954                     destroy the context and release any associated resource */
955               }
956            case DELEGATION_INSTRUCTION_NOTIFY:
957               {
958                  /* Parse the instruction parameters */
959                  wchar_t  pMessage[100];
960                  uint32_t nMessageType;
961                  uint32_t nMessageSize;
962                  memset(pMessage, 0, 100*sizeof(wchar_t));
963
964                  if (nInstructionsIndex + 8 <= nInstructionsBufferSize)
965                  {
966                     nMessageType = pInstruction->sNotify.nMessageType;
967                     nMessageSize = pInstruction->sNotify.nMessageSize;
968                     nInstructionsIndex+=8;
969                  }
970                  else
971                  {
972                     goto instruction_parse_end;
973                  }
974                  if (nMessageSize > (99)*sizeof(wchar_t))
975                  {
976                     /* How to handle the error correctly in this case ? */
977                     goto instruction_parse_end;
978                  }
979                  if (nInstructionsIndex + nMessageSize <= nInstructionsBufferSize)
980                  {
981                     memcpy(pMessage, &pInstruction->sNotify.nMessage[0], nMessageSize);
982                     nInstructionsIndex+=nMessageSize;
983                  }
984                  else
985                  {
986                     goto instruction_parse_end;
987                  }
988                  /* Align the pInstructionsIndex on 4 bytes */
989                  nInstructionsIndex = (nInstructionsIndex+3)&~3;
990                  notify(pMessage, nMessageType);
991                  break;
992               }
993            default:
994               LogError("Unknown instruction identifier: %02X", nInstructionID);
995               nError = S_ERROR_BAD_PARAMETERS;
996               break;
997            }
998         }
999         else
1000         {
1001            /* Partition-specific instruction */
1002            uint32_t nPartitionID = (nInstructionID & 0xF0) >> 4;
1003            if (g_pExchangeBuffer->sAdministrativeData.nPartitionErrorStates[nPartitionID] == S_SUCCESS)
1004            {
1005               /* Execute the instruction only if there is currently no
1006                  error on the partition */
1007               switch (nInstructionID & 0x0F)
1008               {
1009               case DELEGATION_INSTRUCTION_PARTITION_CREATE:
1010                  nError = partitionCreate(nPartitionID);
1011                  TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nError);
1012                  break;
1013               case DELEGATION_INSTRUCTION_PARTITION_OPEN:
1014                  {
1015                     uint32_t nPartitionSize = 0;
1016                     nError = partitionOpen(nPartitionID, &nPartitionSize);
1017                     TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d pSize=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nPartitionSize, nError);
1018                     if (nError == S_SUCCESS)
1019                     {
1020                        g_pExchangeBuffer->sAdministrativeData.nPartitionOpenSizes[nPartitionID] = nPartitionSize;
1021                     }
1022                     break;
1023                  }
1024               case DELEGATION_INSTRUCTION_PARTITION_READ:
1025                  {
1026                     /* Parse parameters */
1027                     uint32_t nSectorID;
1028                     uint32_t nWorkspaceOffset;
1029                     if (nInstructionsIndex + 8 <= nInstructionsBufferSize)
1030                     {
1031                        nSectorID        = pInstruction->sReadWrite.nSectorID;
1032                        nWorkspaceOffset = pInstruction->sReadWrite.nWorkspaceOffset;
1033                        nInstructionsIndex+=8;
1034                     }
1035                     else
1036                     {
1037                        goto instruction_parse_end;
1038                     }
1039                     nError = partitionRead(nPartitionID, nSectorID, nWorkspaceOffset);
1040                     TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d sid=%d woff=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nSectorID, nWorkspaceOffset, nError);
1041                     break;
1042                  }
1043               case DELEGATION_INSTRUCTION_PARTITION_WRITE:
1044                  {
1045                     /* Parse parameters */
1046                     uint32_t nSectorID;
1047                     uint32_t nWorkspaceOffset;
1048                     if (nInstructionsIndex + 8 <= nInstructionsBufferSize)
1049                     {
1050                        nSectorID        = pInstruction->sReadWrite.nSectorID;
1051                        nWorkspaceOffset = pInstruction->sReadWrite.nWorkspaceOffset;
1052                        nInstructionsIndex+=8;
1053                     }
1054                     else
1055                     {
1056                        goto instruction_parse_end;
1057                     }
1058                     nError = partitionWrite(nPartitionID, nSectorID, nWorkspaceOffset);
1059                     TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d sid=%d woff=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nSectorID, nWorkspaceOffset, nError);
1060                     break;
1061                  }
1062               case DELEGATION_INSTRUCTION_PARTITION_SYNC:
1063                  nError = partitionSync(nPartitionID);
1064                  TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nError);
1065                  if (nError == S_SUCCESS)
1066                  {
1067                     g_pExchangeBuffer->sAdministrativeData.nSyncExecuted++;
1068                  }
1069                  break;
1070               case DELEGATION_INSTRUCTION_PARTITION_SET_SIZE:
1071                  {
1072                     uint32_t nNewSize;
1073                     if (nInstructionsIndex + 4 <= nInstructionsBufferSize)
1074                     {
1075                        nNewSize = pInstruction->sSetSize.nNewSize;
1076                        nInstructionsIndex+=4;
1077                     }
1078                     else
1079                     {
1080                        goto instruction_parse_end;
1081                     }
1082                     nError = partitionSetSize(nPartitionID, nNewSize);
1083                     TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d nNewSize=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nNewSize, nError);
1084                     break;
1085                  }
1086               case DELEGATION_INSTRUCTION_PARTITION_CLOSE:
1087                  nError = partitionClose(nPartitionID);
1088                  TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nError);
1089                  break;
1090               case DELEGATION_INSTRUCTION_PARTITION_DESTROY:
1091                  nError = partitionDestroy(nPartitionID);
1092                  TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nError);
1093                  break;
1094               }
1095               g_pExchangeBuffer->sAdministrativeData.nPartitionErrorStates[nPartitionID] = nError;
1096            }
1097         }
1098      }
1099instruction_parse_end:
1100      memset(pOperation, 0, sizeof(TEEC_Operation));
1101   }
1102}
1103
1104/**
1105 * This function opens a new session to the delegation service.
1106 **/
1107static int createSession(TEEC_Context* pContext, TEEC_Session* pSession, TEEC_Operation* pOperation)
1108{
1109   TEEC_Result nError;
1110   uint32_t nExchangeBufferSize;
1111
1112   memset(pOperation, 0, sizeof(TEEC_Operation));
1113   pOperation->paramTypes = TEEC_PARAM_TYPES(
1114      TEEC_VALUE_OUTPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
1115   nError = TEEC_OpenSession(pContext,
1116                             pSession,                /* OUT session */
1117                             &g_sServiceId,           /* destination UUID */
1118                             TEEC_LOGIN_PRIVILEGED,   /* connectionMethod */
1119                             NULL,                    /* connectionData */
1120                             pOperation,              /* IN OUT operation */
1121                             NULL                     /* OUT errorOrigin, optional */
1122                             );
1123   if (nError != TEEC_SUCCESS)
1124   {
1125      LogError("Error on TEEC_OpenSession : 0x%x", nError);
1126      exit(2);
1127   }
1128   /* Read sector size */
1129   g_nSectorSize = pOperation->params[0].value.a;
1130   LogInfo("Sector Size: %d bytes", g_nSectorSize);
1131
1132   /* Check sector size */
1133   if (!(g_nSectorSize == 512 || g_nSectorSize == 1024 || g_nSectorSize == 2048 || g_nSectorSize == 4096))
1134   {
1135      LogError("Incorrect sector size: terminating...");
1136      exit(2);
1137   }
1138
1139   /* Check workspace size */
1140   if (g_nWorkspaceSize < 8 * g_nSectorSize)
1141   {
1142      g_nWorkspaceSize = 8 * g_nSectorSize;
1143      LogWarning("Workspace size too small, automatically set to %d bytes", g_nWorkspaceSize);
1144   }
1145   /* Compute the size of the exchange buffer */
1146   nExchangeBufferSize = sizeof(DELEGATION_EXCHANGE_BUFFER)-1+g_nWorkspaceSize;
1147   g_pExchangeBuffer  = (DELEGATION_EXCHANGE_BUFFER*)malloc(nExchangeBufferSize);
1148	 if (g_pExchangeBuffer == NULL)
1149   {
1150      LogError("Cannot allocate exchange buffer of %d bytes", nExchangeBufferSize);
1151      LogError("Now exiting...");
1152      exit(2);
1153   }
1154   g_pWorkspaceBuffer = (uint8_t*)g_pExchangeBuffer->sWorkspace;
1155   memset(g_pExchangeBuffer, 0x00, nExchangeBufferSize);
1156   memset(g_pPartitionFiles,0,16*sizeof(FILE*));
1157
1158   /* Register the exchange buffer as a shared memory block */
1159   sExchangeSharedMem.buffer = g_pExchangeBuffer;
1160   sExchangeSharedMem.size   = nExchangeBufferSize;
1161   sExchangeSharedMem.flags  = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
1162   nError = TEEC_RegisterSharedMemory(pContext, &sExchangeSharedMem);
1163   if (nError != TEEC_SUCCESS)
1164   {
1165      LogError("Error on TEEC_RegisterSharedMemory : 0x%x", nError);
1166      free(g_pExchangeBuffer);
1167      exit(2);
1168   }
1169   LogInfo("Daemon now connected");
1170   return 0;
1171}
1172
1173
1174/*----------------------------------------------------------------------------
1175 * Main
1176 *----------------------------------------------------------------------------*/
1177
1178#ifdef INCLUDE_CLIENT_DELEGATION
1179int delegation_main(int argc, char* argv[])
1180#else
1181int main(int argc, char* argv[])
1182#endif
1183{
1184   TEEC_Result    nError;
1185   TEEC_Context   sContext;
1186   TEEC_Session   sSession;
1187   TEEC_Operation sOperation;
1188   bool        debug = false;
1189
1190#ifndef SUPPORT_DELEGATION_EXTENSION
1191   char * baseDir = NULL;
1192
1193   LogInfo("TFSW Normal-World Daemon");
1194#else
1195   LogInfo("TFSW Normal-World Ext Daemon");
1196#endif
1197   LogInfo(S_VERSION_STRING);
1198   LogInfo("");
1199
1200   /* Skip program name */
1201   argv++;
1202   argc--;
1203
1204   while (argc != 0)
1205   {
1206      if (strcmp(argv[0], "-d") == 0)
1207      {
1208         debug = true;
1209      }
1210#ifdef SUPPORT_DELEGATION_EXTENSION
1211      else if (strcmp(argv[0], "-c") == 0)
1212      {
1213         int error;
1214         argc--;
1215         argv++;
1216         if (argc == 0)
1217         {
1218            printUsage();
1219            return 1;
1220         }
1221         /* Note that the function parseCommandLineExtension can modify the
1222            content of the g_partitionNames array */
1223         error = parseCommandLineExtension(argv[0], g_pPartitionNames);
1224         if ( error != 0 )
1225         {
1226            printUsage();
1227            return error;
1228         }
1229      }
1230#else
1231      else if (strcmp(argv[0], "-storageDir") == 0)
1232      {
1233         uint32_t i = 0;
1234         argc--;
1235         argv++;
1236         if (argc == 0)
1237         {
1238            printUsage();
1239            return 1;
1240         }
1241         if (baseDir != NULL)
1242         {
1243            LogError("Only one storage directory may be specified");
1244            return 1;
1245         }
1246         baseDir = malloc(strlen(argv[0])+1); /* Zero-terminated string */
1247         if (baseDir == NULL)
1248         {
1249             LogError("Out of memory");
1250             return 2;
1251         }
1252
1253         strcpy(baseDir, argv[0]);
1254
1255         /* Set default names to the partitions */
1256         for ( i=0; i<16 ;i++ )
1257         {
1258            g_pPartitionNames[i] = NULL;
1259            g_pPartitionNames[i] = malloc(strlen(baseDir) + 1 /* separator */ + sizeof("Store_X.tf"));
1260            if (g_pPartitionNames[i] != NULL)
1261            {
1262               sprintf(g_pPartitionNames[i], "%s%cStore_%1X.tf", baseDir, PATH_SEPARATOR, i);
1263            }
1264            else
1265            {
1266               free(baseDir);
1267               i=0;
1268               while(g_pPartitionNames[i] != NULL) free(g_pPartitionNames[i++]);
1269               LogError("Out of memory");
1270               return 2;
1271            }
1272         }
1273      }
1274      else if (strcmp(argv[0], "-workspaceSize") == 0)
1275      {
1276         argc--;
1277         argv++;
1278         if (argc == 0)
1279         {
1280            printUsage();
1281            return 1;
1282         }
1283         g_nWorkspaceSize=atol(argv[0]);
1284      }
1285#endif /* ! SUPPORT_DELEGATION_EXTENSION */
1286      /*****************************************/
1287      else if (strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-h") == 0)
1288      {
1289         printUsage();
1290         return 0;
1291      }
1292      else
1293      {
1294         printUsage();
1295         return 1;
1296      }
1297      argc--;
1298      argv++;
1299   }
1300
1301#ifndef SUPPORT_DELEGATION_EXTENSION
1302   if (baseDir == NULL)
1303   {
1304      LogError("-storageDir option is mandatory");
1305      return 1;
1306   }
1307   else
1308   {
1309      if (static_checkStorageDirAndAccessRights(baseDir) != 0)
1310      {
1311         return 1;
1312      }
1313   }
1314#endif /* #ifndef SUPPORT_DELEGATION_EXTENSION */
1315
1316   /*
1317    * Detach the daemon from the console
1318    */
1319
1320#if defined(LINUX) || (defined __ANDROID32__)
1321   {
1322      /*
1323       * Turns this application into a daemon => fork off parent process, setup logging, ...
1324       */
1325
1326      /* Our process ID and Session ID */
1327      pid_t pid, sid;
1328
1329      if (!debug)
1330      {
1331         LogInfo("tf_daemon is detaching from console... Further traces go to syslog");
1332         /* Fork off the parent process */
1333         pid = fork();
1334         if (pid < 0)
1335         {
1336            LogError("daemon forking failed");
1337            return 1;
1338         }
1339
1340         if (pid > 0)
1341         {
1342            /* parent */
1343            return 0;
1344         }
1345         bDetached = true;
1346      }
1347
1348      /* Change the file mode mask */
1349      umask(0077);
1350
1351      if (!debug)
1352      {
1353         /* Open any logs here */
1354         openlog("tf_daemon", 0, LOG_DAEMON);
1355
1356         /* Detach from the console */
1357         sid = setsid();
1358         if (sid < 0)
1359         {
1360            /* Log the failure */
1361            LogError("daemon group creation failed");
1362            return 1;
1363         }
1364         /* Close out the standard file descriptors */
1365         close(STDIN_FILENO);
1366         close(STDOUT_FILENO);
1367         close(STDERR_FILENO);
1368      }
1369   }
1370   /* Change priority so that tf_driver.ko with no polling thread is faster */
1371   if (setpriority(PRIO_PROCESS, 0, 19)!=0)
1372   {
1373      LogError("Daemon cannot change priority");
1374      return 1;
1375   }
1376
1377#endif
1378
1379   TRACE_INFO("Sector size is %d", g_nSectorSize);
1380
1381   LogInfo("tf_daemon - started");
1382
1383   nError = TEEC_InitializeContext(NULL,  /* const char * name */
1384                                   &sContext);   /* TEEC_Context* context */
1385   if (nError != TEEC_SUCCESS)
1386   {
1387      LogError("TEEC_InitializeContext error: 0x%08X", nError);
1388      LogError("Now exiting...");
1389      exit(2);
1390   }
1391
1392   /* Open a session */
1393   if(createSession(&sContext, &sSession, &sOperation) == 0)
1394   {
1395      /* Run the session. This should never return */
1396      runSession(&sContext, &sSession, &sOperation);
1397   }
1398   TEEC_FinalizeContext(&sContext);
1399
1400   return 3;
1401}
1402