text: /****************************************************************************
   *
   *   Copyright (C) 2012 PX4 Development Team. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   * 3. Neither the name PX4 nor the names of its contributors may be
   *    used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   *
   ****************************************************************************/
  
  /**
   * @file test_file2.c
   *
   * File write test.
   */
  
  #include <px4_defines.h>
  #include <sys/stat.h>
  #include <dirent.h>
  #include <stdio.h>
  #include <stddef.h>
  #include <unistd.h>
  #include <fcntl.h>
  #include <perf/perf_counter.h>
  #include <string.h>
  #include <stdlib.h>
  #include <px4_getopt.h>
  
  #include "tests_main.h"
  
  #define FLAG_FSYNC 1
  #define FLAG_LSEEK 2
  
  #define LOG_PATH PX4_STORAGEDIR
  
  /*
    return a predictable value for any file offset to allow detection of corruption
   */
  static uint8_t get_value(uint32_t ofs)
  {
  	union {
  		uint32_t ofs;
  		uint8_t buf[4];
  	} u;
  	u.ofs = ofs;
  	return u.buf[ofs % 4];
  }
  
  static int test_corruption(const char *filename, uint32_t write_chunk, uint32_t write_size, uint16_t flags)
  {
  	printf("Testing on %s with write_chunk=%u write_size=%u\n",
  	       filename, (unsigned)write_chunk, (unsigned)write_size);
  
  	uint32_t ofs = 0;
  	int fd = open(filename, O_CREAT | O_RDWR | O_TRUNC, PX4_O_MODE_666);
  
  	if (fd == -1) {
  		perror(filename);
  		return 1;
  	}
  
  	// create a file of size write_size, in write_chunk blocks
  	uint8_t counter = 0;
  
  	while (ofs < write_size) {
  		uint8_t buffer[write_chunk];
  
  		for (uint16_t j = 0; j < write_chunk; j++) {
  			buffer[j] = get_value(ofs);
  			ofs++;
  		}
  
  		if (write(fd, buffer, sizeof(buffer)) != (int)sizeof(buffer)) {
  			printf("write failed at offset %u\n", ofs);
  			return 1;
  		}
  
  		if (flags & FLAG_FSYNC) {
  			fsync(fd);
  		}
  
  		if (counter % 100 == 0) {
  			printf("write ofs=%u\r", ofs);
  		}
  
  		counter++;
  	}
  
  	close(fd);
  
  	printf("write ofs=%u\n", ofs);
  
  	// read and check
  	fd = open(filename, O_RDONLY);
  
  	if (fd == -1) {
  		perror(filename);
  		return 1;
  	}
  
  	counter = 0;
  	ofs = 0;
  
  	while (ofs < write_size) {
  		uint8_t buffer[write_chunk];
  
  		if (counter % 100 == 0) {
  			printf("read ofs=%u\r", ofs);
  		}
  
  		counter++;
  
  		if (read(fd, buffer, sizeof(buffer)) != (int)sizeof(buffer)) {
  			printf("read failed at offset %u\n", ofs);
  			close(fd);
  			return 1;
  		}
  
  		for (uint16_t j = 0; j < write_chunk; j++) {
  			if (buffer[j] != get_value(ofs)) {
  				printf("corruption at ofs=%u got %u\n", ofs, buffer[j]);
  				close(fd);
  				return 1;
  			}
  
  			ofs++;
  		}
  
  		if (flags & FLAG_LSEEK) {
  			lseek(fd, 0, SEEK_CUR);
  		}
  	}
  
  	printf("read ofs=%u\n", ofs);
  	close(fd);
  	unlink(filename);
  	printf("All OK\n");
  	return 0;
  }
  
  static void usage(void)
  {
  	printf("test file2 [options] [filename]\n");
  	printf("\toptions:\n");
  	printf("\t-s SIZE     set file size\n");
  	printf("\t-c CHUNK    set IO chunk size\n");
  	printf("\t-F          fsync on every write\n");
  	printf("\t-L          lseek on every read\n");
  }
  
  int test_file2(int argc, char *argv[])
  {
  	int opt;
  	uint16_t flags = 0;
  	const char *filename = LOG_PATH "/testfile2.dat";
  	uint32_t write_chunk = 64;
  	uint32_t write_size = 5 * 1024;
  
  	int myoptind = 1;
  	const char *myoptarg = NULL;
  
  	while ((opt = px4_getopt(argc, argv, "c:s:FLh", &myoptind, &myoptarg)) != EOF) {
  		switch (opt) {
  		case 'F':
  			flags |= FLAG_FSYNC;
  			break;
  
  		case 'L':
  			flags |= FLAG_LSEEK;
  			break;
  
  		case 's':
  			write_size = strtoul(myoptarg, NULL, 0);
  			break;
  
  		case 'c':
  			write_chunk = strtoul(myoptarg, NULL, 0);
  			break;
  
  		case 'h':
  		default:
  			usage();
  			return 1;
  		}
  	}
  
  	argc -= myoptind;
  	argv += myoptind;
  
  	if (argc > 0) {
  		filename = argv[0];
  	}
  
  	/* check if microSD card is mounted */
  	struct stat buffer;
  
  	if (stat(LOG_PATH, &buffer)) {
  		fprintf(stderr, "no microSD card mounted, aborting file test");
  		return 1;
  	}
  
  	return test_corruption(filename, write_chunk, write_size, flags);
  }
  
  