0

Having this:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>

int main (void) {

    int fd = open("./sometext.txt", O_RDONLY);
    struct stat sb;

    if(fstat(fd,&sb)) perror("in function fstat"); 

    printf("file size is %ld bytes\n\n",sb.st_size);
    char* file_p = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);

    printf("printing file as an array of chars \n\n");
    for(int i =0; i<sb.st_size ; i++){
        if(file_p[i]=='a') //cannot do this
            file_p[i]='5'; //string/char is read-only, but then how?
        printf("%c",file_p[i]);
    }

    munmap(file_p, sb.st_size);
    close(fd);

    return 0;
}

As I found from other questions, to change string literal, which is read-only by defaul, I have to either have array storage (char arr[] = "sometext_to_change_in_loop") or make another pointer, malloc need space and then copy the first address of the string that pointer from malloc. But how to change the string "in-line" without doing either of those?

EDIT: Yes, the main issue was I did not bitwise ORed the int prot argument in mmap call. However, how it possible that only that is sufficient?

1) - I do not change int flags in open call, so why does it work when opened with O_RDONLY and not with O_RDWD which makes the same file writeable as well (which I am doing by file_p[i] = '5': writting).

2)How can be actually changes saved when in mmap I have in argument int flags MAP_PRIVATE, but I want to save changes, so I should have MAP_SHARED? according to this tutorial : enter link description here where both - the open flag and the mmap flag were changed as I write. I need to explain this as well.

EDIT2: from the tutorial. It is indeed needed in case of different writing to the file:

for(int i =0; i<sb.st_size ; i++){
        file_p[i] = toupper(file_p[i]);
        printf("%c",file_p[i]);
    }

This will REQUIRED to have set open flag to O_RDWR and mmap flag to MAP_SHARED. But WHY? Why is one change to file file_p[i]='5' different in another change file_p[i]=toupper(file_p[i])? Why does not one change requires to set the two flags (the first case), but the other does require it (second case)? It is confusing for me now.

autistic456
  • 183
  • 1
  • 10
  • You can map a `O_RDONLY` file descriptor with `PROT_READ | PROT_WRITE` when using `MAP_PRIVATE` – Ajay Brahmakshatriya May 27 '20 at 17:08
  • String literals are read-only, **period**. Undefined behavior arises from any attempt to modify one. You can make modifiable copies, but subsequently modifying those is not changing a string *literal*, it is just changing a string. That's what both of the alternatives presented at the end of the question do. If you want a modifiable string then it must *not* be a literal, and it must reside either in an array of (non-`const`) `char` or in dynamically-allocated space. – John Bollinger May 27 '20 at 17:21
  • Or in otherwise-obtained *writable* space, such as writably `mmap`ped space, in the event that you have such options available to you. – John Bollinger May 27 '20 at 17:28
  • @JohnBollinger look at the edit. The flags are not explained – autistic456 May 27 '20 at 21:49

2 Answers2

1

Try changing

char* file_p = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);

to

char* file_p = mmap(0, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
0

You seem to ask two completely different questions, one about mmap() and one about string literals. Taking the latter first:

to change string literal, which is read-only by defaul, I have to either have array storage (char arr[] = "sometext_to_change_in_loop") or make another pointer, malloc need space and then copy the first address of the string that pointer from malloc. But how to change the string "in-line" without doing either of those?

Echoing my comments on the question, there is no defined way to modify a string literal. Any attempt to do so produces undefined behavior. Many strings are not literals, however, and if they have elements of modifiable type and are stored in writable storage then they can be modified via various string and I/O functions and by ordinary accesses to the arrays in which they reside. In particular, the contents of a string literal can be copied into such a space, and the resulting separate string then modified.


As for the mmap() question(s), the overarching one seems to be how to map a file so that the contents can be modified via the mapping. That is already addressed by your other answer, which observes that the allowed accesses to the mapping are controlled by the bitmask passed as mmap()'s third argument, as described by POSIX. There are separate bits for read, write, and execute access to the contents of the mapping.

You go on to ask for clarifications:

1) - I do not change int flags in open call, so why does it work when opened with O_RDONLY and not with O_RDWD [sic] which makes the same file writeable as well

I can understand how that observation might come as a surprise. The mapping established by mmap() does not access the underlying object via the provided file descriptor, so the descriptor's mode is irrelevant. The file descriptor serves only as (partial) identification of the data to be mapped. Permissions for access via the mapping are defined by the arguments to mmap() alone, and the mmap() call will fail if the process does not have sufficient privilege on the mapped object to access it in the requested modes.

How can be actually changes saved when in mmap I have in argument int flags MAP_PRIVATE, but I want to save changes, so I should have MAP_SHARED?

You are conflating two different things. The access modes already discussed determine whether you can write to the mapping. MAP_PRIVATE vs. MAP_SHARED controls whether other processes can see such writes, either in their own copy of the mapping or in the underlying file. POSIX expresses this as being about the disposition of writes to the mapping. With MAP_SHARED, they modify the underlying object. With MAP_PRIVATE, they do not modify the underlying object, and they are visible only to the process that performed the write.

It's not entirely clear what you are asking about in the second edit, but I think it's about writing to the underlying object via the file descriptor and trying to observe the changes via the mapping. Naturally, the file descriptor needs to be open in a mode that permits writing if you want to use it to write to the file. Furthermore, however, to see the changes via an already-established memory mapping, that mapping should be configured as a shared one.

As POSIX puts it: "It is unspecified whether modifications to the underlying object done after the MAP_PRIVATE mapping is established are visible through the MAP_PRIVATE mapping." This is pretty much the flip side of the already-discussed semantics of MAP_PRIVATE: not only are writes to the mapping not reflected in the underlying object, but modifications to the underlying object may not be visible through the mapping, either. Remember, again, that he mapping established by mmap() does not access the underlying object via the provided file descriptor.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • `Any attempt to do so produces undefined behavior` - But I can do by changing array index: `string foo[] = "bla"; foo[0]='z'` (changing literal), Or does it mean something different? – autistic456 May 28 '20 at 17:10
  • 2) `and the mmap() call will fail if the process does not have sufficient privilege on the mapped object to access it in the requested modes`, then why it did **NOT** failed in the first case? In the first case -> ` file_p[i]='5'; ` It did not have any problem to be file open with `O_RDONLY` as well as mapped `PROT_READ | PROT_WRITE`. You firstly mention the the file mode is irrelevant, but then you say it could fail otherwise. And indeed in first case it is irrelevant, but second it will file if not `O_RDWR`. You still did not explain the difference on these two cases (examples) – autistic456 May 28 '20 at 18:48
  • So I am still confused on relation between permission on `fd object` and memory mapped protection, please explain it on the 2 EXAMPLES (cases), where I can see it the best – autistic456 May 28 '20 at 18:49
  • I have observed, The `O_RDONLY` can be only used with `MAP_PRIVATE`, fail otherwise – autistic456 May 28 '20 at 18:54
  • Ok so as I understand it now, is that, as long as the mapped memory is MAP_PRIVATE, about the file permissions does not care anyone (because we are in private space, so whatever we do, we do for ourselves). BUT *once* we want to be MAP_SHARED (to e.g. output the changes), then mapped region **DO** takes care about file permission to which mapping is made. So the biggest role in permission and coexisting with other processes/files has 3 argument (mapping), and not the 2 argument (protection), which only reflects, how will be the region mapped. Do I understand it right? – autistic456 May 28 '20 at 19:04
  • @autistic456, `char *foo[] = "bla";` declares an ordinary character array whose initial contents are the same as those of string literal `"bla"`. Array `foo` is an independent array object, and is not a string literal, so `foo[0] = 'z'` does not express a modification of a string literal. – John Bollinger May 28 '20 at 21:27
  • @autistic456, you have missed a crucial distinction. It is the mode in which the file descriptor was opened that is irrelevant to `mmap()`, because, I repeat: the mapping does not access the underlying object via the file descriptor. But the object itself has access control properties -- file permissions, for example -- and it is *these* that may prevent `mmap()` from mapping the object with a particular set of access modes enabled. – John Bollinger May 28 '20 at 21:31
  • @autistic456, "biggest" is a somewhat subjective evaluation in this case. If you map a file with `MAP_PRIVATE`, then no other process will be able to observe any writes you make to the mapping, and the will not affect the underlying object. But the same is trivially true if you map without `PROT_WRITE`, because then you *won't* successfully write to the mapping. Additionally, if you map with `MAP_SHARED` then you can expect the mapping to reflect modifications to the underlying object, by the same or other processes, but whether that will be the case for private mappings is unspecified. – John Bollinger May 28 '20 at 21:40
  • well I would need an example (or a link to one somewhere), because you are surely right, but since you are explaining general rule, It is hardly understood as I would need an example of your statements (I am not that experienced to image a real situation - I do not use `mmap` that much). So if you wrote some with the mmap, and provide a link for me (github or whatever), I would be glad. Thanks anyway – autistic456 May 28 '20 at 23:06