Как отобразить двойной C указатель на структуру в C#? - PullRequest
1 голос
/ 14 июля 2020

У меня есть следующие C -вызовы к libACL:

extern int acl_get_entry(acl_t acl, int entry_id, acl_entry_t *entry_p);
extern int acl_get_permset(acl_entry_t entry_d, acl_permset_t *permset_p);

И я отследил определения типов до

typedef struct __acl_permset_ext *acl_permset_t;
typedef struct __acl_entry_ext  *acl_entry_t;
typedef struct __acl_ext *acl_t;

в / usr / include / acl / libacl .h и /usr/include/sys/acl.h

Итак, если я не сделал ошибки, это означает, что приведенные выше собственные вызовы эквивалентны:

extern int acl_get_entry(__acl_ext *acl, int entry_id, __acl_entry_ext **entry_p);
extern int acl_get_permset(__acl_ext *entry_d, __acl_permset_ext **permset_p);

Теперь я немного не понимаю, как сопоставить их с C# ... Сначала я подумал, что могу просто сделать это:

// extern int acl_get_entry(acl_t acl, int entry_id, acl_entry_t *entry_p);
[SuppressUnmanagedCodeSecurity]
[DllImport("acl", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "acl_get_entry")]
internal static extern int acl_get_entry(__acl_ext* acl, AclEntryConstants entry_id, ref __acl_entry_ext entry_p); // Double pointer, correct ???

И это даже работает, по крайней мере, очевидно. Но когда я делаю то же самое с acl_get_permset

// extern int acl_get_permset(acl_entry_t entry_d, acl_permset_t *permset_p);
[SuppressUnmanagedCodeSecurity]
[DllImport("acl", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl,
   EntryPoint = "acl_get_permset")]
internal static extern int acl_get_permset(__acl_entry_ext* entry_d, __acl_permset_ext** permset_p); // double pointer ?

aka

internal static extern int acl_get_permset(__acl_entry_ext* entry_d, ref __acl_permset_ext permset_p); // double pointer ?

, тогда это не работает ...

Я написал следующее C -код для проверки:

int main()
{
   // Get all the entries
   acl_entry_t acl_entry_;
   acl_permset_t permission_set;
   acl_tag_t acl_kind_tag;

   const char* _filename = "/root/Desktop/CppSharp.txt";
   acl_t acl_file = acl_get_file(_filename, ACL_TYPE_ACCESS);
   int found = acl_get_entry(acl_file, ACL_FIRST_ENTRY, &acl_entry_);


   int a = acl_get_permset(acl_entry_, &permission_set);
   int b = acl_get_tag_type(acl_entry_, &acl_kind_tag);
   printf("a: %d; b: %d\n", a, b);

   acl_entry new_acl;
   new_acl.reading = ACL_GET_PERM(permission_set, ACL_READ);
   new_acl.writing = ACL_GET_PERM(permission_set, ACL_WRITE);
   new_acl.execution = ACL_GET_PERM(permission_set, ACL_EXECUTE);


   return 0;
}

, который возвращает не -1 значение для a и b. Но мой код C#, который делает то же самое (или я так думал), достигает int found = 1 (как C), но затем возвращает -1 для a и b ...

static unsafe void ReadACL()
{
   string fileName = "/root/Desktop/CppSharp.txt";
  
   global::acl.__acl_ext* acl_file = NativeMethods.acl_get_file(fileName, global::acl.acl_type_t.ACL_TYPE_ACCESS);


   global::acl.__acl_entry_ext acl_entry_ = new global::acl.__acl_entry_ext();
   int found = NativeMethods.acl_get_entry(acl_file, global::acl.AclEntryConstants.ACL_FIRST_ENTRY, ref acl_entry_);
   System.Console.WriteLine(found);


   global::acl.__acl_permset_ext permission_set;
   acl_tag_t acl_kind_tag = acl_tag_t.ACL_UNDEFINED_TAG;
  
  
   int a = NativeMethods.acl_get_permset(&acl_entry_, &permission_set);
   global::acl.acl_tag_t tag_type = acl_tag_t.ACL_UNDEFINED_TAG;
   int b = NativeMethods.acl_get_tag_type(&acl_entry_, &tag_type);

   System.Console.WriteLine($"{a} {b}");

Также, что самое странное - я искал следующие файлы заголовков:

/usr/include/acl/libacl.h 
/usr/include/sys/acl.h

и всю папку /usr/include для __acl_permset_ext и __acl_entry_ext, но мне нужно было их погуглить. , поскольку они нигде не определены ... Использует ли C -компилятор только указатели, вообще не нуждаясь в структурах?

Кроме того, перед тем, как сделать это вручную, я попытался создать привязки автоматически с CppSharp, но эти автоматически сгенерированные привязки имели ту же проблему ...

mono ./CppSharp.CLI.exe --arch=x64 --output=/home/username/RiderProjects/TestProject/TestProject/AUTOMAPPED/  /usr/include/acl/libacl.h /usr/include/sys/acl.h

И еще одна вещь, которую я заметил: какой смысл в передаче двойного указателя? Это похоже на

struct x;
function(ref &x)

, что имеет очень мало смысла ИМХО, поскольку вы передаете адрес по ссылке. Возможно, это массивы? Например,

struct[] x;
function(ref x)

Вот константы:

// #define ACL_UNDEFINED_ID    ((id_t)-1)

// acl_check error codes
public enum acl_check_errors
   : int
{
    ACL_MULTI_ERROR = (0x1000), // multiple unique objects
    ACL_DUPLICATE_ERROR = (0x2000), // duplicate Id's in entries
    ACL_MISS_ERROR = (0x3000), // missing required entry
    ACL_ENTRY_ERROR = (0x4000) // wrong entry type 
}


// 23.2.2 acl_perm_t values
public enum acl_perm_t
   : uint
{
   ACL_READ = (0x04),
   ACL_WRITE = (0x02),
   ACL_EXECUTE = (0x01),
   // ACL_ADD = (0x08),
   // ACL_DELETE = (0x10),
}


// 23.2.5 acl_tag_t values
public enum acl_tag_t
   : int
{
   ACL_UNDEFINED_TAG = (0x00),
   ACL_USER_OBJ = (0x01),
   ACL_USER = (0x02),
   ACL_GROUP_OBJ = (0x04),
   ACL_GROUP = (0x08),
   ACL_MASK = (0x10),
   ACL_OTHER = (0x20)
}

public enum acl_type_t
   : uint
{
   ACL_TYPE_ACCESS = (0x8000),
   ACL_TYPE_DEFAULT = (0x4000)
}   
// 23.2.8 ACL Entry Constants
public enum AclEntryConstants
   : int
{
   ACL_FIRST_ENTRY = 0,
   ACL_NEXT_ENTRY = 1,
}

А вот структуры, которые я искал вместе:

// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/libobj.h
// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/libobj.h
// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/libacl.h
// https://allstar.jhuapl.edu/repo/p1/amd64/acl/libacl.h
// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/libacl.h
// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/acl_get_fd.c


[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct obj_prefix
{
   public ulong p_magic;
   public ulong p_flags;
}

// typedef struct __acl_permset_ext *acl_permset_t;
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __acl_permset_ext
{
   // permset_t      s_perm; // typedef unsigned int permset_t;
   public uint s_perm;
};

// typedef struct acl_permset_obj_tag acl_permset_obj;
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct acl_permset_obj_tag
{
   public obj_prefix o_prefix;
   public __acl_permset_ext i;
};

// #define __U32_TYPE     unsigned int
// #define __ID_T_TYPE    __U32_TYPE
// __STD_TYPE __ID_T_TYPE __id_t;     /* General type for IDs.  */
// typedef __id_t id_t;

/* qualifier object */
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __qualifier_ext
{
   //id_t                    q_id;
   public uint q_id;
}

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct qualifier_obj_tag
{
   public obj_prefix o_prefix;
   public __qualifier_ext i;
}


[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct acl_entry_obj_tag
{
   public obj_prefix o_prefix;
   public __acl_entry_ext i;
}


// typedef struct __acl_ext    *acl_t;
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __acl_ext
{
   // typedef struct acl_entry_obj_tag acl_entry_obj;
   // acl_entry_obj      *a_prev, *a_next;
   // acl_entry_obj      *a_curr;
   // acl_entry_obj      *a_prealloc, *a_prealloc_end;

   public acl_entry_obj_tag* a_prev;
   public acl_entry_obj_tag* a_next;
   public acl_entry_obj_tag* a_curr;
   public acl_entry_obj_tag* a_prealloc;

   public acl_entry_obj_tag* a_prealloc_end;

   // size_t a_used; // typedef __SIZE_TYPE__ size_t;
   public ulong a_used;
}


[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct acl_obj_tag
{
   public obj_prefix o_prefix;
   public __acl_ext i;
}


[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __acl_entry
{
   acl_tag_t e_tag;

   // qualifier_obj      e_id; // typedef struct qualifier_obj_tag qualifier_obj;
   qualifier_obj_tag e_id;

   // acl_permset_obj    e_perm;  //typedef struct acl_permset_obj_tag acl_permset_obj;
   acl_permset_obj_tag e_perm;
}


// typedef struct __acl_entry_ext  *acl_entry_t;
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __acl_entry_ext
{
   // acl_entry_obj      *e_prev, *e_next; // typedef struct acl_entry_obj_tag acl_entry_obj;
   public acl_entry_obj_tag* e_prev;

   public acl_entry_obj_tag* e_next;

   // acl_obj       *e_container; // typedef struct acl_obj_tag acl_obj;
   public acl_obj_tag* e_container;
   public __acl_entry e_entry;
}

1 Ответ

1 голос
/ 14 июля 2020

Если посмотреть здесь , эти типы ACL определены как:

struct __acl_ext;
struct __acl_entry_ext;
struct __acl_permset_ext;

typedef struct __acl_ext    *acl_t;
typedef struct __acl_entry_ext  *acl_entry_t;
typedef struct __acl_permset_ext *acl_permset_t;

Нам сказали, что структура __acl_ext существует, но мы не видим как это определено: мы не знаем, какие поля у него есть. Очевидно, что это правильно определено в другом (частном) заголовке или исходном файле, но мы не видим их: они частные.

На первый взгляд это кажется проблемой: как мы можем использовать эти структуры, если мы не знаем, насколько они велики или как расположены их поля? Посмотрите дальше, и вы увидите, что мы всегда взаимодействуем только с указателями на эти структуры: функции ACL вернут нам указатель, который мы затем можем передать другим функциям ACL. Мы никогда не ожидаем, что сами разыменуем указатели. Код ACL, конечно, знает, на что указывают указатели, но это скрыто от нас.

Это называется непрозрачный указатель .

(Это может быть полезной стратегией. Например, он позволяет библиотеке ACL изменять способ определения структуры, не нарушая потребителей. Он также не дает нам напрямую изменять поля в этих структурах, что может нарушить работу библиотеки ACL). Нам вообще не следует пытаться определять типы C# для этих структур: мы не должны этого делать. Тип C# для непрозрачного указателя - IntPtr, поэтому давайте воспользуемся этим:

// extern int acl_get_entry(acl_t acl, int entry_id, acl_entry_t *entry_p);
[SuppressUnmanagedCodeSecurity]
[DllImport("acl", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acl_get_entry")]
internal static extern int acl_get_entry(IntPtr acl, AclEntryConstants entry_id, out IntPtr entry_p);

// extern int acl_get_permset(acl_entry_t entry_d, acl_permset_t *permset_p);
[SuppressUnmanagedCodeSecurity]
[DllImport("acl", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acl_get_permset")]
internal static extern int acl_get_permset(IntPtr entry_d, out IntPtr permset_p);

Мы могли бы использовать ref или out для IntPtr. Читая документацию, похоже, что код C никогда не считывает значение переданного вами двойного указателя: он просто использует его как способ передачи указателя обратно. Поэтому мы используем out.

...