summaryrefslogtreecommitdiffstats
path: root/main/common/tfsapi.c
blob: c0385ceef8d71de85f30801c79ac1c8120cae1a4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
/**************************************************************************
 *
 * Copyright (c) 2013 Alcatel-Lucent
 *
 * Alcatel Lucent licenses this file to You under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License.  A copy of the License is contained the
 * file LICENSE at the top level of this repository.
 * You may also obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **************************************************************************
 *
 * tfsapi.c:
 *
 * This file contains the portion of TFS that provides the function-level
 * API to the application.  If this is not being used by the application
 * then it can be omitted from the monitor build.
 * Note that not all of the api-specific code is here; some of it is in
 * tfs.c.  This is because the the MicroMonitor uses some of the api itself,
 * so it cannot be omitted from the TFS package without screwing up some
 * other monitor functionality that needs it.
 *
 * Original author:     Ed Sutter (ed.sutter@alcatel-lucent.com)
 *
 */
#include "config.h"
#include "cpu.h"
#include "stddefs.h"
#include "genlib.h"
#include "tfs.h"
#include "tfsprivate.h"
#if INCLUDE_TFSAPI

/* tfstruncate():
 *  To support the ability to truncate a file (make it smaller); this
 *  function allows the user to adjust the high-water point of the currently
 *  opened (and assumed to be opened for modification) file and replaces
 *  that with the incoming argument.  This replacement is only done if the
 *  current high-water point is higher than the incoming length.
 *  MONLIB NOTICE: this function is accessible through monlib.c.
 */
int
tfstruncate(int fd, long len)
{
    struct tfsdat *tdat;

    if(tfsTrace > 1) {
        printf("tfstruncate(%d,%ld)\n",fd,len);
    }

    /* Verify valid range of incoming file descriptor. */
    if((fd < 0) || (fd >= TFS_MAXOPEN)) {
        return(TFSERR_BADFD);
    }

    tdat = &tfsSlots[fd];

    /* Make sure the file pointed to by the incoming descriptor is active
     * and that the incoming length is greater than the current high-water
     * point...
     */
    if(tdat->offset == -1) {
        return(TFSERR_BADFD);
    }
    if(len > tdat->hwp) {
        return(TFSERR_BADARG);
    }

    /* Make the adjustment... */
    tdat->hwp = len;
    return(TFS_OKAY);
}

/* tfseof():
 *  Return 1 if at the end of the file, else 0 if not at end; else negative
 *  if error.
 *  MONLIB NOTICE: this function is accessible through monlib.c.
 */
int
tfseof(int fd)
{
    struct tfsdat *tdat;

    /* Verify valid range of incoming file descriptor. */
    if((fd < 0) || (fd >= TFS_MAXOPEN)) {
        return(TFSERR_BADARG);
    }

    tdat = &tfsSlots[fd];

    /* Make sure the file pointed to by the incoming descriptor is active. */
    if(tdat->offset == -1) {
        return(TFSERR_BADFD);
    }

    if(tdat->offset >= tdat->hdr.filsize) {
        return(1);
    } else {
        return(0);
    }
}

/* tfsread():
 *  Similar to a standard read call to a file.
 *  MONLIB NOTICE: this function is accessible through monlib.c.
 */
int
tfsread(int fd, char *buf, int cnt)
{
    struct tfsdat *tdat;
    uchar *from;

    if(tfsTrace > 1) {
        printf("tfsread(%d,0x%lx,%d)\n",fd,(ulong)buf,cnt);
    }

    /* Verify valid range of incoming file descriptor. */
    if((cnt < 1) || (fd < 0) || (fd >= TFS_MAXOPEN)) {
        return(TFSERR_BADARG);
    }

    tdat = &tfsSlots[fd];

    /* Make sure the file pointed to by the incoming descriptor is active. */
    if(tdat->offset == -1) {
        return(TFSERR_BADFD);
    }

    if(tdat->offset >= tdat->hdr.filsize) {
        return(TFSERR_EOF);
    }

    from = (uchar *) tdat->base + tdat->offset;

    /* If request size is within the range of the file and current
     * then copy the data to the requestors buffer, increment offset
     * and return the count.
     */
    if((tdat->offset + cnt) <= tdat->hdr.filsize) {
        if(s_memcpy((char *)buf, (char *)from, cnt,0,0) != 0) {
            return(TFSERR_MEMFAIL);
        }
    }
    /* If request size goes beyond the size of the file, then copy
     * to the end of the file and return that smaller count.
     */
    else {
        cnt = tdat->hdr.filsize - tdat->offset;
        if(s_memcpy((char *)buf, (char *)from, cnt, 0, 0) != 0) {
            return(TFSERR_MEMFAIL);
        }
    }
    tdat->offset += cnt;
    return(cnt);
}

/* tfswrite():
 *  Similar to a standard write call to a file.
 *  MONLIB NOTICE: this function is accessible through monlib.c.
 */
int
tfswrite(int fd, char *buf, int cnt)
{
    struct tfsdat *tdat;

    if(tfsTrace > 1) {
        printf("tfswrite(%d,0x%lx,%d)\n", fd,(ulong)buf,cnt);
    }

    /* Verify valid range of incoming file descriptor. */
    if((cnt < 1) || (fd < 0) || (fd >= TFS_MAXOPEN)) {
        return(TFSERR_BADARG);
    }

    /* Make sure the file pointed to by the incoming descriptor is active. */
    if(tfsSlots[fd].offset == -1) {
        return(TFSERR_BADARG);
    }

    tdat = &tfsSlots[fd];

    /* Make sure file is not opened as read-only */
    if(tdat->flagmode & TFS_RDONLY) {
        return(TFSERR_RDONLY);
    }

    if(s_memcpy((char *)tdat->base+tdat->offset,(char *)buf,cnt,0,0) != 0) {
        return(TFSERR_MEMFAIL);
    }

    tdat->offset += cnt;

    /* If new offset is greater than current high-water point, then
     * adjust the high water point so that it is always reflecting the
     * highest offset into which the file has had some data written.
     */
    if(tdat->offset > tdat->hwp) {
        tdat->hwp = tdat->offset;
    }

    return(TFS_OKAY);
}

/* tfsseek():
 *  Adjust the current pointer into the specified file.
 *  If file is read-only, then the offset cannot exceed the file size;
 *  otherwise, the only check made to the offset is that it is positive.
 *  MONLIB NOTICE: this function is accessible through monlib.c.
 */
int
tfsseek(int fd, int offset, int whence)
{
    int o_offset;
    struct tfsdat *tdat;

    if(tfsTrace > 1) {
        printf("tfsseek(%d,%d,%d)\n",fd,offset,whence);
    }

    if((fd < 0) || (fd >= TFS_MAXOPEN)) {
        return(TFSERR_BADARG);
    }

    tdat = &tfsSlots[fd];
    o_offset = tdat->offset;

    switch(whence) {
    case TFS_BEGIN:
        tdat->offset = offset;
        break;
    case TFS_CURRENT:
        tdat->offset += offset;
        break;
    default:
        return(TFSERR_BADARG);
    }

    /* If new offset is less than zero or if the file is read-only and the
     * new offset is greater than the file size, return EOF...
     */
    if((tdat->offset < 0) ||
            ((tdat->flagmode & TFS_RDONLY) && (tdat->offset > tdat->hdr.filsize))) {
        tdat->offset = o_offset;
        return(TFSERR_EOF);
    }
    return(tdat->offset);
}

/* tfsipmod():
 *  Modify "in-place" a portion of a file in TFS.
 *  This is a cheap and dirty way to modify a file...
 *  The idea is that a file is created with a lot of writeable flash space
 *  (data = 0xff).  This function can then be called to immediately modify
 *  blocks of space in that flash.  It will not do any tfsunlink/tfsadd, and
 *  it doesn't even require a tfsopen() tfsclose() wrapper.  Its a fast and
 *  efficient way to modify flash in the file system.
 *  Arguments:
 *  name    =   name of the file to be in-place-modified;
 *  buf     =   new data to be written to flash;
 *  offset  =   offset into file into which new data is to be written;
 *  size    =   size of new data (in bytes).
 *
 *  With offset of -1, set offset to location containing first 0xff value.
 *  MONLIB NOTICE: this function is accessible through monlib.c.
 */
int
tfsipmod(char *name,char *buf,int offset,int size)
{
    int     rc;
    TFILE   *fp;
    uchar   *cp;

    fp = tfsstat(name);
    if(!fp) {
        return (TFSERR_NOFILE);
    }
    if(!(fp->flags & TFS_IPMOD)) {
        return(TFSERR_NOTIPMOD);
    }

    if(offset == -1) {
        cp = (uchar *)(TFS_BASE(fp));
        for(offset=0; offset<fp->filsize; offset++,cp++) {
            if(*cp == 0xff) {
                break;
            }
        }
    } else if(offset < -1) {
        return(TFSERR_BADARG);
    }

    if((offset + size) > fp->filsize) {
        return(TFSERR_WRITEMAX);
    }

    /* BUG fixed: 2/21/2001:
     * The (ulong *) cast was done prior to adding offset to the base.
     * This caused the offset to be quadrupled.
     */
    rc = tfsflashwrite((uchar *)(TFS_BASE(fp)+offset),(uchar *)buf,size);
    if(rc != TFS_OKAY) {
        return(rc);
    }

    tfslog(TFSLOG_IPM,name);
    return(TFS_OKAY);
}

/* tfsopen():
 *  Open a file for reading or creation.  If file is opened for writing,
 *  then the caller must provide a RAM buffer  pointer to be used for
 *  the file storage until it is transferred to flash by tfsclose().
 *  Note that the "buf" pointer is only needed for opening a file for
 *  creation or append (writing).
 *  MONLIB NOTICE: this function is accessible through monlib.c.
 */
int
tfsopen(char *file,long flagmode,char *buf)
{
    register int i;
    int     errno, retval;
    long    fmode;
    TFILE   *fp;
    struct  tfsdat *slot;

    errno = TFS_OKAY;

    fmode = flagmode & (TFS_RDONLY | TFS_APPEND | TFS_CREATE | TFS_CREATERM);

    /* See if file exists... */
    fp = tfsstat(file);

    /* If file exists, do a crc32 on the data.
     * If the file is in-place-modifiable, then the only legal flagmode
     * is TFS_RDONLY.  Plus, in this case, the crc32 test is skipped.
     */
    if(fp) {
        if(!((fmode == TFS_RDONLY) && (fp->flags & TFS_IPMOD))) {
            if(crc32((unsigned char *)TFS_BASE(fp),fp->filsize) != fp->filcrc) {
                retval = TFSERR_BADCRC;
                goto done;
            }
        }
    }

    /* This switch verifies...
     * - that the file exists if TFS_RDONLY or TFS_APPEND
     * - that the file does not exist if TFS_CREATE
     */
    switch(fmode) {
    case TFS_RDONLY:    /* Read existing file only, no change to file at all. */
        if(!fp) {
            if(_tfsstat(file,0)) {
                errno = TFSERR_LINKERROR;
            } else {
                errno = TFSERR_NOFILE;
            }
        } else {
            if((fp->flags & TFS_UNREAD) && (TFS_USRLVL(fp) > getUsrLvl())) {
                errno = TFSERR_USERDENIED;
            }
        }
        break;
    case TFS_APPEND:    /* Append to the end of the current file. */
        if(!fp) {
            errno = TFSERR_NOFILE;
        } else {
            if(TFS_USRLVL(fp) > getUsrLvl()) {
                errno = TFSERR_USERDENIED;
            }
        }
        break;
    case TFS_CREATERM:      /* Create a new file, allow tfsadd() to remove */
        fmode = TFS_CREATE; /* it if it exists. */
        break;
    case TFS_CREATE:    /* Create a new file, error if it exists. */
        if(fp) {
            errno = TFSERR_FILEEXISTS;
        }
        break;
    case(TFS_APPEND|TFS_CREATE):    /* If both mode bits are set, clear one */
        if(fp) {                    /* based on the presence of the file. */
            if(TFS_USRLVL(fp) > getUsrLvl()) {
                errno = TFSERR_USERDENIED;
            }
            fmode = TFS_APPEND;
        } else {
            fmode = TFS_CREATE;
        }
        break;
    default:
        errno = TFSERR_BADARG;
        break;
    }

    if(errno != TFS_OKAY) {
        retval = errno;
        goto done;
    }

    /* Find an empty slot...
     */
    slot = tfsSlots;
    for(i=0; i<TFS_MAXOPEN; i++,slot++) {
        if(slot->offset == -1) {
            break;
        }
    }

    /* Populate the slot structure if a slot is found to be
     * available...
     */
    if(i < TFS_MAXOPEN) {
        retval = i;
        slot->hwp = 0;
        slot->offset = 0;
        slot->flagmode = fmode;
        if(fmode & TFS_CREATE) {
            strncpy(slot->hdr.name,file,TFSNAMESIZE);
            slot->flagmode |= (flagmode & TFS_FLAGMASK);
            slot->base = (uchar *)buf;
        } else if(fmode & TFS_APPEND) {
            memcpy((char *)&slot->hdr,(char *)fp,sizeof(struct tfshdr));
            if(s_memcpy((char *)buf,(char *)(TFS_BASE(fp)),
                        fp->filsize,0,0) != 0) {
                retval = TFSERR_MEMFAIL;
                goto done;
            }
            slot->flagmode = fp->flags;
            slot->flagmode |= TFS_APPEND;
            slot->base = (uchar *)buf;
            slot->hwp = fp->filsize;
            slot->offset = fp->filsize;
        } else {
            slot->base = (uchar *)(TFS_BASE(fp));
            memcpy((char *)&slot->hdr,(char *)fp,sizeof(struct tfshdr));
        }
    } else {
        retval = TFSERR_NOSLOT;
    }

done:
    if(tfsTrace > 0) {
        printf("tfsopen(%s,0x%lx,0x%lx)=%d\n",file,flagmode,(ulong)buf,retval);
    }

    return(retval);
}

/* tfsclose():
 *  If the file was opened for reading only, then just close out the
 *  entry in the tfsSlots table.  If the file was opened for creation,
 *  then add it to the tfs list.  Note the additional argument is
 *  only needed for tfsclose() of a newly created file.
 *  info  = additional text describing the file.
 *  MONLIB NOTICE: this function is accessible through monlib.c.
 */
int
tfsclose(int fd,char *info)
{
    int     err;
    struct tfsdat *tdat;

    if(!info) {
        info = "";
    }

    if(tfsTrace > 0) {
        printf("tfsclose(%d,%s)\n",fd,info);
    }

    if((fd < 0) || (fd >= TFS_MAXOPEN)) {
        return(TFSERR_BADARG);
    }

    tdat = &tfsSlots[fd];

    if(tdat->offset == -1) {
        return(TFSERR_BADFD);
    }

    /* Mark the file as closed by setting the offset to -1.
     * Note that this is done prior to potentially calling tfsadd() so
     * that tfsadd() will not think the file is opened and reject the add...
     */
    tdat->offset = -1;

    /* If the file was opened for creation or append, and the hwp
     * (high-water-point) is greater than zero, then add it now.
     *
     * Note regarding hwp==0...
     * In cases where a non-existent file is opened for creation,
     * but then nothing is written to the file, the hwp value will
     * be zero; hence, no need to call tfsadd().
     */
    if((tdat->flagmode & (TFS_CREATE | TFS_APPEND)) && (tdat->hwp > 0)) {
        char    buf[16];

        err = tfsadd(tdat->hdr.name, info, tfsflagsbtoa(tdat->flagmode,buf),
                     tdat->base, tdat->hwp);
        if(err != TFS_OKAY) {
            printf("%s: %s\n",tdat->hdr.name,tfserrmsg(err));
            return(err);
        }
    }
    return(TFS_OKAY);
}

#else /* INCLUDE_TFSAPI */

int
tfstruncate(int fd, long len)
{
    return(TFSERR_NOTAVAILABLE);
}

int
tfseof(int fd)
{
    return(TFSERR_NOTAVAILABLE);
}

int
tfsread(int fd, char *buf, int cnt)
{
    return(TFSERR_NOTAVAILABLE);
}

int
tfswrite(int fd, char *buf, int cnt)
{
    return(TFSERR_NOTAVAILABLE);
}

int
tfsseek(int fd, int offset, int whence)
{
    return(TFSERR_NOTAVAILABLE);
}

int
tfsopen(char *file,long flagmode,char *buf)
{
    return(TFSERR_NOTAVAILABLE);
}

int
tfsclose(int fd,char *info)
{
    return(TFSERR_NOTAVAILABLE);
}

int
tfsipmod(char *name,char *buf,int offset,int size)
{
    return(TFSERR_NOTAVAILABLE);
}

#endif  /* INCLUDE_TFSAPI else */

#if INCLUDE_TFSAPI || INCLUDE_TFSSCRIPT

/* tfsgetline():
 *  Read into the buffer a block of characters upto the next EOL delimiter
 *  the file.  After the EOL, or after max-1 chars are loaded, terminate
 *  with a NULL.  Return the number of characters loaded.
 *  At end of file return 0.
 *  MONLIB NOTICE: this function is accessible through monlib.c.
 */
int
tfsgetline(int fd,char *buf,int max)
{
    uchar   *from;
    int     tot, rtot;
    struct  tfsdat *tdat;
    volatile char   *to;

    max--;

    if(tfsTrace > 1) {
        printf("tfsgetline(%d,0x%lx,%d)\n",fd,(ulong)buf,max);
    }

    /* Verify valid range of incoming file descriptor. */
    if((max < 1) || (fd < 0) || (fd >= TFS_MAXOPEN)) {
        return(TFSERR_BADARG);
    }

    /* Make sure the file pointed to by the incoming descriptor is active. */
    if(tfsSlots[fd].offset == -1) {
        return(TFSERR_BADARG);
    }

    tdat = &tfsSlots[fd];

    if(tdat->offset == -1) {
        return(TFSERR_BADFD);
    }

    if(tdat->offset >= tdat->hdr.filsize) {
        return(0);
    }

    from = (uchar *) tdat->base + tdat->offset;
    to = buf;

    /* If the incoming buffer size is larger than needed, adjust the
     * 'max' value so that we don't pass the end of the file...
     */
    if((tdat->offset + max) > tdat->hdr.filsize) {
        max = tdat->hdr.filsize - tdat->offset + 1;
    }

    /* Read from the file data area until newline (0x0a) is found
     * (or until the 'max buffer space' value is reached).
     * Strip 0x0d (if present) and terminate with NULL  in all cases.
     */
    for(rtot=0,tot=0; tot < max; from++) {
        /* Terminate on Ctrl-Z, non-ASCII, Newline or NULL.
         * Ignore carriage return completely...
         */
        if((*from == 0x1a) || (*from > 0x7f) || (*from == 0)) {
            break;
        }

        tot++;

        if(*from == 0x0d) {
            continue;
        }

        *to = *from;
        if(*to != *from) {
            return(TFSERR_MEMFAIL);
        }

        to++;
        rtot++;

        if(*from == 0x0a) {
            break;
        }
    }
    *to = 0;

    tdat->offset += tot;
    return(rtot);
}

#else

int
tfsgetline(int fd,char *buf,int max)
{
    return(TFSERR_NOTAVAILABLE);
}

#endif