0

I need to be able to check in a kernel module whether or not a file descriptor, dentry, or inode falls under a certain path. To do this, I am going to have to write a function that when given a dentry or a file descriptor (not sure which, yet), will return said object's full path name.

What is the way to write a function that returns variable-length strings?

3
  • C strings are zero-terminated. Either the function can return a pointer to a static string, or a string allocated from the heap with malloc, or the caller can pass a pointer to a buffer large enough to hold the largest expected string (and that buffer could be either allocated from the heap, static, or on its stack). Commented Oct 5, 2015 at 13:17
  • @lurker Those count, too. Commented Oct 5, 2015 at 13:20
  • 2
    Possible duplicate of best practice for returning a variable length string in c Commented Oct 5, 2015 at 18:03

5 Answers 5

3

You can try like this:

char *myFunction(void)
{
    char *word;
    word = malloc (sizeof (some_random_length));
    //add some random characters

    return word;
}

You can also refer related thread: best practice for returning a variable length string in c

Sign up to request clarification or add additional context in comments.

3 Comments

As most of the answers here, malloc is mentioned. But as far as I know, malloc is not available for the OP since he develops a Linux kernel module.
@SirDarius I know next to nothing about the Linux kernel, but isn't there actually a "special snowflake malloc" available, called kmalloc?
There are a few specific functions like kmalloc and vmalloc which behave differently according to the intended use. In this case, kmalloc should be sufficient. See priyaranjan-technicalzone.blogspot.fr/2014/02/… for interesting technical details. Depending on the OP's requirements, it's not certain that allocating dynamic memory is the best strategy. Passing an already existing buffer along with maximal size to the function is a pattern that comes to mind.
3

The typical way to do this in C, is not to return anything at all:

void func (char* buf, size_t buf_size, size_t* length);

Where buf is a pointer to the buffer which will hold the string, allocated by the caller. buf_size is the size of that buffer. And length is how much of that buffer that the function used.

You could return a pointer to buf as done by for example strcpy. But this doesn't make much sense, since the same pointer already exists in one of the parameters. It adds nothing but confusion.

(Don't use strcpy, strcat etc functions as some role model for how to write functions. Many C standard library functions have obscure prototypes, because they are so terribly old, from a time when good programming practice wasn't invented, or at least not known by Dennis Ritchie.)

Comments

2

There are two common approaches:

One is to have a fixed size buffer to store the result:

int makeFullPath(char *buffer,size_t max_size,...)
{
    int actual_size = snprintf(buffer,max_size,...);
    return actual_size;
}

Examples of standard functions which use this approach are strncpy() and snprintf(). This approach has the advantage that no dynamic memory allocation is needed, which will give better performance for time-critical functions. The downside is that it puts more responsibility on the caller to be able to determine the largest possible result size in advance or be ready to reallocate if a larger size is necessary.

The second common approach is to calculate how big of a buffer to use and allocate that many bytes internally:

// Caller eventually needs to free() the result.
char* makeFullPath(...)
{
    size_t max_size = calculateFullPathSize(...);
    char *buffer = malloc(max_size);
    if (!buffer) return NULL;
    int actual_size = snprintf(buffer,max_size,...);
    assert(actual_size<max_size);
    return buffer;
}

An example of a standard function that uses this approach is strdup(). The advantage is that the caller no longer needs to worry about the size, but they now need to make sure that they free the result. For a kernel module, you would use kmalloc() and kfree() instead of malloc() and free().

A less common approach is to have a static buffer:

const char *makeFullPath(char *buffer,size_t max_size,...)
{
    static char buffer[MAX_PATH];
    int actual_size = snprintf(buffer,MAX_PATH,...);
    return buffer;
}

This avoids the caller having to worry about the size or freeing the result, and it is also efficient, but it has the downside that the caller now has to make sure that they don't call the function a second time while the result of the first call is still being used.

char *result1 = makeFullPath(...);
char *result2 = makeFullPath(...);

printf("%s",result1);
printf("%s",result2); /* oops! */

Here, the caller probably meant to print two separate strings, but they'll actually just get the second string twice. This is also problematic in multi-threaded code, and probably unusable for kernel code.

2 Comments

Myself, I vote #2. #3 also precludes using in threaded environments.
The static buffer option certainly cannot work in the context of the OP (a kernel module).
0

For example:

char * fn( int file_id )
{
  static char res[MAX_PATH];

  // fill res[]
  return res;
}

6 Comments

Might be useful to note that you've now got an issue with char *a = fn(1), *b = fn(2) ...
@John Ledbetter Ok, you are correct. This was simple example without dynamic allocation. But the answer of question was mostly in definition char * fn() - the rest is bonus :)
Also, this will make the function unsafe for multi-threading.
@i486 Even though I have no particular experience of the subject of kernel programming, it would seem like a wise idea to me to make kernel-level code re-entrant. Because... it is the kernel of a multi-thread os...
@i486: OP is trying to write a kernel module, in which multithreading is always a consideration, so this approach cannot work.
|
0
/*
let do it the BSTR way (BasicString of VB)
*/
char * CopyString(char *str){
    unsigned short len;
    char *buff;
    len=lstrlen(str);
    buff=malloc(sizeof(short)+len+1);
    if(buff){
        ((short*)buff)[0]=len+1;
        buff=&((short*)buff)[1];
        strcpy(buff,str);
    }

    return buff;

}

#define len_of_string(s)    ((short*)s)[-1])
#define free_string(s)        free(&((short*)s)[-1]))

int main(){
    char *buff=CopyString("full_path_name");
    if(buff){
        printf("len of string= %d\n",len_of_string(buff));
        free_string(buff);
    }else{
        printf("Error: malloc failed\n");
    }
    return 0;

}

/*
    now you can imagine how to reallocate the string to a new size
*/

3 Comments

Start of a good idea. Yet 1) char * CopyString(char *str) returns char * but is fundamental different than the type passed in - "full_path_name" with its hidden "pre"-length. Better to use a struct with a length and char s[] fields. 2) Unclear why code uses short, size_t would match the type returned by the standard strlen().
short or even ushort is far enough to hold the size of such strings (path)
short is (2 bytes lengh), size_t is at least 2 bytes lengh (it is almost 4), but it is up to you to choose the right one, i consider unsigned short far enough to hold the size of such strings (path and so on).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.