I am learning Linux file I/O operation functions and trying to write a program that could creat a new file, read or write it as well as change the permissions and attributes of a file. When I try to use function "open(pathname, O_RDWR);" to get a file descriptor and use "read()" function to read current file, there is always "bad address" error. However, when I use "open(pathname, O_RDWR|O_CREATE,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)" before write, the file will be writen currectly.
Here are the source code when I using that two function.
//the welcome function is a function that serve as a function nemu bar
void Create()
{
char filepath[8];
int fd;
printf("请输入要创建的文件所在的目录及其名称:\n");
scanf("%s",&filepath);
fd = open(filepath, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH );//总是以默认的方式创建文件
if(fd == -1)
{
printf("创建失败,请重新输入文件名及其所在目录创建\n");
sleep(5);
Create();
}
else
{
char is;
printf("创建成功!是否对新创建的文件进行写操作?");
scanf("%s",&is);
if(is == 'y'||is == 'Y')
Write(fd,filepath);
else if(is == 'n'||is == 'N')
{
close(fd);
welcome();
}
else
{
printf("您的输入有误,回到欢迎界面中…\n");
close(fd);
sleep(5);
welcome();
}
}
}
void Write(int fd, char *path)
{
volatile int len;
if(fd == -1)
{
len = 0;
char filepath[8];
printf("请输入要写的文件的完整路径及其名称:");
scanf("%s",&filepath);
fd = open(filepath, O_RDWR);
if(fd == -1)
{
perror("open during write");
Write(-1, "");
}
char w1[BUFFERSIZE], *w;
printf("请输入要写入文件的内容,以回车结束:\n");
scanf("%s",&w1);
w = w1;
//用于写入的模块
int n = 1;
while(n > 0)
{
if(n = write(fd, w+len, (strlen(w)-len))<0)
{
perror("write");
return;
}
len += n;
}
char is;
printf("是否查看写入的内容?");
scanf("%s",&is);
if(is == 'Y'||is == 'y')
{
close(fd);
Read(filepath,strlen(w));
}
else
{
close(fd);
welcome();
}
}
else
{
printf("请输入要写入文件的数据:\n");
char w1[BUFFERSIZE];
char *w;
scanf("%s",&w1);
w = w1;
len = 0;
int n = 1;
while(n > 0)
{
n = write(fd, w+len, (strlen(w)-len));
len += n;
}
char is;
printf("是否查看写入的内容?");
scanf("%s",&is);
if(is == 'Y'||is == 'y')
{
close(fd);
Read(path,strlen(w));
}
else
{
close(fd);
welcome();
}
}
};
Could you please help me solve this question? I would be highly appreciated if you could help me!
Related
The tee utility copies its standard input to both stdout and to a file. This allows the user to view the output of a command on the console while writing a log to a file at the same time.
My program implements the tee command from linux POSIX system calls, with the -a option.
How can I modify the program to test for possible memory allocation errors? Positive memory leaks.
Also, the memory allocation doesn't seem right to me. When creating a new buffer each time I call getline(), should I declare and initialize line outside the loop and reallocate it only after the loop has ended?
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "apue.h"
int writeAll(int fd, char *buf, int buflen);
int main(int argc, char *argv[]) {
struct stat status;
int option;
bool append = false;
int errCode = 0;
while ((option = getopt(argc, argv, "a")) != -1) {
switch (option) {
case 'a':
append = true;
break;
}
}
// We need to write in all the files given as parameter AND stdout.
int numFileDescriptors = argc - optind + 1;
int *fileDescriptors = malloc((numFileDescriptors + 1) * sizeof(*fileDescriptors));
char **fileNames = malloc((numFileDescriptors + 1) * sizeof(*fileNames));
int lastFileDescriptor = 0;
fileDescriptors[0] = STDOUT_FILENO;
fileNames[0] = "stdout";
int flags = O_CREAT | O_WRONLY;
if (append) {
flags = flags | O_APPEND;
} else {
flags = flags | O_TRUNC;
}
for (int i = optind; i < argc; i++) {
if (access(argv[i], F_OK) == 0) {
if (access(argv[i], W_OK) < 0) {
err_msg("%s: Permission denied", argv[i]);
errCode = 1;
continue;
}
}
if (lstat(argv[i], &status) < 0) {
status.st_mode = 0;
}
if (S_ISDIR(status.st_mode)) {
err_msg("%s: Is a directory", argv[i]);
errCode = 1;
continue;
}
int fd = open(argv[i], flags, 0644);
if (fd < 0) {
err_msg("%s: Failed to open", argv[i]);
errCode = 1;
continue;
}
lastFileDescriptor = lastFileDescriptor + 1;
fileDescriptors[lastFileDescriptor] = fd;
fileNames[lastFileDescriptor] = argv[i];
}
while (true) {
size_t len = 0;
ssize_t read = 0;
char *line = NULL;
read = getline(&line, &len, stdin);
if (read == -1) {
break;
}
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, strlen(line));
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
}
for (int i = 0; i <= lastFileDescriptor; i++) {
close(fileDescriptors[i]);
}
free(fileDescriptors);
free(fileNames);
return errCode;
}
int writeAll(int fd, char *buf, int buflen) {
ssize_t written = 0;
while (written < buflen) {
int writtenThisTime = write(fd, buf + written, buflen - written);
if (writtenThisTime < 0) {
return writtenThisTime;
}
written = written + writtenThisTime;
}
return written;
}
Testing for memory allocation failure is simple: just add tests, report the failure and exit with a non zero exit status.
To avoid memory leaks, you must free the line that was allocated by getline inside the while (true) loop:
while (true) {
size_t len = 0;
char *line = NULL;
ssize_t nread = getline(&line, &len, stdin);
if (nread == -1) {
if (errno == ENOMEM) {
fprintf(stderr, "out of memory\n");
exit(1);
}
free(line);
break;
}
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, nread);
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
free(line);
}
Alternately, you can reuse the same line for the next iteration and only free the buffer after the while loop:
size_t len = 0;
char *line = NULL;
while (true) {
ssize_t nread = getline(&line, &len, stdin);
if (nread == -1) {
if (errno == ENOMEM) {
fprintf(stderr, "out of memory\n");
exit(1);
}
break;
}
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, nread);
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
}
free(line);
Note that reading a full line at a time is risky as the input might contain very long, possibly unlimited lines (eg: /dev/zero). You might want to use fgets() to read a line with a limited length and dispatch the contents as you read, possibly splitting long lines:
char line[4096];
while (fgets(line, sizeof line, stdin)) {
size_t len = strlen(line);
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, len);
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
}
The above code has a limitation: if the input streams contains null bytes, they will cause some data to be lost in translation. A solution is to not use fgets(), but getchar() directly:
for (;;) {
char line[4096];
size_t len = 0;
int c;
while (len < sizeof(line) && (c = getchar()) != EOF)) {
if ((line[len++] = c) == '\n')
break;
}
if (len > 0) {
for (int i = 0; i <= lastFileDescriptor; i++) {
int written = writeAll(fileDescriptors[i], line, len);
if (written < 0) {
err_msg("%s: Failed to write", fileNames[i]);
errCode = 1;
}
}
}
if (c == EOF)
break;
}
I managed to compile ncat. I am using -k option to keep server open. Instead of accepting data to STDOUT, my goal is to write to files instead. So far I was able to write to a file instead of STDOUT but my goal is to loop through new files on each new connection. Right now it is appending to the same filename_0 and f++ is not incrementing. Here is what I have so far. The original code will be below. The difference is in the else clause, basically if n is actually greater than 0. On each loop, n is 512 bytes until the last chunk. I just want to be able to have new files from each new connection. filename_0, filename_1, filename_3, etc.
MODIFIED CODE:
/* Read from a client socket and write to stdout. Return the number of bytes
read from the socket, or -1 on error. */
int read_socket(int recv_fd)
{
char buf[DEFAULT_TCP_BUF_LEN];
struct fdinfo *fdn;
int nbytes, pending;
int f = 0;
fdn = get_fdinfo(&client_fdlist, recv_fd);
ncat_assert(fdn != NULL);
nbytes = 0;
do {
int n, s;
n = ncat_recv(fdn, buf, 512, &pending);
if (n <= 0) {
if (o.debug)
logdebug("Closing fd %d.\n", recv_fd);
#ifdef HAVE_OPENSSL
if (o.ssl && fdn->ssl) {
if (nbytes == 0)
SSL_shutdown(fdn->ssl);
SSL_free(fdn->ssl);
}
#endif
close(recv_fd);
checked_fd_clr(recv_fd, &master_readfds);
rm_fd(&client_fdlist, recv_fd);
checked_fd_clr(recv_fd, &master_broadcastfds);
rm_fd(&broadcast_fdlist, recv_fd);
conn_inc--;
if (get_conn_count() == 0)
checked_fd_clr(STDIN_FILENO, &master_readfds);
return n;
}
else {
char filename[20];
snprintf(filename, sizeof(char) * 20, "filename_%i", f);
FILE *fp = fopen(filename, "a");
if (fp == NULL)
{
printf("Could not open file");
return 0;
}
//Write(STDOUT_FILENO, buf, n);
s = fwrite(buf, 1, n, fp);
fclose(fp);
f++;
nbytes += n;
}
} while (pending);
return nbytes;
}
ORIGINAL CODE:
int read_socket(int recv_fd)
{
char buf[DEFAULT_TCP_BUF_LEN];
struct fdinfo *fdn;
int nbytes, pending;
fdn = get_fdinfo(&client_fdlist, recv_fd);
ncat_assert(fdn != NULL);
nbytes = 0;
do {
int n;
n = ncat_recv(fdn, buf, sizeof(buf), &pending);
if (n <= 0) {
if (o.debug)
logdebug("Closing fd %d.\n", recv_fd);
#ifdef HAVE_OPENSSL
if (o.ssl && fdn->ssl) {
if (nbytes == 0)
SSL_shutdown(fdn->ssl);
SSL_free(fdn->ssl);
}
#endif
close(recv_fd);
checked_fd_clr(recv_fd, &master_readfds);
rm_fd(&client_fdlist, recv_fd);
checked_fd_clr(recv_fd, &master_broadcastfds);
rm_fd(&broadcast_fdlist, recv_fd);
conn_inc--;
if (get_conn_count() == 0)
checked_fd_clr(STDIN_FILENO, &master_readfds);
return n;
}
else {
Write(STDOUT_FILENO, buf, n);
nbytes += n;
}
} while (pending);
return nbytes;
}
I was able to figure out using the other functions involved. i passed a pointer into this function to write to it. the handler is a function i added the open() file pointer to.
This is supposed to flips upper and lower case letters but its not flipping just adding random characters.
int in = open(argv[1], O_RDONLY);
int out = open(argv[2], O_CREAT | O_WRONLY, 0624);
char buff[65];
buff[64] = '\0';
if(argc < 2){
printf("Not enough arguments");
return 1;
}
else if(argv[1] == 0 || argv[2] == 0){
printf("No file");
return 1;
}
int i = read(in,buff,64);
for (i = 0; buff[i]!='\0'; i++) {
if(buff[i] >= 'a' && buff[i] <= 'z') {
printf("%d", buff[i]-32);
} else if (buff[i] >= 'A' && buff[i] <= 'Z') {
printf("%d", buff[i]+32);
} else {
printf("%d", buff[i]);
}
}
write(out, buff, 64);
close(in);
close(out);
return 0;
}
How do I get it to read the character and flip without extras?
If your input file does not contain a '\0' as last character, your condition buff[i]!='\0' depends on random contents.
Change these lines:
char buff[65];
buff[64] = '\0';
to this line:
char buff[65] = { 0 };
However, read() tells you the number of bytes it read. You can use that value to mark the end:
int n = read(in,buff,64);
for (i = 0; i < n; i++) {
/* ... */
}
write(out, buff, n);
Write a function that reads a line, up to some maximum size; separate the logic of reading the file from other processing,
int readline(int fh, char* buff, int maxsize) {
int rc = read(fh,buff,maxsize);
if( rc < 0 ) {
printf("read error, %d\n",rc);
return rc;
}
return rc;
}
Write a function that writes the converted buffer, separate the logic of writing the file and other processing,
int writeline(int fh, char* buff, int len) {
int wc = write(fh, buff, len);
return wc;
}
Write a function that flips the case; separate the logic from reading and writing the file,
char* flipcase(char* buff, int len) {
if(!buff || len<1) return buff;
char* cp = buff;
for (int ix = 0; ix<len; ix++, cp++ ) {
if( isupper(*cp) { // in [A-Z]
// printf("%d", *cp-32); // not portable
*cp = tolower(*cp); // modify buff[ix]
}
else if( islower(*cp) ) { // in [a-z]
// printf("%d", *cp+32); // not portable
*cp = toupper(*cp); // modify buff[ix]
}
// else {
// unchanged
// }
// printf("%d", *cp);
}
return buff;
}
Build a function that handles each line separately,
# define MAXLINE (256) // named 'constant'
int doline(int fin, int fout) {
char buff[MAXLINE+1] = { 0 };
int rc = readline(fin, buff, MAXLINE);
// check results of readline here
flipcase(buff, rc);
int wc = writeline(fout, buff, rc);
// check results of writeline here
return rc;
}
Here you would handle your (argc, argv) and open your files,
if(argc < 3) {
printf("Not enough arguments");
return 1;
}
if(argv[1] == 0 || argv[2] == 0) {
printf("No file");
return 1;
}
int fin = open(argv[1], O_RDONLY);
if( !fin ) {
printf("open %s failed\n",argv[1]);
return 2;
}
int fout = open(argv[2], O_CREAT | O_WRONLY, 0624);
if( !fout ) {
printf("open %s failed\n",argv[2]);
close(fout);
return 2;
}
int rc = 0;
// process one line
rc = doline(fin,fout);
// or, process every line in file
for( ; rc = doline(fin,fout) >= 0; ) {
}
close(fin);
close(fh);
i wrote a program to answer this question
but when i run the program i get this result
as of the answer of this exercise, i expect that the volume of the f1 be 2MB but when i run the program the result was 1Mb. can someone explain this difference?
the code was compiled by gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04).
int main(int argc, char *argv[]) {
int is_append = 1;
int fd;
int flags;
int number_of_bytes;
char buf = '0';
off_t offset;
mode_t mode;
if (argc < 3 || argv[3] == "help")
{
usage_error();
return -1;
}
if(4 == argc && strcmp("x", argv[3])) {
usage_error();
return -1;
}
if(4 == argc) { // the x argument is specified
is_append = 0;
}
flags = O_CREAT | (O_APPEND & is_append) | O_WRONLY;
mode = S_IWUSR | S_IRUSR;
number_of_bytes = atoi(argv[3]);
if (number_of_bytes <= 0)
{
printf("num-bytes must be grater than 0!\n");
return -1;
}
fd = open(argv[3], flags, mode);
if(-1 == fd) {
printf("error in open.\n");
return -1;
}
for (size_t i = 0; i < number_of_bytes; i++)
{
if(!is_append) {
offset = lseek(fd, 0, SEEK_END);
}
if(-1 == write(fd, &buf, 1)) {
printf("error in write.\n");
return -1;
}
}
return 0;
}
I want to open my door with a RFID Transponder. For this I use a Raspberry Pi and an 125Khz RFID Reader with UART. So now I have written a little C programm, wich sets up the RS232 ('ttyAMA0'). This works all fine, and I can read the Transponder, but it reads some sh**
Here is my Code:
char read_rfid(char* rfid_num)
{
fd_set input_fdset;
ssize_t length;
while(1)
{
FD_ZERO(&input_fdset);
FD_SET(fd,&input_fdset);
if(select(fd+1 ,&input_fdset, NULL,NULL,NULL)==-1)
perror("Terminal select() failed");
if(FD_ISSET(fd,&input_fdset))
{
if((length = read(fd,rfid_num,14)) ==-1)
perror("Terminal: read() failed");
else
{
write(STDOUT_FILENO,rfid_num,length);
return;
}
}
}
}
int setupRS232()
{
struct termios term_attr;
if((fd = open(RFID,O_RDWR)) == -1)
{
perror("Can't open Device");
return(1);
}
if(tcgetattr(fd,&term_attr) != 0)
{
perror("terminal: tcgetattr() failed");
return(1);
}
term_attr.c_cflag = BAUD|CS8|CRTSCTS|CLOCAL|CREAD;
term_attr.c_iflag = 0;
term_attr.c_oflag = 0;
term_attr.c_lflag = 0;
if(tcsetattr(fd,TCSAFLUSH,&term_attr) != 0)
{
perror("terminal: tcsetattr() failed");
return(1);
}
}
int main(int argc, char** argv)
{
MYSQL *mysql = NULL;
char rfid_num[14];
int i;
if(init_mysql(mysql) == 1)
return(1);
if(setupRS232() == 1)
return(1);
puts("Warte auf Transponder...");
read_rfid(rfid_num);
for(i=0;i<20;i++)
{
printf("%x\n",rfid_num[i]);
}
}
PS: Sorry for my bad English
Minimal approach to buffering. You should probably check the contents of the buffer before returning valid (is there a final \n ? )
char read_rfid(char* rfid_num) {
fd_set input_fdset;
ssize_t length;
int done;
for(done=0; done < 14; ) {
FD_ZERO(&input_fdset);
FD_SET(fd,&input_fdset);
if(select(fd+1 ,&input_fdset, NULL,NULL,NULL) == -1) {
if (errno == EAGAIN) continue;
perror("Terminal select() failed");
return -1;
}
if(FD_ISSET(fd,&input_fdset)) {
if((length = read(fd,rfid_num+done,14-done)) == -1) {
if (errno == EAGAIN) continue;
perror("Terminal: read() failed");
return -1;
}
write(STDOUT_FILENO,rfid_num+done,length);
done += length;
}
}
return 0;
}
Note: I don't understand why this function returns char.