Hard Fault when optimization level 1 is enabled (FreeRTOS) - c

I have the following function used to test the file system (yaffs2). It is called by a FreeRTOS task with a stack size of 65535. In debug mode, everything appears to work fine, but when I enable -O1, I get a hard fault immediately after printing "Testing filesystem...".
I tried changing the test size to a much lower value, thinking maybe there is a memory issue (note that the FreeRTOS heap is 7MB wide), but nothing changed. Any ideas what might be causing this?
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "yaffs_trace.h"
#include "yaffsfs.h"
int fs_test(void)
{
int fd;
int test_size = 1000000;
const char path[] = "nand/testfile.dat";
int pattern = 0x55;
uint8_t *test;
uint8_t *test_read;
uint32_t start_time, stop_time;
DEBUG_PRINT("Testing filesystem...\n");
fd = yaffs_open(path, O_CREAT | O_EXCL | O_WRONLY, S_IREAD | S_IWRITE);
if(fd < 0) {
DEBUG_PRINT("cannot create test file\n");
return -1;
}
test = pvPortMalloc(test_size);
test_read = pvPortMalloc(test_size);
if(test == NULL || test_read == NULL) {
DEBUG_PRINT("not enough memory\n");
vPortFree(test);
vPortFree(test_read);
yaffs_close(fd);
yaffs_rm(path);
return -2;
}
memset(test, pattern, test_size);
start_time = xTaskGetTickCount();
yaffs_write(fd, test, test_size);
yaffs_close(fd);
stop_time = xTaskGetTickCount();
DEBUG_PRINTF("Average write speed = %d kB/s\n", test_size / (stop_time-start_time));
fd = yaffs_open(path, O_RDWR, S_IREAD | S_IWRITE);
if(fd < 0) {
DEBUG_PRINT("cannot open test file\n");
vPortFree(test);
vPortFree(test_read);
yaffs_close(fd);
yaffs_rm(path);
return -1;
}
start_time = xTaskGetTickCount();
yaffs_read(fd, test_read, test_size);
yaffs_close(fd);
stop_time = xTaskGetTickCount();
DEBUG_PRINTF("Average read speed = %d kB/s\n", test_size / (stop_time-start_time));
if (!memcmp(test, test_read, test_size))
DEBUG_PRINT("file integrity test successful\n");
else
DEBUG_PRINT("file integrity test failed!\n");
yaffs_rm(path);
vPortFree(test);
vPortFree(test_read);
return 0;
}

Related

Shared memory corrupting data

I'm trying to write a program that uses counting semaphores, a mutex, and two threads. One thread is a producer that writes items to shared memory. Each item has a sequence number, timestamp, checksum, and some data. The consumer thread copies the original checksum from an item then calculates its own checksum from the item's data and compares the two to make sure the data wasn't corrupted.
My program runs, however, it reports incorrect checksums far more than correct checksums. I did some print statements to see what was going on, and it looks like the item's data is changing between writing to shared memory and reading from it. The item's stored checksum is also changing, and I have no idea what is causing this.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <errno.h>
#include <stdint.h>
#include <semaphore.h>
#include <time.h>
#include <pthread.h>
typedef struct{
int seqNo;
uint16_t checksum;
uint32_t timeStamp;
uint8_t data[22];
}Item;
char* shm_name = "buffer";
int shm_fd;
uint8_t *shm_ptr;
pthread_t producers;
pthread_t consumers;
pthread_mutex_t mutex;
sem_t *empty, *full;
int shmSize;
int in = 0;
int out = 0;
//method for initializing shared memory
void CreateSharedMemory(){
shm_fd = shm_open(shm_name, O_CREAT | O_RDWR, 0644);
if (shm_fd == -1) {
fprintf(stderr, "Error unable to create shared memory, '%s, errno = %d (%s)\n", shm_name,
errno, strerror(errno));
return -1;
}
/* configure the size of the shared memory segment */
if (ftruncate(shm_fd, shmSize) == -1) {
fprintf(stderr, "Error configure create shared memory, '%s, errno = %d (%s)\n", shm_name,
errno, strerror(errno));
shm_unlink(shm_name);
return -1;
}
printf("shared memory create success, shm_fd = %d\n", shm_fd);
}
uint16_t checksum(char *addr, uint32_t count)
{
register uint32_t sum = 0;
uint16_t *buf = (uint16_t *) addr;
// Main summing loop
while(count > 1)
{
sum = sum + *(buf)++;
count = count - 2;
}
// Add left-over byte, if any
if (count > 0)
sum = sum + *addr;
// Fold 32-bit sum to 16 bits
while (sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return(~sum);
}
Item CreateItem(){
Item item;
uint16_t cksum;
int j = 0;
time_t seconds;
seconds = time(NULL);
item.seqNo = j;
item.timeStamp = seconds; //FIX this
for(int i = 0; i < 22; ++i){
item.data[i] = rand() % 256;
}
cksum = checksum(&item.data[0], shmSize-2);
item.checksum = cksum;
++j;
return item;
}
void* producer() {
shm_fd = shm_open(shm_name, O_RDWR, 0644);
shm_ptr = (uint8_t *)mmap(0, 32, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
while(1) {
Item tempItem = CreateItem();
tempItem.seqNo = in;
sem_wait(empty);
pthread_mutex_lock(&mutex);
while (((in + 1)%shmSize) == out)
; // waiting
if(in < shmSize) {
//&shm_ptr[counter] = item;
\
memcpy(&shm_ptr[in], &tempItem, 32);
printf("%d\n", tempItem.seqNo);
in = (in + 1) % shmSize;
printf("Producer: %x\n", tempItem.checksum);
}
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(full);
}
}
void* consumer() {
uint16_t cksum1, cksum2;
shm_fd = shm_open(shm_name, O_RDWR, 0644);
shm_ptr = (uint8_t *)mmap(0, shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
while(1) {
sem_wait(full);
pthread_mutex_lock(&mutex);
while (in == out)
; // waiting
if(out > 0) {
Item tempItem;
memcpy(&tempItem, &shm_ptr[out], 32);
cksum1 = tempItem.checksum;
cksum2 = checksum(&tempItem.data[0], shmSize-2);
if (cksum1 != cksum2) {
printf("Checksum mismatch: expected %02x, received %02x \n", cksum2, cksum1);
}
else{
printf("removed from shm\n");
}
//printf("Checksums match !!! \n");
out = (out + 1)%shmSize;
}
sleep(1);
pthread_mutex_unlock(&mutex);
sem_post(empty);
}
}
int main(int argc, char **argv){
sem_unlink(&empty);
sem_unlink(&full);
shm_unlink(shm_name);
shmSize = atoi(argv[1]);
out = shmSize;
if(shmSize < 0){
printf("Error: Size of buffer cannot be negative. ");
return -1;
}
pthread_mutex_init(&mutex, NULL);
empty = sem_open("/empty", O_CREAT, 0644, shmSize);
full = sem_open("/full", O_CREAT, 0644, 0);
CreateSharedMemory();
pthread_create(&producers, NULL, producer, NULL);
pthread_create(&consumers, NULL, consumer, NULL);
pthread_exit(NULL);

Is the "mmap tutorial" incorrect, or does GCC miscompile it?

This mmap tutorial from 15 years ago ranks high in Google searches, but it actually runs subtly incorrectly on my Linux system.
mmap_write.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define FILEPATH "/tmp/mmapped.bin"
#define NUMINTS (1000)
#define FILESIZE (NUMINTS * sizeof(int))
int main(int argc, char *argv[])
{
int i;
int fd;
int result;
int *map; /* mmapped array of int's */
/* Open a file for writing.
* - Creating the file if it doesn't exist.
* - Truncating it to 0 size if it already exists. (not really needed)
*
* Note: "O_WRONLY" mode is not sufficient when mmaping.
*/
fd = open(FILEPATH, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
if (fd == -1) {
perror("Error opening file for writing");
exit(EXIT_FAILURE);
}
/* Stretch the file size to the size of the (mmapped) array of ints
*/
result = lseek(fd, FILESIZE-1, SEEK_SET);
if (result == -1) {
close(fd);
perror("Error calling lseek() to 'stretch' the file");
exit(EXIT_FAILURE);
}
/* Something needs to be written at the end of the file to
* have the file actually have the new size.
* Just writing an empty string at the current file position will do.
*
* Note:
* - The current position in the file is at the end of the stretched
* file due to the call to lseek().
* - An empty string is actually a single '\0' character, so a zero-byte
* will be written at the last byte of the file.
*/
result = write(fd, "", 1);
if (result != 1) {
close(fd);
perror("Error writing last byte of the file");
exit(EXIT_FAILURE);
}
/* Now the file is ready to be mmapped.
*/
map = mmap(0, FILESIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
/* Now write int's to the file as if it were memory (an array of ints).
*/
for (i = 1; i <=NUMINTS; ++i) {
map[i] = 2 * i;
}
/* Don't forget to free the mmapped memory
*/
if (munmap(map, FILESIZE) == -1) {
perror("Error un-mmapping the file");
/* Decide here whether to close(fd) and exit() or not. Depends... */
}
/* Un-mmaping doesn't close the file, so we still need to do that.
*/
close(fd);
return 0;
}
mmap_read.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#define FILEPATH "/tmp/mmapped.bin"
#define NUMINTS (1000)
#define FILESIZE (NUMINTS * sizeof(int))
int main(int argc, char *argv[])
{
int i;
int fd;
int *map; /* mmapped array of int's */
fd = open(FILEPATH, O_RDONLY);
if (fd == -1) {
perror("Error opening file for reading");
exit(EXIT_FAILURE);
}
map = mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
/* Read the file int-by-int from the mmap
*/
for (i = 1; i <=NUMINTS; ++i) {
printf("%d: %d\n", i, map[i]);
}
if (munmap(map, FILESIZE) == -1) {
perror("Error un-mmapping the file");
}
close(fd);
return 0;
}
If the file does not already exist, the output of mmap_read is
...
998: 1996
999: 1998
1000: 2000
But if it does, the output is
...
998: 1996
999: 1998
1000: 0
Should the author have flushed the write? Or is GCC miscompiling the code?
Edit: I noticed that it's the prior existence or non-existence of the file that makes a difference, not the compilation flag.
You are starting at the second element, and writing 2000 after the end of the map.
for (i = 1; i <=NUMINTS; ++i) {
map[i] = 2 * i;
}
should be
for (i = 0; i < NUMINTS; ++i) {
map[i] = 2 * ( i + 1 );
}
Demo
It's not a buffering issue. write is a system call, so the data passed to the OS directly. It doesn't mean the data has been written to disk when write returns, but it is in the OS's hands, so it's as if it was on disk as far as OS functions are concerned, including its memory-mapping functionality.
In C indexes are from zero. Writing and reading index 1000 you invoke undefined behaviour
Change to in the write.:
for (i = 1; i <=NUMINTS; ++i) {
map[i - 1] = 2 * i;
}
and reading to:
for (i = 1; i <=NUMINTS; ++i) {
printf("%d: %d\n", i, map[i-1]);
}

Effect of Buffer Size in File I/O in Unix

I am trying to understand the inner workings of Unix based OSs. I was reading on buffered I/O, and how the buffer size affects the number of system calls made, which in turn affects the total time taken by say, a copy program. To being with, here is my program:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
long currentTimeMillis();
int main(int argc, char *argv[]) {
int bufsize = atoi(argv[3]);
printf("copying with buffer size %d\n", bufsize);
char buf[bufsize];
//open the file
int fd_from = open(argv[1], O_RDWR);
if(-1 == fd_from) {
printf("Error opening source file\n");
return -1;
}
//file to be copied to
int fd_to = open(argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if(-1 == fd_to) {
printf("Error opening destination file\n");
return -1;
}
//copy
long startTime = currentTimeMillis();
int bytes_read = 0;
long totalTimeForRead = 0;
long totalTimeForWrite = 0;
while(1) {
long readStartTime = currentTimeMillis();
int bytes_read = read(fd_from,buf,bufsize);
long readEndTime = currentTimeMillis();
if(0 == bytes_read) {
break;
}
if(-1 == bytes_read) {
printf("Error occurred while reading source file\n");
return -1;
}
totalTimeForRead += readEndTime - readStartTime;
long writeStartTime = currentTimeMillis();
int bytes_written = write(fd_to,buf,bufsize);
long writeEndTime = currentTimeMillis();
totalTimeForWrite += (writeEndTime - writeStartTime);
if(-1 == bytes_written) {
printf("Some error occurred while writing file\n");
return -1;
}
}
long endTime = currentTimeMillis();
printf("Total time to copy%ld\n", endTime - startTime);
printf("Total time to write%ld\n", totalTimeForWrite);
printf("Total time to read%ld\n", totalTimeForRead);
}
long currentTimeMillis() {
struct timeval time;
gettimeofday(&time, NULL);
return time.tv_sec * 1000 + time.tv_usec / 1000;
}
I am using a 16G MacBook Pro with 2.9GHz Intel i7 (if this information would be useful). The size of source file is 2.8G. I was a bit surprised to see that total time take by read() is much smaller than write(). Here're my findings with a buffer size of 16K:
./a.out largefile dest 16382
copying with buffer size 16382
Total time to copy5987
Total time to write5330
Total time to read638
From what I have read, write() returns immediately after transferring the data to the kernel buffer from the user buffer. So the time taken by it is this time + the time taken for the system call initiation. read() also reads from kernel buffer to user buffer, so the total time taken should be same (in both cases, there is no disk I/O).
Why is it then that there is such drastic difference in the results? Or am I benchmarking it wrong? Final question: Is it alright to do such benchmarking on a SSD, which has limited write cycles?

Sharing text file between processes with open() and mmap()

I'm trying to share a text file between forked processes on my Ubuntu x86_64: the file will not be absurdly large, since strings will be written only if there is not already another identical string in the file; strings will be hostnames of visited websites, so I'll assume no more than 255 bytes for each hostname.
When it is a process' turn to write in shared object, it is OK; once all the processes wrote in shared object, msync should make the writing effective on the disk, but the mapped.txt file created only contain one string from arrayString, i.e. the string the last process wrote in shared object.
Here's the code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <string.h>
// first forked process will write "first" in file, and so on
const char *arrayString[] = {
"first",
"second",
"third"
};
int main(void) {
int index;
int children = 3;
const char *filepath = "mapped.txt";
sem_t *sem;
sem = sem_open("semaphore", O_CREAT | O_EXCL, 0644, 1);
sem_unlink("semaphore");
int fd;
fd = open(filepath, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("open:");
return EXIT_FAILURE;
}
char *data;
data = (char *)mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
perror("mmap:");
return EXIT_FAILURE;
}
for (index=0; index<children; index++) {
if (fork() == 0) {
sem_wait(sem);
size_t textsize = strlen(arrayString[index])+1;
if (ftruncate(fd, sizeof(textsize)) == -1) {
perror("ftruncate:");
return EXIT_FAILURE;
}
for (size_t i = 0; i < textsize; i++) {
printf("%d Writing character %c at %zu\n", getpid(), arrayString[index][i], i);
data[i] = arrayString[index][i];
}
printf("%d wrote ", getpid());
for (size_t i = 0; i < textsize; i++) {
printf("%c", data[i]);
}
printf("\n");
if (msync(data, textsize, MS_SYNC) == -1) {
perror("Could not sync the file to disk");
}
sem_post(sem);
_exit(EXIT_SUCCESS);
}
}
close(fd);
return EXIT_SUCCESS;
}
This is one possible output of the code above for three child processes (this is fine):
20373 Writing character s at 0
20373 Writing character e at 1
20373 Writing character c at 2
20373 Writing character o at 3
20373 Writing character n at 4
20373 Writing character d at 5
20373 Writing character at 6
20373 wrote second
20374 Writing character t at 0
20374 Writing character h at 1
20374 Writing character i at 2
20374 Writing character r at 3
20374 Writing character d at 4
20374 Writing character at 5
20374 wrote third
20372 Writing character f at 0
20372 Writing character i at 1
20372 Writing character r at 2
20372 Writing character s at 3
20372 Writing character t at 4
20372 Writing character at 5
20372 wrote first
And here's the content of mapped.txt (this is bad):
first^#^#^#
I expected:
second
third
first
but all I get is only the string of the last process, with those strange symbols. I'd like to keep this file persistent in memory, but because of the I/O slowness, I'm trying to use memory mapping.
Any idea why my file only contains the string written by the last process accessing the shared file?
Edit: I think I get it, it seems to work now: I hope it will be of help to someone. Compiled with g++ -g -o mapthis mapthis.cpp -lrt -pthread. Beware that some error checking are missing, like for fsync, snprintf and lseek.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
const char *arrayString[] = {
"www.facebook.com",
"www.google.com",
"www.cnn.com",
"www.speechrepository.com",
"www.youtube.com",
"www.facebook.com",
"www.google.com",
"www.cnn.com",
"www.speechrepository.com",
"www.youtube.com",
"www.facebook.com",
"www.google.com",
"www.cnn.com",
"www.speechrepository.com",
"www.youtube.com"
};
int main(void) {
int index;
int children = sizeof(arrayString) / sizeof(char*);;
const char *filepath = "mapped.txt";
sem_t *sem;
char *data;
struct stat filestats;
sem = sem_open("semaphore", O_CREAT | O_EXCL, 0644, 1);
sem_unlink("semaphore");
int fd;
fd = open(filepath, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("open:");
return EXIT_FAILURE;
}
if (fstat(fd, &filestats) < 0) {
close(fd);
perror("fstat:");
return EXIT_FAILURE;
}
data = (char *)mmap(NULL, filestats.st_size ? filestats.st_size : 1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
perror("first map:");
return EXIT_FAILURE;
}
for (index=0; index<children; index++) {
sleep(1);
pid_t pid = fork();
if (pid == 0) {
int nw = 0;
int hostnameSize = 0;
const size_t origsize = filestats.st_size;
char *hostPos = NULL;
char *numPos = NULL;
char *backslashPos = NULL;
char tempBuff[64];
memset((char *)tempBuff, 0, sizeof(tempBuff));
sem_wait(sem);
// remap to current file size if it changed
fstat(fd, &filestats);
// file empty, just insert
if (filestats.st_size == 0) {
nw = snprintf(tempBuff, sizeof(tempBuff), "%s %010lu\n", arrayString[index], (unsigned long)time(NULL));
write(fd, tempBuff, nw);
fsync(fd);
}
else {
// file not empty, let's look for string
hostPos = strstr(data, arrayString[index]);
if (hostPos) {
// string is already inserted, search for offset of number of seconds
lseek(fd, hostPos-data, SEEK_SET);
numPos = strchr(hostPos, ' ')+1;
backslashPos = strchr(numPos, '\n');
long unsigned before = atoi(numPos);
long unsigned now = (unsigned long)time(NULL);
long unsigned difference = now - before;
printf("%s visited %ld seconds ago (%ld - %ld)\n",
arrayString[index], difference, now, before);
nw = snprintf(tempBuff, backslashPos-hostPos+1, "%s %010lu", arrayString[index], now);
write(fd, tempBuff, nw);
write(fd, "\n", 1);
fsync(fd);
}
else {
data = (char *)mremap(data, origsize, filestats.st_size, MREMAP_MAYMOVE);
if (data == MAP_FAILED) {
close(fd);
sem_post(sem);
perror("mmap:");
_exit(EXIT_FAILURE);
}
lseek(fd, 0, SEEK_END);
nw = snprintf(tempBuff, sizeof(tempBuff), "%s %010lu\n", arrayString[index], (unsigned long)time(NULL));
write(fd, tempBuff, nw);
fsync(fd);
}
}
munmap(data, filestats.st_size);
close(fd);
sem_post(sem);
_exit(EXIT_SUCCESS);
}
else if (pid > 0) {
wait(NULL);
}
}
munmap(data, filestats.st_size);
close(fd);
return EXIT_SUCCESS;
}
This line is problematic:
if (ftruncate(fd, sizeof(textsize)) == -1) {
textsize is a size_t, and taking its sizeof is just going to get 4 or 8 (on 32 and 64 bit systems). Looks like you're on a 64 bit system, so you're unconditionally truncating the file to 8 bytes in this case before every write. The "strange symbols" are just how your editor displays NUL/zero bytes. Even if you used ftruncate(fd, textsize), you'd still truncate down to just the string you're about to write, overwriting any data other children may have written; I doubt you want to ftruncate at all here.
For continual appends from separate processes (where they can't share information about the size or offset of the data they're adding), memory mapping just doesn't make sense; why aren't you just having each of them take the lock, lseek to end of file, then call write? You could still use memory mappings for the duplicate checking (some of it without locking), it would just be a bit different. Something like this:
int main(void) {
struct stat filestats;
int index;
int children = 3;
const char *filepath = "mapped.txt";
sem_t *sem;
char *data;
sem = sem_open("semaphore", O_CREAT | O_EXCL, 0644, 1);
sem_unlink("semaphore");
int fd;
fd = open(filepath, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
perror("open:");
return EXIT_FAILURE;
}
// Mostly just to ensure it's mappable, we map the current size of the file
// If the file might already have values, and many child workers won't add
// to it, this might save some mapping work in the children; you could
// just map in the children when needed though
if (fstat(fd, &filestats) != 0) {
close(fd);
perror("fstat:");
return EXIT_FAILURE;
}
data = mmap(NULL, filestats.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
perror("mmap:");
return EXIT_FAILURE;
}
for (index=0; index<children; index++) {
if (fork() == 0) {
const size_t origsize = filestats.st_size;
sem_wait(sem);
// remap to current file size if it changed
// If you're not on Linux, you'd just have to mmap from scratch
// since mremap isn't standard
fstat(fd, &filestats);
if (origsize != filestats.st_size) {
data = mremap(data, origsize, filestats.st_size, MREMAP_MAYMOVE);
if (data == MAP_FAILED) {
close(fd);
sem_post(sem);
perror("mmap:");
_exit(EXIT_FAILURE);
}
}
// Not safe to use strstr since mapping might not end with NUL byte
// You'd need to workaround this, or implement a your own memstr-like function
if (!memstr(data, arrayString[index])) {
// Move fd to end of file, so we append new data
lseek(fd, 0, SEEK_END);
write(fd, arrayString[index], strlen(arrayString[index]));
write(fd, "\n", 1);
fsync(fd);
}
munmap(data, filestats.st_size);
close(fd);
sem_post(sem);
_exit(EXIT_SUCCESS);
}
}
munmap(data, filestats.st_size);
close(fd);
return EXIT_SUCCESS;
}
That memstr I referenced would need to be hand-implemented (or you'd need to do terrible things like ensure the file always had a NUL byte at the end so you could use strstr on it); you can get some tips on that here.
You're writing all the strings at offset 0 of the file, each over the top of the previous. The core of your loop should be something like
struct stat status;
fstat(fd, &status);
size_t cursize = status.st_size;
ftruncate(fd, cursize + textsize);
for (size_t i = 0; i < textsize; i++) {
data[cursize + i] = arrayString[index][i];
}

How to get USB connected hard disk serial in linux?

I'm having a requirement to create a file in the externally mounted hard disk .created file should contain the serial no of the harddisk and that file can be used by other process.
I tried to use the following code
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
int main(int argc, char *argv[])
{
static struct hd_driveid hd;
int fd;
if (geteuid() > 0) {
printf("ERROR: Must be root to use\n");
exit(1);
}
if ((fd = open(argv[1], O_RDONLY|O_NONBLOCK)) < 0) {
printf("ERROR: Cannot open device %s\n", argv[1]);
exit(1);
}
if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
printf("Hard Disk Model: %.40s\n", hd.model);
printf(" Serial Number: %.20s\n", hd.serial_no);
} else if (errno == -ENOMSG) {
printf("No hard disk identification information available\n");
} else {
perror("ERROR: HDIO_GET_IDENTITY");
exit(1);
}
exit(0);
}
this is working fine for internal hard disk but when i do this for external hard disk(usb) it is giving me the following error
ERROR: HDIO_GET_IDENTITY: Invalid argument
Because the device is connected to a USB bridge, you can't send the HDIO_GET_IDENTITY command.
You can try hdparm to query the identity of the device. With the default options, hdparm fails to identify the device so you have to specify the type of the device with -d (see USB devices and smartmontools).
Without the -d option, I get:
$ sudo smartctl /dev/sdc
/dev/sdc: Unknown USB bridge [0x059f:0x1011 (0x000)]
Please specify device type with the -d option.
With -d sat,auto, hdparm manages to display some information about the device:
$ sudo smartctl -d sat,auto -i /dev/sdc
/dev/sdc [SCSI]: Device open changed type from 'sat,auto' to 'scsi'
=== START OF INFORMATION SECTION ===
Vendor: ST2000VN
Product: 000-1H3164
User Capacity: 2 000 398 934 016 bytes [2,00 TB]
Logical block size: 512 bytes
Device type: disk
Local Time is: Thu Mar 13 09:41:32 2014 CET
SMART support is: Unavailable - device lacks SMART capability.
You can try to do the same thing as smartctl in your C program, but it's probably easier to write a script that invokes smartctl.
Thanks for the explanation and i got the below to identify the serial no of a external hardisk
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>
int scsi_get_serial(int fd, void *buf, size_t buf_len) {
// we shall retrieve page 0x80 as per http://en.wikipedia.org/wiki/SCSI_Inquiry_Command
unsigned char inq_cmd[] = {INQUIRY, 1, 0x80, 0, buf_len, 0};
unsigned char sense[32];
struct sg_io_hdr io_hdr;
int result;
memset(&io_hdr, 0, sizeof (io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmdp = inq_cmd;
io_hdr.cmd_len = sizeof (inq_cmd);
io_hdr.dxferp = buf;
io_hdr.dxfer_len = buf_len;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.sbp = sense;
io_hdr.mx_sb_len = sizeof (sense);
io_hdr.timeout = 5000;
result = ioctl(fd, SG_IO, &io_hdr);
if (result < 0)
return result;
if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK)
return 1;
return 0;
}
void trim(char * s) {
char * p = s;
int l = strlen(p);
while(isspace(p[l - 1])) p[--l] = 0;
while(* p && isspace(* p)) ++p, --l;
memmove(s, p, l + 1);
}
int storeData (char *filepath, char *data) {
int rc = 0;
FILE *fOut = fopen (filepath, "a");
if (fOut != NULL) {
if (fputs (data, fOut) != EOF) {
rc = 1;
}
fclose (fOut); // or for the paranoid: if (fclose (fOut) == EOF) rc = 0;
}
return rc;
}
int main(int argc, char** argv) {
if(argc>1){
char *dev = (char *)argv[1];
char outStr[1024];
printf("\nEntered Serial no : %s\n",argv[1]);
char scsi_serial[255];
int rc;
int fd;
fd = open(dev, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
perror(dev);
}
memset(scsi_serial, 0, sizeof (scsi_serial));
rc = scsi_get_serial(fd, scsi_serial, 255);
// scsi_serial[3] is the length of the serial number
// scsi_serial[4] is serial number (raw, NOT null terminated)
if (rc < 0) {
printf("FAIL, rc=%d, errno=%d\n", rc, errno);
} else
if (rc == 1) {
printf("FAIL, rc=%d, drive doesn't report serial number\n", rc);
} else {
if (!scsi_serial[3]) {
printf("Failed to retrieve serial for %s\n", dev);
return -1;
}
printf("Serial Number: %.*s\n", (size_t) scsi_serial[3], (char *) & scsi_serial[4]);
scsi_serial[4+scsi_serial[3]]='\0';
trim(&scsi_serial[4]);
sprintf(outStr,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> \n<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\"> \n<properties>\n<comment/>\n<entry key=\"SerialNo\">%s</entry>\n</properties>\n", (char *) & scsi_serial[4]);
//strcat((char *)argv[2],(char *)"/hdd.xml");
printf("\n%s",outStr);
// printf("\n%s",(char *)argv[2]);
//storeData((char *)argv[1],(char *) outStr);
}
close(fd);
}else{
printf("\nInsufficient no of arguments \n");
}
return (EXIT_SUCCESS);
}

Resources